scroll/src/bun/terminal_io.ts

90 lines
2.6 KiB
JavaScript

/**
* Wrap the runtime-specific hook into stdin
*/
import process from 'node:process';
import Ansi from '../common/ansi.ts';
import { defaultTerminalSize } from '../common/config.ts';
import { readKey } from '../common/fns.ts';
import { ITerminal, ITerminalSize } from '../common/types.ts';
const encoder = new TextEncoder();
async function _getTerminalSizeFromAnsi(): Promise<ITerminalSize> {
// Tell the cursor to move to Row 999 and Column 999
// Since this command specifically doesn't go off the screen
// When we ask where the cursor is, we should get the size of the screen
await BunTerminalIO.writeStdout(
Ansi.moveCursorForward(999) + Ansi.moveCursorDown(999),
);
// Ask where the cursor is
await BunTerminalIO.writeStdout(Ansi.GetCursorLocation);
// Get the first chunk from stdin
// The response is \x1b[(rows);(cols)R..
const chunk = await BunTerminalIO.readStdinRaw();
if (chunk === null) {
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 BunTerminalIO.writeStdout(Ansi.ClearScreen + Ansi.ResetCursor);
return {
rows,
cols,
};
}
const BunTerminalIO: ITerminal = {
// Deno only returns arguments passed to the script, so
// remove the bun runtime executable, and entry script arguments
// to have consistent argument lists
argv: (Bun.argv.length > 2) ? Bun.argv.slice(2) : [],
inputLoop: async function* inputLoop() {
// for await (const chunk of Bun.stdin.stream()) {
// yield chunk;
// }
//
// return null;
for await (const chunk of process.stdin) {
yield encoder.encode(chunk);
}
return null;
},
/**
* Get the size of the terminal window via ANSI codes
* @see https://viewsourcecode.org/snaptoken/kilo/03.rawInputAndOutput.html#window-size-the-hard-way
*/
getTerminalSize: function getTerminalSize(): Promise<ITerminalSize> {
const [cols, rows] = process.stdout.getWindowSize();
return Promise.resolve({
rows,
cols,
});
},
readStdin: async function (): Promise<string | null> {
const raw = await BunTerminalIO.readStdinRaw();
return readKey(raw ?? new Uint8Array(0));
},
readStdinRaw: async function (): Promise<Uint8Array | null> {
const chunk = await BunTerminalIO.inputLoop().next();
return chunk.value ?? null;
},
writeStdout: async function write(s: string): Promise<void> {
const buffer = encoder.encode(s);
await Bun.write(Bun.stdout, buffer);
},
};
export default BunTerminalIO;