Skip to content

gh-136251: Improvements to WASM demo REPL #136252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Jul 21, 2025
Merged
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
af4aa0d
EMSCRIPTEN BUILD: export HEAPU32 from emscripten build so that python…
adqm Jul 2, 2025
394815e
WASM REPL: fix loading xterm.js (broken in 2f1cee8477)
adqm Jul 2, 2025
aab9ccc
WASM REPL: basic navigation in REPL (arrow keys, home/end, etc)
adqm Jul 2, 2025
bd52abe
WASM REPL: reset the python worker after a program exits so that we c…
adqm Jul 2, 2025
77cc952
WASM REPL: handle a few more special keys (tab, ctrl+c)
adqm Jul 2, 2025
4cfa3f1
WASM REPL: use ace editor instead of a plain textbox for code snippet
adqm Jul 2, 2025
b688663
WASM REPL: some organization, refactoring, fixes for history
adqm Jul 2, 2025
43a47dc
WASM REPL: put demo at index.html and change text of the 'Run Code' b…
adqm Jul 3, 2025
6d5f698
WASM REPL: update README.md to account for demo location moving
adqm Jul 3, 2025
16cfea2
WASM REPL: add HEAPU32 to exports in configure.ac
adqm Jul 3, 2025
faf2ba4
WASM REPL: fix for ACE setup in Chromium
adqm Jul 3, 2025
70d4a17
WASM REPL: run prettier on index.html
adqm Jul 3, 2025
ce5ebca
WASM REPL: lower-case 'code' on 'Run code' button
adqm Jul 3, 2025
66cf49d
WASM REPL: remove trailing slashes from void tags in index.html
adqm Jul 5, 2025
8fe92d6
add short news blurb
adqm Jul 5, 2025
25fd7fc
change text of news blurb
adqm Jul 16, 2025
9a26d70
WASM REPL: better implementation of ctrl+c behavior
adqm Jul 16, 2025
c477e30
WASM REPL: show error message when SharedArrayBuffer is not available
adqm Jul 16, 2025
4dcc6fb
WASM REPL: maintain history during browsing session
adqm Jul 16, 2025
e06a7b2
WASM REPL: make the magic ctrl+c string vary with each run
adqm Jul 17, 2025
3e76313
Merge branch 'main' into wasm_repl
hoodmane Jul 21, 2025
26ddd07
Fix emscripten in browser
hoodmane Jul 21, 2025
7d33525
Merge branch 'main' into wasm_repl
hoodmane Jul 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
WASM REPL: basic navigation in REPL (arrow keys, home/end, etc)
  • Loading branch information
adqm committed Jul 2, 2025
commit aab9cccfd8a0f5ccb7c6f79cfc21b5cb5cd0a2e1
158 changes: 149 additions & 9 deletions Tools/wasm/emscripten/web_example/python.html
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,7 @@

class WasmTerminal {
constructor() {
this.inputBuffer = new BufferQueue();
this.input = "";
this.resolveInput = null;
this.activeInput = false;
this.inputStartCursor = null;
this.reset()

this.xterm = new Terminal({
scrollback: 10000,
Expand All @@ -155,6 +151,19 @@
this.xterm.onData(this.handleTermData);
}

reset(){
this.inputBuffer = new BufferQueue();
this.input = "";
this.resolveInput = null;
this.activeInput = false;
this.inputStartCursor = null;

this.cursorPosition = 0;
this.history = [];
this.historyIndex = -1;
this.beforeHistoryNav = "";
}

open(container) {
this.xterm.open(container);
}
Expand Down Expand Up @@ -186,9 +195,34 @@
if (!(ord === 0x1b || ord == 0x7f || ord < 32)) {
this.inputBuffer.addData(data);
}
// TODO: Handle ANSI escape sequences
// TODO: Handle more escape sequences?
} else if (ord === 0x1b) {
// Handle special characters
switch(data.slice(1)){
case '[A': // up
this.historyBack();
break;
case '[B': // down
this.historyForward();
break;
case '[C': // right
this.cursorRight();
break;
case '[D': // left
this.cursorLeft();
break;
case '[H': // home key
this.cursorHome();
break;
case '[F': // end key
this.cursorEnd();
break;
case '[3~': // delete key
this.deleteAtCursor();
break;
default:
break;
}
} else if (ord < 32 || ord === 0x7f) {
switch (data) {
case "\x0c": // CTRL+L
Expand Down Expand Up @@ -226,8 +260,14 @@
}

handleCursorInsert(data) {
this.input += data;
const trailing = this.input.slice(this.cursorPosition);
this.input = this.input.slice(0, this.cursorPosition) + data + trailing;
this.cursorPosition += data.length;
this.xterm.write(data);
if (trailing.length !== 0){
this.xterm.write(trailing);
this.xterm.write('\x1b[' + trailing.length + 'D');
}
}

handleCursorErase() {
Expand All @@ -238,9 +278,102 @@
) {
return;
}
this.input = this.input.slice(0, -1);
const trailing = this.input.slice(this.cursorPosition);
this.input = this.input.slice(0, this.cursorPosition - 1) + trailing;
this.cursorPosition -= 1;
this.xterm.write("\x1B[D");
this.xterm.write("\x1B[P");
this.xterm.write("\x1B[K");
if (trailing){
this.xterm.write(trailing);
this.xterm.write('\x1b[' + trailing.length + 'D');
}
}

deleteAtCursor(){
if (this.cursorPosition < this.input.length){
const trailing = this.input.slice(this.cursorPosition + 1);
this.input = this.input.slice(0, this.cursorPosition) + trailing;
this.xterm.write("\x1B[K");
if (trailing){
this.xterm.write(trailing);
this.xterm.write('\x1b[' + trailing.length + 'D');
}
}
}

historyBack(){
if (this.history.length === 0){
return;
}else if (this.historyIndex === -1){
// we're not currently navigating the history; store
// the current command and then look at the end of our
// history buffer
this.beforeHistoryNav = this.input;
this.historyIndex = this.history.length - 1;
}else if (this.historyIndex > 0){
this.historyIndex -= 1;
}
this.input = this.history[this.historyIndex];
// jump back to the start of the line
if (this.cursorPosition > 0){
this.xterm.write('\x1b[' + (this.cursorPosition) + 'D');
}
// clear the line
this.xterm.write('\x1b[K')
this.xterm.write(this.input);
this.cursorPosition = this.input.length;
}

historyForward(){
if (this.history.length === 0 || this.historyIndex === -1){
// we're not currently navigating the history; NOP.
return;
}else if (this.historyIndex < this.history.length - 1){
this.historyIndex += 1;
this.input = this.history[this.historyIndex];
}else if (this.historyIndex == this.history.length - 1){
// we're coming back from the last history value; reset
// the input to whatever it was when we started going
// through the history
this.input = this.beforeHistoryNav;
this.historyIndex = -1;
}
// jump back to the start of the line
if (this.cursorPosition > 0){
this.xterm.write('\x1b[' + (this.cursorPosition) + 'D');
}
// clear the line
this.xterm.write('\x1b[K')
this.xterm.write(this.input);
this.cursorPosition = this.input.length;
}

cursorRight(){
if (this.cursorPosition < this.input.length){
this.cursorPosition += 1;
this.xterm.write('\x1b[C');
}
}

cursorLeft(){
if (this.cursorPosition > 0){
this.cursorPosition -= 1;
this.xterm.write('\x1b[D');
}
}

cursorHome() {
if (this.cursorPosition > 0){
this.xterm.write('\x1b[' + this.cursorPosition + 'D');
this.cursorPosition = 0;
}
}

cursorEnd() {
if (this.cursorPosition < this.input.length){
this.xterm.write('\x1b[' + (this.input.length - this.cursorPosition) + 'C');
this.cursorPosition = this.input.length;
}
}

prompt = async () => {
Expand Down Expand Up @@ -269,6 +402,11 @@
}
return new Promise((resolve, reject) => {
this.resolveInput = (value) => {
if (value !== ""){
this.history.push(value.slice(0, -1));
this.historyIndex = -1;
this.cursorPosition = 0;
}
resolve(value);
};
});
Expand Down Expand Up @@ -362,6 +500,7 @@

runButton.addEventListener("click", (e) => {
terminal.clear();
terminal.reset(); // reset the history
programRunning(true);
const code = codeBox.value;
pythonWorkerManager.run({
Expand All @@ -372,6 +511,7 @@

replButton.addEventListener("click", (e) => {
terminal.clear();
terminal.reset(); // reset the history
programRunning(true);
// Need to use "-i -" to force interactive mode.
// Looks like isatty always returns false in emscripten
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy