Merge pull request #654 from coolreader18/wasm-explicit-stdout

[WASM] Make setStdout arguments more explicit in what they print to
This commit is contained in:
Windel Bouwman
2019-03-13 12:12:14 +01:00
committed by GitHub
4 changed files with 61 additions and 83 deletions

View File

@@ -36,7 +36,15 @@ function runCodeFromTextarea() {
const code = editor.getValue();
try {
const result = rp.pyEval(code, {
stdout: '#console'
stdout: output => {
const shouldScroll =
consoleElement.scrollHeight - consoleElement.scrollTop ===
consoleElement.clientHeight;
consoleElement.value += output;
if (shouldScroll) {
consoleElement.scrollTop = consoleElement.scrollHeight;
}
}
});
if (result !== null) {
consoleElement.value += `\n${result}\n`;
@@ -74,57 +82,58 @@ snippets.addEventListener('change', updateSnippet);
// option selected for the `select`, but the textarea won't be updated)
updateSnippet();
const prompt = ">>>>> ";
const prompt = '>>>>> ';
const term = new Terminal();
term.open(document.getElementById('terminal'));
term.write(prompt);
function removeNonAscii(str) {
if ((str===null) || (str===''))
return false;
else
str = str.toString();
if (str === null || str === '') return false;
else str = str.toString();
return str.replace(/[^\x20-\x7E]/g, '');
}
function printToConsole(data) {
term.write(removeNonAscii(data) + "\r\n");
term.write(removeNonAscii(data) + '\r\n');
}
const terminalVM = rp.vmStore.init("term_vm");
const terminalVM = rp.vmStore.init('term_vm');
terminalVM.setStdout(printToConsole);
var input = "";
term.on("data", (data) => {
const code = data.charCodeAt(0);
if (code == 13) { // CR
if (input[input.length - 1] == ':') {
input += data
term.write("\r\n.....");
} else {
term.write("\r\n");
try {
terminalVM.exec(input);
} catch (err) {
if (err instanceof WebAssembly.RuntimeError) {
err = window.__RUSTPYTHON_ERROR || err;
var input = '';
term.on('data', data => {
const code = data.charCodeAt(0);
if (code == 13) {
// CR
if (input[input.length - 1] == ':') {
input += data;
term.write('\r\n.....');
} else {
term.write('\r\n');
try {
terminalVM.exec(input);
} catch (err) {
if (err instanceof WebAssembly.RuntimeError) {
err = window.__RUSTPYTHON_ERROR || err;
}
printToConsole(err);
}
printToConsole(err);
term.write(prompt);
input = '';
}
term.write(prompt);
input = "";
} else if (code == 127) {
if (input.length > 0) {
term.write('\b \b');
input = input.slice(0, -1);
}
} else if (code < 32 || code == 127) {
// Control
return;
} else {
// Visible
term.write(data);
input += data;
}
} else if (code == 127) {
if (input.length > 0) {
term.write("\b \b");
input = input.slice(0, -1);
}
} else if (code < 32 || code == 127) { // Control
return;
} else { // Visible
term.write(data);
input += data;
}
});

View File

@@ -26,7 +26,6 @@ features = [
"console",
"Document",
"Element",
"HtmlTextAreaElement",
"Window",
"Headers",
"Request",

View File

@@ -11,7 +11,7 @@ use rustpython_vm::{
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::{Rc, Weak};
use wasm_bindgen::prelude::*;
use wasm_bindgen::{prelude::*, JsCast};
pub trait HeldRcInner {}
@@ -271,13 +271,16 @@ impl WASMVirtualMachine {
ref mut scope,
..
}| {
fn error() -> JsValue {
TypeError::new("Unknown stdout option, please pass a function or 'console'")
.into()
}
let print_fn: Box<Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult> =
if let Some(selector) = stdout.as_string() {
Box::new(
move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult {
wasm_builtins::builtin_print_html(vm, args, &selector)
},
)
if let Some(s) = stdout.as_string() {
match s.as_str() {
"console" => Box::new(wasm_builtins::builtin_print_console),
_ => return Err(error()),
}
} else if stdout.is_function() {
let func = js_sys::Function::from(stdout);
Box::new(
@@ -291,12 +294,12 @@ impl WASMVirtualMachine {
},
)
} else if stdout.is_undefined() || stdout.is_null() {
Box::new(wasm_builtins::builtin_print_console)
fn noop(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
Ok(vm.get_none())
}
Box::new(noop)
} else {
return Err(TypeError::new(
"stdout must be null, a function or a css selector",
)
.into());
return Err(error());
};
scope.store_name(&vm, "print", vm.ctx.new_rustfunc(print_fn));
Ok(())

View File

@@ -4,43 +4,16 @@
//! desktop.
//! Implements functions listed here: https://docs.python.org/3/library/builtins.html.
use crate::convert;
use js_sys::{self, Array};
use rustpython_vm::obj::{objstr, objtype};
use rustpython_vm::pyobject::{IdProtocol, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol};
use rustpython_vm::VirtualMachine;
use wasm_bindgen::{prelude::*, JsCast};
use web_sys::{self, console, HtmlTextAreaElement};
use web_sys::{self, console};
pub(crate) fn window() -> web_sys::Window {
web_sys::window().expect("Window to be available")
}
// The HTML id of the textarea element that act as our STDOUT
pub fn print_to_html(text: &str, selector: &str) -> Result<(), JsValue> {
let document = window().document().expect("Document to be available");
let element = document
.query_selector(selector)?
.ok_or_else(|| js_sys::TypeError::new("Couldn't get element"))?;
let textarea = element
.dyn_ref::<HtmlTextAreaElement>()
.ok_or_else(|| js_sys::TypeError::new("Element must be a textarea"))?;
let value = textarea.value();
let scroll_height = textarea.scroll_height();
let scrolled_to_bottom = scroll_height - textarea.scroll_top() == textarea.client_height();
textarea.set_value(&format!("{}{}", value, text));
if scrolled_to_bottom {
textarea.scroll_with_x_and_y(0.0, scroll_height.into());
}
Ok(())
}
pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result<String, PyObjectRef> {
// Handle 'sep' kwarg:
let sep_arg = args
@@ -93,12 +66,6 @@ pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result<St
Ok(output)
}
pub fn builtin_print_html(vm: &mut VirtualMachine, args: PyFuncArgs, selector: &str) -> PyResult {
let output = format_print_args(vm, args)?;
print_to_html(&output, selector).map_err(|err| convert::js_to_py(vm, err))?;
Ok(vm.get_none())
}
pub fn builtin_print_console(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
let arr = Array::new();
for arg in args.args {