Basic deletion functionality

This commit is contained in:
Timothy Warren 2023-11-22 11:07:33 -05:00
parent a7f5fed9a3
commit b665ce8ce7
5 changed files with 68 additions and 32 deletions

View File

@ -18,6 +18,12 @@ const {
testSuite, testSuite,
} = await getTestRunner(); } = await getTestRunner();
const testKeyMap = (codes: string[], expected: string) => {
codes.forEach((code) => {
assertEquals(readKey(code), expected);
});
};
testSuite({ testSuite({
'ANSI::ANSI utils': { 'ANSI::ANSI utils': {
'Ansi.moveCursor': () => { 'Ansi.moveCursor': () => {
@ -39,27 +45,21 @@ testSuite({
assertEquals(readKey(KeyCommand.ArrowUp), KeyCommand.ArrowUp); assertEquals(readKey(KeyCommand.ArrowUp), KeyCommand.ArrowUp);
assertEquals(readKey(KeyCommand.Home), KeyCommand.Home); assertEquals(readKey(KeyCommand.Home), KeyCommand.Home);
assertEquals(readKey(KeyCommand.Delete), KeyCommand.Delete); assertEquals(readKey(KeyCommand.Delete), KeyCommand.Delete);
// And pass through whatever else
assertEquals(readKey('foobaz'), 'foobaz');
}, },
'readKey Esc': () => { 'readKey Esc': () =>
['\x1b', Util.ctrlKey('l')].forEach((code) => { testKeyMap(['\x1b', Util.ctrlKey('l')], KeyCommand.Escape),
assertEquals(readKey(code), KeyCommand.Escape); 'readKey Backspace': () =>
}); testKeyMap(
}, [Util.ctrlKey('h'), '\x7f'],
'readKey Backspace': () => { KeyCommand.Backspace,
[Util.ctrlKey('h'), String.fromCodePoint(127)].forEach((code) => { ),
assertEquals(readKey(code), KeyCommand.Backspace); 'readKey Home': () =>
}); testKeyMap(['\x1b[1~', '\x1b[7~', '\x1b[H', '\x1bOH'], KeyCommand.Home),
}, 'readKey End': () =>
'readKey Home': () => { testKeyMap(['\x1b[4~', '\x1b[8~', '\x1b[F', '\x1bOF'], KeyCommand.End),
['\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);
});
},
}, },
Buffer: { Buffer: {
'Buffer exists': () => { 'Buffer exists': () => {
@ -137,6 +137,12 @@ testSuite({
assertEquals(doc.numRows, 2); assertEquals(doc.numRows, 2);
assertTrue(doc.dirty); 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: { Editor: {
'new Editor': () => { 'new Editor': () => {

View File

@ -76,7 +76,7 @@ export function readKey(parsed: string): string {
return KeyCommand.Escape; return KeyCommand.Escape;
case ctrlKey('h'): case ctrlKey('h'):
case String.fromCodePoint(127): case '\x7f':
return KeyCommand.Backspace; return KeyCommand.Backspace;
default: default:

View File

@ -1,7 +1,6 @@
import { chars } from './utils.ts';
import Row from './row.ts'; import Row from './row.ts';
import { getRuntime } from './runtime.ts'; import { getRuntime } from './runtime.ts';
import { Position, SCROLL_TAB_SIZE } from './mod.ts'; import { Position } from './mod.ts';
export class Document { export class Document {
#filename: string | null; #filename: string | null;
@ -61,12 +60,22 @@ export class Document {
this.appendRow(c); this.appendRow(c);
} else { } else {
this.#rows[at.y].insertChar(at.x, c); this.#rows[at.y].insertChar(at.x, c);
this.updateRow(this.#rows[at.y]); this.#rows[at.y].updateRender();
} }
this.dirty = true; 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 { public row(i: number): Row | null {
return this.#rows[i] ?? null; return this.#rows[i] ?? null;
} }
@ -74,15 +83,11 @@ export class Document {
public appendRow(s: string): void { public appendRow(s: string): void {
const at = this.numRows; const at = this.numRows;
this.#rows[at] = new Row(s); this.#rows[at] = new Row(s);
this.updateRow(this.#rows[at]); this.#rows[at].updateRender();
this.dirty = true; this.dirty = true;
} }
private updateRow(r: Row): void {
r.render = chars(r.toString().replace('\t', ' '.repeat(SCROLL_TAB_SIZE)));
}
private rowsToString(): string { private rowsToString(): string {
return this.#rows.map((r) => r.toString()).join('\n'); return this.#rows.map((r) => r.toString()).join('\n');
} }

View File

@ -113,9 +113,17 @@ class Editor {
} }
break; break;
case KeyCommand.Backspace:
case KeyCommand.Delete: 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; break;
case KeyCommand.PageUp: case KeyCommand.PageUp:

View File

@ -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'; 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 { public cxToRx(cx: number): number {
let rx = 0; let rx = 0;
let j = 0; let j = 0;
@ -60,6 +68,15 @@ export class Row {
public toString(): string { public toString(): string {
return this.chars.join(''); 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; export default Row;