From 33f19ddec111c2e2914c8cf7920fde5ea04d683f Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Thu, 29 Feb 2024 15:48:11 -0500 Subject: [PATCH] Basic number highlighting --- src/common/all_test.ts | 4 +-- src/common/document.ts | 74 +++++++---------------------------------- src/common/editor.ts | 2 +- src/common/highlight.ts | 16 +++++++++ src/common/row.ts | 58 ++++++++++++++++++++++++++------ src/common/search.ts | 67 +++++++++++++++++++++++++++++++++++++ src/common/types.ts | 10 ------ 7 files changed, 145 insertions(+), 86 deletions(-) create mode 100644 src/common/highlight.ts create mode 100644 src/common/search.ts diff --git a/src/common/all_test.ts b/src/common/all_test.ts index 7bb82a4..77e7f22 100644 --- a/src/common/all_test.ts +++ b/src/common/all_test.ts @@ -220,8 +220,8 @@ testSuite({ }, '.cxToRx, .rxToCx': () => { const row = Row.from('foo\tbar\tbaz'); - row.updateRender(); - assertNotEquals(row.chars, row.render); + row.update(); + assertNotEquals(row.chars, row.rchars); assertNotEquals(row.size, row.rsize); assertEquals(row.size, 11); assertEquals(row.rsize, row.size + (SCROLL_TAB_SIZE * 2) - 2); diff --git a/src/common/document.ts b/src/common/document.ts index 1e92e05..f5663f9 100644 --- a/src/common/document.ts +++ b/src/common/document.ts @@ -1,49 +1,8 @@ import Row from './row.ts'; import { arrayInsert } from './fns.ts'; import { getRuntime } from './runtime.ts'; -import { Position, SearchDirection } from './types.ts'; -import { KeyCommand } from './ansi.ts'; - -class Search { - public lastMatch: number = -1; - public current: number = -1; - public direction: SearchDirection = SearchDirection.Forward; - - public parseInput(key: string) { - switch (key) { - case KeyCommand.ArrowRight: - case KeyCommand.ArrowDown: - this.direction = SearchDirection.Forward; - break; - - case KeyCommand.ArrowLeft: - case KeyCommand.ArrowUp: - this.direction = SearchDirection.Backward; - break; - - default: - this.lastMatch = -1; - this.direction = SearchDirection.Forward; - } - - if (this.lastMatch === -1) { - this.direction = SearchDirection.Forward; - } - - this.current = this.lastMatch; - } - - public getNextRow(rowCount: number): number { - this.current += this.direction; - if (this.current === -1) { - this.current = rowCount - 1; - } else if (this.current === rowCount) { - this.current = 0; - } - - return this.current; - } -} +import { Position } from './types.ts'; +import { Search } from './search.ts'; export class Document { #rows: Row[]; @@ -65,7 +24,10 @@ export class Document { } public static default(): Document { - return new Document(); + const self = new Document(); + self.#search.parent = self; + + return self; } public isEmpty(): boolean { @@ -105,26 +67,14 @@ export class Document { public resetFind() { this.#search = new Search(); + this.#search.parent = this; } public find( q: string, key: string, ): Position | null { - this.#search.parseInput(key); - - let i = 0; - for (; i < this.numRows; i++) { - const current = this.#search.getNextRow(this.numRows); - - const possible = this.#rows[current].find(q); - if (possible !== null) { - this.#search.lastMatch = current; - return Position.at(possible, current); - } - } - - return null; + return this.#search.search(q, key); } public insert(at: Position, c: string): void { @@ -132,7 +82,7 @@ export class Document { this.insertRow(this.numRows, c); } else { this.#rows[at.y].insertChar(at.x, c); - this.#rows[at.y].updateRender(); + this.#rows[at.y].update(); } this.dirty = true; @@ -149,7 +99,7 @@ export class Document { } const newRow = this.#rows[at.y].split(at.x); - newRow.updateRender(); + newRow.update(); this.#rows = arrayInsert(this.#rows, at.y + 1, newRow); this.dirty = true; @@ -188,7 +138,7 @@ export class Document { row.delete(at.x); } - row.updateRender(); + row.update(); this.dirty = true; } @@ -199,7 +149,7 @@ export class Document { public insertRow(at: number = this.numRows, s: string = ''): void { this.#rows = arrayInsert(this.#rows, at, Row.from(s)); - this.#rows[at].updateRender(); + this.#rows[at].update(); this.dirty = true; } diff --git a/src/common/editor.ts b/src/common/editor.ts index c90afc3..fe40d92 100644 --- a/src/common/editor.ts +++ b/src/common/editor.ts @@ -482,7 +482,7 @@ class Editor { this.#screen.cols, ); - this.#buffer.append(row.rstring(this.#offset.x), len); + this.#buffer.append(row.render(this.#offset.x, len)); } private drawPlaceholderRow(y: number): void { diff --git a/src/common/highlight.ts b/src/common/highlight.ts new file mode 100644 index 0000000..3c9c58f --- /dev/null +++ b/src/common/highlight.ts @@ -0,0 +1,16 @@ +import Ansi, { AnsiColor } from './ansi.ts'; + +export enum HighlightType { + None, + Number, +} + +export function highlightToColor(type: HighlightType): string { + switch (type) { + case HighlightType.Number: + return Ansi.color256(196); + + default: + return Ansi.color(AnsiColor.FgDefault); + } +} diff --git a/src/common/row.ts b/src/common/row.ts index 8575e77..dd20620 100644 --- a/src/common/row.ts +++ b/src/common/row.ts @@ -1,8 +1,12 @@ import { SCROLL_TAB_SIZE } from './config.ts'; -import { arrayInsert, strChars } from './fns.ts'; +import { arrayInsert, isAsciiDigit, strChars } from './fns.ts'; +import { highlightToColor, HighlightType } from './highlight.ts'; +import Ansi from './ansi.ts'; /** - * One row of text in the current document + * One row of text in the current document. In order to handle + * multi-byte graphemes, all operations are done on an + * array of 'character' strings. */ export class Row { /** @@ -14,16 +18,16 @@ export class Row { * The characters rendered for the current row * (like replacing tabs with spaces) */ - render: string[] = []; + rchars: string[] = []; /** * The syntax highlighting map */ - hl: string[] = []; + hl: HighlightType[] = []; private constructor(s: string | string[] = '') { this.chars = Array.isArray(s) ? s : strChars(s); - this.render = []; + this.rchars = []; } public get size(): number { @@ -31,11 +35,11 @@ export class Row { } public get rsize(): number { - return this.render.length; + return this.rchars.length; } public rstring(offset: number = 0): string { - return this.render.slice(offset).join(''); + return this.rchars.slice(offset).join(''); } public static default(): Row { @@ -52,7 +56,7 @@ export class Row { public append(s: string): void { this.chars = this.chars.concat(strChars(s)); - this.updateRender(); + this.update(); } public insertChar(at: number, c: string): void { @@ -67,7 +71,7 @@ export class Row { public split(at: number): Row { const newRow = new Row(this.chars.slice(at)); this.chars = this.chars.slice(0, at); - this.updateRender(); + this.update(); return newRow; } @@ -161,13 +165,45 @@ export class Row { return this.chars.join(''); } - public updateRender(): void { + public update(): void { const newString = this.chars.join('').replaceAll( '\t', ' '.repeat(SCROLL_TAB_SIZE), ); - this.render = strChars(newString); + this.rchars = strChars(newString); + this.highlight(); + } + + public render(offset: number, len: number): string { + const end = Math.min(len, this.rsize); + const start = Math.min(offset, len); + let result = ''; + + for (let i = start; i < end; i++) { + // if (this.chars[i] === '\t') { + // result += ' '.repeat(SCROLL_TAB_SIZE); + // } else { + result += highlightToColor(this.hl[i]); + result += this.rchars[i]; + result += Ansi.ResetFormatting; + } + // } + + return result; + } + + private highlight(): void { + const highlighting = []; + for (const ch of this.rchars) { + if (isAsciiDigit(ch)) { + highlighting.push(HighlightType.Number); + } else { + highlighting.push(HighlightType.None); + } + } + + this.hl = highlighting; } } diff --git a/src/common/search.ts b/src/common/search.ts new file mode 100644 index 0000000..3c4c2a4 --- /dev/null +++ b/src/common/search.ts @@ -0,0 +1,67 @@ +import { Position } from './types.ts'; +import { KeyCommand } from './ansi.ts'; +import Document from './document.ts'; + +enum SearchDirection { + Forward = 1, + Backward = -1, +} + +export class Search { + private lastMatch: number = -1; + private current: number = -1; + private direction: SearchDirection = SearchDirection.Forward; + public parent: Document | null = null; + + private parseInput(key: string) { + switch (key) { + case KeyCommand.ArrowRight: + case KeyCommand.ArrowDown: + this.direction = SearchDirection.Forward; + break; + + case KeyCommand.ArrowLeft: + case KeyCommand.ArrowUp: + this.direction = SearchDirection.Backward; + break; + + default: + this.lastMatch = -1; + this.direction = SearchDirection.Forward; + } + + if (this.lastMatch === -1) { + this.direction = SearchDirection.Forward; + } + + this.current = this.lastMatch; + } + + private getNextRow(rowCount: number): number { + this.current += this.direction; + if (this.current === -1) { + this.current = rowCount - 1; + } else if (this.current === rowCount) { + this.current = 0; + } + + return this.current; + } + + public search(q: string, key: string): Position | null { + this.parseInput(key); + + let i = 0; + for (; i < this.parent!.numRows; i++) { + const current = this.getNextRow(this.parent!.numRows); + + const possible = this.parent!.row(current)!.find(q); + if (possible !== null) { + this.lastMatch = current; + return Position.at(possible, current); + } + } + + return null; + } +} diff --git a/src/common/types.ts b/src/common/types.ts index 7745468..8d8aa44 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -157,16 +157,6 @@ export class Position { } } -export enum SearchDirection { - Forward = 1, - Backward = -1, -} - -export enum HighlightType { - None, - Number, -} - // ---------------------------------------------------------------------------- // Testing // ----------------------------------------------------------------------------