From 9bd33a9de56dd7e010aee8caa48a97ea8067140a Mon Sep 17 00:00:00 2001 From: emizzle Date: Wed, 27 Feb 2019 13:13:49 +1100 Subject: [PATCH] feature(@embark/console): Suggestions improvements **Cache suggestions** Suggestions are cached for better performance on each request. When contracts are deployed, the suggestion cache is cleared, allowing for updated contract methods to be built in to the suggestions. ** Handle edge cases ** Handle cases when console command is parsed as an empty string, or possibly returns an empty object, which would cause `Object.getOwnPropertyNames` to throw an error (that is ultimately swallowed). --- .../src/lib/modules/console/suggestions.json | 59 +++++++++++++++++++ .../src/lib/modules/console/suggestions.ts | 58 ++++++++++-------- 2 files changed, 94 insertions(+), 23 deletions(-) create mode 100644 packages/embark/src/lib/modules/console/suggestions.json diff --git a/packages/embark/src/lib/modules/console/suggestions.json b/packages/embark/src/lib/modules/console/suggestions.json new file mode 100644 index 000000000..79f37c7d9 --- /dev/null +++ b/packages/embark/src/lib/modules/console/suggestions.json @@ -0,0 +1,59 @@ +{ + "suggestions": [ + { + "value": "web3.eth", + "command_type": "web3 object", + "description": "module for interacting with the Ethereum network" + }, + { + "value": "web3.net", + "command_type": "web3 object", + "description": "module for interacting with network properties" + }, + { + "value": "web3.shh", + "command_type": "web3 object", + "description": "module for interacting with the whisper protocol" + }, + { + "value": "web3.bzz", + "command_type": "web3 object", + "description": "module for interacting with the swarm network" + }, + { + "value": "web3.eth.getAccounts()", + "command_type": "web3 object", + "description": "get list of accounts" + }, + { + "value": "help", + "command_type": "embark command", + "description": "displays quick list of some of the available embark commands" + }, + { + "value": "versions", + "command_type": "embark command", + "description": "display versions in use for libraries and tools like web3 and solc" + }, + { + "value": "ipfs", + "command_type": "javascript object", + "description": "instantiated js-ipfs object configured to the current environment (available if ipfs is enabled)" + }, + { + "value": "swarm", + "command_type": "javascript object", + "description": "instantiated swarm-api object configured to the current environment (available if swarm is enabled)" + }, + { + "value": "web3", + "command_type": "javascript object", + "description": "instantiated web3.js object configured to the current environment" + }, + { + "value": "EmbarkJS", + "command_type": "javascript object", + "description": "EmbarkJS static functions for Storage, Messages, Names, etc." + } + ] +} \ No newline at end of file diff --git a/packages/embark/src/lib/modules/console/suggestions.ts b/packages/embark/src/lib/modules/console/suggestions.ts index b9317e762..46a61e8f4 100644 --- a/packages/embark/src/lib/modules/console/suggestions.ts +++ b/packages/embark/src/lib/modules/console/suggestions.ts @@ -18,6 +18,8 @@ export default class Suggestions { private embark: Embark; private events: Events; private contracts: ContractsManager; + private static readonly DEFAULT_SUGGESTIONS = require("./suggestions.json").suggestions; + private _suggestions: SuggestionsList = []; constructor(embark: Embark, options?: object) { this.embark = embark; @@ -28,6 +30,30 @@ export default class Suggestions { this.listenToEvents(); } + private get suggestions(): SuggestionsList { + if (this._suggestions.length) { + return this._suggestions; + } + + this._suggestions = [...Suggestions.DEFAULT_SUGGESTIONS]; + + Object.values(this.contracts).forEach((contract: any) => { + this._suggestions.push({value: contract.className, command_type: "web3 object", description: "contract deployed at " + contract.deployedAddress}); + this._suggestions.push({value: "profile " + contract.className, command_type: "embark command", description: "profile " + contract.className + " contract"}); + }); + + const plugins = this.embark.config.plugins.getPluginsProperty("console", "console"); + for (const plugin of plugins) { + this._suggestions.push({ + command_type: "plugin command", + description: plugin.description, + value: (JSON.stringify(plugin.usage) || "").replace(/\"/g, "").trim() || this.formatMatches(plugin.matches), + }); + } + + return this._suggestions; + } + private registerApi() { this.embark.registerAPICall("post", "/embark-api/suggestions", (req: any, res: any) => { const cmd: string = req.body.command || ""; @@ -40,6 +66,9 @@ export default class Suggestions { private listenToEvents() { this.events.on("deploy:contract:deployed", (contract: any) => { this.contracts[contract.className] = contract; + + // reset backing variable so contracts suggestions can be re-built for next request + this._suggestions = []; }); } @@ -64,29 +93,8 @@ export default class Suggestions { public getSuggestions(cmd: string, cb: (results: SuggestionsList) => any) { if (cmd === "") { return cb([]); } - const suggestions: SuggestionsList = []; - suggestions.push({value: "web3.eth", command_type: "web3 object", description: "module for interacting with the Ethereum network"}); - suggestions.push({value: "web3.net", command_type: "web3 object", description: "module for interacting with network properties"}); - suggestions.push({value: "web3.shh", command_type: "web3 object", description: "module for interacting with the whisper protocol"}); - suggestions.push({value: "web3.bzz", command_type: "web3 object", description: "module for interacting with the swarm network"}); - suggestions.push({value: "web3.eth.getAccounts()", command_type: "web3 object", description: "get list of accounts"}); - - Object.values(this.contracts).forEach((contract: any) => { - suggestions.push({value: contract.className, command_type: "web3 object", description: "contract deployed at " + contract.deployedAddress}); - suggestions.push({value: "profile " + contract.className, command_type: "embark command", description: "profile " + contract.className + " contract"}); - }); - - const plugins = this.embark.config.plugins.getPluginsProperty("console", "console"); - for (const plugin of plugins) { - suggestions.push({value: plugin.usage || this.formatMatches(plugin.matches), command_type: "plugin command", description: plugin.description}); - } - suggestions.push({value: "help", command_type: "embark command", description: "displays quick list of some of the available embark commands"}); - suggestions.push({value: "versions", command_type: "embark command", description: "display versions in use for libraries and tools like web3 and solc"}); - suggestions.push({value: "ipfs", command_type: "javascript object", description: "instantiated js-ipfs object configured to the current environment (available if ipfs is enabled)"}); - suggestions.push({value: "swarm", command_type: "javascript object", description: "instantiated swarm-api object configured to the current environment (available if swarm is enabled)"}); - suggestions.push({value: "web3", command_type: "javascript object", description: "instantiated web3.js object configured to the current environment"}); - suggestions.push({value: "EmbarkJS", command_type: "javascript object", description: "EmbarkJS static functions for Storage, Messages, Names, etc."}); + const suggestions = this.suggestions; if (cmd.indexOf(".") <= 0) { return cb(this.searchSuggestions(cmd.toLowerCase(), suggestions)); @@ -95,7 +103,11 @@ export default class Suggestions { try { const toRemove: string = "." + cmd.split(".").reverse()[0]; const cmdToSearch: string = cmd.replace((new RegExp(toRemove + "$")), ""); - return this.events.request("runcode:eval", "Object.getOwnPropertyNames(" + cmdToSearch + ")", (err: any, result: any) => { + + if (!cmdToSearch) { + return cb(this.searchSuggestions(cmd, suggestions)); + } + return this.events.request("runcode:eval", `${cmdToSearch} && Object.getOwnPropertyNames(${cmdToSearch})`, (err: any, result: any) => { try { if (Array.isArray(result)) { result.forEach((match: string) => {