From 3f1326ebd006e21a66c438a4515e4d83f85c8c52 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 10 Nov 2023 19:17:36 -0500 Subject: [PATCH] Add PageUp, PageDown, Home, and End input handling --- src/common/editor/ansi.ts | 4 ++++ src/common/editor/editor.ts | 38 +++++++++++++++++++++++++++++++++---- src/scroll.ts | 30 ++++++++++++++++++++++++++++- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/common/editor/ansi.ts b/src/common/editor/ansi.ts index 5520a32..f46f649 100644 --- a/src/common/editor/ansi.ts +++ b/src/common/editor/ansi.ts @@ -12,6 +12,10 @@ export enum KeyCommand { ArrowDown = `${ANSI_PREFIX}B`, ArrowRight = `${ANSI_PREFIX}C`, ArrowLeft = `${ANSI_PREFIX}D`, + PageUp = `${ANSI_PREFIX}5~`, + PageDown = `${ANSI_PREFIX}6~`, + Home = 'LineHome', + End = 'LineEnd', } export const Ansi = { diff --git a/src/common/editor/editor.ts b/src/common/editor/editor.ts index da5c76e..e2288ed 100644 --- a/src/common/editor/editor.ts +++ b/src/common/editor/editor.ts @@ -29,6 +29,28 @@ export class Editor { this.clearScreen().then(() => {}); return false; + case KeyCommand.Home: + this.#cursor.x = 0; + break; + + case KeyCommand.End: + this.#cursor.x = this.#screen.cols - 1; + break; + + case KeyCommand.PageUp: + case KeyCommand.PageDown: + { + let times = this.#screen.rows; + while (times--) { + this.moveCursor( + input === KeyCommand.PageUp + ? KeyCommand.ArrowUp + : KeyCommand.ArrowDown, + ); + } + } + break; + case KeyCommand.ArrowUp: case KeyCommand.ArrowDown: case KeyCommand.ArrowRight: @@ -43,16 +65,24 @@ export class Editor { private moveCursor(char: string): void { switch (char) { case KeyCommand.ArrowLeft: - this.#cursor.x--; + if (this.#cursor.x > 0) { + this.#cursor.x--; + } break; case KeyCommand.ArrowRight: - this.#cursor.x++; + if (this.#cursor.x < this.#screen.cols) { + this.#cursor.x++; + } break; case KeyCommand.ArrowUp: - this.#cursor.y--; + if (this.#cursor.y > 0) { + this.#cursor.y--; + } break; case KeyCommand.ArrowDown: - this.#cursor.y++; + if (this.#cursor.y < this.#screen.rows) { + this.#cursor.y++; + } break; } } diff --git a/src/scroll.ts b/src/scroll.ts index 9c6f5de..0101a56 100644 --- a/src/scroll.ts +++ b/src/scroll.ts @@ -2,9 +2,37 @@ * The starting point for running scroll */ import { Editor, getRuntime, getTermios } from './common/mod.ts'; +import { KeyCommand } from './common/editor/ansi.ts'; const decoder = new TextDecoder(); +function readKey(raw: Uint8Array): string { + const parsed = decoder.decode(raw); + + // Return the input if it's unambiguous + if (parsed in KeyCommand) { + return parsed; + } + + // Some keycodes have multiple potential inputs + switch (parsed) { + case '\x1bOH': + case '\x1b[7~': + case '\x1b[1~': + case '\x1b[H': + return KeyCommand.Home; + + case '\x1bOF': + case '\x1b[8~': + case '\x1b[4~': + case '\x1b[F': + return KeyCommand.End; + + default: + return parsed; + } +} + export async function main() { const runTime = await getRuntime(); const { io, onExit } = runTime; @@ -25,7 +53,7 @@ export async function main() { // The main event loop for await (const chunk of io.inputLoop()) { // Process input - const char = String(decoder.decode(chunk)); + const char = readKey(chunk); const shouldLoop = editor.processKeyPress(char); if (!shouldLoop) { return 0;