diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index 2f6de563d1..7e934dfc98 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -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 == 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 < 32 || code == 127) { // Control - return; - } else { // Visible - term.write(data); - input += data; - } }); diff --git a/wasm/lib/Cargo.toml b/wasm/lib/Cargo.toml index 3f89ba9acb..769455aea1 100644 --- a/wasm/lib/Cargo.toml +++ b/wasm/lib/Cargo.toml @@ -26,7 +26,6 @@ features = [ "console", "Document", "Element", - "HtmlTextAreaElement", "Window", "Headers", "Request", diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 18de108957..b3ef506272 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -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 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(()) diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index 7f2b7a6225..8bb6a2db2b 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -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::() - .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 { // Handle 'sep' kwarg: let sep_arg = args @@ -93,12 +66,6 @@ pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result 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 {