diff --git a/.gitignore b/.gitignore index 4f0a8bafd7..0da94f8b75 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ soljson.js *.launch .settings/ *.sublime-workspace +.vscode/ # IDE - VSCode .vscode/* diff --git a/apps/remix-ide/src/app/panels/terminal.js b/apps/remix-ide/src/app/panels/terminal.js index 95bca9da9a..eb67e15e55 100644 --- a/apps/remix-ide/src/app/panels/terminal.js +++ b/apps/remix-ide/src/app/panels/terminal.js @@ -69,26 +69,6 @@ class Terminal extends Plugin { this._INDEX.allMain = [] this._INDEX.commands = {} this._INDEX.commandsMain = {} - this.registerCommand('html', this._blocksRenderer('html'), { activate: true }) - this.registerCommand('log', this._blocksRenderer('log'), { activate: true }) - this.registerCommand('info', this._blocksRenderer('info'), { activate: true }) - this.registerCommand('warn', this._blocksRenderer('warn'), { activate: true }) - this.registerCommand('error', this._blocksRenderer('error'), { activate: true }) - this.registerCommand('script', function execute (args, scopedCommands, append) { - var script = String(args[0]) - this._shell(script, scopedCommands, function (error, output) { - if (error) scopedCommands.error(error) - else if (output) scopedCommands.log(output) - }) - }, { activate: true }) - function basicFilter (value, query) { try { return value.indexOf(query) !== -1 } catch (e) { return false } } - - this.registerFilter('log', basicFilter) - this.registerFilter('info', basicFilter) - this.registerFilter('warn', basicFilter) - this.registerFilter('error', basicFilter) - this.registerFilter('script', basicFilter) - if (opts.shell) this._shell = opts.shell // ??? register(this) } @@ -109,12 +89,18 @@ class Terminal extends Plugin { this.renderComponent() } + onDeactivation () { + this.off('scriptRunner', 'log') + this.off('scriptRunner', 'info') + this.off('scriptRunner', 'warn') + this.off('scriptRunner', 'error') + } + render () { return this.element } renderComponent () { - ReactDOM.render( , this.element ) @@ -213,6 +200,8 @@ class Terminal extends Plugin { 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 () { diff --git a/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts b/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts index e69de29bb2..4e14ac46c8 100644 --- a/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts +++ b/libs/remix-ui/terminal/src/lib/actions/terminalAction.ts @@ -0,0 +1,103 @@ + +export const registerCommandAction = (name, command, activate, dispatch) => { + const commands: any = {} + const _commands: any = {} + _commands[name] = command + const data: any = { + // lineLength: props.options.lineLength || 80, + session: [], + activeFilters: { commands: {}, input: '' }, + filterFns: {} + } + const _INDEX = { + all: [], + allMain: [], + commands: {}, + commandsMain: {} + } + + const registerFilter = (commandName, filterFn) => { + data.filterFns[commandName] = filterFn + } + + // const _appendItem = (item) => { + // var { el, gidx } = item + // _JOURNAL[gidx] = item + // if (!_jobs.length) { + // requestAnimationFrame(function updateTerminal () { + // _jobs.forEach(el => _view.journal.appendChild(el)) + // .scroll2bottom() + // ._jobs = [] + // }) + // } + // if (data.activeFilters.commands[item.cmd]) _jobs.push(el) + // } + + commands[name] = function () { + const args = [...arguments] + const steps = [] + const root = { steps, cmd: name, gidx: 0, idx: 0 } + const ITEM = { root, cmd: name } + root.gidx = _INDEX.allMain.push(ITEM) - 1 + // root.idx = _INDEX.commandsMain[name].push(ITEM) - 1 + let item + function append (cmd, params, el) { + if (cmd) { // subcommand + item = { el, cmd, root } + } else { // command + item = ITEM + item.el = el + cmd = name + } + item.gidx = _INDEX.all.push(item) - 1 + item.idx = _INDEX.commands[cmd].push(item) - 1 + item.step = steps.push(item) - 1 + item.args = params + // _appendItem(item) + console.log({ item }, 'append items') + // self._appendItem(item) + } + var scopedCommands = _scopeCommands(append) + command(args, scopedCommands, el => append(null, args, blockify(el))) + console.log({ args }) + } + const help = typeof command.help === 'string' ? command.help : [ + '// no help available for:', `terminal.command.${name}` + ].join('\n') + commands[name].toString = () => { return help } + commands[name].help = help + data.activeFilters.commands[name] = activate && activate.activate + if (activate.filterFn) { + registerFilter(name, activate.filterFn) + } + dispatch({ type: name, payload: { commands: commands, _commands: _commands, data: data } }) + + const blockify = (el) => { + return `
${el}
` + } + + const _scopeCommands = (append) => { + const scopedCommands = {} + Object.keys(commands).forEach(function makeScopedCommand (cmd) { + var command = _commands[cmd] + scopedCommands[cmd] = function _command () { + var args = [...arguments] + console.log({ cmd }, { args }, { blockify }) + command(args, scopedCommands, el => append(cmd, args, blockify(el))) + } + }) + console.log({ scopedCommands }) + return scopedCommands + } + +} + +export const filterFnAction = (name, filterFn, dispatch) => { + const data: any = { + // session: [], + // activeFilters: { commands: {}, input: '' }, + filterFns: {} + } + data.filterFns[name] = filterFn + dispatch({ type: name, payload: { data: data } }) +} diff --git a/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts b/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts index e69de29bb2..43fae4b344 100644 --- a/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts +++ b/libs/remix-ui/terminal/src/lib/reducers/terminalReducer.ts @@ -0,0 +1,120 @@ +export const initialState = { + journalBlocks: { + }, + data: { + // lineLength: props.options.lineLength || 80, + session: [], + activeFilters: { commands: {}, input: '' }, + filterFns: {} + }, + _commandHistory: [], + _commands: {}, + commands: {}, + _JOURNAL: [], + _jobs: [], + _INDEX: { + }, + _INDEXall: [], + _INDEXallMain: [], + _INDEXcommands: {}, + _INDEXcommandsMain: {} +} + +export const registerCommandReducer = (state, action) => { + switch (action.type) { + case 'html' : + return { + ...state, + _commands: Object.assign(initialState._commands, action.payload._commands), + commands: Object.assign(initialState.commands, action.payload.commands), + data: Object.assign(initialState.data, { ...action.payload.data }) + } + case 'log': + return { + ...state, + _commands: Object.assign(initialState._commands, action.payload._commands), + commands: Object.assign(initialState.commands, action.payload.commands), + data: Object.assign(initialState.data, { ...action.payload.data }) + + } + case 'info': + return { + ...state, + _commands: Object.assign(initialState._commands, action.payload._commands), + commands: Object.assign(initialState.commands, action.payload.commands), + data: Object.assign(initialState.data, action.payload.data) + } + case 'warn': + return { + ...state, + _commands: Object.assign(initialState._commands, action.payload._commands), + commands: Object.assign(initialState.commands, action.payload.commands), + data: Object.assign(initialState.data, action.payload.data) + } + case 'error': + return { + ...state, + _commands: Object.assign(initialState._commands, action.payload._commands), + commands: Object.assign(initialState.commands, action.payload.commands), + data: Object.assign(initialState.data, action.payload.data) + } + case 'script': + return { + ...state, + _commands: Object.assign(initialState._commands, action.payload._commands), + commands: Object.assign(initialState.commands, action.payload.commands), + data: Object.assign(initialState.data, action.payload.data) + } + default : + return { state } + } +} + +export const registerFilterReducer = (state, action) => { + switch (action.type) { + case 'log': + console.log({ action }, { state }, 'register Filter') + return { + ...state, + data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns) + + } + case 'info': + console.log({ action }, 'registerFilter') + return { + ...state, + data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns) + } + case 'warn': + return { + ...state, + data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns) + } + case 'error': + return { + ...state, + data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns) + } + case 'script': + return { + ...state, + data: Object.assign(initialState.data.filterFns, action.payload.data.filterFns) + } + default : + return { state } + } +} + +export const addCommandHistoryReducer = (state, action) => { + switch (action.type) { + case 'cmdHistory': + console.log({ action }, { state }, 'cmd history') + return { + ...state, + _commandHistory: initialState._commandHistory.unshift(action.payload.script) + + } + default : + return { state } + } +} diff --git a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx index 185ab2843f..9f2f421b09 100644 --- a/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx +++ b/libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx @@ -1,6 +1,10 @@ -import React, { useState, useEffect, 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 { useWindowResize } from 'beautiful-react-hooks' +import { registerCommandAction, filterFnAction } from './actions/terminalAction' +import { initialState, registerCommandReducer, registerFilterReducer, addCommandHistoryReducer } from './reducers/terminalReducer' +import javascriptserialize from 'javascript-serialize' +import jsbeautify from 'js-beautify' import './remix-ui-terminal.css' @@ -15,10 +19,10 @@ export interface RemixUiTerminalProps { options: any data: any cmdInterpreter: any - registerCommand: any command: any version: any config: any + thisState: any // blockRenderHtml: any // blockRenderLog: any @@ -44,6 +48,10 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { const [separatorYPosition, setSeparatorYPosition] = useState(undefined) const [dragging, setDragging] = useState(false) + const [newstate, dispatch] = useReducer(registerCommandReducer, initialState) + const [filterState, filterDispatch] = useReducer(registerFilterReducer, initialState) + const [cmdHistory, cmdHistoryDispatch] = useReducer(addCommandHistoryReducer, initialState) + const [state, setState] = useState({ journalBlocks: { intro: ( @@ -102,16 +110,27 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { const inputEl = useRef(null) // events useEffect(() => { - // window.addEventListener('resize', function () { - // props.event.trigger('resize', []) - // props.event.trigger('resize', []) - // }) - // return () => { - // window.removeEventListener('resize', function () { - // props.event.trigger('resize', []) - // props.event.trigger('resize', []) - // }) - // } + registerCommandAction('html', _blocksRenderer('html'), { activate: true }, dispatch) + registerCommandAction('log', _blocksRenderer('log'), { activate: true }, dispatch) + registerCommandAction('info', _blocksRenderer('info'), { activate: true }, dispatch) + registerCommandAction('warn', _blocksRenderer('warn'), { activate: true }, dispatch) + registerCommandAction('error', _blocksRenderer('error'), { activate: true }, dispatch) + registerCommandAction('script', function execute (args, scopedCommands, append) { + var script = String(args[0]) + props.thisState._shell(script, scopedCommands, function (error, output) { + if (error) scopedCommands.error(error) + else if (output) scopedCommands.log(output) + }) + }, { activate: true }, dispatch) + filterFnAction('log', basicFilter, filterDispatch) + filterFnAction('info', basicFilter, filterDispatch) + filterFnAction('warn', basicFilter, filterDispatch) + filterFnAction('error', basicFilter, filterDispatch) + filterFnAction('script', basicFilter, filterDispatch) + // console.log({ htmlresullt }, { logresult }) + // dispatch({ type: 'html', payload: { commands: htmlresullt.commands } }) + // dispatch({ type: 'log', payload: { _commands: logresult._commands } }) + // registerCommand('log', _blocksRenderer('log'), { activate: true }) }, []) const placeCaretAtEnd = (el) => { @@ -157,56 +176,6 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { } } - // const reattached = (event) => { - // let el = event.currentTarget - // var isBottomed = el.scrollHeight - el.scrollTop - el.clientHeight < 30 - // if (isBottomed) { - - // } else { - // // if (!inserted) - // } - // } - - const registerCommand = (name, command, opts) => { - const { _commands, _INDEXcommands, _INDEXallMain, _INDEXcommandsMain, _INDEXall, commands } = state - // TODO if no _commands[name] throw an error - - // TODO if typeof command !== 'function' throw error - - _commands[name] = command - _INDEXcommands[name] = [] - _INDEXallMain[name] = [] - - // TODO _command function goes here - commands[name] = function _command () { - const steps = [] - const args = [...arguments] - const gidx = 0 - const idx = 0 - const step = 0 - const root = { steps, cmd: name, gidx, idx } - const ITEM = { root, cmd: name, el: {} } - root.gidx = _INDEXallMain.push(ITEM) - 1 - root.idx = _INDEXcommandsMain[name].push(ITEM) - 1 - function append (cmd, params, el) { - let item = { el, cmd, root, gidx, idx, step, args: [...arguments] } - if (cmd) { - item = { el, cmd, root, gidx, idx, step, args } - } else { - // item = ITEM - item.el = el - cmd = name - } - item.gidx = _INDEXall.push(item) - 1 - item.idx = _INDEXcommands[cmd].push(item) - 1 - item.step = steps.push(item) - 1 - item.args = params - _appendItem(item) - } - } - - return commands[name] - } const _appendItem = (item: any) => { let { _JOURNAL, _jobs, data } = state @@ -259,16 +228,17 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { const isKnownScript = ['remix.', 'git'].some(prefix => script.trim().startsWith(prefix)) if (isKnownScript) return script return ` - try { - const ret = ${script}; - if (ret instanceof Promise) { - ret.then((result) => { console.log(result) }).catch((error) => { console.log(error) }) - } else { - console.log(ret) - } - } catch (e) { - console.log(e.message) - }` + try { + const ret = ${script}; + if (ret instanceof Promise) { + ret.then((result) => { console.log(result) }).catch((error) => { console.log(error) }) + } else { + console.log(ret) + } + } catch (e) { + console.log(e.message) + } + ` } const handleKeyDown = (event) => { @@ -289,10 +259,16 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { // scroll2botton () function not implemented props.autoCompletePopup.removeAutoComplete() } else { // + console.log('hit enter') setCmdIndex(-1) setCmdTemp('') const script = inputEl.current.innerText.trim() console.log({ script }, ' script ') + if (script.length) { + cmdHistoryDispatch({ type: 'cmdHistory', payload: { script } }) + const result = newstate.commands.script(wrapScript(script)) + console.log({ result }) + } // inputEl.current.innerText += '\n' // if (script.length) { // // self._cmdHistory.unshift(script) @@ -417,9 +393,58 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => { } }, [leftHeight, setLeftHeight]) + /* block contents that gets rendered from scriptRunner */ + + const _blocksRenderer = (mode) => { + if (mode === 'html') { + return function logger (args) { + console.log({ args }) + if (args.length) { + return args[0] + } + } + } + mode = { + log: 'text-info', + info: 'text-info', + warn: 'text-warning', + error: 'text-danger' + }[mode] // defaults + + if (mode) { + const filterUndefined = (el) => el !== undefined && el !== null + return function logger (args) { + var types = args.filter(filterUndefined).map(type => type) + var values = javascriptserialize.apply(null, args.filter(filterUndefined)).map(function (val, idx) { + if (typeof args[idx] === 'string') { + const el = document.createElement('div') + el.innerHTML = args[idx].replace(/(\r\n|\n|\r)/gm, '
') + val = el.children.length === 0 ? el.firstChild : el + } + if (types[idx] === 'element') val = jsbeautify.html(val) + return val + }) + if (values.length) { + console.log({ values }) + return `${values}` + } + } + } else { + throw new Error('mode is not supported') + } + } + + function basicFilter (value, query) { try { return value.indexOf(query) !== -1 } catch (e) { return false } } + + const registerCommand = (name, command, opts) => { + // setState((prevState) => ({ ...prevState, _commands[name]: command })) + } + + /* end of block content that gets rendered from script Runner */ + return (
- {console.log({ props })} + {console.log({ newstate })}
{/* ${self._view.dragbar} */}