Skip to content

Commit 8dcea92

Browse files
Merge pull request RustPython#654 from coolreader18/wasm-explicit-stdout
[WASM] Make setStdout arguments more explicit in what they print to
2 parents f0e285d + 88fff71 commit 8dcea92

File tree

4 files changed

+61
-83
lines changed

4 files changed

+61
-83
lines changed

wasm/demo/src/main.js

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,15 @@ function runCodeFromTextarea() {
3636
const code = editor.getValue();
3737
try {
3838
const result = rp.pyEval(code, {
39-
stdout: '#console'
39+
stdout: output => {
40+
const shouldScroll =
41+
consoleElement.scrollHeight - consoleElement.scrollTop ===
42+
consoleElement.clientHeight;
43+
consoleElement.value += output;
44+
if (shouldScroll) {
45+
consoleElement.scrollTop = consoleElement.scrollHeight;
46+
}
47+
}
4048
});
4149
if (result !== null) {
4250
consoleElement.value += `\n${result}\n`;
@@ -74,57 +82,58 @@ snippets.addEventListener('change', updateSnippet);
7482
// option selected for the `select`, but the textarea won't be updated)
7583
updateSnippet();
7684

77-
const prompt = ">>>>> ";
85+
const prompt = '>>>>> ';
7886

7987
const term = new Terminal();
8088
term.open(document.getElementById('terminal'));
8189
term.write(prompt);
8290

8391
function removeNonAscii(str) {
84-
if ((str===null) || (str===''))
85-
return false;
86-
else
87-
str = str.toString();
92+
if (str === null || str === '') return false;
93+
else str = str.toString();
8894

8995
return str.replace(/[^\x20-\x7E]/g, '');
9096
}
9197

9298
function printToConsole(data) {
93-
term.write(removeNonAscii(data) + "\r\n");
99+
term.write(removeNonAscii(data) + '\r\n');
94100
}
95101

96-
const terminalVM = rp.vmStore.init("term_vm");
102+
const terminalVM = rp.vmStore.init('term_vm');
97103
terminalVM.setStdout(printToConsole);
98104

99-
var input = "";
100-
term.on("data", (data) => {
101-
const code = data.charCodeAt(0);
102-
if (code == 13) { // CR
103-
if (input[input.length - 1] == ':') {
104-
input += data
105-
term.write("\r\n.....");
106-
} else {
107-
term.write("\r\n");
108-
try {
109-
terminalVM.exec(input);
110-
} catch (err) {
111-
if (err instanceof WebAssembly.RuntimeError) {
112-
err = window.__RUSTPYTHON_ERROR || err;
105+
var input = '';
106+
term.on('data', data => {
107+
const code = data.charCodeAt(0);
108+
if (code == 13) {
109+
// CR
110+
if (input[input.length - 1] == ':') {
111+
input += data;
112+
term.write('\r\n.....');
113+
} else {
114+
term.write('\r\n');
115+
try {
116+
terminalVM.exec(input);
117+
} catch (err) {
118+
if (err instanceof WebAssembly.RuntimeError) {
119+
err = window.__RUSTPYTHON_ERROR || err;
120+
}
121+
printToConsole(err);
113122
}
114-
printToConsole(err);
123+
term.write(prompt);
124+
input = '';
115125
}
116-
term.write(prompt);
117-
input = "";
118-
}
119-
} else if (code == 127) {
120-
if (input.length > 0) {
121-
term.write("\b \b");
122-
input = input.slice(0, -1);
126+
} else if (code == 127) {
127+
if (input.length > 0) {
128+
term.write('\b \b');
129+
input = input.slice(0, -1);
130+
}
131+
} else if (code < 32 || code == 127) {
132+
// Control
133+
return;
134+
} else {
135+
// Visible
136+
term.write(data);
137+
input += data;
123138
}
124-
} else if (code < 32 || code == 127) { // Control
125-
return;
126-
} else { // Visible
127-
term.write(data);
128-
input += data;
129-
}
130139
});

wasm/lib/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ features = [
2626
"console",
2727
"Document",
2828
"Element",
29-
"HtmlTextAreaElement",
3029
"Window",
3130
"Headers",
3231
"Request",

wasm/lib/src/vm_class.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustpython_vm::{
1111
use std::cell::RefCell;
1212
use std::collections::HashMap;
1313
use std::rc::{Rc, Weak};
14-
use wasm_bindgen::prelude::*;
14+
use wasm_bindgen::{prelude::*, JsCast};
1515

1616
pub trait HeldRcInner {}
1717

@@ -271,13 +271,16 @@ impl WASMVirtualMachine {
271271
ref mut scope,
272272
..
273273
}| {
274+
fn error() -> JsValue {
275+
TypeError::new("Unknown stdout option, please pass a function or 'console'")
276+
.into()
277+
}
274278
let print_fn: Box<Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult> =
275-
if let Some(selector) = stdout.as_string() {
276-
Box::new(
277-
move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult {
278-
wasm_builtins::builtin_print_html(vm, args, &selector)
279-
},
280-
)
279+
if let Some(s) = stdout.as_string() {
280+
match s.as_str() {
281+
"console" => Box::new(wasm_builtins::builtin_print_console),
282+
_ => return Err(error()),
283+
}
281284
} else if stdout.is_function() {
282285
let func = js_sys::Function::from(stdout);
283286
Box::new(
@@ -291,12 +294,12 @@ impl WASMVirtualMachine {
291294
},
292295
)
293296
} else if stdout.is_undefined() || stdout.is_null() {
294-
Box::new(wasm_builtins::builtin_print_console)
297+
fn noop(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
298+
Ok(vm.get_none())
299+
}
300+
Box::new(noop)
295301
} else {
296-
return Err(TypeError::new(
297-
"stdout must be null, a function or a css selector",
298-
)
299-
.into());
302+
return Err(error());
300303
};
301304
scope.store_name(&vm, "print", vm.ctx.new_rustfunc(print_fn));
302305
Ok(())

wasm/lib/src/wasm_builtins.rs

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,16 @@
44
//! desktop.
55
//! Implements functions listed here: https://docs.python.org/3/library/builtins.html.
66
7-
use crate::convert;
87
use js_sys::{self, Array};
98
use rustpython_vm::obj::{objstr, objtype};
109
use rustpython_vm::pyobject::{IdProtocol, PyFuncArgs, PyObjectRef, PyResult, TypeProtocol};
1110
use rustpython_vm::VirtualMachine;
12-
use wasm_bindgen::{prelude::*, JsCast};
13-
use web_sys::{self, console, HtmlTextAreaElement};
11+
use web_sys::{self, console};
1412

1513
pub(crate) fn window() -> web_sys::Window {
1614
web_sys::window().expect("Window to be available")
1715
}
1816

19-
// The HTML id of the textarea element that act as our STDOUT
20-
21-
pub fn print_to_html(text: &str, selector: &str) -> Result<(), JsValue> {
22-
let document = window().document().expect("Document to be available");
23-
let element = document
24-
.query_selector(selector)?
25-
.ok_or_else(|| js_sys::TypeError::new("Couldn't get element"))?;
26-
let textarea = element
27-
.dyn_ref::<HtmlTextAreaElement>()
28-
.ok_or_else(|| js_sys::TypeError::new("Element must be a textarea"))?;
29-
30-
let value = textarea.value();
31-
32-
let scroll_height = textarea.scroll_height();
33-
let scrolled_to_bottom = scroll_height - textarea.scroll_top() == textarea.client_height();
34-
35-
textarea.set_value(&format!("{}{}", value, text));
36-
37-
if scrolled_to_bottom {
38-
textarea.scroll_with_x_and_y(0.0, scroll_height.into());
39-
}
40-
41-
Ok(())
42-
}
43-
4417
pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result<String, PyObjectRef> {
4518
// Handle 'sep' kwarg:
4619
let sep_arg = args
@@ -93,12 +66,6 @@ pub fn format_print_args(vm: &mut VirtualMachine, args: PyFuncArgs) -> Result<St
9366
Ok(output)
9467
}
9568

96-
pub fn builtin_print_html(vm: &mut VirtualMachine, args: PyFuncArgs, selector: &str) -> PyResult {
97-
let output = format_print_args(vm, args)?;
98-
print_to_html(&output, selector).map_err(|err| convert::js_to_py(vm, err))?;
99-
Ok(vm.get_none())
100-
}
101-
10269
pub fn builtin_print_console(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
10370
let arr = Array::new();
10471
for arg in args.args {

0 commit comments

Comments
 (0)