feat: clear-console, terminal-search, fix-input terminal input to buttom

pull/5370/head
davidzagi93@gmail.com 3 years ago
parent 4c47d0a817
commit 0cdac9d3af
  1. 7
      apps/remix-ide/src/app.js
  2. 483
      apps/remix-ide/src/app/panels/terminal.js
  3. 106
      libs/remix-ui/terminal/src/lib/actions/terminalAction.ts
  4. 37
      libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts
  5. 123
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.css
  6. 690
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  7. 176
      libs/remix-ui/terminal/src/lib/utils/helper.ts
  8. 34
      libs/remix-ui/terminal/src/lib/utils/utils.ts

@ -288,6 +288,8 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
// -------------------Terminal---------------------------------------- // -------------------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( const terminal = new Terminal(
{ appManager, blockchain }, { appManager, blockchain },
{ {
@ -300,10 +302,11 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
return height - newpos 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 }) const contextualListener = new ContextualListener({ editor })
engine.register([ engine.register([

@ -18,6 +18,11 @@ var AutoCompletePopup = require('../ui/auto-complete-popup')
var css = require('./styles/terminal-styles') 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 = [] var KONSOLES = []
function register (api) { KONSOLES.push(api) } function register (api) { KONSOLES.push(api) }
@ -32,12 +37,32 @@ const profile = {
} }
class Terminal extends Plugin { class Terminal extends Plugin {
constructor (opts, api, config) { constructor (opts, api, config, registry) {
super(profile) 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 = document.createElement('div')
this.element.setAttribute('class', 'panel_2A0YE0') this.element.setAttribute('class', 'panel_2A0YE0')
this.element.setAttribute('id', 'terminal-view') 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.blockchain = opts.blockchain
this.vm = vm this.vm = vm
this._api = api this._api = api
@ -54,14 +79,14 @@ class Terminal extends Plugin {
this._components = {} this._components = {}
this._components.cmdInterpreter = new CommandInterpreterAPI(this, null, this.blockchain) this._components.cmdInterpreter = new CommandInterpreterAPI(this, null, this.blockchain)
this._components.autoCompletePopup = new AutoCompletePopup(this._opts) this._components.autoCompletePopup = new AutoCompletePopup(this._opts)
this._components.autoCompletePopup.event.register('handleSelect', function (input) { // this._components.autoCompletePopup.event.register('handleSelect', function (input) {
const textList = this._view.input.innerText.split(' ') // const textList = this._view.input.innerText.split(' ')
textList.pop() // textList.pop()
textList.push(input) // textList.push(input)
this._view.input.innerText = textList // this._view.input.innerText = textList
this._view.input.focus() // this._view.input.focus()
this.putCursor2End(this._view.input) // this.putCursor2End(this._view.input)
}) // })
this._commands = {} this._commands = {}
this.commands = {} this.commands = {}
this._JOURNAL = [] this._JOURNAL = []
@ -86,31 +111,10 @@ class Terminal extends Plugin {
this.off('scriptRunner', 'error') this.off('scriptRunner', 'error')
} }
log (message) { // logHtml (html) {
var command = this.commands[message.type] // var command = this.commands.html
if (typeof command === 'function') { // if (typeof command === 'function') command(html)
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) { logHtml (html) {
var command = this.commands.html var command = this.commands.html
@ -128,7 +132,7 @@ class Terminal extends Plugin {
autoCompletePopupEvent = {this._components.autoCompletePopup.event} autoCompletePopupEvent = {this._components.autoCompletePopup.event}
blockchain = {this.blockchain} blockchain = {this.blockchain}
api = {this._api} api = {this._api}
options = {this.opts} options = {this._opts}
data = {this.data} data = {this.data}
cmdInterpreter = {this._components.cmdInterpreter} cmdInterpreter = {this._components.cmdInterpreter}
autoCompletePopup = {this._components.autoCompletePopup} autoCompletePopup = {this._components.autoCompletePopup}
@ -138,170 +142,285 @@ class Terminal extends Plugin {
config = {this.config} config = {this.config}
thisState = {this} thisState = {this}
vm = {this.vm} 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 this.element
) )
} }
_appendItem (item) { // _appendItem (item) {
var self = this // var self = this
var { el, gidx } = item // var { el, gidx } = item
self._JOURNAL[gidx] = item // self._JOURNAL[gidx] = item
if (!self._jobs.length) { // if (!self._jobs.length) {
requestAnimationFrame(function updateTerminal () { // requestAnimationFrame(function updateTerminal () {
self._jobs.forEach(el => self._view.journal.appendChild(el)) // self._jobs.forEach(el => self._view.journal.appendChild(el))
self.scroll2bottom() // self.scroll2bottom()
self._jobs = [] // self._jobs = []
}) // })
} // }
if (self.data.activeFilters.commands[item.cmd]) self._jobs.push(el) // if (self.data.activeFilters.commands[item.cmd]) self._jobs.push(el)
} // }
scroll2bottom () { scroll2bottom () {
var self = this var self = this
setTimeout(function () { setTimeout(function () {
self._view.term.scrollTop = self._view.term.scrollHeight // self._view.term.scrollTop = self._view.term.scrollHeight
}, 0) }, 0)
} }
_blocksRenderer (mode) { // _blocksRenderer (mode) {
if (mode === 'html') { // if (mode === 'html') {
return function logger (args, scopedCommands, append) { // return function logger (args, scopedCommands, append) {
if (args.length) append(args[0]) // if (args.length) append(args[0])
} // }
} // }
mode = { // mode = {
log: 'text-info', // log: 'text-info',
info: 'text-info', // info: 'text-info',
warn: 'text-warning', // warn: 'text-warning',
error: 'text-danger' // error: 'text-danger'
}[mode] // defaults // }[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, '<br>')
val = el.children.length === 0 ? el.firstChild : el
}
if (types[idx] === 'element') val = jsbeautify.html(val)
return val
})
if (values.length) {
append(yo`<span class="${mode}" >${values}</span>`)
}
}
} else {
throw new Error('mode is not supported')
}
}
_scopeCommands (append) { // if (mode) {
var self = this // const filterUndefined = (el) => el !== undefined && el !== null
var scopedCommands = {} // return function logger (args, scopedCommands, append) {
Object.keys(self.commands).forEach(function makeScopedCommand (cmd) { // var types = args.filter(filterUndefined).map(type)
var command = self._commands[cmd] // var values = javascriptserialize.apply(null, args.filter(filterUndefined)).map(function (val, idx) {
scopedCommands[cmd] = function _command () { // if (typeof args[idx] === 'string') {
var args = [...arguments] // const el = document.createElement('div')
command(args, scopedCommands, el => append(cmd, args, blockify(el))) // el.innerHTML = args[idx].replace(/(\r\n|\n|\r)/gm, '<br>')
} // val = el.children.length === 0 ? el.firstChild : el
}) // }
return scopedCommands // if (types[idx] === 'element') val = jsbeautify.html(val)
} // return val
// })
// if (values.length) {
// append(yo`<span class="${mode}" >${values}</span>`)
// }
// }
// } else {
// throw new Error('mode is not supported')
// }
// }
registerFilter (commandName, filterFn) { // _scopeCommands (append) {
this.data.filterFns[commandName] = filterFn // 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) { // registerFilter (commandName, filterFn) {
var self = this // this.data.filterFns[commandName] = filterFn
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]
}
async _shell (script, scopedCommands, done) { // default shell // registerCommand (name, command, opts) {
if (script.indexOf('remix:') === 0) { // var self = this
return done(null, 'This type of command has been deprecated and is not functionning anymore. Please run remix.help() to list available commands.') // name = String(name)
} // if (this._commands[name]) throw new Error(`command "${name}" exists already`)
var self = this // if (typeof command !== 'function') throw new Error(`invalid command: ${command}`)
if (script.indexOf('remix.') === 0) { // this._commands[name] = command
// we keep the old feature. This will basically only be called when the command is querying the "remix" object. // console.log({ command })
// for all the other case, we use the Code Executor plugin // console.log(self._commands)
var context = domTerminalFeatures(scopedCommands, self.blockchain) // this._INDEX.commands[name] = []
try { // this._INDEX.commandsMain[name] = []
var cmds = vm.createContext(context) // this.commands[name] = function _command () {
var result = vm.runInContext(script, cmds) // var args = [...arguments]
return done(null, result) // var steps = []
} catch (error) { // var root = { steps, cmd: name }
return done(error.message) // var ITEM = { root, cmd: name }
} // root.gidx = self._INDEX.allMain.push(ITEM) - 1
} // root.idx = self._INDEX.commandsMain[name].push(ITEM) - 1
try { // function append (cmd, params, el) {
let result // var item
if (script.trim().startsWith('git')) { // if (cmd) { // subcommand
// result = await this.call('git', 'execute', script) // item = { el, cmd, root }
} else { // } else { // command
result = await this.call('scriptRunner', 'execute', script) // item = ITEM
} // item.el = el
if (result) self.commands.html(yo`<pre>${result}</pre>`) // cmd = name
done() // }
} catch (error) { // item.gidx = self._INDEX.all.push(item) - 1
done(error.message || error) // 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) { // async _shell (script, scopedCommands, done) { // default shell
return { // if (script.indexOf('remix:') === 0) {
remix: this._components.cmdInterpreter // 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`<pre>${result}</pre>`)
// 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`<div></div>`
// for (var k in self.commandHelp) {
// help.appendChild(yo`<div>${k}: ${self.commandHelp[k]}</div>`)
// help.appendChild(yo`<br>`)
// }
// self._components.terminal.commands.html(help)
// if (cb) cb()
// return ''
// }
}
function blockify (el) { return yo`<div class="px-4 ${css.block}" data-id="block_${el.getAttribute ? el.getAttribute('id') : ''}">${el}</div>` } function blockify (el) { return yo`<div class="px-4 ${css.block}" data-id="block_${el.getAttribute ? el.getAttribute('id') : ''}">${el}</div>` }
module.exports = Terminal module.exports = Terminal

@ -70,8 +70,9 @@ export const registerCommandAction = (name, command, activate, dispatch) => {
if (activate.filterFn) { if (activate.filterFn) {
registerFilter(name, 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) => { const blockify = (el) => {
return `<div class="px-4 block_2A0YE0" data-id="block_null">${el}</div>` return `<div class="px-4 block_2A0YE0" data-id="block_null">${el}</div>`
} }
@ -89,7 +90,6 @@ export const registerCommandAction = (name, command, activate, dispatch) => {
console.log({ scopedCommands }) console.log({ scopedCommands })
return scopedCommands return scopedCommands
} }
} }
export const filterFnAction = (name, filterFn, dispatch) => { export const filterFnAction = (name, filterFn, dispatch) => {
@ -133,3 +133,103 @@ export const registerErrorScriptRunnerAction = (event, commandName, commandFn, d
export const registerRemixWelcomeTextAction = (welcomeText, dispatch) => { export const registerRemixWelcomeTextAction = (welcomeText, dispatch) => {
dispatch({ type: 'welcomeText', payload: { welcomeText } }) 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)
})
}

@ -67,6 +67,17 @@ export const registerCommandReducer = (state, action) => {
commands: Object.assign(initialState.commands, action.payload.commands), commands: Object.assign(initialState.commands, action.payload.commands),
data: Object.assign(initialState.data, action.payload.data) 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 : default :
return { state } return { state }
} }
@ -75,14 +86,12 @@ export const registerCommandReducer = (state, action) => {
export const registerFilterReducer = (state, action) => { export const registerFilterReducer = (state, action) => {
switch (action.type) { switch (action.type) {
case 'log': case 'log':
console.log({ action }, { state }, 'register Filter')
return { return {
...state, ...state,
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns) data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
} }
case 'info': case 'info':
console.log({ action }, 'registerFilter')
return { return {
...state, ...state,
data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns) data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns)
@ -132,7 +141,8 @@ export const remixWelcomeTextReducer = (state, action) => {
} }
export const registerScriptRunnerReducer = (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) { switch (action.type) {
case 'log': case 'log':
return { return {
@ -159,5 +169,26 @@ export const registerScriptRunnerReducer = (state, action) => {
...state, ...state,
journalBlocks: initialState.journalBlocks.push({ message: action.payload.message, style: 'text-log' }) 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: '' })
}
} }
} }

@ -17,6 +17,18 @@ element.style {
input #terminalCliInput { input #terminalCliInput {
} }
.border-primary {
border-color: #007aa6!important;
}
.border {
border: 1px solid #3f4455!important;
}
.selectedOptions {
background-color: #222336;
}
.panel { .panel {
position : relative; position : relative;
display : flex; display : flex;
@ -138,6 +150,10 @@ input #terminalCliInput {
cursor : row-resize; cursor : row-resize;
z-index : 999; z-index : 999;
} }
.console {
cursor : pointer;
}
.dragbarHorizontal:hover { .dragbarHorizontal:hover {
background-color: #007AA6; background-color: #007AA6;
@ -171,7 +187,6 @@ input #terminalCliInput {
.popup { .popup {
position : absolute; position : absolute;
text-align : left; text-align : left;
display : none;
width : 95%; width : 95%;
font-family : monospace; font-family : monospace;
background-color : var(--secondary); background-color : var(--secondary);
@ -234,4 +249,108 @@ input #terminalCliInput {
@keyframes animatetop { @keyframes animatetop {
from {bottom: -300px; opacity: 0} from {bottom: -300px; opacity: 0}
to {bottom: 0; opacity: 1} to {bottom: 0; opacity: 1}
} }
/* 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;
}

@ -1,15 +1,26 @@
import React, { useState, useEffect, useReducer, useRef, SyntheticEvent, MouseEvent } from 'react' // eslint-disable-line import React, { useState, useEffect, useReducer, useRef, SyntheticEvent, MouseEvent } from 'react' // eslint-disable-line
import { useKeyPress } from './custom-hooks/useKeyPress' // eslint-disable-line import { useKeyPress } from './custom-hooks/useKeyPress' // eslint-disable-line
import { useWindowResize } from 'beautiful-react-hooks' 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 { initialState, registerCommandReducer, registerFilterReducer, addCommandHistoryReducer, registerScriptRunnerReducer, remixWelcomeTextReducer } from './reducers/terminalReducer'
import { remixWelcome } from './reducers/remixWelcom' 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 {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 javascriptserialize from 'javascript-serialize'
import jsbeautify from 'js-beautify' 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 './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 */ /* eslint-disable-next-line */
export interface RemixUiTerminalProps { export interface RemixUiTerminalProps {
@ -27,6 +38,15 @@ export interface RemixUiTerminalProps {
config: any config: any
thisState: any thisState: any
vm: any vm: any
commandHelp: any,
_deps: any,
fileImport: any,
gistHandler: any,
sourceHighlighter: any,
registry: any,
commands: any,
txListener: any,
eventsDecoder: any
} }
export interface ClipboardEvent<T = Element> extends SyntheticEvent<T, any> { export interface ClipboardEvent<T = Element> extends SyntheticEvent<T, any> {
@ -34,7 +54,6 @@ export interface ClipboardEvent<T = Element> extends SyntheticEvent<T, any> {
} }
export const RemixUiTerminal = (props: RemixUiTerminalProps) => { export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const [toggleDownUp, setToggleDownUp] = useState('fa-angle-double-down') const [toggleDownUp, setToggleDownUp] = useState('fa-angle-double-down')
const [inserted, setInserted] = useState(false) const [inserted, setInserted] = useState(false)
const [_cmdIndex, setCmdIndex] = useState(-1) const [_cmdIndex, setCmdIndex] = useState(-1)
@ -51,12 +70,14 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const [cmdHistory, cmdHistoryDispatch] = useReducer(addCommandHistoryReducer, initialState) const [cmdHistory, cmdHistoryDispatch] = useReducer(addCommandHistoryReducer, initialState)
const [scriptRunnserState, scriptRunnerDispatch] = useReducer(registerScriptRunnerReducer, initialState) const [scriptRunnserState, scriptRunnerDispatch] = useReducer(registerScriptRunnerReducer, initialState)
const [welcomeTextState, welcomTextDispath] = useReducer(remixWelcomeTextReducer, initialState) const [welcomeTextState, welcomTextDispath] = useReducer(remixWelcomeTextReducer, initialState)
const [isListeningOnNetwork, setIsListeningOnNetwork] = useState(false)
const [autoCompletState, setAutoCompleteState] = useState({ const [autoCompletState, setAutoCompleteState] = useState({
activeSuggestion: 0, activeSuggestion: 0,
data: { data: {
_options: [] _options: []
}, },
_startingElement: 0, _startingElement: 0,
autoCompleteSelectedItem: {},
_elementToShow: 4, _elementToShow: 4,
_selectedElement: 0, _selectedElement: 0,
filteredCommands: [], filteredCommands: [],
@ -64,58 +85,13 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
showSuggestions: false, showSuggestions: false,
text: '', text: '',
userInput: '', userInput: '',
extraCommands: [] extraCommands: [],
}) commandHistoryIndex: 0
const [state, setState] = useState({
journalBlocks: {
intro: (
<div>
<div> - Welcome to Remix {props.version} - </div>
<br/>
<div>You can use this terminal to: </div>
<ul className='ul'>
<li>Check transactions details and start debugging.</li>
<li>Execute JavaScript scripts:
<br />
<i> - Input a script directly in the command line interface </i>
<br />
<i> - Select a Javascript file in the file explorer and then run \`remix.execute()\` or \`remix.exeCurrent()\` in the command line interface </i>
<br />
<i> - Right click on a JavaScript file in the file explorer and then click \`Run\` </i>
</li>
</ul>
<div>The following libraries are accessible:</div>
<ul className='ul'>
<li><a target="_blank" href="https://web3js.readthedocs.io/en/1.0/">web3 version 1.0.0</a></li>
<li><a target="_blank" href="https://docs.ethers.io">ethers.js</a> </li>
<li><a target="_blank" href="https://www.npmjs.com/package/swarmgw">swarmgw</a> </li>
<li>remix (run remix.help() for more info)</li>
</ul>
</div>
),
text: (<div>David</div>)
},
data: {
// lineLength: props.options.lineLength || 80,
session: [],
activeFilters: { commands: {}, input: '' },
filterFns: {}
},
_commands: {},
commands: {},
_JOURNAL: [],
_jobs: [],
_INDEX: {},
_INDEXall: [],
_INDEXallMain: [],
_INDEXcommands: {},
_INDEXcommandsMain: {}
}) })
const _scopedCommands = () => { const [searchInput, setSearchInput] = useState('')
// const [showTableDetails, setShowTableDetails] = useState([])
} const [showTableDetails, setShowTableDetails] = useState(null)
useWindowResize(() => { useWindowResize(() => {
setWindowHeight(window.innerHeight) setWindowHeight(window.innerHeight)
@ -125,7 +101,8 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const inputEl = useRef(null) const inputEl = useRef(null)
// events // events
useEffect(() => { useEffect(() => {
registerRemixWelcomeTextAction(remixWelcome, welcomTextDispath) initListeningOnNetwork(props, scriptRunnerDispatch)
// registerRemixWelcomeTextAction(remixWelcome, welcomTextDispath)
registerLogScriptRunnerAction(props.thisState, 'log', newstate.commands, scriptRunnerDispatch) registerLogScriptRunnerAction(props.thisState, 'log', newstate.commands, scriptRunnerDispatch)
registerInfoScriptRunnerAction(props.thisState, 'info', newstate.commands, scriptRunnerDispatch) registerInfoScriptRunnerAction(props.thisState, 'info', newstate.commands, scriptRunnerDispatch)
registerWarnScriptRunnerAction(props.thisState, 'warn', 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('info', _blocksRenderer('info'), { activate: true }, dispatch)
registerCommandAction('warn', _blocksRenderer('warn'), { activate: true }, dispatch) registerCommandAction('warn', _blocksRenderer('warn'), { activate: true }, dispatch)
registerCommandAction('error', _blocksRenderer('error'), { activate: true }, dispatch) registerCommandAction('error', _blocksRenderer('error'), { activate: true }, dispatch)
registerCommandAction('script', function execute (args, scopedCommands, append) { registerCommandAction('script', function execute (args, scopedCommands, append) {
var script = String(args[0]) var script = String(args[0])
console.log({ script })
console.log({ scopedCommands })
_shell(script, scopedCommands, function (error, output) { _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 } }) 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) }, { activate: true }, dispatch)
filterFnAction('log', basicFilter, filterDispatch) filterFnAction('log', basicFilter, filterDispatch)
filterFnAction('info', basicFilter, filterDispatch) filterFnAction('info', basicFilter, filterDispatch)
filterFnAction('warn', basicFilter, filterDispatch) filterFnAction('warn', basicFilter, filterDispatch)
@ -157,7 +137,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
// dispatch({ type: 'html', payload: { commands: htmlresullt.commands } }) // dispatch({ type: 'html', payload: { commands: htmlresullt.commands } })
// dispatch({ type: 'log', payload: { _commands: logresult._commands } }) // dispatch({ type: 'log', payload: { _commands: logresult._commands } })
// registerCommand('log', _blocksRenderer('log'), { activate: true }) // registerCommand('log', _blocksRenderer('log'), { activate: true })
}, [newstate.journalBlocks, props.thisState.autoCompletePopup, autoCompletState.text]) }, [props.thisState.autoCompletePopup, autoCompletState.text])
const placeCaretAtEnd = (el) => { const placeCaretAtEnd = (el) => {
el.focus() el.focus()
@ -169,6 +149,53 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
sel.addRange(range) 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 const _shell = async (script, scopedCommands, done) => { // default shell
if (script.indexOf('remix:') === 0) { 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.') 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) { if (script.indexOf('remix.') === 0) {
// we keep the old feature. This will basically only be called when the command is querying the "remix" object. // 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 // for all the other case, we use the Code Executor plugin
var context = props.cmdInterpreter var context = domTerminalFeatures()
try { try {
var cmds = props.vm.createContext(context) const cmds = vm.createContext(context)
var result = props.vm.runInContext(script, cmds) // const result
return done(null, result) let result = vm.runInContext(script, cmds)
if (script === 'remix.exeCurrent()') {
result = exeCurrent(undefined)
} else {
if (result === {}) {
for (const k in result) {
result = +`<div> {k}: ${result[k]}</div> <br>`
}
}
}
console.log(result === {}, ' is result === object')
console.log({ result })
return done(null, '')
} catch (error) { } catch (error) {
return done(error.message) return done(error.message)
} }
} }
try { try {
let result: any
if (script.trim().startsWith('git')) { if (script.trim().startsWith('git')) {
// result = await this.call('git', 'execute', script) // result = await this.call('git', 'execute', script)
} else { } else {
result = await props.thisState.call('scriptRunner', 'execute', script) result = await props.thisState.call('scriptRunner', 'execute', script)
} }
console.log({ result })
done() done()
} catch (error) { } catch (error) {
done(error.message || error) done(error.message || error)
@ -231,7 +273,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
} }
} }
const _appendItem = (item: any) => { const _appendItem = (item: any) => {
let { _JOURNAL, _jobs, data } = state let { _JOURNAL, _jobs, data } = state
const self = props const self = props
@ -268,7 +309,20 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
} }
const handleKeyDown = (event) => { 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) { // <ctrl+enter> if (event.ctrlKey) { // <ctrl+enter>
// on enter, append the value in the cli input to the journal // on enter, append the value in the cli input to the journal
inputEl.current.focus() inputEl.current.focus()
@ -287,7 +341,50 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
inputEl.current.focus() inputEl.current.focus()
setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false })) setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false }))
} }
} else if (event.which === 38) { // <arrowUp> } 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) { // <arrowUp>
const len = _cmdHistory.length const len = _cmdHistory.length
if (len === 0) event.preventDefault() if (len === 0) event.preventDefault()
if (_cmdHistory.length - 1 > _cmdIndex) { if (_cmdHistory.length - 1 > _cmdIndex) {
@ -295,8 +392,14 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
} }
inputEl.current.innerText = _cmdHistory[_cmdIndex] inputEl.current.innerText = _cmdHistory[_cmdIndex]
inputEl.current.focus() inputEl.current.focus()
} } else if (event.which === 40 && autoCompletState.showSuggestions) {
else if (event.which === 40) { 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) { if (_cmdIndex > -1) {
setCmdIndex(prevState => prevState--) setCmdIndex(prevState => prevState--)
} }
@ -305,6 +408,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
} else { } else {
setCmdTemp(inputEl.current.innerText) setCmdTemp(inputEl.current.innerText)
} }
console.log({ autoCompletState })
} }
const moveGhostbar = (event) => { const moveGhostbar = (event) => {
@ -326,14 +430,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
const mousedown = (event: MouseEvent) => { const mousedown = (event: MouseEvent) => {
setSeparatorYPosition(event.clientY) setSeparatorYPosition(event.clientY)
setDragging(true) 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) => { const onMouseMove: any = (e: MouseEvent) => {
@ -352,36 +448,13 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
setDragging(false) setDragging(false)
} }
/* end of mouse event */ /* end of mouse event */
const cancelGhostbar = (event) => {
if (event.keyCode === 27) {
console.log('event .key code 27')
}
}
useEffect(() => { 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('mousemove', onMouseMove)
document.addEventListener('mouseup', onMouseUp) document.addEventListener('mouseup', onMouseUp)
return () => { 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('mousemove', onMouseMove)
document.removeEventListener('mouseup', onMouseUp) document.removeEventListener('mouseup', onMouseUp)
} }
@ -448,85 +521,378 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
/* end of block content that gets rendered from script Runner */ /* end of block content that gets rendered from script Runner */
const handleClearConsole = () => {
dispatch({ type: 'clearconsole', payload: [] })
inputEl.current.focus()
}
/* start of autoComplete */ /* 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) => { const onChange = (event: any) => {
event.preventDefault() event.preventDefault()
const inputString = event.target.value const inputString = event.target.value
console.log(event) if (matched(allPrograms, inputString) || inputString.includes('.')) {
console.log({ inputString }) setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: true, userInput: inputString }))
setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: true, userInput: inputString })) const textList = inputString.split('.')
const textList = inputString.split(' ') if (textList.length === 1) {
const autoCompleteInput = textList.length > 1 ? textList[textList.length - 1] : textList[0] setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [] } }))
allPrograms.forEach(item => { const result = Objectfilter(allPrograms, autoCompletState.userInput)
const program = getKeyOf(item) setAutoCompleteState(prevState => ({ ...prevState, data: { _options: result } }))
console.log({ program }) } else {
if (program.substring(0, program.length - 1).includes(autoCompleteInput.trim())) { setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [] } }))
setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [item] } })) const result = Objectfilter(allCommands, autoCompletState.userInput)
} else if (autoCompleteInput.trim().includes(program) || (program === autoCompleteInput.trim())) { setAutoCompleteState(prevState => ({ ...prevState, data: { _options: result } }))
allCommands.forEach(item => {
console.log({ item })
const command = getKeyOf(item)
if (command.includes(autoCompleteInput.trim())) {
setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [item] } }))
}
})
} }
}) } else {
autoCompletState.extraCommands.forEach(item => { setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false, userInput: inputString }))
const command = getKeyOf(item) }
if (command.includes(autoCompleteInput.trim())) { }
setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [item] } }))
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 (<i className='txStatus succeeded fas fa-check-circle'></i>)
}
if (type === 'call' || type === 'unknownCall' || type === 'unknown') {
return (<i className='txStatus call'>call</i>)
} else if (tx.status === '0x0' || tx.status === false) {
return (<i className='txStatus failed fas fa-times-circle'></i>)
} else {
return (<i className='txStatus notavailable fas fa-circle-thin' title='Status not available' ></i>)
}
}
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 (
<div>
<span className='txLog_7Xiho'>
<span className='tx'>[{vm}]</span>
<div className='txItem'><span className='txItemTitle'>from:</span> {from}</div>
<div className='txItem'><span className='txItemTitle'>to:</span> {to}</div>
<div className='txItem'><span className='txItemTitle'>value:</span> {value} wei</div>
<div className='txItem'><span className='txItemTitle'>data:</span> {input}</div>
<div className='txItem'><span className='txItemTitle'>logs:</span> {logs}</div>
<div className='txItem'><span className='txItemTitle'>hash:</span> {hash}</div>
</span>
</div>)
} else if (blockchain.getProvider() !== 'vm' && data.resolvedData) {
return (
<div>
<span className='txLog_7Xiho'>
<span className='tx'>[block:${block} txIndex:${i}]</span>
<div className='txItem'><span className='txItemTitle'>from:</span> {from}</div>
<div className='txItem'><span className='txItemTitle'>to:</span> {to}</div>
<div className='txItem'><span className='txItemTitle'>value:</span> {value} wei</div>
<div className='txItem'><span className='txItemTitle'>data:</span> {input}</div>
<div className='txItem'><span className='txItemTitle'>logs:</span> {logs}</div>
<div className='txItem'><span className='txItemTitle'>hash:</span> {hash}</div>
</span>
</div>)
} else {
to = helper.shortenHexData(to)
hash = helper.shortenHexData(data.blockHash)
return (
<div>
<span className='txLog'>
<span className='tx'>[block:${block} txIndex:${i}]</span>
<div className='txItem'><span className='txItemTitle'>from:</span> {from}</div>
<div className='txItem'><span className='txItemTitle'>to:</span> {to}</div>
<div className='txItem'><span className='txItemTitle'>value:</span> {value} wei</div>
</span>
</div>)
}
}
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 let stringified = ' - '
event.preventDefault() if (opts.logs && opts.logs.decoded) {
textList.pop() stringified = typeConversion.stringify(opts.logs.decoded)
textList.push(getKeyOf(autoCompletState.data._options[0])) }
handleSelect(`${textList}`.replace(/,/g, ' ')) const val = opts.val != null ? typeConversion.toInt(opts.val) : 0
return (
<table className='txTable' id='txTable' data-id={`txLoggerTable${opts.hash}`}>
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> status </td>
<td className='td' data-id={`txLoggerTableStatus${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.status}{msg}</td>
</tr>
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> transaction hash </td>
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.hash}
<CopyToClipboard content={opts.hash}/>
</td>
</tr>
{
opts.contractAddress && (
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> contract address </td>
<td className='td' data-id={`txLoggerTableContractAddress${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.contractAddress}
<CopyToClipboard content={opts.contractAddress}/>
</td>
</tr>
)
}
{
opts.from && (
<tr className='tr'>
<td className='td tableTitle' data-shared={`key_${opts.hash}`}> from </td>
<td className='td' data-id={`txLoggerTableFrom${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.from}
<CopyToClipboard content={opts.from}/>
</td>
</tr>
)
}
{
opts.to && (
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> to </td>
<td className='td' data-id={`txLoggerTableTo${opts.hash}`} data-shared={`pair_${opts.hash}`}>{toHash}
<CopyToClipboard content={data.to ? data.to : toHash}/>
</td>
</tr>
)
}
{
opts.gas && (
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> gas </td>
<td className='td' data-id={`txLoggerTableGas${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.gas} gas
<CopyToClipboard content={opts.gas}/>
</td>
</tr>
)
}
{
opts.transactionCost && (
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> transaction cost </td>
<td className='td' data-id={`txLoggerTableTransactionCost${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.transactionCost} gas {callWarning}
<CopyToClipboard content={opts.transactionCost}/>
</td>
</tr>
)
}
{
opts.executionCost && (
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> execution cost </td>
<td className='td' data-id={`txLoggerTableExecutionHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.executionCost} gas {callWarning}
<CopyToClipboard content={opts.executionCost}/>
</td>
</tr>
)
}
{opts.hash && (
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> hash </td>
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts.hash}
<CopyToClipboard content={opts.hash}/>
</td>
</tr>
)}
{opts.input && (
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> input </td>
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{helper.shortenHexData(opts.input)}
<CopyToClipboard content={opts.input}/>
</td>
</tr>
)}
{opts['decoded input'] && (
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> decode input </td>
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts['decoded input']}
<CopyToClipboard content={opts['decoded input']}/>
</td>
</tr>
)}
{opts['decoded output'] && (
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> decode output </td>
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{opts['decoded output']}
<CopyToClipboard content={opts['decoded output']}/>
</td>
</tr>
)}
{opts.logs && (
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> logs </td>
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>
{JSON.stringify(stringified, null, '\t')}
<CopyToClipboard content={JSON.stringify(stringified, null, '\t')}/>
<CopyToClipboard content={JSON.stringify(opts.logs.raw || '0')}/>
</td>
</tr>
)}
{opts.val && (
<tr className='tr'>
<td className='td' data-shared={`key_${opts.hash}`}> val </td>
<td className='td' data-id={`txLoggerTableHash${opts.hash}`} data-shared={`pair_${opts.hash}`}>{val} wei
<CopyToClipboard content={`${val} wei`}/>
</td>
</tr>
)}
</table>
)
}
const debug = (event, tx) => {
event.stopPropagation()
if (tx.isCall && tx.envMode !== 'vm') {
console.log('start debugging')
return (<ModalDialog
hide={false}
handleHide={() => {} }
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 (
<span id={`tx${tx.hash}`} key={index}>
<div className="log" onClick={(event) => txDetails(event, tx, obj)}>
{/* onClick={e => txDetails(e, tx, data, obj)} */}
{checkTxStatus(receipt || tx, txType)}
{context({ from, to, tx }, props.blockchain)}
<div className='buttons'>
<div className='debug btn btn-primary btn-sm' onClick={(event) => debug(event, tx)}>Debug</div>
</div>
<i className = {`arrow fas ${(showDetails) ? 'fa-angle-up' : 'fa-angle-down'}`}></i>
</div>
{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}
</span>
)
}
const handleAutoComplete = () => ( const handleAutoComplete = () => (
<div className="popup alert alert-secondary"> <div className='popup alert alert-secondary' style={{ display: autoCompletState.showSuggestions && autoCompletState.userInput !== '' ? 'block' : 'none' }}>
<div> <div>
${autoCompletState.data._options.map((item, index) => { {autoCompletState.data._options.map((item, index) => {
return ( return (
<div key={index}>auto complete here</div> <div key={index} data-id="autoCompletePopUpAutoCompleteItem" className={`autoCompleteItem listHandlerShow item ${autoCompletState.data._options[autoCompletState.activeSuggestion] === item ? 'border border-primary selectedOptions' : ''}`} onKeyDown={ handleSelect }>
// <div data-id="autoCompletePopUpAutoCompleteItem" className={`autoCompleteItem listHandlerHide item ${_selectedElement === index ? 'border border-primary' : ''}`}> <div>
// <div value={index} onClick={(event) => { handleSelect(event.srcElement.innerText) }}> {getKeyOf(item)}
// {getKeyOf(item)} </div>
// </div> <div>
// <div> {getValueOf(item)}
// {getValueOf(item)} </div>
// </div> </div>
// </div>
) )
})} })}
</div> </div>
{/* <div className="listHandlerHide">
<div className="pageNumberAlignment">Page ${(self._startingElement / self._elementsToShow) + 1} of ${Math.ceil(data._options.length / self._elementsToShow)}</div>
</div> */}
</div> </div>
) )
/* end of autoComplete */ /* end of autoComplete */
return ( return (
<div style={{ height: '323px' }} className='panel_2A0YE0'> <div style={{ height: '323px', flexGrow: 1 }} className='panel_2A0YE0'>
{console.log({ newstate })} {console.log({ newstate })}
{console.log({ props })} {console.log({ props })}
{console.log({ autoCompletState })}
<div className="bar_2A0YE0"> <div className="bar_2A0YE0">
{/* ${self._view.dragbar} */} {/* ${self._view.dragbar} */}
<div className="dragbarHorizontal" onMouseDown={mousedown} id='dragId'></div> <div className="dragbarHorizontal" onMouseDown={mousedown} id='dragId'></div>
<div className="menu_2A0YE0 border-top border-dark bg-light" data-id="terminalToggleMenu"> <div className="menu_2A0YE0 border-top border-dark bg-light" data-id="terminalToggleMenu">
{/* ${self._view.icon} */} {/* ${self._view.icon} */}
<i className={`mx-2 toggleTerminal_2A0YE0 fas ${toggleDownUp}`} data-id="terminalToggleIcon" onClick={ handleMinimizeTerminal }></i> <i className={`mx-2 toggleTerminal_2A0YE0 fas ${toggleDownUp}`} data-id="terminalToggleIcon" onClick={ handleMinimizeTerminal }></i>
<div className="mx-2" id="clearConsole" data-id="terminalClearConsole" > <div className="mx-2 console" id="clearConsole" data-id="terminalClearConsole" onClick={handleClearConsole} >
<i className="fas fa-ban" aria-hidden="true" title="Clear console" <i className="fas fa-ban" aria-hidden="true" title="Clear console"
></i> ></i>
</div> </div>
@ -537,7 +903,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
<input <input
className="custom-control-input" className="custom-control-input"
id="listenNetworkCheck" id="listenNetworkCheck"
// onChange=${listenOnNetwork} onChange={listenOnNetwork}
type="checkbox" type="checkbox"
title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you" title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you"
/> />
@ -554,6 +920,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
{/* ${self._view.inputSearch} */} {/* ${self._view.inputSearch} */}
<input <input
// spellcheck = "false" // spellcheck = "false"
onChange={(event) => setSearchInput(event.target.value) }
type="text" type="text"
className="border filter_2A0YE0 form-control" className="border filter_2A0YE0 form-control"
id="searchInput" id="searchInput"
@ -565,7 +932,7 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
</div> </div>
<div tabIndex={-1} className="terminal_container_2A0YE0" data-id="terminalContainer" > <div tabIndex={-1} className="terminal_container_2A0YE0" data-id="terminalContainer" >
{ {
(autoCompletState.showSuggestions && autoCompletState.userInput) && handleAutoComplete() handleAutoComplete()
} }
<div data-id="terminalContainerDisplay" style = {{ <div data-id="terminalContainerDisplay" style = {{
position: 'absolute', position: 'absolute',
@ -576,11 +943,26 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
}}></div> }}></div>
<div className="terminal_2A0YE0"> <div className="terminal_2A0YE0">
<div id="journal" className="journal_2A0YE0" data-id="terminalJournal"> <div id="journal" className="journal_2A0YE0" data-id="terminalJournal">
{(newstate.journalBlocks).map((x, index) => ( {newstate.journalBlocks && newstate.journalBlocks.map((x, index) => {
<div className="px-4 block_2A0YE0" data-id="block_null" key={index}> if (x.name === 'emptyBlock') {
<span className={x.style}>{x.message}</span> return (
</div> <div className="px-4 block_2A0YE0" data-id="block_null" key={index}>
))} <span className='txLog'>
<span className='tx'><div className='txItem'>[<span className='txItemTitle'>block:{x.message} - </span> 0 {'transactions'} ] </div></span></span>
</div>
)
} 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 (<div className='px-4 block_2A0YE0' data-id={`block_tx${trans.tx.hash}`}> {renderKnownTransactions(trans.tx, trans.receipt, index)} </div>)
})
} else {
return (
<div className="px-4 block_2A0YE0" data-id="block_null" key={index}>
<span className={x.style}>{x.message}</span>
</div>
)
}
})}
<div className="anchor"> <div className="anchor">
{/* ${background} */} {/* ${background} */}
<div className="overlay background"></div> <div className="overlay background"></div>

@ -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
// }

@ -6,3 +6,37 @@ export const getKeyOf = (item) => {
export const getValueOf = (item) => { export const getValueOf = (item) => {
return Object.values(item)[0] 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
}

Loading…
Cancel
Save