Use Tsx/node implementation for file/terminal_io in Bun runtime
Some checks failed
timw4mail/scroll/pipeline/head There was a failure building this commit

This commit is contained in:
Timothy Warren 2024-07-09 10:23:25 -04:00
parent 0148561240
commit 88bf3da4e7
5 changed files with 44 additions and 109 deletions

View File

@ -1,18 +0,0 @@
import { IFileIO } from '../common/runtime.ts';
import { appendFile } from 'node:fs/promises';
const BunFileIO: IFileIO = {
openFile: async (path: string): Promise<string> => {
const file = await Bun.file(path);
return await file.text();
},
appendFile: async function (path: string, contents: string): Promise<void> {
return await appendFile(path, contents);
},
saveFile: async function (path: string, contents: string): Promise<void> {
await Bun.write(path, contents);
return;
},
};
export default BunFileIO;

View File

@ -3,8 +3,8 @@
*/ */
import { IRuntime, RunTimeType } from '../common/runtime.ts'; import { IRuntime, RunTimeType } from '../common/runtime.ts';
import BunTerminalIO from './terminal_io.ts'; import TerminalIO from '../tsx/terminal_io.ts';
import BunFileIO from './file_io.ts'; import FileIO from '../tsx/file_io.ts';
import process from 'node:process'; import process from 'node:process';
@ -13,8 +13,8 @@ import process from 'node:process';
*/ */
const BunRuntime: IRuntime = { const BunRuntime: IRuntime = {
name: RunTimeType.Bun, name: RunTimeType.Bun,
file: BunFileIO, file: FileIO,
term: BunTerminalIO, term: TerminalIO,
onEvent: (eventName: string, handler) => process.on(eventName, handler), onEvent: (eventName: string, handler) => process.on(eventName, handler),
onExit: (cb: () => void): void => { onExit: (cb: () => void): void => {
process.on('beforeExit', cb); process.on('beforeExit', cb);

View File

@ -1,85 +0,0 @@
/**
* 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() {
yield (await BunTerminalIO.readStdinRaw()) ?? new Uint8Array(0);
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: function (): Promise<Uint8Array | null> {
return new Promise((resolve) => {
process.stdin.resume().once('data', (buffer: Uint8Array) => {
resolve(buffer);
});
});
},
writeStdout: async function write(s: string): Promise<void> {
const buffer = encoder.encode(s);
await Bun.write(Bun.stdout, buffer);
},
};
export default BunTerminalIO;

View File

@ -35,6 +35,45 @@ let scrollRuntime: IRuntime | null = null;
// Misc runtime functions // Misc runtime functions
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/**
* Get the size of the terminal window via ANSI codes
* @see https://viewsourcecode.org/snaptoken/kilo/03.rawInputAndOutput.html#window-size-the-hard-way
*/
async function _getTerminalSizeFromAnsi(): Promise<ITerminalSize> {
const { term } = await getRuntime();
// 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 term.writeStdout(
Ansi.moveCursorForward(999) + Ansi.moveCursorDown(999),
);
// Ask where the cursor is
await term.writeStdout(Ansi.GetCursorLocation);
// Get the first chunk from stdin
// The response is \x1b[(rows);(cols)R..
const chunk = await term.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 term.writeStdout(Ansi.ClearScreen + Ansi.ResetCursor);
return {
rows,
cols,
};
}
export function log(s: unknown, level: LogLevel = LogLevel.Notice): void { export function log(s: unknown, level: LogLevel = LogLevel.Notice): void {
getRuntime().then(({ file }) => { getRuntime().then(({ file }) => {
const raw = JSON.stringify(s, null, 2); const raw = JSON.stringify(s, null, 2);

View File

@ -1,13 +1,12 @@
// @ts-nocheck: Bun is used for typescript checks, bun does not have 'node:assert' or 'node:test'
/** /**
* Adapt the node test interface to the shared testing interface * Adapt the node test interface to the shared testing interface
*/ */
// @ts-ignore: Only in newer versions of node
import { import {
deepStrictEqual, deepStrictEqual,
notStrictEqual, notStrictEqual,
strictEqual, strictEqual,
} from 'node:assert/strict'; } from 'node:assert/strict';
// @ts-ignore: Only in newer versions of node
import { describe, it } from 'node:test'; import { describe, it } from 'node:test';
import { ITestBase } from '../common/types.ts'; import { ITestBase } from '../common/types.ts';