diff --git a/justfile b/justfile index 8958864..939cb65 100644 --- a/justfile +++ b/justfile @@ -50,7 +50,7 @@ deno-test: # Create test coverage report with deno deno-coverage: deno test --allow-all --coverage=.deno-cover - deno coverage --lcov .deno-cover + deno coverage --unstable-ffi .deno-cover # Run with deno deno-run: diff --git a/src/common/editor/ansi.ts b/src/common/editor/ansi.ts index 4b90daa..0df5304 100644 --- a/src/common/editor/ansi.ts +++ b/src/common/editor/ansi.ts @@ -9,7 +9,7 @@ export const Ansi = { HideCursor: esc`?25l`, ShowCursor: esc`?25h`, moveCursor: function moveCursor(row: number, col: number): string { - return `\x1b${row};${col}H`; + return `\x1b[${row};${col}H`; }, }; diff --git a/src/common/editor/editor.ts b/src/common/editor/editor.ts index 9299fd6..fa2ad77 100644 --- a/src/common/editor/editor.ts +++ b/src/common/editor/editor.ts @@ -3,6 +3,7 @@ import Buffer from './buffer.ts'; import { ctrl_key, importDefaultForRuntime, + IPoint, ITerminalSize, truncate, VERSION, @@ -10,14 +11,21 @@ import { export class Editor { #buffer: Buffer; - #screenRows: number; - #screenCols: number; + #screen: ITerminalSize; + #cursor: IPoint; constructor(terminalSize: ITerminalSize) { this.#buffer = new Buffer(); - this.#screenRows = terminalSize.rows; - this.#screenCols = terminalSize.cols; + this.#screen = terminalSize; + this.#cursor = { + x: 0, + y: 0, + }; } + // -------------------------------------------------------------------------- + // Command/input mapping + // -------------------------------------------------------------------------- + /** * Determine what to do based on input * @param input - the decoded chunk of stdin @@ -28,14 +36,37 @@ export class Editor { this.clearScreen().then(() => {}); return false; - default: - return true; + case 'w': + case 's': + case 'a': + case 'd': + this.moveCursor(input); + break; + } + + return true; + } + + private moveCursor(char: string): void { + switch (char) { + case 'a': + this.#cursor.x--; + break; + case 'd': + this.#cursor.x++; + break; + case 'w': + this.#cursor.y--; + break; + case 's': + this.#cursor.y++; + break; } } - // ------------------------------------------------------------------------------------------------------------------- + // -------------------------------------------------------------------------- // Terminal Output / Drawing - // ------------------------------------------------------------------------------------------------------------------- + // -------------------------------------------------------------------------- /** * Clear the screen and write out the buffer @@ -44,6 +75,9 @@ export class Editor { this.#buffer.append(Ansi.HideCursor); this.#buffer.append(Ansi.ResetCursor); this.drawRows(); + this.#buffer.append( + Ansi.moveCursor(this.#cursor.y + 1, this.#cursor.x + 1), + ); this.#buffer.append(Ansi.ShowCursor); await this.writeToScreen(); @@ -61,11 +95,13 @@ export class Editor { } private drawPlaceholderRows(): void { - for (let y = 0; y < this.#screenRows; y++) { - if (y === Math.trunc(this.#screenRows / 2)) { + for (let y = 0; y < this.#screen.rows; y++) { + if (y === Math.trunc(this.#screen.rows / 2)) { const message = `Kilo editor -- version ${VERSION}`; - const messageLen = (message.length > this.#screenCols) ? this.#screenCols : message.length; - let padding = Math.trunc((this.#screenCols - messageLen) / 2); + const messageLen = (message.length > this.#screen.cols) + ? this.#screen.cols + : message.length; + let padding = Math.trunc((this.#screen.cols - messageLen) / 2); if (padding > 0) { this.#buffer.append('~'); padding -= 1; @@ -79,7 +115,7 @@ export class Editor { } this.#buffer.append(Ansi.ClearLine); - if (y < this.#screenRows - 1) { + if (y < this.#screen.rows - 1) { this.#buffer.appendLine(''); } } diff --git a/src/common/types.ts b/src/common/types.ts index a9ce2ea..eb085fc 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -1,17 +1,15 @@ -/** - * The shared test interface, so tests can be run by both runtimes - */ -export interface ITestBase { - test(name: string, fn: () => void): void; - assertEquals(actual: unknown, expected: unknown): void; - assertNotEquals(actual: unknown, expected: unknown): void; - assertStrictEquals(actual: unknown, expected: unknown): void; - assertExists(actual: unknown): void; - assertInstanceOf(actual: unknown, expectedType: any): void; - assertTrue(actual: boolean): void; - assertFalse(actual: boolean): void; +// ---------------------------------------------------------------------------- +// General types +// ---------------------------------------------------------------------------- + +export interface IPoint { + x: number; + y: number; } +// ---------------------------------------------------------------------------- +// Runtime adapter interfaces +// ---------------------------------------------------------------------------- /** * The native functions for terminal settings */ @@ -69,3 +67,21 @@ export interface IRuntime { */ onExit(cb: () => void): void; } + +// ---------------------------------------------------------------------------- +// Testing +// ---------------------------------------------------------------------------- + +/** + * The shared test interface, so tests can be run by both runtimes + */ +export interface ITestBase { + test(name: string, fn: () => void): void; + assertEquals(actual: unknown, expected: unknown): void; + assertNotEquals(actual: unknown, expected: unknown): void; + assertStrictEquals(actual: unknown, expected: unknown): void; + assertExists(actual: unknown): void; + assertInstanceOf(actual: unknown, expectedType: any): void; + assertTrue(actual: boolean): void; + assertFalse(actual: boolean): void; +} diff --git a/src/common/utils.ts b/src/common/utils.ts index a77e534..a8e3138 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -1,6 +1,6 @@ -// --------------------------------------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- // Strings -// --------------------------------------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- /** * Split a string by graphemes, not just bytes