diff --git a/justfile b/justfile index 98432e3..b338e7d 100644 --- a/justfile +++ b/justfile @@ -50,11 +50,11 @@ bun-run file="": # Lint code and check types deno-check: deno lint - deno check --unstable --all -c deno.jsonc ./src/deno/*.ts ./src/common/*.ts + deno check --unstable-ffi --all -c deno.jsonc ./src/deno/*.ts ./src/common/*.ts # Test with deno deno-test: - deno test --allow-all --unstable + deno test --allow-all --unstable-ffi # Create test coverage report with deno deno-coverage: @@ -62,4 +62,4 @@ deno-coverage: # Run with deno deno-run file="": - deno run --allow-all --allow-ffi --deny-hrtime --unstable ./src/scroll.ts {{file}} + deno run --allow-all --allow-ffi --deny-hrtime --unstable-ffi ./src/scroll.ts {{file}} diff --git a/src/common/all_test.ts b/src/common/all_test.ts index 8b0c3a9..57e98c1 100644 --- a/src/common/all_test.ts +++ b/src/common/all_test.ts @@ -138,6 +138,19 @@ testSuite({ assertEquals(p.x, 5); assertEquals(p.y, 7); }, + '.from': () => { + const p1 = Position.at(1, 2); + const p2 = Position.from(p1); + + p1.x = 2; + p1.y = 4; + + assertEquals(p1.x, 2); + assertEquals(p1.y, 4); + + assertEquals(p2.x, 1); + assertEquals(p2.y, 2); + }, }, Row: { '.default': () => { diff --git a/src/common/editor.ts b/src/common/editor.ts index 17692d4..6b5ac14 100644 --- a/src/common/editor.ts +++ b/src/common/editor.ts @@ -231,10 +231,18 @@ class Editor { return true; } - public async prompt(p: string): Promise { + public async prompt( + p: string, + callback?: (query: string, char: string) => void, + ): Promise { const { term } = await getRuntime(); let res = ''; + const maybeCallback = (query: string, char: string) => { + if (callback !== undefined) { + callback(query, char); + } + }; outer: while (true) { if (p.includes('%s')) { @@ -250,26 +258,38 @@ class Editor { continue; } - // End the prompt - if (char === KeyCommand.Enter) { - this.setStatusMessage(''); - if (res.length === 0) { + switch (char) { + // Remove the last character from the prompt input + case KeyCommand.Backspace: + case KeyCommand.Delete: + res = truncate(res, res.length - 1); + maybeCallback(res, char); + continue outer; + + // End the prompt + case KeyCommand.Escape: + this.setStatusMessage(''); + maybeCallback(res, char); + return null; - } - return res; + // Return the input and end the prompt + case KeyCommand.Enter: + if (res.length > 0) { + this.setStatusMessage(''); + maybeCallback(res, char); + return res; + } + break; + + // Add to the prompt result + default: + if (!isControl(char)) { + res += char; + } } - // Allow backspacing - if (char === KeyCommand.Backspace || char === KeyCommand.Delete) { - res = truncate(res, res.length - 1); - continue outer; - } - - // Add to the prompt result - if (!isControl(char!)) { - res += char; - } + maybeCallback(res, char); } } } @@ -278,14 +298,32 @@ class Editor { * Find text within the document */ public async find(): Promise { - const res = await this.prompt('Search: %s (ESC to cancel)'); - if (res !== null && res.length > 0) { - const pos = this.#document.find(res); - if (pos !== null) { - this.#cursor = pos; - } else { - this.setStatusMessage('Not found'); - } + const savedCursor = Position.from(this.#cursor); + const savedOffset = Position.from(this.#offset); + + const query = await this.prompt( + 'Search: %s (ESC to cancel)', + (query: string, key: string) => { + if (key === KeyCommand.Enter || key === KeyCommand.Escape) { + return; + } + + if (query !== null && query.length > 0) { + const pos = this.#document.find(query); + if (pos !== null) { + this.#cursor = pos; + } else { + this.setStatusMessage('Not found'); + } + } + }, + ); + + // Return to document position before search + // when you cancel the search (press the escape key) + if (query !== null) { + this.#cursor = Position.from(savedCursor); + this.#offset = Position.from(savedOffset); } } diff --git a/src/common/types.ts b/src/common/types.ts index d7953a6..271a26c 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -139,7 +139,7 @@ export class Position { public x: number; public y: number; - private constructor(x: number, y: number) { + private constructor(x: number = 0, y: number = 0) { this.x = x; this.y = y; } @@ -148,9 +148,18 @@ export class Position { return new Position(x, y); } - public static default(): Position { - return new Position(0, 0); + public static from(p: Position): Position { + return new Position(p.x, p.y); } + + public static default(): Position { + return new Position(); + } +} + +export enum SearchDirection { + Forward = 1, + Backward = -1, } // ----------------------------------------------------------------------------