From 96dfaa0eae1ee30f0eb20acbd581ef30950d07b9 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Thu, 9 Jan 2020 20:33:57 -0600 Subject: [PATCH] Combine index.js and main.js into one to improve UX while loading rustpython --- wasm/demo/src/index.js | 154 +++++++++++++++++++++++++++++++++++++++-- wasm/demo/src/main.js | 138 ------------------------------------ 2 files changed, 147 insertions(+), 145 deletions(-) delete mode 100644 wasm/demo/src/main.js diff --git a/wasm/demo/src/index.js b/wasm/demo/src/index.js index 602e25241..24714131b 100644 --- a/wasm/demo/src/index.js +++ b/wasm/demo/src/index.js @@ -1,11 +1,151 @@ import './style.css'; -import 'codemirror/lib/codemirror.css'; import 'xterm/lib/xterm.css'; +import CodeMirror from 'codemirror'; +import 'codemirror/mode/python/python'; +import 'codemirror/addon/comment/comment'; +import 'codemirror/lib/codemirror.css'; +import { Terminal } from 'xterm'; +import LocalEchoController from 'local-echo'; -// A dependency graph that contains any wasm must all be imported -// asynchronously. This `index.js` file does the single async import, so -// that no one else needs to worry about it again. -import('./main.js').catch(e => { - console.error('Error importing `main.js`:', e); - document.getElementById('error').textContent = e; +let rp; + +// A dependency graph that contains any wasm must be imported asynchronously. +import('rustpython') + .then(rustpy => { + rp = rustpy; + // so people can play around with it + window.rp = rustpy; + onReady(); + }) + .catch(e => { + console.error('Error importing `rustpython`:', e); + document.getElementById('error').textContent = e; + }); + +const editor = CodeMirror.fromTextArea(document.getElementById('code'), { + extraKeys: { + 'Ctrl-Enter': runCodeFromTextarea, + 'Cmd-Enter': runCodeFromTextarea, + 'Shift-Tab': 'indentLess', + 'Ctrl-/': 'toggleComment', + 'Cmd-/': 'toggleComment', + Tab: editor => { + var spaces = Array(editor.getOption('indentUnit') + 1).join(' '); + editor.replaceSelection(spaces); + } + }, + lineNumbers: true, + mode: 'text/x-python', + indentUnit: 4, + autofocus: true }); + +const consoleElement = document.getElementById('console'); +const errorElement = document.getElementById('error'); + +function runCodeFromTextarea() { + // Clean the console and errors + consoleElement.value = ''; + errorElement.textContent = ''; + + const code = editor.getValue(); + try { + rp.pyExec(code, { + stdout: output => { + const shouldScroll = + consoleElement.scrollHeight - consoleElement.scrollTop === + consoleElement.clientHeight; + consoleElement.value += output; + if (shouldScroll) { + consoleElement.scrollTop = consoleElement.scrollHeight; + } + } + }); + } catch (err) { + if (err instanceof WebAssembly.RuntimeError) { + err = window.__RUSTPYTHON_ERROR || err; + } + errorElement.textContent = err; + console.error(err); + } +} + +const snippets = document.getElementById('snippets'); + +function updateSnippet() { + const selected = snippets.value; + + // the require here creates a webpack context; it's fine to use it + // dynamically. + // https://webpack.js.org/guides/dependency-management/ + const snippet = require(`raw-loader!../snippets/${selected}.py`); + + editor.setValue(snippet); + runCodeFromTextarea(); +} + +const term = new Terminal(); +term.open(document.getElementById('terminal')); + +const localEcho = new LocalEchoController(term); + +let terminalVM; + +function getPrompt(name) { + terminalVM.exec(` +try: + import sys as __sys + __prompt = __sys.${name} +except: + __prompt = '' +finally: + del __sys +`); + return String(terminalVM.eval('__prompt')); +} + +async function readPrompts() { + let continuing = false; + + while (true) { + const ps1 = getPrompt('ps1'); + const ps2 = getPrompt('ps2'); + let input; + if (continuing) { + const prom = localEcho.read(ps2, ps2); + localEcho._activePrompt.prompt = ps1; + localEcho._input = localEcho.history.entries.pop() + '\n'; + localEcho._cursor = localEcho._input.length; + localEcho._active = true; + input = await prom; + if (!input.endsWith('\n')) continue; + } else { + input = await localEcho.read(ps1, ps2); + } + try { + terminalVM.execSingle(input); + } catch (err) { + if (err instanceof SyntaxError && err.message.includes('EOF')) { + continuing = true; + continue; + } else if (err instanceof WebAssembly.RuntimeError) { + err = window.__RUSTPYTHON_ERROR || err; + } + localEcho.println(err); + } + continuing = false; + } +} + +function onReady() { + snippets.addEventListener('change', updateSnippet); + document + .getElementById('run-btn') + .addEventListener('click', runCodeFromTextarea); + // Run once for demo + runCodeFromTextarea(); + + terminalVM = rp.vmStore.init('term_vm'); + terminalVM.setStdout(data => localEcho.println(data)); + readPrompts().catch(err => console.error(err)); +} diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js deleted file mode 100644 index 2565d3ee2..000000000 --- a/wasm/demo/src/main.js +++ /dev/null @@ -1,138 +0,0 @@ -import * as rp from 'rustpython'; -import CodeMirror from 'codemirror'; -import 'codemirror/mode/python/python'; -import 'codemirror/addon/comment/comment'; -import { Terminal } from 'xterm'; -import LocalEchoController from 'local-echo'; - -// so people can play around with it -window.rp = rp; - -const editor = CodeMirror.fromTextArea(document.getElementById('code'), { - extraKeys: { - 'Ctrl-Enter': runCodeFromTextarea, - 'Cmd-Enter': runCodeFromTextarea, - 'Shift-Tab': 'indentLess', - 'Ctrl-/': 'toggleComment', - 'Cmd-/': 'toggleComment', - Tab: editor => { - var spaces = Array(editor.getOption('indentUnit') + 1).join(' '); - editor.replaceSelection(spaces); - } - }, - lineNumbers: true, - mode: 'text/x-python', - indentUnit: 4, - autofocus: true -}); - -const consoleElement = document.getElementById('console'); -const errorElement = document.getElementById('error'); - -function runCodeFromTextarea() { - // Clean the console and errors - consoleElement.value = ''; - errorElement.textContent = ''; - - const code = editor.getValue(); - try { - rp.pyExec(code, { - stdout: output => { - const shouldScroll = - consoleElement.scrollHeight - consoleElement.scrollTop === - consoleElement.clientHeight; - consoleElement.value += output; - if (shouldScroll) { - consoleElement.scrollTop = consoleElement.scrollHeight; - } - } - }); - } catch (err) { - if (err instanceof WebAssembly.RuntimeError) { - err = window.__RUSTPYTHON_ERROR || err; - } - errorElement.textContent = err; - console.error(err); - } -} - -document - .getElementById('run-btn') - .addEventListener('click', runCodeFromTextarea); - -const snippets = document.getElementById('snippets'); - -const updateSnippet = () => { - const selected = snippets.value; - - // the require here creates a webpack context; it's fine to use it - // dynamically. - // https://webpack.js.org/guides/dependency-management/ - const snippet = require(`raw-loader!../snippets/${selected}.py`); - - editor.setValue(snippet); - runCodeFromTextarea(); -}; - -snippets.addEventListener('change', updateSnippet); - -// Run once for demo (updateSnippet b/c the browser might try to keep the same -// option selected for the `select`, but the textarea won't be updated) -updateSnippet(); - -const term = new Terminal(); -term.open(document.getElementById('terminal')); - -const localEcho = new LocalEchoController(term); - -const terminalVM = rp.vmStore.init('term_vm'); - -terminalVM.setStdout(data => localEcho.println(data)); - -function getPrompt(name) { - terminalVM.exec(` -try: - import sys as __sys - __prompt = __sys.${name} -except: - __prompt = '' -finally: - del __sys -`); - return String(terminalVM.eval('__prompt')); -} - -async function readPrompts() { - let continuing = false; - - while (true) { - const ps1 = getPrompt('ps1'); - const ps2 = getPrompt('ps2'); - let input; - if (continuing) { - const prom = localEcho.read(ps2, ps2); - localEcho._activePrompt.prompt = ps1; - localEcho._input = localEcho.history.entries.pop() + '\n'; - localEcho._cursor = localEcho._input.length; - localEcho._active = true; - input = await prom; - if (!input.endsWith('\n')) continue; - } else { - input = await localEcho.read(ps1, ps2); - } - try { - terminalVM.execSingle(input); - } catch (err) { - if (err instanceof SyntaxError && err.message.includes('EOF')) { - continuing = true; - continue; - } else if (err instanceof WebAssembly.RuntimeError) { - err = window.__RUSTPYTHON_ERROR || err; - } - localEcho.println(err); - } - continuing = false; - } -} - -readPrompts().catch(err => console.error(err));