diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js
index 2347737a20..518d93c64c 100644
--- a/apps/remix-ide/src/app.js
+++ b/apps/remix-ide/src/app.js
@@ -288,6 +288,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
// -------------------Terminal----------------------------------------
+ // move this up for *** level so as to access it in the terminal
+ makeUdapp(blockchain, compilersArtefacts, (domEl) => terminal.logHtml(domEl))
const terminal = new Terminal(
{ appManager, blockchain },
{
@@ -300,10 +302,11 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
return height - newpos
}
},
- { config: registry.get('config').api }
+ { config: registry.get('config').api },
+ registry
)
- makeUdapp(blockchain, compilersArtefacts, (domEl) => terminal.logHtml(domEl))
+ // previous *** level for makeUdapp method
const contextualListener = new ContextualListener({ editor })
engine.register([
diff --git a/apps/remix-ide/src/app/panels/terminal.js b/apps/remix-ide/src/app/panels/terminal.js
index 9facd299ef..fb3bc4a130 100644
--- a/apps/remix-ide/src/app/panels/terminal.js
+++ b/apps/remix-ide/src/app/panels/terminal.js
@@ -18,6 +18,11 @@ var AutoCompletePopup = require('../ui/auto-complete-popup')
var css = require('./styles/terminal-styles')
+import { CompilerImports } from '@remix-project/core-plugin' // eslint-disable-line
+var globalRegistry = require('../../global/registry')
+var SourceHighlighter = require('../../app/editor/sourceHighlighter')
+var GistHandler = require('../../lib/gist-handler')
+
var KONSOLES = []
function register (api) { KONSOLES.push(api) }
@@ -32,12 +37,32 @@ const profile = {
}
class Terminal extends Plugin {
- constructor (opts, api, config) {
+ constructor (opts, api, config, registry) {
super(profile)
+ this.fileImport = new CompilerImports()
+ this.gistHandler = new GistHandler()
+ this.event = new EventManager()
+ this.registry = registry
+ this.globalRegistry = globalRegistry
this.element = document.createElement('div')
this.element.setAttribute('class', 'panel_2A0YE0')
this.element.setAttribute('id', 'terminal-view')
- this.event = new EventManager()
+ this.eventsDecoder = this.globalRegistry.get('eventsDecoder').api
+ this.txListener = this.globalRegistry.get('txlistener').api
+ this.sourceHighlighter = new SourceHighlighter()
+ this._deps = {
+ fileManager: this.registry.get('filemanager').api,
+ editor: this.registry.get('editor').api,
+ compilersArtefacts: this.registry.get('compilersartefacts').api,
+ offsetToLineColumnConverter: this.registry.get('offsettolinecolumnconverter').api
+ }
+ this.commandHelp = {
+ '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.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'
+ }
this.blockchain = opts.blockchain
this.vm = vm
this._api = api
@@ -54,14 +79,14 @@ class Terminal extends Plugin {
this._components = {}
this._components.cmdInterpreter = new CommandInterpreterAPI(this, null, this.blockchain)
this._components.autoCompletePopup = new AutoCompletePopup(this._opts)
- this._components.autoCompletePopup.event.register('handleSelect', function (input) {
- const textList = this._view.input.innerText.split(' ')
- textList.pop()
- textList.push(input)
- this._view.input.innerText = textList
- this._view.input.focus()
- this.putCursor2End(this._view.input)
- })
+ // this._components.autoCompletePopup.event.register('handleSelect', function (input) {
+ // const textList = this._view.input.innerText.split(' ')
+ // textList.pop()
+ // textList.push(input)
+ // this._view.input.innerText = textList
+ // this._view.input.focus()
+ // this.putCursor2End(this._view.input)
+ // })
this._commands = {}
this.commands = {}
this._JOURNAL = []
@@ -86,31 +111,10 @@ class Terminal extends Plugin {
this.off('scriptRunner', 'error')
}
- log (message) {
- var command = this.commands[message.type]
- if (typeof command === 'function') {
- if (typeof message.value === 'string' && message.type === 'html') {
- var el = document.createElement('div')
- el.innerHTML = remixBleach.sanitize(message.value, {
- list: [
- 'a',
- 'b',
- 'p',
- 'em',
- 'strong',
- 'div',
- 'span',
- 'ul',
- 'li',
- 'ol',
- 'hr'
- ]
- })
- message.value = el
- }
- command(message.value)
- };
- }
+ // logHtml (html) {
+ // var command = this.commands.html
+ // if (typeof command === 'function') command(html)
+ // }
logHtml (html) {
var command = this.commands.html
@@ -128,7 +132,7 @@ class Terminal extends Plugin {
autoCompletePopupEvent = {this._components.autoCompletePopup.event}
blockchain = {this.blockchain}
api = {this._api}
- options = {this.opts}
+ options = {this._opts}
data = {this.data}
cmdInterpreter = {this._components.cmdInterpreter}
autoCompletePopup = {this._components.autoCompletePopup}
@@ -138,170 +142,285 @@ class Terminal extends Plugin {
config = {this.config}
thisState = {this}
vm = {this.vm}
+ blockchain = {this.blockchain}
+ commandHelp = {this.commandHelp}
+ event = {this.event}
+ _deps = {this._deps}
+ fileImport = {this.fileImport}
+ sourceHighlighter = {this.sourceHighlighter}
+ gistHandler ={this.gistHandler}
+ registry = {this.registry}
+ commands = {this.commands}
+ txListener = {this.txListener}
+ eventsDecoder = {this.eventsDecoder}
/>,
this.element
)
}
- _appendItem (item) {
- var self = this
- var { el, gidx } = item
- self._JOURNAL[gidx] = item
- if (!self._jobs.length) {
- requestAnimationFrame(function updateTerminal () {
- self._jobs.forEach(el => self._view.journal.appendChild(el))
- self.scroll2bottom()
- self._jobs = []
- })
- }
- if (self.data.activeFilters.commands[item.cmd]) self._jobs.push(el)
- }
+ // _appendItem (item) {
+ // var self = this
+ // var { el, gidx } = item
+ // self._JOURNAL[gidx] = item
+ // if (!self._jobs.length) {
+ // requestAnimationFrame(function updateTerminal () {
+ // self._jobs.forEach(el => self._view.journal.appendChild(el))
+ // self.scroll2bottom()
+ // self._jobs = []
+ // })
+ // }
+ // if (self.data.activeFilters.commands[item.cmd]) self._jobs.push(el)
+ // }
scroll2bottom () {
var self = this
setTimeout(function () {
- self._view.term.scrollTop = self._view.term.scrollHeight
+ // self._view.term.scrollTop = self._view.term.scrollHeight
}, 0)
}
- _blocksRenderer (mode) {
- if (mode === 'html') {
- return function logger (args, scopedCommands, append) {
- if (args.length) append(args[0])
- }
- }
- mode = {
- log: 'text-info',
- info: 'text-info',
- warn: 'text-warning',
- error: 'text-danger'
- }[mode] // defaults
-
- if (mode) {
- const filterUndefined = (el) => el !== undefined && el !== null
- return function logger (args, scopedCommands, append) {
- var types = args.filter(filterUndefined).map(type)
- var values = javascriptserialize.apply(null, args.filter(filterUndefined)).map(function (val, idx) {
- if (typeof args[idx] === 'string') {
- const el = document.createElement('div')
- el.innerHTML = args[idx].replace(/(\r\n|\n|\r)/gm, '
')
- val = el.children.length === 0 ? el.firstChild : el
- }
- if (types[idx] === 'element') val = jsbeautify.html(val)
- return val
- })
- if (values.length) {
- append(yo`${values}`)
- }
- }
- } else {
- throw new Error('mode is not supported')
- }
- }
+ // _blocksRenderer (mode) {
+ // if (mode === 'html') {
+ // return function logger (args, scopedCommands, append) {
+ // if (args.length) append(args[0])
+ // }
+ // }
+ // mode = {
+ // log: 'text-info',
+ // info: 'text-info',
+ // warn: 'text-warning',
+ // error: 'text-danger'
+ // }[mode] // defaults
- _scopeCommands (append) {
- var self = this
- var scopedCommands = {}
- Object.keys(self.commands).forEach(function makeScopedCommand (cmd) {
- var command = self._commands[cmd]
- scopedCommands[cmd] = function _command () {
- var args = [...arguments]
- command(args, scopedCommands, el => append(cmd, args, blockify(el)))
- }
- })
- return scopedCommands
- }
+ // if (mode) {
+ // const filterUndefined = (el) => el !== undefined && el !== null
+ // return function logger (args, scopedCommands, append) {
+ // var types = args.filter(filterUndefined).map(type)
+ // var values = javascriptserialize.apply(null, args.filter(filterUndefined)).map(function (val, idx) {
+ // if (typeof args[idx] === 'string') {
+ // const el = document.createElement('div')
+ // el.innerHTML = args[idx].replace(/(\r\n|\n|\r)/gm, '
')
+ // val = el.children.length === 0 ? el.firstChild : el
+ // }
+ // if (types[idx] === 'element') val = jsbeautify.html(val)
+ // return val
+ // })
+ // if (values.length) {
+ // append(yo`${values}`)
+ // }
+ // }
+ // } else {
+ // throw new Error('mode is not supported')
+ // }
+ // }
- registerFilter (commandName, filterFn) {
- this.data.filterFns[commandName] = filterFn
- }
+ // _scopeCommands (append) {
+ // var self = this
+ // var scopedCommands = {}
+ // Object.keys(self.commands).forEach(function makeScopedCommand (cmd) {
+ // var command = self._commands[cmd]
+ // scopedCommands[cmd] = function _command () {
+ // var args = [...arguments]
+ // command(args, scopedCommands, el => append(cmd, args, blockify(el)))
+ // }
+ // })
+ // return scopedCommands
+ // }
- registerCommand (name, command, opts) {
- var self = this
- name = String(name)
- if (self._commands[name]) throw new Error(`command "${name}" exists already`)
- if (typeof command !== 'function') throw new Error(`invalid command: ${command}`)
- self._commands[name] = command
- console.log({ command })
- console.log(self._commands)
- self._INDEX.commands[name] = []
- self._INDEX.commandsMain[name] = []
- self.commands[name] = function _command () {
- var args = [...arguments]
- var steps = []
- var root = { steps, cmd: name }
- var ITEM = { root, cmd: name }
- root.gidx = self._INDEX.allMain.push(ITEM) - 1
- root.idx = self._INDEX.commandsMain[name].push(ITEM) - 1
- function append (cmd, params, el) {
- var item
- if (cmd) { // subcommand
- item = { el, cmd, root }
- } else { // command
- item = ITEM
- item.el = el
- cmd = name
- }
- item.gidx = self._INDEX.all.push(item) - 1
- item.idx = self._INDEX.commands[cmd].push(item) - 1
- item.step = steps.push(item) - 1
- item.args = params
- self._appendItem(item)
- }
- var scopedCommands = self._scopeCommands(append)
- command(args, scopedCommands, el => append(null, args, blockify(el)))
- }
- var help = typeof command.help === 'string' ? command.help : [
- '// no help available for:',
- `terminal.commands.${name}(...)`
- ].join('\n')
- self.commands[name].toString = _ => { return help }
- self.commands[name].help = help
- self.data.activeFilters.commands[name] = opts && opts.activate
- if (opts.filterFn) {
- self.registerFilter(name, opts.filterFn)
- }
- return self.commands[name]
- }
+ // registerFilter (commandName, filterFn) {
+ // this.data.filterFns[commandName] = filterFn
+ // }
- async _shell (script, scopedCommands, done) { // default shell
- if (script.indexOf('remix:') === 0) {
- return done(null, 'This type of command has been deprecated and is not functionning anymore. Please run remix.help() to list available commands.')
- }
- var self = this
- if (script.indexOf('remix.') === 0) {
- // we keep the old feature. This will basically only be called when the command is querying the "remix" object.
- // for all the other case, we use the Code Executor plugin
- var context = domTerminalFeatures(scopedCommands, self.blockchain)
- try {
- var cmds = vm.createContext(context)
- var result = vm.runInContext(script, cmds)
- return done(null, result)
- } catch (error) {
- return done(error.message)
- }
- }
- try {
- let result
- if (script.trim().startsWith('git')) {
- // result = await this.call('git', 'execute', script)
- } else {
- result = await this.call('scriptRunner', 'execute', script)
- }
- if (result) self.commands.html(yo`
${result}
`)
- done()
- } catch (error) {
- done(error.message || error)
- }
- }
-}
+ // registerCommand (name, command, opts) {
+ // var self = this
+ // name = String(name)
+ // if (this._commands[name]) throw new Error(`command "${name}" exists already`)
+ // if (typeof command !== 'function') throw new Error(`invalid command: ${command}`)
+ // this._commands[name] = command
+ // console.log({ command })
+ // console.log(self._commands)
+ // this._INDEX.commands[name] = []
+ // this._INDEX.commandsMain[name] = []
+ // this.commands[name] = function _command () {
+ // var args = [...arguments]
+ // var steps = []
+ // var root = { steps, cmd: name }
+ // var ITEM = { root, cmd: name }
+ // root.gidx = self._INDEX.allMain.push(ITEM) - 1
+ // root.idx = self._INDEX.commandsMain[name].push(ITEM) - 1
+ // function append (cmd, params, el) {
+ // var item
+ // if (cmd) { // subcommand
+ // item = { el, cmd, root }
+ // } else { // command
+ // item = ITEM
+ // item.el = el
+ // cmd = name
+ // }
+ // item.gidx = self._INDEX.all.push(item) - 1
+ // item.idx = self._INDEX.commands[cmd].push(item) - 1
+ // item.step = steps.push(item) - 1
+ // item.args = params
+ // // self._appendItem(item)
+ // }
+ // var scopedCommands = self._scopeCommands(append)
+ // command(args, scopedCommands, el => append(null, args, blockify(el)))
+ // }
+ // var help = typeof command.help === 'string' ? command.help : [
+ // '// no help available for:',
+ // `terminal.commands.${name}(...)`
+ // ].join('\n')
+ // this.commands[name].toString = _ => { return help }
+ // this.commands[name].help = help
+ // this.data.activeFilters.commands[name] = opts && opts.activate
+ // if (opts.filterFn) {
+ // this.registerFilter(name, opts.filterFn)
+ // }
+ // return this.commands[name]
+ // }
-function domTerminalFeatures (scopedCommands, blockchain) {
- return {
- remix: this._components.cmdInterpreter
- }
-}
+ // async _shell (script, scopedCommands, done) { // default shell
+ // if (script.indexOf('remix:') === 0) {
+ // return done(null, 'This type of command has been deprecated and is not functionning anymore. Please run remix.help() to list available commands.')
+ // }
+ // var self = this
+ // if (script.indexOf('remix.') === 0) {
+ // // we keep the old feature. This will basically only be called when the command is querying the "remix" object.
+ // // for all the other case, we use the Code Executor plugin
+ // var context = domTerminalFeatures(scopedCommands, self.blockchain)
+ // try {
+ // var cmds = vm.createContext(context)
+ // var result = vm.runInContext(script, cmds)
+ // return done(null, result)
+ // } catch (error) {
+ // return done(error.message)
+ // }
+ // }
+ // try {
+ // let result
+ // if (script.trim().startsWith('git')) {
+ // // result = await this.call('git', 'execute', script)
+ // } else {
+ // result = await this.call('scriptRunner', 'execute', script)
+ // }
+ // if (result) self.commands.html(yo`${result}
`)
+ // done()
+ // } catch (error) {
+ // done(error.message || error)
+ // }
+ // }
+ // }
+
+ // function domTerminalFeatures (scopedCommands, blockchain) {
+ // return {
+ // remix: {
+ // blockchain: this.blockchain,
+ // commandHelp: this.commandHelp,
+ // event: this.event,
+ // _deps: this._deps
+ // }
+ // }
+ // }
+
+ // function loadgist (id, cb) {
+ // const self = this
+ // self._components.gistHandler.loadFromGist({ gist: id }, this._deps.fileManager)
+ // if (cb) cb()
+ // }
+
+ // function loadurl (url, cb) {
+ // const self = this
+ // self._components.fileImport.import(url,
+ // (loadingMsg) => { toolTip(loadingMsg) },
+ // (err, content, cleanUrl, type, url) => {
+ // if (err) {
+ // toolTip(`Unable to load ${url}: ${err}`)
+ // if (cb) cb(err)
+ // } else {
+ // self._deps.fileManager.writeFile(type + '/' + cleanUrl, content)
+ // try {
+ // content = JSON.parse(content)
+ // async.eachOfSeries(content.sources, (value, file, callbackSource) => {
+ // var url = value.urls[0] // @TODO retrieve all other contents ?
+ // self._components.fileImport.import(url,
+ // (loadingMsg) => { toolTip(loadingMsg) },
+ // async (error, content, cleanUrl, type, url) => {
+ // if (error) {
+ // toolTip(`Cannot retrieve the content of ${url}: ${error}`)
+ // return callbackSource(`Cannot retrieve the content of ${url}: ${error}`)
+ // } else {
+ // try {
+ // await self._deps.fileManager.writeFile(type + '/' + cleanUrl, content)
+ // callbackSource()
+ // } catch (e) {
+ // callbackSource(e.message)
+ // }
+ // }
+ // })
+ // }, (error) => {
+ // if (cb) cb(error)
+ // })
+ // } catch (e) {}
+ // if (cb) cb()
+ // }
+ // })
+ // }
+ // function exeCurrent (cb) {
+ // return this.execute(undefined, cb)
+ // }
+
+ // function execute (file, cb) {
+ // const self = this
+
+ // function _execute (content, cb) {
+ // if (!content) {
+ // toolTip('no content to execute')
+ // if (cb) cb()
+ // return
+ // }
+
+ // self._components.terminal.commands.script(content)
+ // }
+
+ // if (typeof file === 'undefined') {
+ // var content = self._deps.editor.currentContent()
+ // _execute(content, cb)
+ // return
+ // }
+
+ // var provider = self._deps.fileManager.fileProviderOf(file)
+
+ // if (!provider) {
+ // toolTip(`provider for path ${file} not found`)
+ // if (cb) cb()
+ // return
+ // }
+
+ // provider.get(file, (error, content) => {
+ // if (error) {
+ // // toolTip(error)
+ // // TODO: pop up
+ // if (cb) cb()
+ // return
+ // }
+
+ // _execute(content, cb)
+ // })
+ // }
+
+// function help (cb) {
+// const self = this
+// var help = yo``
+// for (var k in self.commandHelp) {
+// help.appendChild(yo`${k}: ${self.commandHelp[k]}
`)
+// help.appendChild(yo`
`)
+// }
+// self._components.terminal.commands.html(help)
+// if (cb) cb()
+// return ''
+// }
+}
function blockify (el) { return yo`${el}
` }
module.exports = Terminal
diff --git a/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts b/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts
index 38c58f3555..d032daafc5 100644
--- a/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts
+++ b/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts
@@ -70,8 +70,9 @@ export const registerCommandAction = (name, command, activate, dispatch) => {
if (activate.filterFn) {
registerFilter(name, activate.filterFn)
}
- dispatch({ type: name, payload: { commands: commands, _commands: _commands, data: data } })
-
+ if (name !== ('knownTransaction' || 'unkownTransaction' || 'emptyBlock')) {
+ dispatch({ type: name, payload: { commands: commands, _commands: _commands, data: data } })
+ }
const blockify = (el) => {
return `${el}
`
}
@@ -89,7 +90,6 @@ export const registerCommandAction = (name, command, activate, dispatch) => {
console.log({ scopedCommands })
return scopedCommands
}
-
}
export const filterFnAction = (name, filterFn, dispatch) => {
@@ -133,3 +133,103 @@ export const registerErrorScriptRunnerAction = (event, commandName, commandFn, d
export const registerRemixWelcomeTextAction = (welcomeText, dispatch) => {
dispatch({ type: 'welcomeText', payload: { welcomeText } })
}
+
+export const listenOnNetworkAction = async (props, isListening) => {
+ props.event.trigger('listenOnNetWork', [isListening])
+}
+
+export const initListeningOnNetwork = (props, dispatch) => {
+ props.txListener.event.register('newBlock', (block) => {
+ if (!block.transactions || (block.transactions && !block.transactions.length)) {
+ dispatch({ type: 'emptyBlock', payload: { message: 0 } })
+ console.log({ block }, ' david')
+ // registerCommandAction('emptyBlock', (args, cmds, append) => {
+ // const data = args[0]
+ // console.log({ data }, ' useEffect props')
+ // // // var el = renderEmptyBlock(this, data)
+ // // // append(el)
+ // }, { activate: true }, dispatch)
+ } else {
+ registerCommandAction('knownTransaction', function (args, cmds, append) {
+ var data = args[0]
+ console.log({ data })
+ // let el
+ // if (data.tx.isCall) {
+ // console.log({ data })
+ // // el = renderCall(this, data)
+ // } else {
+ // // el = renderKnownTransaction(this, data, blockchain)
+ // }
+ // this.seen[data.tx.hash] = el
+ // append(el)
+ }, { activate: true }, dispatch)
+ }
+ })
+ props.txListener.event.register('newCall', (tx) => {
+ console.log('new call action')
+ // log(this, tx, null)
+ })
+ props.txListener.event.register('newTransaction', (tx, receipt) => {
+ log(props, tx, receipt, dispatch)
+ registerCommandAction('knownTransaction', function (args, cmds, append) {
+ var data = args[0]
+ console.log({ data })
+ // let el
+ // if (data.tx.isCall) {
+ // console.log({ data })
+ // // el = renderCall(this, data)
+ // } else {
+ // // el = renderKnownTransaction(this, data, blockchain)
+ // }
+ // this.seen[data.tx.hash] = el
+ // append(el)
+ }, { activate: true }, dispatch)
+ // const result = Object.assign([], tx)
+ // console.log({ result })
+ // scriptRunnerDispatch({ type: 'knownTransaction', payload: { message: result } })
+ })
+
+ const log = async (props, tx, receipt, dispatch) => {
+ const resolvedTransaction = await props.txListener.resolvedTransaction(tx.hash)
+ if (resolvedTransaction) {
+ var compiledContracts = null
+ if (props._deps.compilersArtefacts.__last) {
+ compiledContracts = await props._deps.compilersArtefacts.__last.getContracts()
+ }
+ console.log({ compiledContracts })
+ await props.eventsDecoder.parseLogs(tx, resolvedTransaction.contractName, compiledContracts, (error, logs) => {
+ if (!error) {
+ console.log({ tx: tx, receipt: receipt, resolvedData: resolvedTransaction, logs: logs })
+ console.log('knownTransaction')
+ // logKnownTX({ tx: tx, receipt: receipt, resolvedData: resolvedTransaction, logs: logs })
+ registerCommandAction('knownTransaction', function (args, cmds, append) {
+ var data = args[0]
+ console.log({ data }, 'knownTransaction')
+ // let el
+ // if (data.tx.isCall) {
+ // console.log({ data })
+ // // el = renderCall(this, data)
+ // } else {
+ // // el = renderKnownTransaction(this, data, blockchain)
+ // }
+ // this.seen[data.tx.hash] = el
+ // append(el)
+ }, { activate: true }, dispatch)
+ }
+ })
+ } else {
+ console.log('unknownTransaction')
+ // contract unknown - just displaying raw tx.
+ // logUnknownTX({ tx: tx, receipt: receipt })
+ await dispatch({ type: 'unknownTransaction', payload: { message: [{ tx: tx, receipt: receipt }] } })
+ }
+ }
+
+ props.txListener.event.register('debuggingRequested', async (hash) => {
+ // TODO should probably be in the run module
+ console.log({ hash }, 'register Call')
+ if (!await props.options.appManager.isActive('debugger')) await props.options.appManager.activatePlugin('debugger')
+ props.thisState.call('menuicons', 'select', 'debugger')
+ props.thisState.call('debugger', 'debug', hash)
+ })
+}
diff --git a/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts b/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
index 18d8a0a0d8..7b9043f6c5 100644
--- a/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
+++ b/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
@@ -67,6 +67,17 @@ export const registerCommandReducer = (state, action) => {
commands: Object.assign(initialState.commands, action.payload.commands),
data: Object.assign(initialState.data, action.payload.data)
}
+ case 'clearconsole':
+ return {
+ ...state,
+ ...state.journalBlocks.splice(0)
+ }
+ case 'listenOnNetWork':
+ console.log({ action: action.payload })
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-info' })
+ }
default :
return { state }
}
@@ -75,14 +86,12 @@ export const registerCommandReducer = (state, action) => {
export const registerFilterReducer = (state, action) => {
switch (action.type) {
case 'log':
- console.log({ action }, { state }, 'register Filter')
return {
...state,
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
}
case 'info':
- console.log({ action }, 'registerFilter')
return {
...state,
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
@@ -132,7 +141,8 @@ export const remixWelcomeTextReducer = (state, action) => {
}
export const registerScriptRunnerReducer = (state, action) => {
- console.log({ state }, { action }, 'register script runner reducer')
+ const result = Object.assign([], action.payload.message)
+ console.log({ result })
switch (action.type) {
case 'log':
return {
@@ -159,5 +169,26 @@ export const registerScriptRunnerReducer = (state, action) => {
...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log' })
}
+ case 'knownTransaction':
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'knownTransaction' })
+ }
+ case 'unknownTransaction':
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'knownTransaction' })
+ }
+ case 'emptyBlock':
+ console.log({ action: action.payload.message }, ' emptyBLock reducer')
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '', name: 'emptyBlock' })
+ }
+ case 'newTransaction':
+ return {
+ ...state,
+ journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: '' })
+ }
}
}
diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.css b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.css
index 0091042df2..890460b43c 100644
--- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.css
+++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.css
@@ -17,6 +17,18 @@ element.style {
input #terminalCliInput {
}
+
+.border-primary {
+ border-color: #007aa6!important;
+}
+.border {
+ border: 1px solid #3f4455!important;
+}
+
+.selectedOptions {
+ background-color: #222336;
+}
+
.panel {
position : relative;
display : flex;
@@ -138,6 +150,10 @@ input #terminalCliInput {
cursor : row-resize;
z-index : 999;
}
+
+ .console {
+ cursor : pointer;
+ }
.dragbarHorizontal:hover {
background-color: #007AA6;
@@ -171,7 +187,6 @@ input #terminalCliInput {
.popup {
position : absolute;
text-align : left;
- display : none;
width : 95%;
font-family : monospace;
background-color : var(--secondary);
@@ -234,4 +249,108 @@ input #terminalCliInput {
@keyframes animatetop {
from {bottom: -300px; opacity: 0}
to {bottom: 0; opacity: 1}
- }
\ No newline at end of file
+ }
+
+
+
+
+ /* tx logger css*/
+
+ .log {
+ display: flex;
+ cursor: pointer;
+ align-items: center;
+ cursor: pointer;
+ }
+ .log:hover {
+ opacity: 0.8;
+ }
+ .arrow {
+ color: var(--text-info);
+ font-size: 20px;
+ cursor: pointer;
+ display: flex;
+ margin-left: 10px;
+ }
+ .arrow:hover {
+ color: var(--secondary);
+ }
+ .txLog {
+ }
+ .txStatus {
+ display: flex;
+ font-size: 20px;
+ margin-right: 20px;
+ float: left;
+ }
+ .succeeded {
+ color: var(--success);
+ }
+ .failed {
+ color: var(--danger);
+ }
+ .notavailable {
+ }
+ .call {
+ font-size: 7px;
+ border-radius: 50%;
+ min-width: 20px;
+ min-height: 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: var(--text-info);
+ text-transform: uppercase;
+ font-weight: bold;
+ }
+ .txItem {
+ color: var(--text-info);
+ margin-right: 5px;
+ float: left;
+ }
+ .txItemTitle {
+ font-weight: bold;
+ }
+ .tx {
+ color: var(--text-info);
+ font-weight: bold;
+ float: left;
+ margin-right: 10px;
+ }
+ .txTable,
+ .tr,
+ .td {
+ border-collapse: collapse;
+ font-size: 10px;
+ color: var(--text-info);
+ border: 1px solid var(--text-info);
+ }
+ #txTable {
+ margin-top: 1%;
+ margin-bottom: 5%;
+ align-self: center;
+ width: 85%;
+ }
+ .tr, .td {
+ padding: 4px;
+ vertical-align: baseline;
+ }
+ .td:first-child {
+ min-width: 30%;
+ width: 30%;
+ align-items: baseline;
+ font-weight: bold;
+ }
+ .tableTitle {
+ width: 25%;
+ }
+ .buttons {
+ display: flex;
+ margin-left: auto;
+ }
+ .debug {
+ white-space: nowrap;
+ }
+ .debug:hover {
+ opacity: 0.8;
+ }
diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
index cb2d91393a..94c45e2b90 100644
--- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
+++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
@@ -1,15 +1,26 @@
import React, { useState, useEffect, useReducer, useRef, SyntheticEvent, MouseEvent } from 'react' // eslint-disable-line
import { useKeyPress } from './custom-hooks/useKeyPress' // eslint-disable-line
import { useWindowResize } from 'beautiful-react-hooks'
-import { registerCommandAction, filterFnAction, registerLogScriptRunnerAction, registerInfoScriptRunnerAction, registerErrorScriptRunnerAction, registerWarnScriptRunnerAction, registerRemixWelcomeTextAction } from './actions/terminalAction'
+import { registerCommandAction, filterFnAction, registerLogScriptRunnerAction, registerInfoScriptRunnerAction, registerErrorScriptRunnerAction, registerWarnScriptRunnerAction, registerRemixWelcomeTextAction, listenOnNetworkAction, initListeningOnNetwork } from './actions/terminalAction'
import { initialState, registerCommandReducer, registerFilterReducer, addCommandHistoryReducer, registerScriptRunnerReducer, remixWelcomeTextReducer } from './reducers/terminalReducer'
import { remixWelcome } from './reducers/remixWelcom'
-import {getKeyOf, getValueOf} from './utils/utils'
+import { getKeyOf, getValueOf, Objectfilter, matched, find } from './utils/utils'
import {allCommands, allPrograms} from './commands' // eslint-disable-line
+import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
+import { ModalDialog } from '@remix-ui/modal-dialog'
+// const TxLogger from '../../../apps/'
+import vm from 'vm'
import javascriptserialize from 'javascript-serialize'
import jsbeautify from 'js-beautify'
+import helper from '../../../../../apps/remix-ide/src/lib/helper'
+import parse from 'html-react-parser'
import './remix-ui-terminal.css'
+import { debug } from 'console'
+import { eventNames } from 'process'
+
+const remixLib = require('@remix-project/remix-lib')
+var typeConversion = remixLib.execution.typeConversion
/* eslint-disable-next-line */
export interface RemixUiTerminalProps {
@@ -27,6 +38,15 @@ export interface RemixUiTerminalProps {
config: any
thisState: any
vm: any
+ commandHelp: any,
+ _deps: any,
+ fileImport: any,
+ gistHandler: any,
+ sourceHighlighter: any,
+ registry: any,
+ commands: any,
+ txListener: any,
+ eventsDecoder: any
}
export interface ClipboardEvent extends SyntheticEvent {
@@ -34,7 +54,6 @@ export interface ClipboardEvent extends SyntheticEvent {
}
export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
-
const [toggleDownUp, setToggleDownUp] = useState('fa-angle-double-down')
const [inserted, setInserted] = useState(false)
const [_cmdIndex, setCmdIndex] = useState(-1)
@@ -51,12 +70,14 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const [cmdHistory, cmdHistoryDispatch] = useReducer(addCommandHistoryReducer, initialState)
const [scriptRunnserState, scriptRunnerDispatch] = useReducer(registerScriptRunnerReducer, initialState)
const [welcomeTextState, welcomTextDispath] = useReducer(remixWelcomeTextReducer, initialState)
+ const [isListeningOnNetwork, setIsListeningOnNetwork] = useState(false)
const [autoCompletState, setAutoCompleteState] = useState({
activeSuggestion: 0,
data: {
_options: []
},
_startingElement: 0,
+ autoCompleteSelectedItem: {},
_elementToShow: 4,
_selectedElement: 0,
filteredCommands: [],
@@ -64,58 +85,13 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
showSuggestions: false,
text: '',
userInput: '',
- extraCommands: []
- })
-
- const [state, setState] = useState({
- journalBlocks: {
- intro: (
-
-
- Welcome to Remix {props.version} -
-
-
You can use this terminal to:
-
- - 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:
-
-
- ),
- text: (David
)
- },
- data: {
- // lineLength: props.options.lineLength || 80,
- session: [],
- activeFilters: { commands: {}, input: '' },
- filterFns: {}
- },
- _commands: {},
- commands: {},
- _JOURNAL: [],
- _jobs: [],
- _INDEX: {},
- _INDEXall: [],
- _INDEXallMain: [],
- _INDEXcommands: {},
- _INDEXcommandsMain: {}
+ extraCommands: [],
+ commandHistoryIndex: 0
})
- const _scopedCommands = () => {
-
- }
+ const [searchInput, setSearchInput] = useState('')
+ // const [showTableDetails, setShowTableDetails] = useState([])
+ const [showTableDetails, setShowTableDetails] = useState(null)
useWindowResize(() => {
setWindowHeight(window.innerHeight)
@@ -125,7 +101,8 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const inputEl = useRef(null)
// events
useEffect(() => {
- registerRemixWelcomeTextAction(remixWelcome, welcomTextDispath)
+ initListeningOnNetwork(props, scriptRunnerDispatch)
+ // registerRemixWelcomeTextAction(remixWelcome, welcomTextDispath)
registerLogScriptRunnerAction(props.thisState, 'log', newstate.commands, scriptRunnerDispatch)
registerInfoScriptRunnerAction(props.thisState, 'info', newstate.commands, scriptRunnerDispatch)
registerWarnScriptRunnerAction(props.thisState, 'warn', newstate.commands, scriptRunnerDispatch)
@@ -135,15 +112,18 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
registerCommandAction('info', _blocksRenderer('info'), { activate: true }, dispatch)
registerCommandAction('warn', _blocksRenderer('warn'), { activate: true }, dispatch)
registerCommandAction('error', _blocksRenderer('error'), { activate: true }, dispatch)
+
registerCommandAction('script', function execute (args, scopedCommands, append) {
var script = String(args[0])
+ console.log({ script })
+ console.log({ scopedCommands })
+
_shell(script, scopedCommands, function (error, output) {
- console.log({ error }, 'registerCommand scrpt')
- console.log({ output }, 'registerCommand scrpt 2')
if (error) scriptRunnerDispatch({ type: 'error', payload: { message: error } })
- else if (output) scriptRunnerDispatch({ type: 'error', payload: { message: output } })
+ if (output) scriptRunnerDispatch({ type: 'script', payload: { message: '5' } })
})
}, { activate: true }, dispatch)
+
filterFnAction('log', basicFilter, filterDispatch)
filterFnAction('info', basicFilter, filterDispatch)
filterFnAction('warn', basicFilter, filterDispatch)
@@ -157,7 +137,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
// dispatch({ type: 'html', payload: { commands: htmlresullt.commands } })
// dispatch({ type: 'log', payload: { _commands: logresult._commands } })
// registerCommand('log', _blocksRenderer('log'), { activate: true })
- }, [newstate.journalBlocks, props.thisState.autoCompletePopup, autoCompletState.text])
+ }, [props.thisState.autoCompletePopup, autoCompletState.text])
const placeCaretAtEnd = (el) => {
el.focus()
@@ -169,6 +149,53 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
sel.addRange(range)
}
+ const domTerminalFeatures = () => {
+ return {
+ remix: props.cmdInterpreter
+ }
+ }
+
+ function exeCurrent (cb) {
+ return execute(undefined, cb)
+ }
+
+ function execute (file, cb) {
+ function _execute (content, cb) {
+ if (!content) {
+ // toolTip('no content to execute')
+ if (cb) cb()
+ return
+ }
+
+ newstate.commands.script(content)
+ }
+
+ if (typeof file === 'undefined') {
+ var content = props._deps.editor.currentContent()
+ _execute(content, cb)
+ return
+ }
+
+ var provider = props._deps.fileManager.fileProviderOf(file)
+
+ if (!provider) {
+ // toolTip(`provider for path ${file} not found`)
+ if (cb) cb()
+ return
+ }
+
+ provider.get(file, (error, content) => {
+ if (error) {
+ // toolTip(error)
+ // TODO: pop up
+ if (cb) cb()
+ return
+ }
+
+ _execute(content, cb)
+ })
+ }
+
const _shell = async (script, scopedCommands, done) => { // default shell
if (script.indexOf('remix:') === 0) {
return done(null, 'This type of command has been deprecated and is not functionning anymore. Please run remix.help() to list available commands.')
@@ -177,21 +204,36 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
if (script.indexOf('remix.') === 0) {
// we keep the old feature. This will basically only be called when the command is querying the "remix" object.
// for all the other case, we use the Code Executor plugin
- var context = props.cmdInterpreter
+ var context = domTerminalFeatures()
try {
- var cmds = props.vm.createContext(context)
- var result = props.vm.runInContext(script, cmds)
- return done(null, result)
+ const cmds = vm.createContext(context)
+ // const result
+ let result = vm.runInContext(script, cmds)
+ if (script === 'remix.exeCurrent()') {
+ result = exeCurrent(undefined)
+ } else {
+ if (result === {}) {
+ for (const k in result) {
+ result = +` {k}: ${result[k]}
`
+ }
+ }
+ }
+
+ console.log(result === {}, ' is result === object')
+ console.log({ result })
+ return done(null, '')
} catch (error) {
return done(error.message)
}
}
try {
+ let result: any
if (script.trim().startsWith('git')) {
// result = await this.call('git', 'execute', script)
} else {
result = await props.thisState.call('scriptRunner', 'execute', script)
}
+ console.log({ result })
done()
} catch (error) {
done(error.message || error)
@@ -231,7 +273,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
}
}
-
const _appendItem = (item: any) => {
let { _JOURNAL, _jobs, data } = state
const self = props
@@ -268,7 +309,20 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
}
const handleKeyDown = (event) => {
- if (event.which === 13) {
+ const suggestionCount = autoCompletState.activeSuggestion
+ if (autoCompletState.userInput !== '' && (event.which === 27 || event.which === 8 || event.which === 46)) {
+ console.log(' enter esc and delete')
+ // backspace or any key that should remove the autocompletion
+ setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false }))
+ }
+ if (autoCompletState.showSuggestions && (event.which === 13 || event.which === 9)) {
+ if (autoCompletState.userInput.length === 1) {
+ setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false, userInput: Object.keys(autoCompletState.data._options[0]).toString() }))
+ } else {
+ setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false, userInput: autoCompletState.userInput }))
+ }
+ }
+ if (event.which === 13 && !autoCompletState.showSuggestions) {
if (event.ctrlKey) { //
// on enter, append the value in the cli input to the journal
inputEl.current.focus()
@@ -287,7 +341,50 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
inputEl.current.focus()
setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false }))
}
- } else if (event.which === 38) { //
+ } else if (newstate._commandHistory.length && event.which === 38 && !autoCompletState.showSuggestions && (autoCompletState.userInput === '')) {
+ console.log('previous command up')
+ // if (autoCompletState.commandHistoryIndex < 1) {
+ event.preventDefault()
+ console.log(newstate._commandHistory[0], ' up value')
+ setAutoCompleteState(prevState => ({ ...prevState, userInput: newstate._commandHistory[0] }))
+
+ // }
+ // else if (newstate._commandHistory.length < autoCompletState.commandHistoryIndex) {
+ // setAutoCompleteState(prevState => ({ ...prevState, commandHistoryIndex: --autoCompletState.commandHistoryIndex }))
+ // console.log(newstate._commandHistory[newstate._commandHistory.length > 1 ? autoCompletState.commandHistoryIndex-- : newstate._commandHistory.length + 1], ' up value')
+ // } else if (newstate._commandHistory.length === autoCompletState.commandHistoryIndex) {
+ // console.log(newstate._commandHistory.length === autoCompletState.commandHistoryIndex, ' up value middle')
+ // setAutoCompleteState(prevState => ({ ...prevState, commandHistoryIndex: autoCompletState.commandHistoryIndex - 2, userInput: newstate._commandHistory[autoCompletState.commandHistoryIndex] }))
+ // } else {
+ // setAutoCompleteState(prevState => ({ ...prevState, commandHistoryIndex: autoCompletState.commandHistoryIndex - 1, userInput: newstate._commandHistory[autoCompletState.commandHistoryIndex] }))
+ // console.log(newstate._commandHistory[newstate._commandHistory.length - 1], ' up value last')
+ // }
+ // if (newstate._commandHistory.length === 0) {
+ // setAutoCompleteState(prevState => ({ ...prevState, userInput: newstate[0] }))
+ // }
+ // setAutoCompleteState(prevState => ({ ...prevState, userInput: newstate[autoCompletState.commandHistoryIndex] }))
+ // TODO: giving error => need to work on the logic
+ // // } else if (newstate._commandHistory.length && event.which === 40 && !autoCompletState.showSuggestions && (autoCompletState.userInput !== '')) {
+ // // console.log('previous command down')
+ // // if (autoCompletState.commandHistoryIndex < newstate._commandHistory.length) {
+ // // setAutoCompleteState(prevState => ({ ...prevState, commandHistoryIndex: autoCompletState.commandHistoryIndex + 1, userInput: newstate._commandHistory[autoCompletState.commandHistoryIndex + 1] }))
+ // // console.log(newstate._commandHistory[newstate._commandHistory.length > 1 ? autoCompletState.commandHistoryIndex++ : newstate._commandHistory.length - 1], ' down ++ value')
+ // // } else {
+ // // console.log(newstate._commandHistory[newstate._commandHistory.length - 1], ' down value last')
+ // // setAutoCompleteState(prevState => ({ ...prevState, commandHistoryIndex: newstate._commandHistory.length - 1, userInput: newstate._commandHistory[newstate._commandHistory.length - 1] }))
+ // // }
+ // // // if (autoCompletState.commandHistoryIndex === newstate._commandHistory.length) {
+ // // // return
+ // // // }
+ // setAutoCompleteState(prevState => ({ ...prevState, userInput: newstate[autoCompletState.commandHistoryIndex] + 1 }))
+ } else if (event.which === 38 && autoCompletState.showSuggestions) {
+ event.preventDefault()
+ if (autoCompletState.activeSuggestion === 0) {
+ return
+ }
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: suggestionCount - 1, userInput: Object.keys(autoCompletState.data._options[autoCompletState.activeSuggestion - 1]).toString() }))
+ console.log('disable up an down key in input box')
+ } else if (event.which === 38 && !autoCompletState.showSuggestions) { //
const len = _cmdHistory.length
if (len === 0) event.preventDefault()
if (_cmdHistory.length - 1 > _cmdIndex) {
@@ -295,8 +392,14 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
}
inputEl.current.innerText = _cmdHistory[_cmdIndex]
inputEl.current.focus()
- }
- else if (event.which === 40) {
+ } else if (event.which === 40 && autoCompletState.showSuggestions) {
+ event.preventDefault()
+ if ((autoCompletState.activeSuggestion + 1) === autoCompletState.data._options.length) {
+ return
+ }
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: suggestionCount + 1, userInput: Object.keys(autoCompletState.data._options[autoCompletState.activeSuggestion + 1]).toString() }))
+ console.log('disable up an down key in input box')
+ } else if (event.which === 40 && !autoCompletState.showSuggestions) {
if (_cmdIndex > -1) {
setCmdIndex(prevState => prevState--)
}
@@ -305,6 +408,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
} else {
setCmdTemp(inputEl.current.innerText)
}
+ console.log({ autoCompletState })
}
const moveGhostbar = (event) => {
@@ -326,14 +430,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const mousedown = (event: MouseEvent) => {
setSeparatorYPosition(event.clientY)
setDragging(true)
- // console.log({ windowHeight })
- // console.log(event.which === 1, 'event.which === 1')
- // event.preventDefault()
- // moveGhostbar(event)
- // if (event.which === 1) {
- // console.log('event .which code 1')
- // moveGhostbar(event)
- // }
}
const onMouseMove: any = (e: MouseEvent) => {
@@ -352,36 +448,13 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
setDragging(false)
}
-
/* end of mouse event */
- const cancelGhostbar = (event) => {
- if (event.keyCode === 27) {
- console.log('event .key code 27')
- }
- }
-
useEffect(() => {
- // document.addEventListener('mousemove', changeBg)
- // function changeBg () {
- // document.getElementById('dragId').style.backgroundColor = 'skyblue'
- // }
- // document.addEventListener('mouseup', changeBg2)
- // function changeBg2 () {
- // document.getElementById('dragId').style.backgroundColor = ''
- // }
document.addEventListener('mousemove', onMouseMove)
document.addEventListener('mouseup', onMouseUp)
return () => {
- // document.addEventListener('mousemove', changeBg)
- // function changeBg () {
- // document.getElementById('dragId').style.backgroundColor = 'skyblue'
- // }
- // document.addEventListener('mouseup', changeBg2)
- // function changeBg2 () {
- // document.getElementById('dragId').style.backgroundColor = ''
- // }
document.removeEventListener('mousemove', onMouseMove)
document.removeEventListener('mouseup', onMouseUp)
}
@@ -448,85 +521,378 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
/* end of block content that gets rendered from script Runner */
+ const handleClearConsole = () => {
+ dispatch({ type: 'clearconsole', payload: [] })
+ inputEl.current.focus()
+ }
/* start of autoComplete */
- const handleSelect = (text) => {
- props.thisState.event.trigger('handleSelect', [text])
+
+ const listenOnNetwork = (event: any) => {
+ const isListening = event.target.checked
+ setIsListeningOnNetwork(isListening)
+ listenOnNetworkAction(props, isListening)
}
const onChange = (event: any) => {
event.preventDefault()
const inputString = event.target.value
- console.log(event)
- console.log({ inputString })
- setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: true, userInput: inputString }))
- const textList = inputString.split(' ')
- const autoCompleteInput = textList.length > 1 ? textList[textList.length - 1] : textList[0]
- allPrograms.forEach(item => {
- const program = getKeyOf(item)
- console.log({ program })
- if (program.substring(0, program.length - 1).includes(autoCompleteInput.trim())) {
- setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [item] } }))
- } else if (autoCompleteInput.trim().includes(program) || (program === autoCompleteInput.trim())) {
- allCommands.forEach(item => {
- console.log({ item })
- const command = getKeyOf(item)
- if (command.includes(autoCompleteInput.trim())) {
- setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [item] } }))
- }
- })
+ if (matched(allPrograms, inputString) || inputString.includes('.')) {
+ setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: true, userInput: inputString }))
+ const textList = inputString.split('.')
+ if (textList.length === 1) {
+ setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [] } }))
+ const result = Objectfilter(allPrograms, autoCompletState.userInput)
+ setAutoCompleteState(prevState => ({ ...prevState, data: { _options: result } }))
+ } else {
+ setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [] } }))
+ const result = Objectfilter(allCommands, autoCompletState.userInput)
+ setAutoCompleteState(prevState => ({ ...prevState, data: { _options: result } }))
}
- })
- autoCompletState.extraCommands.forEach(item => {
- const command = getKeyOf(item)
- if (command.includes(autoCompleteInput.trim())) {
- setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [item] } }))
+ } else {
+ setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false, userInput: inputString }))
+ }
+ }
+
+ const handleSelect = (event) => {
+ const suggestionCount = autoCompletState.activeSuggestion
+ if (event.keyCode === 38) {
+ if (autoCompletState.activeSuggestion === 0) {
+ return
+ }
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: suggestionCount - 1 }))
+ } else if (event.keyCode === 40) {
+ if (autoCompletState.activeSuggestion - 1 === autoCompletState.data._options.length) {
+ return
+ }
+ setAutoCompleteState(prevState => ({ ...prevState, activeSuggestion: suggestionCount + 1 }))
+ }
+ // props.thisState.event.trigger('handleSelect', [text])
+ }
+
+ const checkTxStatus = (tx, type) => {
+ if (tx.status === '0x1' || tx.status === true) {
+ return ()
+ }
+ if (type === 'call' || type === 'unknownCall' || type === 'unknown') {
+ return (call)
+ } else if (tx.status === '0x0' || tx.status === false) {
+ return ()
+ } else {
+ return ()
+ }
+ }
+
+ const context = (opts, blockchain) => {
+ const data = opts.tx || ''
+ const from = opts.from ? helper.shortenHexData(opts.from) : ''
+ let to = opts.to
+ if (data.to) to = to + ' ' + helper.shortenHexData(data.to)
+ const val = data.value
+ let hash = data.hash ? helper.shortenHexData(data.hash) : ''
+ const input = data.input ? helper.shortenHexData(data.input) : ''
+ const logs = data.logs && data.logs.decoded && data.logs.decoded.length ? data.logs.decoded.length : 0
+ const block = data.receipt ? data.receipt.blockNumber : data.blockNumber || ''
+ const i = data ? data.transactionIndex : data.transactionIndex
+ const value = val ? typeConversion.toInt(val) : 0
+
+ if (blockchain.getProvider() === 'vm') {
+ return (
+
+
+ [{vm}]
+ from: {from}
+ to: {to}
+ value: {value} wei
+ data: {input}
+ logs: {logs}
+ hash: {hash}
+
+
)
+ } else if (blockchain.getProvider() !== 'vm' && data.resolvedData) {
+ return (
+
+
+ [block:${block} txIndex:${i}]
+ from: {from}
+ to: {to}
+ value: {value} wei
+ data: {input}
+ logs: {logs}
+ hash: {hash}
+
+
)
+ } else {
+ to = helper.shortenHexData(to)
+ hash = helper.shortenHexData(data.blockHash)
+ return (
+
+
+ [block:${block} txIndex:${i}]
+ from: {from}
+ to: {to}
+ value: {value} wei
+
+
)
+ }
+ }
+
+ const txDetails = (event, tx, obj) => {
+ if (showTableDetails === null) {
+ setShowTableDetails(true)
+ } else {
+ setShowTableDetails(null)
+ }
+ // if (showTableDetails.length === 0) {
+ // setShowTableDetails([{ hash: tx.hash, show: true }])
+ // }
+ // const id = showTableDetails.filter(x => x.hash !== tx.hash)
+ // if ((showTableDetails.length !== 0) && (id[0] === tx.hash)) {
+ // setShowTableDetails(currentState => ([...currentState, { hash: tx.hash, show: false }]))
+ // }
+ // console.log((showTableDetails.length !== 0) && (id[0] === tx.hash))
+ // console.log({ showTableDetails }, ' clicked button')
+ }
+
+ const showTable = (opts) => {
+ let msg = ''
+ let toHash
+ const data = opts.data // opts.data = data.tx
+ if (data.to) {
+ toHash = opts.to + ' ' + data.to
+ } else {
+ toHash = opts.to
+ }
+ let callWarning = ''
+ if (opts.isCall) {
+ callWarning = '(Cost only applies when called by a contract)'
+ }
+ if (!opts.isCall) {
+ if (opts.status !== undefined && opts.status !== null) {
+ if (opts.status === '0x0' || opts.status === false) {
+ msg = ' Transaction mined but execution failed'
+ } else if (opts.status === '0x1' || opts.status === true) {
+ msg = ' Transaction mined and execution succeed'
+ }
+ } else {
+ msg = ' Status not available at the moment'
}
- })
- if (autoCompletState.data._options.length === 1 && event.which === 9) {
- // if only one option and tab is pressed, we resolve it
- event.preventDefault()
- textList.pop()
- textList.push(getKeyOf(autoCompletState.data._options[0]))
- handleSelect(`${textList}`.replace(/,/g, ' '))
+ }
+
+ let stringified = ' - '
+ if (opts.logs && opts.logs.decoded) {
+ stringified = typeConversion.stringify(opts.logs.decoded)
+ }
+ const val = opts.val != null ? typeConversion.toInt(opts.val) : 0
+ return (
+
+
+ status |
+ {opts.status}{msg} |
+
+
+ transaction hash |
+ {opts.hash}
+
+ |
+
+ {
+ opts.contractAddress && (
+
+ contract address |
+ {opts.contractAddress}
+
+ |
+
+ )
+ }
+ {
+ opts.from && (
+
+ from |
+ {opts.from}
+
+ |
+
+ )
+ }
+ {
+ opts.to && (
+
+ to |
+ {toHash}
+
+ |
+
+ )
+ }
+ {
+ opts.gas && (
+
+ gas |
+ {opts.gas} gas
+
+ |
+
+ )
+ }
+ {
+ opts.transactionCost && (
+
+ transaction cost |
+ {opts.transactionCost} gas {callWarning}
+
+ |
+
+ )
+ }
+ {
+ opts.executionCost && (
+
+ execution cost |
+ {opts.executionCost} gas {callWarning}
+
+ |
+
+ )
+ }
+ {opts.hash && (
+
+ hash |
+ {opts.hash}
+
+ |
+
+ )}
+ {opts.input && (
+
+ input |
+ {helper.shortenHexData(opts.input)}
+
+ |
+
+ )}
+ {opts['decoded input'] && (
+
+ decode input |
+ {opts['decoded input']}
+
+ |
+
+ )}
+ {opts['decoded output'] && (
+
+ decode output |
+ {opts['decoded output']}
+
+ |
+
+ )}
+ {opts.logs && (
+
+ logs |
+
+ {JSON.stringify(stringified, null, '\t')}
+
+
+ |
+
+ )}
+ {opts.val && (
+
+ val |
+ {val} wei
+
+ |
+
+ )}
+
+ )
+ }
+
+ const debug = (event, tx) => {
+ event.stopPropagation()
+ if (tx.isCall && tx.envMode !== 'vm') {
+ console.log('start debugging')
+ return ( {} }
+ message="Cannot debug this call. Debugging calls is only possible in JavaScript VM mode."
+ />)
+ } else {
+ props.event.trigger('debuggingRequested', [tx.hash])
+ console.log('trigger ', { tx: props.event.trigger })
}
}
+ const renderKnownTransactions = (tx, receipt, index) => {
+ const from = tx.from
+ const to = tx.to
+ const obj = { from, to }
+ const showDetails = showTableDetails === tx.from
+ const txType = 'unknown' + (tx.isCall ? 'Call' : 'Tx')
+ return (
+
+ txDetails(event, tx, obj)}>
+ {/* onClick={e => txDetails(e, tx, data, obj)} */}
+ {checkTxStatus(receipt || tx, txType)}
+ {context({ from, to, tx }, props.blockchain)}
+
+
debug(event, tx)}>Debug
+
+
+
+ {showTableDetails ? showTable({
+ hash: tx.hash,
+ status: receipt !== null ? receipt.status : null,
+ isCall: tx.isCall,
+ contractAddress: tx.contractAddress,
+ data: tx,
+ from,
+ to,
+ gas: tx.gas,
+ input: tx.input,
+ 'decoded input': tx.resolvedData && tx.resolvedData.params ? JSON.stringify(typeConversion.stringify(tx.resoparams), null, '\t') : ' - ',
+ 'decoded output': tx.resolvedData && tx.resolvedData.decodedReturnValue ? JSON.stringify(typeConversion.stringify(tx.resolvedData.decodedReturnValue), null, '\t') : ' - ',
+ logs: tx.logs,
+ val: tx.value,
+ transactionCost: tx.transactionCost,
+ executionCost: tx.executionCost
+ }) : null}
+
+ )
+ }
+
const handleAutoComplete = () => (
-
+
- ${autoCompletState.data._options.map((item, index) => {
+ {autoCompletState.data._options.map((item, index) => {
return (
-
auto complete here
- //
- //
{ handleSelect(event.srcElement.innerText) }}>
- // {getKeyOf(item)}
- //
- //
- // {getValueOf(item)}
- //
- //
+
+
+ {getKeyOf(item)}
+
+
+ {getValueOf(item)}
+
+
)
})}
- {/*
-
Page ${(self._startingElement / self._elementsToShow) + 1} of ${Math.ceil(data._options.length / self._elementsToShow)}
-
*/}
)
/* end of autoComplete */
return (
-
+
{console.log({ newstate })}
{console.log({ props })}
- {console.log({ autoCompletState })}
{/* ${self._view.dragbar} */}
{/* ${self._view.icon} */}
-
{
- (autoCompletState.showSuggestions && autoCompletState.userInput) && handleAutoComplete()
+ handleAutoComplete()
}
{
}}>
- {(newstate.journalBlocks).map((x, index) => (
-
- {x.message}
-
- ))}
+ {newstate.journalBlocks && newstate.journalBlocks.map((x, index) => {
+ if (x.name === 'emptyBlock') {
+ return (
+
+
+ [block:{x.message} - 0 {'transactions'} ]
+
+ )
+ } else if (x.name === 'unknownTransaction' || x.name === 'knownTransaction') {
+ return x.message.filter(x => x.tx.hash.includes(searchInput) || x.tx.from.includes(searchInput) || x.tx.to.includes(searchInput)).map((trans) => {
+ return (
{renderKnownTransactions(trans.tx, trans.receipt, index)}
)
+ })
+ } else {
+ return (
+
+ {x.message}
+
+ )
+ }
+ })}
{/* ${background} */}
diff --git a/libs/remix-ui/terminal/src/lib/utils/helper.ts b/libs/remix-ui/terminal/src/lib/utils/helper.ts
new file mode 100644
index 0000000000..56b50a4377
--- /dev/null
+++ b/libs/remix-ui/terminal/src/lib/utils/helper.ts
@@ -0,0 +1,176 @@
+// var async = require('async')
+// const ethJSUtil = require('ethereumjs-util')
+
+// export const shortenAddress = (address, etherBalance) => {
+// var len = address.length
+// return address.slice(0, 5) + '...' + address.slice(len - 5, len) + (etherBalance ? ' (' + etherBalance.toString() + ' ether)' : '')
+// }
+
+// export const addressToString = (address) => {
+// if (!address) return null
+// if (typeof address !== 'string') {
+// address = address.toString('hex')
+// }
+// if (address.indexOf('0x') === -1) {
+// address = '0x' + address
+// }
+// return ethJSUtil.toChecksumAddress(address)
+// }
+
+// export const shortenHexData = (data) => {
+// if (!data) return ''
+// if (data.length < 5) return data
+// var len = data.length
+// return data.slice(0, 5) + '...' + data.slice(len - 5, len)
+// }
+
+// export const createNonClashingNameWithPrefix = (name, fileProvider, prefix, cb) => {
+// if (!name) name = 'Undefined'
+// var counter = ''
+// var ext = 'sol'
+// var reg = /(.*)\.([^.]+)/g
+// var split = reg.exec(name)
+// if (split) {
+// name = split[1]
+// ext = split[2]
+// }
+// var exist = true
+// async.whilst(
+// () => { return exist },
+// (callback) => {
+// fileProvider.exists(name + counter + prefix + '.' + ext).then(currentExist => {
+// exist = currentExist
+// if (exist) counter = (counter | 0) + 1
+// callback()
+// }).catch(error => {
+// if (error) console.log(error)
+// })
+// },
+// (error) => { cb(error, name + counter + prefix + '.' + ext) }
+// )
+// }
+
+// export const createNonClashingName = (name, fileProvider, cb) => {
+// this.createNonClashingNameWithPrefix(name, fileProvider, '', cb)
+// },
+// export const createNonClashingNameAsync = async (name, fileManager, prefix = '') => {
+// if (!name) name = 'Undefined'
+// let counter = ''
+// let ext = 'sol'
+// const reg = /(.*)\.([^.]+)/g
+// const split = reg.exec(name)
+// if (split) {
+// name = split[1]
+// ext = split[2]
+// }
+// let exist = true
+
+// do {
+// const isDuplicate = await fileManager.exists(name + counter + prefix + '.' + ext)
+
+// if (isDuplicate) counter = (counter | 0) + 1
+// else exist = false
+// } while (exist)
+
+// return name + counter + prefix + '.' + ext
+// }
+
+// export const createNonClashingDirNameAsync = async (name, fileManager) => {
+// if (!name) name = 'Undefined'
+// let counter = ''
+// let exist = true
+
+// do {
+// const isDuplicate = await fileManager.exists(name + counter)
+
+// if (isDuplicate) counter = (counter | 0) + 1
+// else exist = false
+// } while (exist)
+
+// return name + counter
+// }
+
+// export const checkSpecialChars = (name) => {
+// return name.match(/[:*?"<>\\'|]/) != null
+// }
+
+// export const checkSlash = (name) => {
+// return name.match(/\//) != null
+// }
+
+// export const isHexadecimal = (value) => {
+// return /^[0-9a-fA-F]+$/.test(value) && (value.length % 2 === 0)
+// }
+
+// export const is0XPrefixed = (value) => {
+// return value.substr(0, 2) === '0x'
+// }
+
+// export const isNumeric = (value) => {
+// return /^\+?(0|[1-9]\d*)$/.test(value)
+// }
+
+// export const isValidHash = (hash) => { // 0x prefixed, hexadecimal, 64digit
+// const hexValue = hash.slice(2, hash.length)
+// return this.is0XPrefixed(hash) && /^[0-9a-fA-F]{64}$/.test(hexValue)
+// }
+
+// export const removeTrailingSlashes = (text) {
+// // Remove single or consecutive trailing slashes
+// return text.replace(/\/+$/g, '')
+// },
+// removeMultipleSlashes (text) {
+// // Replace consecutive slashes with '/'
+// return text.replace(/\/+/g, '/')
+// },
+// find: find,
+// getPathIcon (path) {
+// return path.endsWith('.txt')
+// ? 'far fa-file-alt' : path.endsWith('.md')
+// ? 'far fa-file-alt' : path.endsWith('.sol')
+// ? 'fak fa-solidity-mono' : path.endsWith('.js')
+// ? 'fab fa-js' : path.endsWith('.json')
+// ? 'fas fa-brackets-curly' : path.endsWith('.vy')
+// ? 'fak fa-vyper-mono' : path.endsWith('.lex')
+// ? 'fak fa-lexon' : path.endsWith('.contract')
+// ? 'fab fa-ethereum' : 'far fa-file'
+// },
+// joinPath (...paths) {
+// paths = paths.filter((value) => value !== '').map((path) => path.replace(/^\/|\/$/g, '')) // remove first and last slash)
+// if (paths.length === 1) return paths[0]
+// return paths.join('/')
+// },
+// extractNameFromKey (key) {
+// const keyPath = key.split('/')
+
+// return keyPath[keyPath.length - 1]
+// }
+
+// const findDeep = (object, fn, found = { break: false, value: undefined }) => {
+// if (typeof object !== 'object' || object === null) return
+// for (var i in object) {
+// if (found.break) break
+// var el = object[i]
+// if (el && el.innerText !== undefined && el.innerText !== null) el = el.innerText
+// if (fn(el, i, object)) {
+// found.value = el
+// found.break = true
+// break
+// } else {
+// findDeep(el, fn, found)
+// }
+// }
+// return found.value
+// }
+
+// const find = (args, query) => {
+// query = query.trim()
+// var isMatch = !!findDeep(args, function check (value, key) {
+// if (value === undefined || value === null) return false
+// if (typeof value === 'function') return false
+// if (typeof value === 'object') return false
+// var contains = String(value).indexOf(query.trim()) !== -1
+// return contains
+// })
+// return isMatch
+// }
diff --git a/libs/remix-ui/terminal/src/lib/utils/utils.ts b/libs/remix-ui/terminal/src/lib/utils/utils.ts
index 46ca37fa89..c5732f1b53 100644
--- a/libs/remix-ui/terminal/src/lib/utils/utils.ts
+++ b/libs/remix-ui/terminal/src/lib/utils/utils.ts
@@ -6,3 +6,37 @@ export const getKeyOf = (item) => {
export const getValueOf = (item) => {
return Object.values(item)[0]
}
+
+export const Objectfilter = (obj: any, filterValue: any) =>
+ obj.filter((item: any) => Object.keys(item)[0].indexOf(filterValue) > -1)
+
+export const matched = (arr, value) => arr.map(x => Object.keys(x).some(x => x.startsWith(value))).some(x => x === true)
+
+const findDeep = (object, fn, found = { break: false, value: undefined }) => {
+ if (typeof object !== 'object' || object === null) return
+ for (var i in object) {
+ if (found.break) break
+ var el = object[i]
+ if (el && el.innerText !== undefined && el.innerText !== null) el = el.innerText
+ if (fn(el, i, object)) {
+ found.value = el
+ found.break = true
+ break
+ } else {
+ findDeep(el, fn, found)
+ }
+ }
+ return found.value
+}
+
+export const find = (args, query) => {
+ query = query.trim()
+ var isMatch = !!findDeep(args, function check (value) {
+ if (value === undefined || value === null) return false
+ if (typeof value === 'function') return false
+ if (typeof value === 'object') return false
+ var contains = String(value).indexOf(query.trim()) !== -1
+ return contains
+ })
+ return isMatch
+}