From b665ce8ce71b277d18054adfdb794073db690591 Mon Sep 17 00:00:00 2001 From: "Timothy J. Warren" Date: Wed, 22 Nov 2023 11:07:33 -0500 Subject: [PATCH] Basic deletion functionality --- src/common/all_test.ts | 46 ++++++++++++++++++++++++------------------ src/common/ansi.ts | 2 +- src/common/document.ts | 21 +++++++++++-------- src/common/editor.ts | 12 +++++++++-- src/common/row.ts | 19 ++++++++++++++++- 5 files changed, 68 insertions(+), 32 deletions(-) diff --git a/src/common/all_test.ts b/src/common/all_test.ts index 88c5897..1aaab5c 100644 --- a/src/common/all_test.ts +++ b/src/common/all_test.ts @@ -18,6 +18,12 @@ const { testSuite, } = await getTestRunner(); +const testKeyMap = (codes: string[], expected: string) => { + codes.forEach((code) => { + assertEquals(readKey(code), expected); + }); +}; + testSuite({ 'ANSI::ANSI utils': { 'Ansi.moveCursor': () => { @@ -39,27 +45,21 @@ testSuite({ assertEquals(readKey(KeyCommand.ArrowUp), KeyCommand.ArrowUp); assertEquals(readKey(KeyCommand.Home), KeyCommand.Home); assertEquals(readKey(KeyCommand.Delete), KeyCommand.Delete); + + // And pass through whatever else + assertEquals(readKey('foobaz'), 'foobaz'); }, - 'readKey Esc': () => { - ['\x1b', Util.ctrlKey('l')].forEach((code) => { - assertEquals(readKey(code), KeyCommand.Escape); - }); - }, - 'readKey Backspace': () => { - [Util.ctrlKey('h'), String.fromCodePoint(127)].forEach((code) => { - assertEquals(readKey(code), KeyCommand.Backspace); - }); - }, - 'readKey Home': () => { - ['\x1b[1~', '\x1b[7~', '\x1b[H', '\x1bOH'].forEach((code) => { - assertEquals(readKey(code), KeyCommand.Home); - }); - }, - 'readKey End': () => { - ['\x1b[4~', '\x1b[8~', '\x1b[F', '\x1bOF'].forEach((code) => { - assertEquals(readKey(code), KeyCommand.End); - }); - }, + 'readKey Esc': () => + testKeyMap(['\x1b', Util.ctrlKey('l')], KeyCommand.Escape), + 'readKey Backspace': () => + testKeyMap( + [Util.ctrlKey('h'), '\x7f'], + KeyCommand.Backspace, + ), + 'readKey Home': () => + testKeyMap(['\x1b[1~', '\x1b[7~', '\x1b[H', '\x1bOH'], KeyCommand.Home), + 'readKey End': () => + testKeyMap(['\x1b[4~', '\x1b[8~', '\x1b[F', '\x1bOF'], KeyCommand.End), }, Buffer: { 'Buffer exists': () => { @@ -137,6 +137,12 @@ testSuite({ assertEquals(doc.numRows, 2); assertTrue(doc.dirty); }, + 'Document.delete': () => { + const doc = Document.empty(); + doc.insert(Position.default(), 'foobar'); + doc.delete(Position.at(3, 0)); + assertEquals(doc.row(0)?.toString(), 'fooar'); + }, }, Editor: { 'new Editor': () => { diff --git a/src/common/ansi.ts b/src/common/ansi.ts index 6c7fd18..c6486fa 100644 --- a/src/common/ansi.ts +++ b/src/common/ansi.ts @@ -76,7 +76,7 @@ export function readKey(parsed: string): string { return KeyCommand.Escape; case ctrlKey('h'): - case String.fromCodePoint(127): + case '\x7f': return KeyCommand.Backspace; default: diff --git a/src/common/document.ts b/src/common/document.ts index 61b6588..15cc3d5 100644 --- a/src/common/document.ts +++ b/src/common/document.ts @@ -1,7 +1,6 @@ -import { chars } from './utils.ts'; import Row from './row.ts'; import { getRuntime } from './runtime.ts'; -import { Position, SCROLL_TAB_SIZE } from './mod.ts'; +import { Position } from './mod.ts'; export class Document { #filename: string | null; @@ -61,12 +60,22 @@ export class Document { this.appendRow(c); } else { this.#rows[at.y].insertChar(at.x, c); - this.updateRow(this.#rows[at.y]); + this.#rows[at.y].updateRender(); } this.dirty = true; } + public delete(at: Position): void { + const row = this.row(at.y); + if (row === null) { + return; + } + + row.delete(at.x); + row.updateRender(); + } + public row(i: number): Row | null { return this.#rows[i] ?? null; } @@ -74,15 +83,11 @@ export class Document { public appendRow(s: string): void { const at = this.numRows; this.#rows[at] = new Row(s); - this.updateRow(this.#rows[at]); + this.#rows[at].updateRender(); this.dirty = true; } - private updateRow(r: Row): void { - r.render = chars(r.toString().replace('\t', ' '.repeat(SCROLL_TAB_SIZE))); - } - private rowsToString(): string { return this.#rows.map((r) => r.toString()).join('\n'); } diff --git a/src/common/editor.ts b/src/common/editor.ts index 428a0e4..8e22843 100644 --- a/src/common/editor.ts +++ b/src/common/editor.ts @@ -113,9 +113,17 @@ class Editor { } break; - case KeyCommand.Backspace: case KeyCommand.Delete: - // TODO + this.#document.delete(this.#cursor); + break; + + case KeyCommand.Backspace: + { + if (this.#cursor.x > 0 || this.#cursor.y > 0) { + this.moveCursor(KeyCommand.ArrowLeft); + this.#document.delete(this.#cursor); + } + } break; case KeyCommand.PageUp: diff --git a/src/common/row.ts b/src/common/row.ts index 5b825d5..0d04532 100644 --- a/src/common/row.ts +++ b/src/common/row.ts @@ -1,4 +1,4 @@ -import { SCROLL_TAB_SIZE } from './mod.ts'; +import { chars, SCROLL_TAB_SIZE } from './mod.ts'; import * as Util from './utils.ts'; /** @@ -44,6 +44,14 @@ export class Row { } } + public delete(at: number): void { + if (at >= this.size) { + return; + } + + this.chars.splice(at, 1); + } + public cxToRx(cx: number): number { let rx = 0; let j = 0; @@ -60,6 +68,15 @@ export class Row { public toString(): string { return this.chars.join(''); } + + public updateRender(): void { + const newString = this.chars.join('').replace( + '\t', + ' '.repeat(SCROLL_TAB_SIZE), + ); + + this.render = chars(newString); + } } export default Row;