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

pull/5370/head
davidzagi93@gmail.com 3 years ago
parent 31341f4661
commit 0b78f4f705
  1. 5
      apps/remix-ide/src/app/panels/terminal.js
  2. 446
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  3. 176
      libs/remix-ui/terminal/src/lib/utils/helper.ts

@ -15,6 +15,11 @@ var globalRegistry = require('../../global/registry')
var SourceHighlighter = require('../../app/editor/sourceHighlighter') var SourceHighlighter = require('../../app/editor/sourceHighlighter')
var GistHandler = require('../../lib/gist-handler') var GistHandler = require('../../lib/gist-handler')
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) }

@ -16,7 +16,6 @@ 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 helper from '../../../../../apps/remix-ide/src/lib/helper'
const remixLib = require('@remix-project/remix-lib') const remixLib = require('@remix-project/remix-lib')
var typeConversion = remixLib.execution.typeConversion var typeConversion = remixLib.execution.typeConversion
@ -180,6 +179,53 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
if (cb) cb() if (cb) cb()
} }
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.')
@ -408,26 +454,10 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
/* end of mouse event */ /* end of mouse event */
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)
} }
@ -945,69 +975,363 @@ 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('.')
const autoCompleteInput = textList.length > 1 ? textList[textList.length - 1] : textList[0] if (textList.length === 1) {
allPrograms.forEach(item => { setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [] } }))
const program = getKeyOf(item) const result = Objectfilter(allPrograms, autoCompletState.userInput)
console.log({ program }) setAutoCompleteState(prevState => ({ ...prevState, data: { _options: result } }))
if (program.substring(0, program.length - 1).includes(autoCompleteInput.trim())) { } else {
setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [item] } })) setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [] } }))
} else if (autoCompleteInput.trim().includes(program) || (program === autoCompleteInput.trim())) { const result = Objectfilter(allCommands, autoCompletState.userInput)
allCommands.forEach(item => { setAutoCompleteState(prevState => ({ ...prevState, data: { _options: result } }))
console.log({ item })
const command = getKeyOf(item)
if (command.includes(autoCompleteInput.trim())) {
setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [item] } }))
} }
}) } else {
setAutoCompleteState(prevState => ({ ...prevState, showSuggestions: false, userInput: inputString }))
} }
})
autoCompletState.extraCommands.forEach(item => {
const command = getKeyOf(item)
if (command.includes(autoCompleteInput.trim())) {
setAutoCompleteState(prevState => ({ ...prevState, data: { _options: [item] } }))
} }
})
if (autoCompletState.data._options.length === 1 && event.which === 9) { const handleSelect = (event) => {
// if only one option and tab is pressed, we resolve it const suggestionCount = autoCompletState.activeSuggestion
event.preventDefault() if (event.keyCode === 38) {
textList.pop() if (autoCompletState.activeSuggestion === 0) {
textList.push(getKeyOf(autoCompletState.data._options[0])) return
handleSelect(`${textList}`.replace(/,/g, ' ')) }
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'
}
} }
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 (
<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 */

@ -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
// }
Loading…
Cancel
Save