From d2f76201dd6143067e70a30ebddc3b84837748d7 Mon Sep 17 00:00:00 2001 From: Trey Hunner Date: Sat, 23 Apr 2022 20:51:19 -0700 Subject: [PATCH] Add buffer for pasted stdin and inactive stdin --- browser-ui/wasm-terminal.js | 86 +++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/browser-ui/wasm-terminal.js b/browser-ui/wasm-terminal.js index 220988c..c6a5cbf 100644 --- a/browser-ui/wasm-terminal.js +++ b/browser-ui/wasm-terminal.js @@ -1,7 +1,40 @@ +class BufferQueue { + + constructor(xterm) { + this.buffer = []; + } + + isEmpty() { + return this.buffer.length == 0; + } + + lastLineIsIncomplete() { + return !this.isEmpty() && !this.buffer[this.buffer.length-1].endsWith("\n"); + } + + hasLineReady() { + return !this.isEmpty() && this.buffer[0].endsWith("\n"); + } + + addData(data) { + let lines = data.split(/(?<=\n)/g); + if (this.lastLineIsIncomplete()) { + this.buffer[this.buffer.length-1] += lines.shift(); + } + for (let line of lines) { + this.buffer.push(line); + } + } + + nextLine() { + return this.buffer.shift(); + } +} export class WasmTerminal { constructor() { + this.inputBuffer = new BufferQueue(); this.input = '' this.resolveInput = null this.activeInput = false @@ -25,41 +58,58 @@ export class WasmTerminal { this.xterm.open(container); } - handleReadComplete(lastChar) { - this.resolveInput(this.input + lastChar) - this.activeInput = false - } - handleTermData = (data) => { - if (!this.activeInput) { - return - } const ord = data.charCodeAt(0); - let ofs; + data = data.replace(/\r\n|\r|\n/g, "\n") + if (data.length > 1 && data.includes("\n")) { + // Handle pasted data + let alreadyWritten = 0; + if (this.input != '') { + // If line already had data on it, merge pasted data with it + this.inputBuffer.addData(this.input); + alreadyWritten = this.input.length; + this.input = ''; + } + this.inputBuffer.addData(data); + if (this.activeInput) { + let line = this.inputBuffer.nextLine(); + this.writeLine(line.slice(alreadyWritten)); + this.resolveInput(line); + this.activeInput = false; + } + } else if (!this.activeInput) { + this.inputBuffer.addData(data); // TODO: Handle ANSI escape sequences - if (ord === 0x1b) { + } else if (ord === 0x1b) { // Handle special characters } else if (ord < 32 || ord === 0x7f) { switch (data) { case "\r": // ENTER case "\x0a": // CTRL+J case "\x0d": // CTRL+M - this.xterm.write('\r\n'); - this.handleReadComplete('\n'); + this.resolveInput(this.input + this.writeLine('\n')); + this.input = ''; + this.activeInput = false; break; case "\x7F": // BACKSPACE case "\x08": // CTRL+H case "\x04": // CTRL+D this.handleCursorErase(true); break; - + } } else { this.handleCursorInsert(data); } } + writeLine(line) { + this.xterm.write(line.slice(0, -1)) + this.xterm.write('\r\n'); + return line; + } + handleCursorInsert(data) { this.input += data; this.xterm.write(data) @@ -79,9 +129,17 @@ export class WasmTerminal { this.activeInput = true // Hack to allow stdout/stderr to finish before we figure out where input starts setTimeout(() => {this.inputStartCursor = this.xterm.buffer.active.cursorX}, 1) + if (this.inputBuffer.hasLineReady()) { + return new Promise((resolve, reject) => { + resolve(this.writeLine(this.inputBuffer.nextLine())); + this.activeInput = false; + }) + } else if (this.inputBuffer.lastLineIsIncomplete()) { + // Hack to ensure cursor input start doesn't end up after user input + setTimeout(() => {this.handleCursorInsert(this.inputBuffer.nextLine())}, 1); + } return new Promise((resolve, reject) => { this.resolveInput = (value) => { - this.input = '' resolve(value) } })