Combine index.js and main.js into one to improve UX while loading rustpython

This commit is contained in:
coolreader18
2020-01-09 20:33:57 -06:00
parent 73b363dd60
commit 96dfaa0eae
2 changed files with 147 additions and 145 deletions

View File

@@ -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));
}

View File

@@ -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));