Fix crash on macOS
This commit is contained in:
parent
8ee17f4eef
commit
9711202c3b
@ -2,151 +2,24 @@ import { getTermios } from './termios.ts';
|
|||||||
import { ctrlKey, noop } from './utils.ts';
|
import { ctrlKey, noop } from './utils.ts';
|
||||||
import { ITestBase } from './types.ts';
|
import { ITestBase } from './types.ts';
|
||||||
import { KeyCommand } from './ansi.ts';
|
import { KeyCommand } from './ansi.ts';
|
||||||
|
import { IRuntime } from './runtime_types.ts';
|
||||||
|
export type * from './runtime_types.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which Typescript runtime is currently being used
|
||||||
|
*/
|
||||||
export enum RunTimeType {
|
export enum RunTimeType {
|
||||||
Bun = 'bun',
|
Bun = 'bun',
|
||||||
Deno = 'deno',
|
Deno = 'deno',
|
||||||
Unknown = 'common',
|
Unknown = 'common',
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
const decoder = new TextDecoder();
|
||||||
// Runtime adapter interfaces
|
let scrollRuntime: IRuntime | null = null;
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The size of terminal in rows and columns
|
|
||||||
*/
|
|
||||||
export interface ITerminalSize {
|
|
||||||
rows: number;
|
|
||||||
cols: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The native functions for getting/setting terminal settings
|
|
||||||
*/
|
|
||||||
export interface IFFI {
|
|
||||||
/**
|
|
||||||
* Get the existing termios settings (for canonical mode)
|
|
||||||
*/
|
|
||||||
tcgetattr(fd: number, termiosPtr: unknown): number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the termios settings
|
|
||||||
*/
|
|
||||||
tcsetattr(fd: number, act: number, termiosPtr: unknown): number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the termios pointer with raw mode settings
|
|
||||||
*/
|
|
||||||
cfmakeraw(termiosPtr: unknown): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a TypedArray to an opaque pointer for ffi calls
|
|
||||||
*/
|
|
||||||
// deno-lint-ignore no-explicit-any
|
|
||||||
getPointer(ta: any): unknown;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the FFI handle
|
|
||||||
*/
|
|
||||||
close(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runtime-specific terminal functionality
|
|
||||||
*/
|
|
||||||
export interface ITerminal {
|
|
||||||
/**
|
|
||||||
* The arguments passed to the program on launch
|
|
||||||
*/
|
|
||||||
argv: string[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The generator function returning chunks of input from the stdin stream
|
|
||||||
*/
|
|
||||||
inputLoop(): AsyncGenerator<Uint8Array, null>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the size of the terminal
|
|
||||||
*/
|
|
||||||
getTerminalSize(): Promise<ITerminalSize>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
writeStdout(s: string): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runtime-specific file handling
|
|
||||||
*/
|
|
||||||
export interface IFileIO {
|
|
||||||
openFile(path: string): Promise<string>;
|
|
||||||
openFileSync(path: string): string;
|
|
||||||
appendFile(path: string, contents: string): Promise<void>;
|
|
||||||
appendFileSync(path: string, contents: string): void;
|
|
||||||
saveFile(path: string, contents: string): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The common interface for runtime adapters
|
|
||||||
*/
|
|
||||||
export interface IRuntime {
|
|
||||||
/**
|
|
||||||
* The name of the runtime
|
|
||||||
*/
|
|
||||||
name: RunTimeType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runtime-specific terminal functionality
|
|
||||||
*/
|
|
||||||
term: ITerminal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runtime-specific file system io
|
|
||||||
*/
|
|
||||||
file: IFileIO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up an event handler
|
|
||||||
*
|
|
||||||
* @param eventName - The event to listen for
|
|
||||||
* @param handler - The event handler
|
|
||||||
*/
|
|
||||||
onEvent: (
|
|
||||||
eventName: string,
|
|
||||||
handler: (e: Event | ErrorEvent) => void,
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a beforeExit/beforeUnload event handler for the runtime
|
|
||||||
* @param cb - The event handler
|
|
||||||
*/
|
|
||||||
onExit(cb: () => void): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop execution
|
|
||||||
*
|
|
||||||
* @param code
|
|
||||||
*/
|
|
||||||
exit(code?: number): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Misc runtime functions
|
// Misc runtime functions
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
const decoder = new TextDecoder();
|
|
||||||
let scrollRuntime: IRuntime | null = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert input from ANSI escape sequences into a form
|
* Convert input from ANSI escape sequences into a form
|
||||||
@ -194,9 +67,12 @@ export function readKey(raw: Uint8Array): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append information to the scroll.log logfile
|
||||||
|
*/
|
||||||
export function logToFile(s: unknown) {
|
export function logToFile(s: unknown) {
|
||||||
importForRuntime('file_io').then((f) => {
|
importForRuntime('file_io').then((f) => {
|
||||||
const raw = (typeof s === 'string') ? s : JSON.stringify(s, null, 2);
|
const raw = typeof s === 'string' ? s : JSON.stringify(s, null, 2);
|
||||||
const output = raw + '\n';
|
const output = raw + '\n';
|
||||||
|
|
||||||
f.appendFile('./scroll.log', output).then(noop);
|
f.appendFile('./scroll.log', output).then(noop);
|
||||||
@ -210,6 +86,7 @@ export function logToFile(s: unknown) {
|
|||||||
export function die(s: string | Error): void {
|
export function die(s: string | Error): void {
|
||||||
getTermios().then((t) => {
|
getTermios().then((t) => {
|
||||||
t.disableRawMode();
|
t.disableRawMode();
|
||||||
|
t.cleanup();
|
||||||
console.error(s);
|
console.error(s);
|
||||||
|
|
||||||
getRuntime().then((r) => r.exit());
|
getRuntime().then((r) => r.exit());
|
||||||
@ -276,7 +153,8 @@ export const importForRuntime = async (path: string) => {
|
|||||||
const suffix = '.ts';
|
const suffix = '.ts';
|
||||||
const base = `../${runtime}/`;
|
const base = `../${runtime}/`;
|
||||||
|
|
||||||
const pathParts = path.split('/')
|
const pathParts = path
|
||||||
|
.split('/')
|
||||||
.filter((part) => part !== '' && part !== '.' && part !== suffix)
|
.filter((part) => part !== '' && part !== '.' && part !== suffix)
|
||||||
.map((part) => part.replace(suffix, ''));
|
.map((part) => part.replace(suffix, ''));
|
||||||
|
|
||||||
|
134
src/common/runtime_types.ts
Normal file
134
src/common/runtime_types.ts
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import { RunTimeType } from './runtime.ts';
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Runtime adapter interfaces
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of terminal in rows and columns
|
||||||
|
*/
|
||||||
|
export interface ITerminalSize {
|
||||||
|
rows: number;
|
||||||
|
cols: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The native functions for getting/setting terminal settings
|
||||||
|
*/
|
||||||
|
export interface IFFI {
|
||||||
|
/**
|
||||||
|
* Get the existing termios settings (for canonical mode)
|
||||||
|
*/
|
||||||
|
tcgetattr(fd: number, termiosPtr: unknown): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the termios settings
|
||||||
|
*/
|
||||||
|
tcsetattr(fd: number, act: number, termiosPtr: unknown): number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the termios pointer with raw mode settings
|
||||||
|
*/
|
||||||
|
cfmakeraw(termiosPtr: unknown): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a TypedArray to an opaque pointer for ffi calls
|
||||||
|
*/
|
||||||
|
// deno-lint-ignore no-explicit-any
|
||||||
|
getPointer(ta: any): unknown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the FFI handle
|
||||||
|
*/
|
||||||
|
close(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The common interface for runtime adapters
|
||||||
|
*/
|
||||||
|
export interface IRuntime {
|
||||||
|
/**
|
||||||
|
* The name of the runtime
|
||||||
|
*/
|
||||||
|
name: RunTimeType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime-specific terminal functionality
|
||||||
|
*/
|
||||||
|
term: {
|
||||||
|
/**
|
||||||
|
* The arguments passed to the program on launch
|
||||||
|
*/
|
||||||
|
argv: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The generator function returning chunks of input from the stdin stream
|
||||||
|
*/
|
||||||
|
inputLoop(): AsyncGenerator<Uint8Array, null>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of the terminal
|
||||||
|
*/
|
||||||
|
getTerminalSize(): Promise<ITerminalSize>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
writeStdout(s: string): Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime-specific file system io
|
||||||
|
*/
|
||||||
|
file: {
|
||||||
|
openFile(path: string): Promise<string>;
|
||||||
|
openFileSync(path: string): string;
|
||||||
|
appendFile(path: string, contents: string): Promise<void>;
|
||||||
|
appendFileSync(path: string, contents: string): void;
|
||||||
|
saveFile(path: string, contents: string): Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up an event handler
|
||||||
|
*
|
||||||
|
* @param eventName - The event to listen for
|
||||||
|
* @param handler - The event handler
|
||||||
|
*/
|
||||||
|
onEvent: (
|
||||||
|
eventName: string,
|
||||||
|
handler: (e: Event | ErrorEvent) => void,
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a beforeExit/beforeUnload event handler for the runtime
|
||||||
|
* @param cb - The event handler
|
||||||
|
*/
|
||||||
|
onExit(cb: () => void): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop execution
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
*/
|
||||||
|
exit(code?: number): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime-specific terminal functionality
|
||||||
|
*/
|
||||||
|
export type ITerminal = IRuntime['term'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime-specific file handling
|
||||||
|
*/
|
||||||
|
export type IFileIO = IRuntime['file'];
|
@ -42,7 +42,7 @@ class Termios {
|
|||||||
* The pointer to the termios struct
|
* The pointer to the termios struct
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
#ptr;
|
#ptr: unknown;
|
||||||
|
|
||||||
constructor(ffi: IFFI) {
|
constructor(ffi: IFFI) {
|
||||||
this.#ffi = ffi;
|
this.#ffi = ffi;
|
||||||
@ -80,7 +80,7 @@ class Termios {
|
|||||||
// @ts-ignore: bad type definition
|
// @ts-ignore: bad type definition
|
||||||
this.#cookedTermios = new Uint8Array(this.#termios, 0, 60);
|
this.#cookedTermios = new Uint8Array(this.#termios, 0, 60);
|
||||||
|
|
||||||
// Update termios struct with (most of the) raw settings
|
// Update termios struct with the raw settings
|
||||||
this.#ffi.cfmakeraw(this.#ptr);
|
this.#ffi.cfmakeraw(this.#ptr);
|
||||||
|
|
||||||
// Actually set the new termios settings
|
// Actually set the new termios settings
|
||||||
|
@ -22,6 +22,9 @@ export function arrayInsert<T>(
|
|||||||
return [...arr.slice(0, at), ...insert, ...arr.slice(at)];
|
return [...arr.slice(0, at), ...insert, ...arr.slice(at)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An empty function
|
||||||
|
*/
|
||||||
export const noop = () => {};
|
export const noop = () => {};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -13,7 +13,6 @@ const DenoRuntime: IRuntime = {
|
|||||||
globalThis.addEventListener(eventName, handler),
|
globalThis.addEventListener(eventName, handler),
|
||||||
onExit: (cb: () => void): void => {
|
onExit: (cb: () => void): void => {
|
||||||
globalThis.addEventListener('onbeforeunload', cb);
|
globalThis.addEventListener('onbeforeunload', cb);
|
||||||
globalThis.onbeforeunload = cb;
|
|
||||||
},
|
},
|
||||||
exit: (code?: number) => Deno.exit(code),
|
exit: (code?: number) => Deno.exit(code),
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@ 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> {
|
inputLoop: async function* (): AsyncGenerator<Uint8Array, null> {
|
||||||
yield await DenoTerminalIO.readStdinRaw() ?? new Uint8Array(0);
|
yield (await DenoTerminalIO.readStdinRaw()) ?? new Uint8Array(0);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
@ -26,7 +26,7 @@ const DenoTerminalIO: ITerminal = {
|
|||||||
reader.releaseLock();
|
reader.releaseLock();
|
||||||
return res ?? null;
|
return res ?? null;
|
||||||
},
|
},
|
||||||
writeStdout: async function write(s: string): Promise<void> {
|
writeStdout: async function (s: string): Promise<void> {
|
||||||
const buffer: Uint8Array = new TextEncoder().encode(s);
|
const buffer: Uint8Array = new TextEncoder().encode(s);
|
||||||
const stdout: WritableStream<Uint8Array> = Deno.stdout.writable;
|
const stdout: WritableStream<Uint8Array> = Deno.stdout.writable;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user