Add incremental (character by character) search functionality
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
This commit is contained in:
parent
b3177cbd48
commit
e21944b4a4
6
justfile
6
justfile
@ -50,11 +50,11 @@ bun-run file="":
|
|||||||
# Lint code and check types
|
# Lint code and check types
|
||||||
deno-check:
|
deno-check:
|
||||||
deno lint
|
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
|
# Test with deno
|
||||||
deno-test:
|
deno-test:
|
||||||
deno test --allow-all --unstable
|
deno test --allow-all --unstable-ffi
|
||||||
|
|
||||||
# Create test coverage report with deno
|
# Create test coverage report with deno
|
||||||
deno-coverage:
|
deno-coverage:
|
||||||
@ -62,4 +62,4 @@ deno-coverage:
|
|||||||
|
|
||||||
# Run with deno
|
# Run with deno
|
||||||
deno-run file="":
|
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}}
|
||||||
|
@ -138,6 +138,19 @@ testSuite({
|
|||||||
assertEquals(p.x, 5);
|
assertEquals(p.x, 5);
|
||||||
assertEquals(p.y, 7);
|
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: {
|
Row: {
|
||||||
'.default': () => {
|
'.default': () => {
|
||||||
|
@ -231,10 +231,18 @@ class Editor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async prompt(p: string): Promise<string | null> {
|
public async prompt(
|
||||||
|
p: string,
|
||||||
|
callback?: (query: string, char: string) => void,
|
||||||
|
): Promise<string | null> {
|
||||||
const { term } = await getRuntime();
|
const { term } = await getRuntime();
|
||||||
|
|
||||||
let res = '';
|
let res = '';
|
||||||
|
const maybeCallback = (query: string, char: string) => {
|
||||||
|
if (callback !== undefined) {
|
||||||
|
callback(query, char);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
outer: while (true) {
|
outer: while (true) {
|
||||||
if (p.includes('%s')) {
|
if (p.includes('%s')) {
|
||||||
@ -250,27 +258,39 @@ class Editor {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// End the prompt
|
switch (char) {
|
||||||
if (char === KeyCommand.Enter) {
|
// Remove the last character from the prompt input
|
||||||
this.setStatusMessage('');
|
case KeyCommand.Backspace:
|
||||||
if (res.length === 0) {
|
case KeyCommand.Delete:
|
||||||
return null;
|
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 the input and end the prompt
|
||||||
|
case KeyCommand.Enter:
|
||||||
|
if (res.length > 0) {
|
||||||
|
this.setStatusMessage('');
|
||||||
|
maybeCallback(res, char);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
// Allow backspacing
|
|
||||||
if (char === KeyCommand.Backspace || char === KeyCommand.Delete) {
|
|
||||||
res = truncate(res, res.length - 1);
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to the prompt result
|
// Add to the prompt result
|
||||||
if (!isControl(char!)) {
|
default:
|
||||||
|
if (!isControl(char)) {
|
||||||
res += char;
|
res += char;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maybeCallback(res, char);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,15 +298,33 @@ class Editor {
|
|||||||
* Find text within the document
|
* Find text within the document
|
||||||
*/
|
*/
|
||||||
public async find(): Promise<void> {
|
public async find(): Promise<void> {
|
||||||
const res = await this.prompt('Search: %s (ESC to cancel)');
|
const savedCursor = Position.from(this.#cursor);
|
||||||
if (res !== null && res.length > 0) {
|
const savedOffset = Position.from(this.#offset);
|
||||||
const pos = this.#document.find(res);
|
|
||||||
|
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) {
|
if (pos !== null) {
|
||||||
this.#cursor = pos;
|
this.#cursor = pos;
|
||||||
} else {
|
} else {
|
||||||
this.setStatusMessage('Not found');
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,7 +139,7 @@ export class Position {
|
|||||||
public x: number;
|
public x: number;
|
||||||
public y: number;
|
public y: number;
|
||||||
|
|
||||||
private constructor(x: number, y: number) {
|
private constructor(x: number = 0, y: number = 0) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
}
|
}
|
||||||
@ -148,9 +148,18 @@ export class Position {
|
|||||||
return new Position(x, y);
|
return new Position(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static default(): Position {
|
public static from(p: Position): Position {
|
||||||
return new Position(0, 0);
|
return new Position(p.x, p.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static default(): Position {
|
||||||
|
return new Position();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SearchDirection {
|
||||||
|
Forward = 1,
|
||||||
|
Backward = -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user