diff --git a/src/bun/file_io.ts b/src/bun/file_io.ts new file mode 100644 index 0000000..7b71c7e --- /dev/null +++ b/src/bun/file_io.ts @@ -0,0 +1,15 @@ +import { IFIO } from '../common/types.ts'; + +import { readFileSync } from 'node:fs'; + +const BunFileIO: IFIO = { + openFile: async (path: string): Promise => { + const file = await Bun.file(path); + return await file.text(); + }, + openFileSync: (path: string): string => { + return readFileSync(path); + }, +}; + +export default BunFileIO; diff --git a/src/bun/mod.ts b/src/bun/mod.ts index e77d475..46fddc8 100644 --- a/src/bun/mod.ts +++ b/src/bun/mod.ts @@ -5,6 +5,7 @@ import { getTermios, IRuntime, RunTimeType } from '../common/mod.ts'; import BunFFI from './ffi.ts'; import BunTerminalIO from './terminal_io.ts'; +import BunFileIO from './file_io.ts'; process.on('error', async (e) => { (await getTermios()).disableRawMode(); @@ -14,8 +15,9 @@ process.on('error', async (e) => { const BunRuntime: IRuntime = { name: RunTimeType.Bun, + file: BunFileIO, ffi: BunFFI, - io: BunTerminalIO, + term: BunTerminalIO, onExit: (cb: () => void): void => { process.on('beforeExit', cb); process.on('exit', cb); diff --git a/src/bun/terminal_io.ts b/src/bun/terminal_io.ts index 933f2a7..330e56e 100644 --- a/src/bun/terminal_io.ts +++ b/src/bun/terminal_io.ts @@ -1,10 +1,11 @@ /** * Wrap the runtime-specific hook into stdin */ -import { ITerminalIO, ITerminalSize } from '../common/mod.ts'; -import Ansi from '../common/editor/ansi.ts'; +import { ITerminal, ITerminalSize } from '../common/mod.ts'; +import Ansi from '../common/ansi.ts'; -const BunTerminalIO: ITerminalIO = { +const BunTerminalIO: ITerminal = { + argv: Bun.argv, inputLoop: async function* inputLoop() { for await (const chunk of Bun.stdin.stream()) { yield chunk; @@ -45,7 +46,7 @@ const BunTerminalIO: ITerminalIO = { cols: 80, }; }, - write: async function write(s: string): Promise { + writeStdout: async function write(s: string): Promise { const buffer = new TextEncoder().encode(s); await Bun.write(Bun.stdout, buffer); diff --git a/src/common/editor/ansi.ts b/src/common/ansi.ts similarity index 100% rename from src/common/editor/ansi.ts rename to src/common/ansi.ts diff --git a/src/common/buffer.ts b/src/common/buffer.ts new file mode 100644 index 0000000..6b9a18d --- /dev/null +++ b/src/common/buffer.ts @@ -0,0 +1,36 @@ +import { strlen } from './utils.ts'; +import { getRuntime } from './runtime.ts'; + +class Buffer { + #b = ''; + + constructor() { + } + + public append(s: string): void { + this.#b += s; + } + + public appendLine(s: string): void { + this.#b += s + '\r\n'; + } + + public clear(): void { + this.#b = ''; + } + + /** + * Output the contents of the buffer into stdout + */ + public async flush() { + const { term } = await getRuntime(); + await term.writeStdout(this.#b); + this.clear(); + } + + public strlen(): number { + return strlen(this.#b); + } +} + +export default Buffer; diff --git a/src/common/editor/document.ts b/src/common/document.ts similarity index 88% rename from src/common/editor/document.ts rename to src/common/document.ts index b5cfbd1..dc7e793 100644 --- a/src/common/editor/document.ts +++ b/src/common/document.ts @@ -1,4 +1,4 @@ -import { chars } from '../utils.ts'; +import { chars } from './utils.ts'; export class Row { chars: string[] = []; @@ -27,7 +27,7 @@ export class Document { return new Document(); } - public static open(): Document { + public static open(_filename: string): Document { const doc = new Document(); const line = 'Hello, World!'; const row = new Row(line); diff --git a/src/common/editor/editor.ts b/src/common/editor.ts similarity index 95% rename from src/common/editor/editor.ts rename to src/common/editor.ts index 809a401..efc9fc6 100644 --- a/src/common/editor/editor.ts +++ b/src/common/editor.ts @@ -1,21 +1,23 @@ import Ansi, { KeyCommand } from './ansi.ts'; import Buffer from './buffer.ts'; import Document from './document.ts'; -import { ctrl_key, IPoint, ITerminalSize, truncate, VERSION } from '../mod.ts'; +import { ctrl_key, IPoint, ITerminalSize, truncate, VERSION } from './mod.ts'; export class Editor { #buffer: Buffer; #screen: ITerminalSize; #cursor: IPoint; #document: Document; - constructor(terminalSize: ITerminalSize) { + constructor(terminalSize: ITerminalSize, args: string[]) { this.#buffer = new Buffer(); this.#screen = terminalSize; this.#cursor = { x: 0, y: 0, }; - this.#document = Document.open(); + this.#document = (args.length >= 2) + ? Document.open(args[1]) + : Document.empty(); } // -------------------------------------------------------------------------- diff --git a/src/common/editor/buffer.ts b/src/common/editor/buffer.ts deleted file mode 100644 index 061a126..0000000 --- a/src/common/editor/buffer.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { strlen } from '../utils.ts'; -import { getRuntime } from '../runtime.ts'; - -class Buffer { - #b = ''; - - constructor() { - } - - append(s: string): void { - this.#b += s; - } - - appendLine(s: string): void { - this.#b += s + '\r\n'; - } - - clear(): void { - this.#b = ''; - } - - getBuffer(): string { - return this.#b; - } - - async flush() { - const { io } = await getRuntime(); - await io.write(this.#b); - this.clear(); - } - - strlen(): number { - return strlen(this.#b); - } -} - -export default Buffer; diff --git a/src/common/editor/mod.ts b/src/common/editor/mod.ts deleted file mode 100644 index 3d93483..0000000 --- a/src/common/editor/mod.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './editor.ts'; diff --git a/src/common/mod.ts b/src/common/mod.ts index f60d4b0..198b147 100644 --- a/src/common/mod.ts +++ b/src/common/mod.ts @@ -1,4 +1,4 @@ -export * from './editor/mod.ts'; +export * from './editor.ts'; export * from './runtime.ts'; export * from './termios.ts'; export * from './utils.ts'; diff --git a/src/common/types.ts b/src/common/types.ts index aa52d02..0ec1f25 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -47,9 +47,13 @@ export interface ITerminalSize { } /** - * Runtime-specific IO streams + * Runtime-specific terminal functionality */ -export interface ITerminalIO { +export interface ITerminal { + /** + * The arguments passed to the program on launch + */ + argv: string[]; /** * The generator function returning chunks of input from the stdin stream */ @@ -63,7 +67,15 @@ export interface ITerminalIO { /** * Pipe a string to stdout */ - write(s: string): Promise; + writeStdout(s: string): Promise; +} + +/** + * Runtime-specific file handling + */ +export interface IFIO { + openFile(path: string): Promise; + openFileSync(path: string): string; } export interface IRuntime { @@ -78,7 +90,11 @@ export interface IRuntime { /** * Runtime-specific terminal functionality */ - io: ITerminalIO; + term: ITerminal; + /** + * Runtime-specific file system io + */ + file: IFIO; /** * Set a beforeExit/beforeUnload event handler for the runtime diff --git a/src/deno/file_io.ts b/src/deno/file_io.ts new file mode 100644 index 0000000..0cadcb2 --- /dev/null +++ b/src/deno/file_io.ts @@ -0,0 +1,16 @@ +import { IFIO } from '../common/types.ts'; + +const DenoFileIO: IFIO = { + openFile: async function (path: string): Promise { + const decoder = new TextDecoder('utf-8'); + const data = await Deno.readFile(path); + return decoder.decode(data); + }, + openFileSync: function (path: string): string { + const decoder = new TextDecoder('utf-8'); + const data = Deno.readFileSync(path); + return decoder.decode(data); + }, +}; + +export default DenoFileIO; diff --git a/src/deno/mod.ts b/src/deno/mod.ts index 18e3959..c8347f3 100644 --- a/src/deno/mod.ts +++ b/src/deno/mod.ts @@ -4,11 +4,13 @@ import { IRuntime, RunTimeType } from '../common/mod.ts'; import DenoFFI from './ffi.ts'; import DenoTerminalIO from './terminal_io.ts'; +import DenoFileIO from './file_io.ts'; const DenoRuntime: IRuntime = { name: RunTimeType.Deno, + file: DenoFileIO, ffi: DenoFFI, - io: DenoTerminalIO, + term: DenoTerminalIO, onExit: (cb: () => void): void => { globalThis.addEventListener('onbeforeunload', cb); globalThis.onbeforeunload = cb; diff --git a/src/deno/terminal_io.ts b/src/deno/terminal_io.ts index 2513882..aae62ff 100644 --- a/src/deno/terminal_io.ts +++ b/src/deno/terminal_io.ts @@ -1,6 +1,7 @@ -import { ITerminalIO, ITerminalSize } from '../common/types.ts'; +import { ITerminal, ITerminalSize } from '../common/types.ts'; -const DenoTerminalIO: ITerminalIO = { +const DenoTerminalIO: ITerminal = { + argv: Deno.args, /** * Wrap the runtime-specific hook into stdin */ @@ -17,7 +18,7 @@ const DenoTerminalIO: ITerminalIO = { cols: size.columns, }); }, - write: async function write(s: string): Promise { + writeStdout: async function write(s: string): Promise { const buffer: Uint8Array = new TextEncoder().encode(s); const stdout: WritableStream = Deno.stdout.writable; diff --git a/src/scroll.ts b/src/scroll.ts index 0101a56..9ca6dd4 100644 --- a/src/scroll.ts +++ b/src/scroll.ts @@ -2,7 +2,7 @@ * The starting point for running scroll */ import { Editor, getRuntime, getTermios } from './common/mod.ts'; -import { KeyCommand } from './common/editor/ansi.ts'; +import { KeyCommand } from './common/ansi.ts'; const decoder = new TextDecoder(); @@ -35,7 +35,7 @@ function readKey(raw: Uint8Array): string { export async function main() { const runTime = await getRuntime(); - const { io, onExit } = runTime; + const { term, onExit } = runTime; // Setup raw mode, and tear down on error or normal exit const t = await getTermios(); @@ -44,14 +44,14 @@ export async function main() { t.disableRawMode(); }); - const terminalSize = await io.getTerminalSize(); + const terminalSize = await term.getTerminalSize(); // Create the editor itself - const editor = new Editor(terminalSize); + const editor = new Editor(terminalSize, term.argv); await editor.refreshScreen(); // The main event loop - for await (const chunk of io.inputLoop()) { + for await (const chunk of term.inputLoop()) { // Process input const char = readKey(chunk); const shouldLoop = editor.processKeyPress(char);