Implement methods to get current console size

This commit is contained in:
Timothy Warren 2023-11-08 17:02:59 -05:00
parent 9cca55b101
commit d99656de66
7 changed files with 90 additions and 50 deletions

View File

@ -1,7 +1,7 @@
/**
* Wrap the runtime-specific hook into stdin
*/
import { ITerminalIO } from '../common/mod.ts';
import { ITerminalIO, ITerminalSize } from '../common/mod.ts';
export async function* inputLoop() {
for await (const chunk of Bun.stdin.stream()) {
@ -15,8 +15,20 @@ export async function write(s: string): Promise<void> {
await Bun.write(Bun.stdout, buffer);
}
export function getSize(): ITerminalSize {
// @TODO implement
// Check for tput
// If has tput, use it to get terminal size
// If not, try FFI fallback
// Otherwise, return 80x25 as a last resort
const fallback: ITerminalSize = { rows: 25, cols: 80 };
return fallback;
}
const BunTerminalIO: ITerminalIO = {
inputLoop,
getSize,
write,
};

View File

@ -1,15 +1,13 @@
function escape(suffix: string): string {
return `\x1b[${suffix}`;
}
function moveCursor(row: number, col: number): string {
return escape(`${row};${col}H`);
function esc(pieces: TemplateStringsArray): string {
return '\x1b[' + pieces[0];
}
export const Ansi = {
ClearScreen: escape('2J'),
ResetCursor: escape('H'),
moveCursor,
ClearScreen: esc`2J`,
ResetCursor: esc`H`,
moveCursor: function moveCursor(row: number, col: number): string {
return `\x1b${row};${col}H`;
},
};
export default Ansi;

View File

@ -12,6 +12,10 @@ class Buffer {
this.#b += s;
}
appendLine(s: string): void {
this.#b += s + '\r\n';
}
clear(): void {
this.#b = '';
}
@ -49,10 +53,18 @@ export class Editor {
const { write } = await importForRuntime('terminal_io');
this.clearScreen();
this.drawRows();
await write(this.#buffer.getBuffer());
this.#buffer.clear();
}
private drawRows(): void {
for (let y = 0; y <= 24; y++) {
this.#buffer.appendLine('~');
}
}
private clearScreen(): void {
this.#buffer.append(Ansi.ClearScreen);
this.#buffer.append(Ansi.ResetCursor);

View File

@ -1,41 +1,3 @@
import { importForRuntime } from './runtime.ts';
import { Editor } from './editor.ts';
import { getTermios } from './termios.ts';
export * from './runtime.ts';
export * from './strings.ts';
export type * from './types.ts';
const decoder = new TextDecoder();
export async function main() {
const { inputLoop, onExit } = await importForRuntime('mod.ts');
// Setup raw mode, and tear down on error or normal exit
const t = await getTermios();
t.enableRawMode();
onExit(() => {
console.info('Exit handler called, disabling raw mode');
t.disableRawMode();
});
// Create the editor itself
const editor = new Editor();
// The main event loop
for await (const chunk of inputLoop()) {
const char = String(decoder.decode(chunk));
// Clear the screen for output
await editor.refreshScreen();
// Process input
const shouldLoop = editor.processKeyPress(char);
if (!shouldLoop) {
return 0;
}
}
return -1;
}

View File

@ -37,6 +37,11 @@ export interface IFFI {
getPointer(ta: any): unknown;
}
export interface ITerminalSize {
rows: number;
cols: number;
}
/**
* Runtime-specific IO streams
*/
@ -46,6 +51,11 @@ export interface ITerminalIO {
*/
inputLoop(): AsyncGenerator<Uint8Array, void, unknown>;
/**
* Get the size of the terminal
*/
getSize(): ITerminalSize;
/**
* Pipe a string to stdout
*/

View File

@ -1,4 +1,4 @@
import { ITerminalIO } from '../common/types.ts';
import { ITerminalIO, ITerminalSize } from '../common/types.ts';
/**
* Wrap the runtime-specific hook into stdin
@ -17,8 +17,18 @@ export async function write(s: string): Promise<void> {
stdout.releaseLock();
}
export function getSize(): ITerminalSize {
const size: { rows: number; columns: number } = Deno.consoleSize();
return {
rows: size.rows,
cols: size.columns,
};
}
const DenoTerminalIO: ITerminalIO = {
inputLoop,
getSize,
write,
};

View File

@ -1,7 +1,43 @@
/**
* The starting point for running scroll
*/
import { main } from './common/mod.ts';
import { importForRuntime } from './common/mod.ts';
import { getTermios } from './common/termios.ts';
import { Editor } from './common/editor.ts';
const decoder = new TextDecoder();
export async function main() {
const { inputLoop, onExit } = await importForRuntime('mod.ts');
// Setup raw mode, and tear down on error or normal exit
const t = await getTermios();
t.enableRawMode();
onExit(() => {
console.info('Exit handler called, disabling raw mode');
t.disableRawMode();
});
// Create the editor itself
const editor = new Editor();
// The main event loop
for await (const chunk of inputLoop()) {
const char = String(decoder.decode(chunk));
// Clear the screen for output
await editor.refreshScreen();
// Process input
const shouldLoop = editor.processKeyPress(char);
if (!shouldLoop) {
return 0;
}
}
return -1;
}
/**
* Start the event loop