Fix prompt in deno, but break in bun :(
This commit is contained in:
parent
4d54d4bf8a
commit
8ee17f4eef
@ -16,8 +16,10 @@ const BunTerminalIO: ITerminal = {
|
|||||||
argv: (Bun.argv.length > 2) ? Bun.argv.slice(2) : [],
|
argv: (Bun.argv.length > 2) ? Bun.argv.slice(2) : [],
|
||||||
inputLoop: async function* inputLoop() {
|
inputLoop: async function* inputLoop() {
|
||||||
for await (const chunk of Bun.stdin.stream()) {
|
for await (const chunk of Bun.stdin.stream()) {
|
||||||
yield readKey(chunk);
|
yield chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
getTerminalSize: async function getTerminalSize(): Promise<ITerminalSize> {
|
getTerminalSize: async function getTerminalSize(): Promise<ITerminalSize> {
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
@ -33,28 +35,32 @@ const BunTerminalIO: ITerminal = {
|
|||||||
|
|
||||||
// Get the first chunk from stdin
|
// Get the first chunk from stdin
|
||||||
// The response is \x1b[(rows);(cols)R..
|
// The response is \x1b[(rows);(cols)R..
|
||||||
for await (const chunk of Bun.stdin.stream()) {
|
const chunk = await BunTerminalIO.readStdinRaw();
|
||||||
const rawCode = (new TextDecoder()).decode(chunk);
|
if (chunk === null) {
|
||||||
const res = rawCode.trim().replace(/^.\[([0-9]+;[0-9]+)R$/, '$1');
|
return defaultTerminalSize;
|
||||||
const [srows, scols] = res.split(';');
|
|
||||||
const rows = parseInt(srows, 10) ?? 24;
|
|
||||||
const cols = parseInt(scols, 10) ?? 80;
|
|
||||||
|
|
||||||
// Clear the screen
|
|
||||||
await write(Ansi.ClearScreen + Ansi.ResetCursor);
|
|
||||||
|
|
||||||
return {
|
|
||||||
rows,
|
|
||||||
cols,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultTerminalSize;
|
const rawCode = (new TextDecoder()).decode(chunk);
|
||||||
|
const res = rawCode.trim().replace(/^.\[([0-9]+;[0-9]+)R$/, '$1');
|
||||||
|
const [srows, scols] = res.split(';');
|
||||||
|
const rows = parseInt(srows, 10) ?? 24;
|
||||||
|
const cols = parseInt(scols, 10) ?? 80;
|
||||||
|
|
||||||
|
// Clear the screen
|
||||||
|
await write(Ansi.ClearScreen + Ansi.ResetCursor);
|
||||||
|
|
||||||
|
return {
|
||||||
|
rows,
|
||||||
|
cols,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
readStdin: async function (): Promise<string> {
|
readStdin: async function (): Promise<string | null> {
|
||||||
const gen = BunTerminalIO.inputLoop();
|
const raw = await BunTerminalIO.readStdinRaw();
|
||||||
const chunk = await gen.next();
|
return readKey(raw ?? new Uint8Array(0));
|
||||||
return chunk.value!;
|
},
|
||||||
|
readStdinRaw: async function (): Promise<Uint8Array | null> {
|
||||||
|
const chunk = await BunTerminalIO.inputLoop().next();
|
||||||
|
return chunk.value ?? null;
|
||||||
},
|
},
|
||||||
writeStdout: async function write(s: string): Promise<void> {
|
writeStdout: async function write(s: string): Promise<void> {
|
||||||
const buffer = new TextEncoder().encode(s);
|
const buffer = new TextEncoder().encode(s);
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
ITerminalSize,
|
ITerminalSize,
|
||||||
logToFile,
|
logToFile,
|
||||||
Position,
|
Position,
|
||||||
|
readKey,
|
||||||
SCROLL_QUIT_TIMES,
|
SCROLL_QUIT_TIMES,
|
||||||
SCROLL_VERSION,
|
SCROLL_VERSION,
|
||||||
} from './mod.ts';
|
} from './mod.ts';
|
||||||
@ -229,23 +230,35 @@ class Editor {
|
|||||||
|
|
||||||
let res = '';
|
let res = '';
|
||||||
|
|
||||||
while (true) {
|
outer: while (true) {
|
||||||
this.setStatusMessage(`${p}${res}`);
|
this.setStatusMessage(`${p}${res}`);
|
||||||
await this.refreshScreen();
|
await this.refreshScreen();
|
||||||
const char = await term.readStdin();
|
for await (const chunk of term.inputLoop()) {
|
||||||
// End the prompt
|
const char = readKey(chunk);
|
||||||
if (char === KeyCommand.Enter) {
|
if (char === null) {
|
||||||
this.setStatusMessage('');
|
continue;
|
||||||
if (res.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
// End the prompt
|
||||||
}
|
if (char === KeyCommand.Enter) {
|
||||||
|
this.setStatusMessage('');
|
||||||
|
if (res.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Add to the prompt result
|
return res;
|
||||||
if (!isControl(char)) {
|
}
|
||||||
res += char;
|
|
||||||
|
// Allow backspacing
|
||||||
|
if (char === KeyCommand.Backspace || char === KeyCommand.Delete) {
|
||||||
|
res = truncate(res, res.length - 1);
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to the prompt result
|
||||||
|
if (!isControl(char!)) {
|
||||||
|
res += char;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { getRuntime } from './runtime.ts';
|
import { getRuntime, readKey } from './runtime.ts';
|
||||||
import { getTermios } from './termios.ts';
|
import { getTermios } from './termios.ts';
|
||||||
import Editor from './editor.ts';
|
import Editor from './editor.ts';
|
||||||
|
|
||||||
@ -37,16 +37,21 @@ export async function main() {
|
|||||||
// Clear the screen
|
// Clear the screen
|
||||||
await editor.refreshScreen();
|
await editor.refreshScreen();
|
||||||
|
|
||||||
for await (const char of term.inputLoop()) {
|
while (true) {
|
||||||
// Process input
|
for await (const char of term.inputLoop()) {
|
||||||
const shouldLoop = await editor.processKeyPress(char);
|
const parsed = readKey(char);
|
||||||
if (!shouldLoop) {
|
if (char.length === 0 || parsed.length === 0) {
|
||||||
return 0;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process input
|
||||||
|
const shouldLoop = await editor.processKeyPress(parsed);
|
||||||
|
if (!shouldLoop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render output
|
||||||
|
await editor.refreshScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render output
|
|
||||||
await editor.refreshScreen();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
@ -64,14 +64,22 @@ export interface ITerminal {
|
|||||||
/**
|
/**
|
||||||
* The generator function returning chunks of input from the stdin stream
|
* The generator function returning chunks of input from the stdin stream
|
||||||
*/
|
*/
|
||||||
inputLoop(): AsyncGenerator<string, void>;
|
inputLoop(): AsyncGenerator<Uint8Array, null>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the size of the terminal
|
* Get the size of the terminal
|
||||||
*/
|
*/
|
||||||
getTerminalSize(): Promise<ITerminalSize>;
|
getTerminalSize(): Promise<ITerminalSize>;
|
||||||
|
|
||||||
readStdin(): Promise<string>;
|
/**
|
||||||
|
* Get the current chunk of input, if it exists
|
||||||
|
*/
|
||||||
|
readStdin(): Promise<string | null>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the raw chunk of input
|
||||||
|
*/
|
||||||
|
readStdinRaw(): Promise<Uint8Array | null>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pipe a string to stdout
|
* Pipe a string to stdout
|
||||||
@ -147,6 +155,9 @@ let scrollRuntime: IRuntime | null = null;
|
|||||||
* @param raw - the raw chunk of input
|
* @param raw - the raw chunk of input
|
||||||
*/
|
*/
|
||||||
export function readKey(raw: Uint8Array): string {
|
export function readKey(raw: Uint8Array): string {
|
||||||
|
if (raw.length === 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
const parsed = decoder.decode(raw);
|
const parsed = decoder.decode(raw);
|
||||||
|
|
||||||
// Return the input if it's unambiguous
|
// Return the input if it's unambiguous
|
||||||
|
@ -2,13 +2,10 @@ import { ITerminal, ITerminalSize, readKey } from '../common/runtime.ts';
|
|||||||
|
|
||||||
const DenoTerminalIO: ITerminal = {
|
const DenoTerminalIO: ITerminal = {
|
||||||
argv: Deno.args,
|
argv: Deno.args,
|
||||||
/**
|
inputLoop: async function* (): AsyncGenerator<Uint8Array, null> {
|
||||||
* Wrap the runtime-specific hook into stdin
|
yield await DenoTerminalIO.readStdinRaw() ?? new Uint8Array(0);
|
||||||
*/
|
|
||||||
inputLoop: async function* inputLoop() {
|
return null;
|
||||||
for await (const chunk of Deno.stdin.readable) {
|
|
||||||
yield readKey(chunk);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
getTerminalSize: function getSize(): Promise<ITerminalSize> {
|
getTerminalSize: function getSize(): Promise<ITerminalSize> {
|
||||||
const size: { rows: number; columns: number } = Deno.consoleSize();
|
const size: { rows: number; columns: number } = Deno.consoleSize();
|
||||||
@ -18,10 +15,16 @@ const DenoTerminalIO: ITerminal = {
|
|||||||
cols: size.columns,
|
cols: size.columns,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
readStdin: async function (): Promise<string> {
|
readStdin: async function (): Promise<string | null> {
|
||||||
const gen = DenoTerminalIO.inputLoop();
|
const raw = await DenoTerminalIO.readStdinRaw();
|
||||||
const chunk = await gen.next();
|
return readKey(raw ?? new Uint8Array(0));
|
||||||
return chunk.value!;
|
},
|
||||||
|
readStdinRaw: async function (): Promise<Uint8Array | null> {
|
||||||
|
const reader = Deno.stdin.readable.getReader();
|
||||||
|
const chunk = await reader.read();
|
||||||
|
const res = chunk?.value;
|
||||||
|
reader.releaseLock();
|
||||||
|
return res ?? null;
|
||||||
},
|
},
|
||||||
writeStdout: async function write(s: string): Promise<void> {
|
writeStdout: async function write(s: string): Promise<void> {
|
||||||
const buffer: Uint8Array = new TextEncoder().encode(s);
|
const buffer: Uint8Array = new TextEncoder().encode(s);
|
||||||
|
Loading…
Reference in New Issue
Block a user