diff --git a/apps/remix-ide-e2e/src/tests/compiler_api.test.ts b/apps/remix-ide-e2e/src/tests/compiler_api.test.ts
index 0b6cf00023f..acb51fbf84c 100644
--- a/apps/remix-ide-e2e/src/tests/compiler_api.test.ts
+++ b/apps/remix-ide-e2e/src/tests/compiler_api.test.ts
@@ -23,7 +23,7 @@ module.exports = {
.addFile('test_jsCompile.js', { content: jsCompile })
.executeScript('remix.exeCurrent()')
.pause(5000)
- .journalChildIncludes(`version: '0.6.8+commit.0bbfe453'`)
+ .journalChildIncludes(`"languageversion": "0.6.8+commit.0bbfe453"`)
},
'Should update the compiler configuration with "setCompilerConfig" API': function (browser: NightwatchBrowser) {
diff --git a/apps/remix-ide-e2e/src/tests/terminal.test.ts b/apps/remix-ide-e2e/src/tests/terminal.test.ts
index 363b0538c99..0ea14271115 100644
--- a/apps/remix-ide-e2e/src/tests/terminal.test.ts
+++ b/apps/remix-ide-e2e/src/tests/terminal.test.ts
@@ -35,24 +35,11 @@ module.exports = {
browser
.waitForElementVisible('*[data-id="terminalCli"]')
.executeScript('remix.help()')
- .journalChildIncludes('remix.call(message: {name, key, payload})')
- .journalChildIncludes('remix.getFile(path)')
- .journalChildIncludes('remix.debug(hash)')
.journalChildIncludes('remix.loadgist(id)')
.journalChildIncludes('remix.loadurl(url)')
- .journalChildIncludes('remix.setproviderurl(url)')
.journalChildIncludes('remix.execute(filepath)')
.journalChildIncludes('remix.exeCurrent()')
.journalChildIncludes('remix.help()')
- .journalChildIncludes('remix.debugHelp()')
- },
-
- 'Should execute remix.debugHelp() command': function (browser: NightwatchBrowser) {
- browser
- .waitForElementVisible('*[data-id="terminalCli"]')
- .executeScript('remix.debugHelp()')
- .journalChildIncludes('Here are some examples of scripts that can be run (using remix.exeCurrent() or directly from the console)')
- .journalChildIncludes('Please see https://www.npmjs.com/package/remix-debug for more informations')
},
'Async/Await Script': function (browser: NightwatchBrowser) {
diff --git a/apps/remix-ide/src/app/files/file-explorer.js b/apps/remix-ide/src/app/files/file-explorer.js
index e829d42dd95..1b6d89797ea 100644
--- a/apps/remix-ide/src/app/files/file-explorer.js
+++ b/apps/remix-ide/src/app/files/file-explorer.js
@@ -16,7 +16,7 @@ const globalRegistry = require('../../global/registry')
const queryParams = new QueryParams()
let MENU_HANDLE
-function fileExplorer (localRegistry, files, menuItems) {
+function fileExplorer (localRegistry, files, menuItems, plugin) {
var self = this
this.events = new EventManager()
// file provider backend
@@ -292,6 +292,14 @@ function fileExplorer (localRegistry, files, menuItems) {
() => {}
)
}
+ if (key.endsWith('.js')) {
+ actions['Run'] = async () => {
+ provider.get(key, (error, content) => {
+ if (error) return console.log(error)
+ plugin.call('scriptRunner', 'execute', content)
+ })
+ }
+ }
}
MENU_HANDLE = contextMenu(event, actions)
})
diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js
index 89279816200..32d8a1e303f 100644
--- a/apps/remix-ide/src/app/panels/file-panel.js
+++ b/apps/remix-ide/src/app/panels/file-panel.js
@@ -54,7 +54,7 @@ module.exports = class Filepanel extends ViewPlugin {
}
function createProvider (key, menuItems) {
- return new FileExplorer(self._components.registry, self._deps.fileProviders[key], menuItems)
+ return new FileExplorer(self._components.registry, self._deps.fileProviders[key], menuItems, self)
}
var fileExplorer = createProvider('browser', ['createNewFile', 'publishToGist', canUpload ? 'uploadFile' : ''])
diff --git a/apps/remix-ide/src/app/panels/terminal.js b/apps/remix-ide/src/app/panels/terminal.js
index 0bce0577c37..672133986c8 100644
--- a/apps/remix-ide/src/app/panels/terminal.js
+++ b/apps/remix-ide/src/app/panels/terminal.js
@@ -5,12 +5,9 @@ import * as packageJson from '../../../../../package.json'
var yo = require('yo-yo')
var javascriptserialize = require('javascript-serialize')
var jsbeautify = require('js-beautify')
-var ethers = require('ethers')
var type = require('component-type')
var vm = require('vm')
var EventManager = require('../../lib/events')
-var Web3 = require('web3')
-var swarmgw = require('swarmgw')()
var CommandInterpreterAPI = require('../../lib/cmdInterpreterAPI')
var AutoCompletePopup = require('../ui/auto-complete-popup')
@@ -77,7 +74,6 @@ class Terminal extends Plugin {
self.registerCommand('error', self._blocksRenderer('error'), { activate: true })
self.registerCommand('script', function execute (args, scopedCommands, append) {
var script = String(args[0])
- scopedCommands.log(`> ${script}`)
self._shell(script, scopedCommands, function (error, output) {
if (error) scopedCommands.error(error)
else if (output) scopedCommands.log(output)
@@ -91,9 +87,6 @@ class Terminal extends Plugin {
self.registerFilter('error', basicFilter)
self.registerFilter('script', basicFilter)
- self._jsSandboxContext = {}
- self._jsSandboxRegistered = {}
-
if (opts.shell) self._shell = opts.shell // ???
register(self)
}
@@ -451,20 +444,26 @@ class Terminal extends Plugin {
var intro = yo`
- Welcome to Remix ${packageJson.version} -
-
You can use this terminal for:
+
You can use this terminal to:
- - Checking transactions details and start debugging.
- - Running JavaScript scripts. The following libraries are accessible:
+
- Check transactions details and start debugging.
+ - Execute JavaScript scripts:
+
+ - Input a script directly in the command line interface
+
+ - Select a Javascript file in the file explorer and then run \`remix.execute()\` or \`remix.exeCurrent()\` in the command line interface
+
+ - Right click on a JavaScript file in the file explorer and then click \`Run\`
+ The following libraries are accessible:
- - Executing common command to interact with the Remix interface (see list of commands above). Note that these commands can also be included and run from a JavaScript script.
- - Use exports/.register(key, obj)/.remove(key)/.clear() to register and reuse object across script executions.
+
`
@@ -481,6 +480,21 @@ class Terminal extends Plugin {
return self._view.el
+ function wrapScript (script) {
+ if (script.startsWith('remix.')) return script
+ return `
+ try {
+ const ret = ${script};
+ if (ret instanceof Promise) {
+ ret.then((result) => { console.log(result) }).catch((error) => { console.log(error) })
+ } else {
+ console.log(ret)
+ }
+ } catch (e) {
+ console.log(e.message)
+ }
+ `
+ }
function change (event) {
if (self._components.autoCompletePopup.handleAutoComplete(
event,
@@ -500,7 +514,7 @@ class Terminal extends Plugin {
self._view.input.innerText = '\n'
if (script.length) {
self._cmdHistory.unshift(script)
- self.commands.script(script)
+ self.commands.script(wrapScript(script))
}
self._components.autoCompletePopup.removeAutoComplete()
}
@@ -624,14 +638,17 @@ class Terminal extends Plugin {
error: 'text-danger' }[mode] // defaults
if (mode) {
+ const filterUndefined = (el) => el !== undefined && el !== null
return function logger (args, scopedCommands, append) {
- var types = args.map(type)
- var values = javascriptserialize.apply(null, args).map(function (val, idx) {
+ var types = args.filter(filterUndefined).map(type)
+ var values = javascriptserialize.apply(null, args.filter(filterUndefined)).map(function (val, idx) {
if (typeof args[idx] === 'string') val = args[idx]
if (types[idx] === 'element') val = jsbeautify.html(val)
return val
})
- append(yo`${values}`)
+ if (values.length) {
+ append(yo`${values}`)
+ }
}
} else {
throw new Error('mode is not supported')
@@ -707,9 +724,8 @@ class Terminal extends Plugin {
// for all the other case, we use the Code Executor plugin
var context = domTerminalFeatures(self, scopedCommands, self.blockchain)
try {
- var cmds = vm.createContext(Object.assign(self._jsSandboxContext, context, self._jsSandboxRegistered))
+ var cmds = vm.createContext(context)
var result = vm.runInContext(script, cmds)
- self._jsSandboxContext = Object.assign(cmds, context)
return done(null, result)
} catch (error) {
return done(error.message)
@@ -726,29 +742,7 @@ class Terminal extends Plugin {
function domTerminalFeatures (self, scopedCommands, blockchain) {
return {
- swarmgw,
- ethers,
- remix: self._components.cmdInterpreter,
- web3: new Web3(blockchain.web3().currentProvider),
- console: {
- log: function () { scopedCommands.log.apply(scopedCommands, arguments) },
- info: function () { scopedCommands.info.apply(scopedCommands, arguments) },
- warn: function () { scopedCommands.warn.apply(scopedCommands, arguments) },
- error: function () { scopedCommands.error.apply(scopedCommands, arguments) }
- },
- setTimeout: (fn, time) => {
- return setTimeout(() => { self._shell('(' + fn.toString() + ')()', scopedCommands, () => {}) }, time)
- },
- setInterval: (fn, time) => {
- return setInterval(() => { self._shell('(' + fn.toString() + ')()', scopedCommands, () => {}) }, time)
- },
- clearTimeout: clearTimeout,
- clearInterval: clearInterval,
- exports: {
- register: (key, obj) => { self._jsSandboxRegistered[key] = obj },
- remove: (key) => { delete self._jsSandboxRegistered[key] },
- clear: () => { self._jsSandboxRegistered = {} }
- }
+ remix: self._components.cmdInterpreter
}
}
diff --git a/apps/remix-ide/src/app/ui/auto-complete-popup.js b/apps/remix-ide/src/app/ui/auto-complete-popup.js
index 92445bc30cc..1bb1ec3386b 100644
--- a/apps/remix-ide/src/app/ui/auto-complete-popup.js
+++ b/apps/remix-ide/src/app/ui/auto-complete-popup.js
@@ -192,7 +192,7 @@ class AutoCompletePopup {
this.opts.appManager.event.on('activate', async (profile) => {
if (!profile.methods) return
profile.methods.forEach((method) => {
- const key = `remix.call({name: '${profile.name}', key:'${method}', payload: []}).then((result) => { console.log(result) }).catch((error) => { console.log(error) })`
+ const key = `remix.call('${profile.name}', '${method}')`
const keyValue = {}
keyValue[key] = `call ${profile.name} - ${method}`
if (this.extraCommands.includes(keyValue)) return
diff --git a/apps/remix-ide/src/lib/cmdInterpreterAPI.js b/apps/remix-ide/src/lib/cmdInterpreterAPI.js
index 8f609c279a3..b9ed96649bb 100644
--- a/apps/remix-ide/src/lib/cmdInterpreterAPI.js
+++ b/apps/remix-ide/src/lib/cmdInterpreterAPI.js
@@ -1,16 +1,12 @@
'use strict'
var yo = require('yo-yo')
var async = require('async')
-var remixDebug = require('@remix-project/remix-debug')
var EventManager = require('../lib/events')
var CompilerImport = require('../app/compiler/compiler-imports')
var toolTip = require('../app/ui/tooltip')
var globalRegistry = require('../global/registry')
var SourceHighlighter = require('../app/editor/sourceHighlighter')
-var RemixDebug = require('@remix-project/remix-debug').EthDebugger
-var TreeView = require('../app/ui/TreeView') // TODO setup a direct reference to the UI components
-var solidityTypeFormatter = require('../app/tabs/debugger/debuggerUI/vmDebugger/utils/SolidityTypeFormatter')
var GistHandler = require('./gist-handler')
class CmdInterpreterAPI {
@@ -31,112 +27,14 @@ class CmdInterpreterAPI {
offsetToLineColumnConverter: self._components.registry.get('offsettolinecolumnconverter').api
}
self.commandHelp = {
- 'remix.call(message: {name, key, payload})': 'Call a registered plugins',
- 'remix.getFile(path)': 'Returns the content of the file located at the given path',
- 'remix.setFile(path, content)': 'set the content of the file located at the given path',
- 'remix.debug(hash)': 'Start debugging a transaction.',
'remix.loadgist(id)': 'Load a gist in the file explorer.',
'remix.loadurl(url)': 'Load the given url in the file explorer. The url can be of type github, swarm, ipfs or raw http',
- 'remix.setproviderurl(url)': 'Change the current provider to Web3 provider and set the url endpoint.',
'remix.execute(filepath)': 'Run the script specified by file path. If filepath is empty, script currently displayed in the editor is executed.',
'remix.exeCurrent()': 'Run the script currently displayed in the editor',
'remix.help()': 'Display this help message',
- 'remix.debugHelp()': 'Display help message for debugging'
}
}
- call (message) {
- return this._components.terminal.externalApi.request(message)
- }
log () { arguments[0] != null ? this._components.terminal.commands.html(arguments[0]) : this._components.terminal.commands.html(arguments[1]) }
- highlight (rawLocation) {
- var self = this
- if (!rawLocation) {
- self._components.sourceHighlighter.currentSourceLocation(null)
- return
- }
- var lineColumnPos = self._deps.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file,
- self._deps.compilersArtefacts['__last'].getSourceCode().sources,
- self._deps.compilersArtefacts['__last'].getAsts())
- self._components.sourceHighlighter.currentSourceLocation(lineColumnPos, rawLocation)
- }
- debug (hash, cb) {
- var self = this
- delete self.d
- self.blockchain.web3().eth.getTransaction(hash, (error, tx) => {
- if (error) return cb(error)
- var debugSession = new RemixDebug({
- compilationResult: () => {
- return self._deps.compilersArtefacts['__last'].getData()
- }
- })
- debugSession.addProvider('web3', self.blockchain.web3())
- debugSession.switchProvider('web3')
- debugSession.debug(tx)
- self.d = debugSession
- this._components.terminal.commands.log('A new debugging session is available at remix.d')
- if (cb) cb(null, debugSession)
- // helpers
- self.d.highlight = (address, vmtraceIndex) => {
- if (!address) return self.highlight()
- self.d.sourceLocationFromVMTraceIndex(address, vmtraceIndex, (error, rawLocation) => {
- if (!error && rawLocation) {
- self.highlight(rawLocation)
- }
- })
- }
- self.d.stateAt = (vmTraceIndex) => {
- self.d.extractStateAt(vmTraceIndex, (error, state) => {
- if (error) return self.log(error)
- self.d.decodeStateAt(vmTraceIndex, state, (error, state) => {
- if (error) return this._components.terminal.commands.html(error)
- var treeView = new TreeView({
- json: true,
- formatSelf: solidityTypeFormatter.formatSelf,
- extractData: solidityTypeFormatter.extractData
- })
- self.log('State at ' + vmTraceIndex)
- self._components.terminal.commands.html(treeView.render(state, true))
- })
- })
- }
- self.d.localsAt = (contractAddress, vmTraceIndex) => {
- debugSession.sourceLocationFromVMTraceIndex(contractAddress, vmTraceIndex, (error, location) => {
- if (error) return self.log(error)
- debugSession.decodeLocalsAt(23, location, (error, locals) => {
- if (error) return this._components.terminal.commands.html(error)
- var treeView = new TreeView({
- json: true,
- formatSelf: solidityTypeFormatter.formatSelf,
- extractData: solidityTypeFormatter.extractData
- })
- self.log('Locals at ' + vmTraceIndex)
- self._components.terminal.commands.html(treeView.render(locals, true))
- })
- })
- }
- self.d.goTo = (row) => {
- if (self._deps.editor.current()) {
- var breakPoint = new remixDebug.BreakpointManager(self.d, (sourceLocation) => {
- return self._deps.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file,
- self._deps.compilersArtefacts['__last'].getSourceCode().sources,
- self._deps.compilersArtefacts['__last'].getAsts())
- })
- breakPoint.event.register('breakpointHit', (sourceLocation, currentStep) => {
- self.log(null, 'step index ' + currentStep)
- self.highlight(sourceLocation)
- self.d.stateAt(currentStep)
- self.d.traceManager.getCurrentCalledAddressAt(currentStep, (error, address) => {
- if (error) return self.log(address)
- self.d.localsAt(address, currentStep)
- })
- })
- breakPoint.event.register('NoBreakpointHit', () => { self.log('line ' + row + ' is not part of the current execution') })
- breakPoint.add({fileName: self._deps.editor.current(), row: row - 1})
- breakPoint.jumpNextBreakpoint(0, true)
- }
- }
- })
- }
loadgist (id, cb) {
const self = this
self._components.gistHandler.loadFromGist({gist: id}, this._deps.fileManager)
@@ -179,36 +77,9 @@ class CmdInterpreterAPI {
}
})
}
- setproviderurl (url, cb) {
- this.blockchain.setProviderFromEndpoint(url, 'web3', (error) => {
- if (error) toolTip(error)
- if (cb) cb()
- })
- }
exeCurrent (cb) {
return this.execute(undefined, cb)
}
- getFile (path, cb) {
- var provider = this._deps.fileManager.fileProviderOf(path)
- if (provider) {
- provider.get(path, cb)
- } else {
- cb('file not found')
- }
- }
- setFile (path, content, cb) {
- cb = cb || function () {}
- var provider = this._deps.fileManager.fileProviderOf(path)
- if (provider) {
- provider.set(path, content, (error) => {
- if (error) return cb(error)
- this._deps.fileManager.syncEditor(path)
- cb()
- })
- } else {
- cb('file not found')
- }
- }
execute (file, cb) {
const self = this
@@ -257,37 +128,6 @@ class CmdInterpreterAPI {
if (cb) cb()
return ''
}
- debugHelp (cb) {
- const self = this
- var help = yo`Here are some examples of scripts that can be run (using remix.exeCurrent() or directly from the console)
`
- help.appendChild(yo`
`)
- help.appendChild(yo`
`)
- help.appendChild(yo`remix.debug('0x3c247ac268afb9a9c183feb9d4e83df51efbc8a2f4624c740789b788dac43029', function (error, debugSession) {
- remix.log = function () { arguments[0] != null ? console.log(arguments[0]) : console.log(arguments[1]) }
-
- remix.d.traceManager.getLength(remix.log)
- remix.storageView = remix.d.storageViewAt(97, '0x692a70d2e424a56d2c6c27aa97d1a86395877b3a')
- console.log('storage at 97 :')
- remix.storageView.storageRange(remix.log)
-})
`)
- help.appendChild(yo`remix.log = function () { arguments[0] != null ? console.log(arguments[0]) : console.log(arguments[1]) }
- remix.d.extractStateAt(2, function (error, state) {
- remix.d.decodeStateAt(97, state, remix.log)
- })
`)
- help.appendChild(yo`
`)
- help.appendChild(yo`remix.highlight(contractAddress, vmTraceIndex)
`)
- help.appendChild(yo`
`)
- help.appendChild(yo`remix.goTo(row) (this log the index in the vm trace, state and local variables)
`)
- help.appendChild(yo`
`)
- help.appendChild(yo`remix.stateAt(vmTraceIndex)
`)
- help.appendChild(yo`
`)
- help.appendChild(yo`remix.localsAt(vmTraceIndex)
`)
- help.appendChild(yo`
`)
- help.appendChild(yo``)
- self._components.terminal.commands.html(help)
- if (cb) cb()
- return ''
- }
}
module.exports = CmdInterpreterAPI
diff --git a/apps/remix-ide/src/lib/commands.js b/apps/remix-ide/src/lib/commands.js
index fe579ee0374..b20783557b6 100644
--- a/apps/remix-ide/src/lib/commands.js
+++ b/apps/remix-ide/src/lib/commands.js
@@ -6,16 +6,11 @@ const allPrograms = [
]
const allCommands = [
- {'remix.debug(hash)': 'Start debugging a transaction.'},
- {'remix.debugHelp()': 'Display help message for debugging'},
{'remix.execute(filepath)': 'Run the script specified by file path. If filepath is empty, script currently displayed in the editor is executed.'},
{'remix.exeCurrent()': 'Run the script currently displayed in the editor.'},
- {'remix.getFile(path)': 'Returns the content of the file located at the given path'},
{'remix.help()': 'Display this help message.'},
{'remix.loadgist(id)': 'Load a gist in the file explorer.'},
{'remix.loadurl(url)': 'Load the given url in the file explorer. The url can be of type github, swarm or ipfs.'},
- {'remix.setFile(path, content)': 'set the content of the file located at the given path'},
- {'remix.setproviderurl(url)': 'Change the current provider to Web3 provider and set the url endpoint.'},
{'swarmgw.get(url, cb)': 'Download files from Swarm via https://swarm-gateways.net/'},
{'swarmgw.put(content, cb)': 'Upload files to Swarm via https://swarm-gateways.net/'},
@@ -30,8 +25,7 @@ const allCommands = [
{'ethers.utils.RLP': 'This encoding method is used internally for several aspects of Ethereum, such as encoding transactions and determining contract addresses.'},
{'ethers.Wallet': 'A wallet manages a private/public key pair which is used to cryptographically sign transactions and prove ownership on the Ethereum network.'},
{'ethers.version': 'Contains the version of the ethers container object.'},
-
- {'web3.bzz': 'Bzz module for interacting with the swarm network.'},
+
{'web3.eth': 'Eth module for interacting with the Ethereum network.'},
{'web3.eth.accounts': 'The web3.eth.accounts contains functions to generate Ethereum accounts and sign transactions and data.'},
{'web3.eth.abi': 'The web3.eth.abi functions let you de- and encode parameters to ABI (Application Binary Interface) for function calls to the EVM (Ethereum Virtual Machine).'},
@@ -49,7 +43,12 @@ const allCommands = [
{'web3.eth.clearSubscriptions();': 'Resets subscriptions.'},
{'web3.eth.Contract(jsonInterface[, address][, options])': 'The web3.eth.Contract object makes it easy to interact with smart contracts on the ethereum blockchain.'},
- {'web3.eth.accounts.create([entropy]);': 'The web3.eth.accounts contains functions to generate Ethereum accounts and sign transactions and data.'}
+ {'web3.eth.accounts.create([entropy]);': 'The web3.eth.accounts contains functions to generate Ethereum accounts and sign transactions and data.'},
+ {'web3.eth.getAccounts();': 'Retrieve the list of accounts'},
+ {'web3.eth.accounts.privateKeyToAccount(privateKey [, ignoreLength ]);': 'Get the account from the private key'},
+ {'web3.eth.accounts.signTransaction(tx, privateKey [, callback]);': 'Sign Transaction'},
+ {'web3.eth.accounts.recoverTransaction(rawTransaction);': 'Sign Transaction'},
+ {'web3.eth.accounts.hashMessage(message);': 'Hash message'},
]
module.exports = {