From e6b53ef3272f21f197d93e8bed941bc6a29705d2 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Fri, 21 Jun 2024 14:14:10 -0400 Subject: [PATCH] Do basic highlighting of search results, finish stop #151 of the kilo tutorial --- justfile | 8 ++++---- src/common/document.ts | 24 ++++++++++++++++++++++-- src/common/editor.ts | 4 +++- src/common/position.ts | 18 ++++++++++++++++++ src/common/row.ts | 10 +++++++--- src/common/search.ts | 15 ++++++++++++--- src/common/types.ts | 28 ++-------------------------- 7 files changed, 68 insertions(+), 39 deletions(-) create mode 100644 src/common/position.ts diff --git a/justfile b/justfile index e7d7bf3..a4282ee 100644 --- a/justfile +++ b/justfile @@ -30,9 +30,9 @@ clean: rm -f scroll.err rm -f tsconfig.tsbuildinfo -######################################################################################################################## +########################################################################################## # Bun-specific commands -######################################################################################################################## +########################################################################################## # Check code with actual Typescript compiler bun-check: @@ -46,9 +46,9 @@ bun-test: bun-run file="": bun run ./src/scroll.ts {{file}} -######################################################################################################################## +########################################################################################## # Deno-specific commands -######################################################################################################################## +########################################################################################## # Lint code and check types deno-check: diff --git a/src/common/document.ts b/src/common/document.ts index f5663f9..75b5dcf 100644 --- a/src/common/document.ts +++ b/src/common/document.ts @@ -1,5 +1,6 @@ import Row from './row.ts'; -import { arrayInsert } from './fns.ts'; +import { arrayInsert, strlen } from './fns.ts'; +import { HighlightType } from './highlight.ts'; import { getRuntime } from './runtime.ts'; import { Position } from './types.ts'; import { Search } from './search.ts'; @@ -74,7 +75,26 @@ export class Document { q: string, key: string, ): Position | null { - return this.#search.search(q, key); + const potential = this.#search.search(q, key); + if (potential !== null) { + // Update highlight of search match + const row = this.#rows[potential.y]; + + // Okay, we have to take the Javascript string index (potential.x), convert + // it to the Row character index, and then convert that to the Row render index + // so that the highlighted color starts in the right place. + const start = row.cxToRx(row.byteIndexToCharIndex(potential.x)); + + // Just to be safe with unicode searches, take the number of 'characters' + // as the search query length, not the JS string length. + const end = start + strlen(q); + + for (let i = start; i < end; i++) { + row.hl[i] = HighlightType.Match; + } + } + + return potential; } public insert(at: Position, c: string): void { diff --git a/src/common/editor.ts b/src/common/editor.ts index fe40d92..258aa70 100644 --- a/src/common/editor.ts +++ b/src/common/editor.ts @@ -295,7 +295,8 @@ class Editor { } /** - * Find text within the document + * Find text within the document. This is roughly equivalent to the + * `editorFindCallback` function in the kilo tutorial. */ public async find(): Promise { const savedCursor = Position.from(this.#cursor); @@ -314,6 +315,7 @@ class Editor { if (query !== null && query.length > 0) { const pos = this.#document.find(query, key); if (pos !== null) { + // We have a match here this.#cursor = pos; this.scroll(); } else { diff --git a/src/common/position.ts b/src/common/position.ts new file mode 100644 index 0000000..d986033 --- /dev/null +++ b/src/common/position.ts @@ -0,0 +1,18 @@ +/** + * Convenience type for (x,y) coordinate values + */ +export class Position { + private constructor(public x: number = 0, public y: number = 0) {} + + public static at(x: number, y: number): Position { + return new Position(x, y); + } + + public static from(p: Position): Position { + return new Position(p.x, p.y); + } + + public static default(): Position { + return new Position(); + } +} diff --git a/src/common/row.ts b/src/common/row.ts index 1b2a65b..d66f409 100644 --- a/src/common/row.ts +++ b/src/common/row.ts @@ -12,18 +12,18 @@ export class Row { /** * The actual characters in the current row */ - chars: string[] = []; + public chars: string[] = []; /** * The characters rendered for the current row * (like replacing tabs with spaces) */ - rchars: string[] = []; + public rchars: string[] = []; /** * The syntax highlighting map */ - hl: HighlightType[] = []; + public hl: HighlightType[] = []; private constructor(s: string | string[] = '') { this.chars = Array.isArray(s) ? s : strChars(s); @@ -84,6 +84,10 @@ export class Row { this.chars.splice(at, 1); } + /** + * Search the current row for the specified string, and return + * the index of the start of that match + */ public find(s: string, offset: number = 0): number | null { const thisStr = this.toString(); if (!this.toString().includes(s)) { diff --git a/src/common/search.ts b/src/common/search.ts index 3c4c2a4..9f6c656 100644 --- a/src/common/search.ts +++ b/src/common/search.ts @@ -49,13 +49,22 @@ export class Search { } public search(q: string, key: string): Position | null { + if (this.parent === null) { + return null; + } + this.parseInput(key); let i = 0; - for (; i < this.parent!.numRows; i++) { - const current = this.getNextRow(this.parent!.numRows); + for (; i < this.parent.numRows; i++) { + const current = this.getNextRow(this.parent.numRows); + const row = this.parent.row(current); - const possible = this.parent!.row(current)!.find(q); + if (row === null) { + continue; + } + + const possible = row.find(q); if (possible !== null) { this.lastMatch = current; return Position.at(possible, current); diff --git a/src/common/types.ts b/src/common/types.ts index 835861e..2838c2f 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,5 +1,7 @@ import { RunTimeType } from './runtime.ts'; +export { Position } from './position.ts'; + /** * The size of terminal in rows and columns */ @@ -100,32 +102,6 @@ export type ITerminal = IRuntime['term']; */ export type IFileIO = IRuntime['file']; -// ---------------------------------------------------------------------------- -// General types -// ---------------------------------------------------------------------------- - -export class Position { - public x: number; - public y: number; - - private constructor(x: number = 0, y: number = 0) { - this.x = x; - this.y = y; - } - - public static at(x: number, y: number): Position { - return new Position(x, y); - } - - public static from(p: Position): Position { - return new Position(p.x, p.y); - } - - public static default(): Position { - return new Position(); - } -} - // ---------------------------------------------------------------------------- // Testing // ----------------------------------------------------------------------------