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