From 8407f7498e44ab98156deab1e80632ba895b0cc1 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 10 Mar 2019 17:20:32 -0500 Subject: [PATCH 1/5] Change setStdout to take "console" instead of undefined --- wasm/lib/src/vm_class.rs | 30 ++++++++++++++++++++++-------- wasm/lib/src/wasm_builtins.rs | 32 ++++++++++++++------------------ 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 18de108957..02e48c4dae 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,11 +271,25 @@ impl WASMVirtualMachine { ref mut scope, .. }| { + fn error() -> JsValue { + TypeError::new( + "Unknown stdout option, please pass a function, a textarea element, or \ + 'console'", + ) + .into() + } let print_fn: Box PyResult> = - if let Some(selector) = stdout.as_string() { + if let Some(s) = stdout.as_string() { + let print = match s.as_str() { + "console" => wasm_builtins::builtin_print_console, + _ => return Err(error()), + }; + Box::new(print) + } else if let Some(element) = stdout.dyn_ref::() { + let element = element.clone(); Box::new( move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { - wasm_builtins::builtin_print_html(vm, args, &selector) + wasm_builtins::builtin_print_html(vm, args, &element) }, ) } else if stdout.is_function() { @@ -291,12 +305,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..a5867ec020 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -9,33 +9,25 @@ 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 wasm_bindgen::prelude::*; use web_sys::{self, console, HtmlTextAreaElement}; 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 +// The HTML id of the element 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"))?; +pub fn print_to_html(text: &str, element: &HtmlTextAreaElement) -> Result<(), JsValue> { + let value = element.value(); - let value = textarea.value(); + let scroll_height = element.scroll_height(); + let scrolled_to_bottom = scroll_height - element.scroll_top() == element.client_height(); - let scroll_height = textarea.scroll_height(); - let scrolled_to_bottom = scroll_height - textarea.scroll_top() == textarea.client_height(); - - textarea.set_value(&format!("{}{}", value, text)); + element.set_value(&format!("{}{}", value, text)); if scrolled_to_bottom { - textarea.scroll_with_x_and_y(0.0, scroll_height.into()); + element.scroll_with_x_and_y(0.0, scroll_height.into()); } Ok(()) @@ -93,9 +85,13 @@ pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result PyResult { +pub fn builtin_print_html( + vm: &mut VirtualMachine, + args: PyFuncArgs, + element: &HtmlTextAreaElement, +) -> PyResult { let output = format_print_args(vm, args)?; - print_to_html(&output, selector).map_err(|err| convert::js_to_py(vm, err))?; + print_to_html(&output, element).map_err(|err| convert::js_to_py(vm, err))?; Ok(vm.get_none()) } From 29ec84ead905cd0696404d1b80cb7d129d3e148c Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 10 Mar 2019 17:22:22 -0500 Subject: [PATCH 2/5] Change main.js to print to the console --- wasm/demo/src/main.js | 73 ++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index 2f6de563d1..b35c9aaa29 100644 --- a/wasm/demo/src/main.js +++ b/wasm/demo/src/main.js @@ -36,7 +36,7 @@ function runCodeFromTextarea() { const code = editor.getValue(); try { const result = rp.pyEval(code, { - stdout: '#console' + stdout: consoleElement }); if (result !== null) { consoleElement.value += `\n${result}\n`; @@ -74,57 +74,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; - } }); From 09ab6d1d7c49dbfd3d57df4bef70d3ce075f62f2 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Sun, 10 Mar 2019 19:12:11 -0500 Subject: [PATCH 3/5] Reformat --- wasm/lib/src/vm_class.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index 02e48c4dae..bdf3eb3254 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -280,11 +280,10 @@ impl WASMVirtualMachine { } let print_fn: Box PyResult> = if let Some(s) = stdout.as_string() { - let print = match s.as_str() { - "console" => wasm_builtins::builtin_print_console, + match s.as_str() { + "console" => Box::new(wasm_builtins::builtin_print_console), _ => return Err(error()), - }; - Box::new(print) + } } else if let Some(element) = stdout.dyn_ref::() { let element = element.clone(); Box::new( From dac75019e58d2940495c10d0b4db796edf9df2ab Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 11 Mar 2019 19:18:21 -0500 Subject: [PATCH 4/5] Remove builtin_print_html, move functionality to JS --- wasm/demo/src/main.js | 10 +++++++++- wasm/lib/Cargo.toml | 1 - wasm/lib/src/vm_class.rs | 10 +--------- wasm/lib/src/wasm_builtins.rs | 31 +------------------------------ 4 files changed, 11 insertions(+), 41 deletions(-) diff --git a/wasm/demo/src/main.js b/wasm/demo/src/main.js index b35c9aaa29..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: consoleElement + 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`; 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 bdf3eb3254..be3bbd8133 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -273,8 +273,7 @@ impl WASMVirtualMachine { }| { fn error() -> JsValue { TypeError::new( - "Unknown stdout option, please pass a function, a textarea element, or \ - 'console'", + "Unknown stdout option, please pass a function or 'console'", ) .into() } @@ -284,13 +283,6 @@ impl WASMVirtualMachine { "console" => Box::new(wasm_builtins::builtin_print_console), _ => return Err(error()), } - } else if let Some(element) = stdout.dyn_ref::() { - let element = element.clone(); - Box::new( - move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult { - wasm_builtins::builtin_print_html(vm, args, &element) - }, - ) } else if stdout.is_function() { let func = js_sys::Function::from(stdout); Box::new( diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index a5867ec020..8bb6a2db2b 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -4,35 +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::*; -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 element element that act as our STDOUT - -pub fn print_to_html(text: &str, element: &HtmlTextAreaElement) -> Result<(), JsValue> { - let value = element.value(); - - let scroll_height = element.scroll_height(); - let scrolled_to_bottom = scroll_height - element.scroll_top() == element.client_height(); - - element.set_value(&format!("{}{}", value, text)); - - if scrolled_to_bottom { - element.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 @@ -85,16 +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, element).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 { From 88fff7102eb97c267c59d86eb599b0cf89b647b1 Mon Sep 17 00:00:00 2001 From: coolreader18 <33094578+coolreader18@users.noreply.github.com> Date: Mon, 11 Mar 2019 20:01:34 -0500 Subject: [PATCH 5/5] cargo fmt --- wasm/lib/src/vm_class.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index be3bbd8133..b3ef506272 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -272,10 +272,8 @@ impl WASMVirtualMachine { .. }| { fn error() -> JsValue { - TypeError::new( - "Unknown stdout option, please pass a function or 'console'", - ) - .into() + TypeError::new("Unknown stdout option, please pass a function or 'console'") + .into() } let print_fn: Box PyResult> = if let Some(s) = stdout.as_string() {