Setup basic terminal output for Deno

This commit is contained in:
Timothy Warren 2023-11-08 11:11:19 -05:00
parent e3083b1743
commit f238b162f6
5 changed files with 93 additions and 21 deletions

View File

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

View File

@ -1,6 +1,60 @@
export class Editor {
import { Ansi } from './ansi.ts';
import { importForRuntime } from './runtime.ts';
import { ctrl_key } from './strings.ts';
class Buffer {
#b = '';
constructor() {
}
public processKeyPress(): void {
append(s: string): void {
this.#b += s;
}
clear(): void {
this.#b = '';
}
getBuffer(): string {
return this.#b;
}
}
export class Editor {
#buffer: Buffer;
constructor() {
this.#buffer = new Buffer();
}
/**
* Determine what to do based on input
* @param input - the decoded chunk of stdin
*/
public processKeyPress(input: string): boolean {
switch (input) {
case ctrl_key('q'):
this.clearScreen();
return false;
default:
return true;
}
}
/**
* Clear the screen and write out the buffer
*/
public async refreshScreen(): Promise<void> {
const { write } = await importForRuntime('terminal_io');
this.clearScreen();
await write(this.#buffer.getBuffer());
this.#buffer.clear();
}
private clearScreen(): void {
this.#buffer.append(Ansi.ClearScreen);
this.#buffer.append(Ansi.ResetCursor);
}
}

View File

@ -1,5 +1,5 @@
import { importForRuntime } from "./runtime";
import { ctrl_key, is_control } from "./strings";
import { importForRuntime } from './runtime.ts';
import { Editor } from './editor.ts';
export * from './runtime.ts';
export * from './strings.ts';
@ -8,31 +8,28 @@ export type { ITestBase } from './test_base.ts';
const decoder = new TextDecoder();
export function readKey(chunk): string {
const char = String(decoder.decode(chunk))
return char;
}
export async function main() {
const { inputLoop, init } = await importForRuntime('mod.ts');
// Set up handlers to enable/disable raw mode for each runtime
await init();
// Create the editor itself
const editor = new Editor();
// The main event loop
for await (const chunk of inputLoop()) {
const char = readKey(chunk);
const char = String(decoder.decode(chunk));
if (char === ctrl_key('q')) {
// Clear the screen for output
await editor.refreshScreen();
// Process input
const shouldLoop = editor.processKeyPress(char);
if (!shouldLoop) {
return 0;
}
if (is_control(char)) {
console.log(char.codePointAt(0) + '\r');
} else {
console.log(`${char} ('${char.codePointAt(0)}')\r`);
}
}
return -1;

View File

@ -1,7 +1,7 @@
/**
* The main entrypoint when using Deno as the runtime
*/
import { getTermios } from "../common/mod.ts";
import { getTermios } from '../common/mod.ts';
export * from './terminal_io.ts';

View File

@ -6,3 +6,11 @@ export async function* inputLoop() {
yield chunk;
}
}
export async function write(s: string): Promise<void> {
const buffer = new TextEncoder().encode(s);
const stdout = Deno.stdout.writable.getWriter();
await stdout.write(buffer);
stdout.releaseLock();
}