2023-11-08 18:07:34 -05:00
|
|
|
import Ansi from './ansi.ts';
|
|
|
|
import Buffer from './buffer.ts';
|
2023-11-09 12:05:30 -05:00
|
|
|
import {
|
|
|
|
ctrl_key,
|
|
|
|
importDefaultForRuntime,
|
2023-11-10 08:36:18 -05:00
|
|
|
IPoint,
|
2023-11-09 12:05:30 -05:00
|
|
|
ITerminalSize,
|
|
|
|
truncate,
|
|
|
|
VERSION,
|
|
|
|
} from '../mod.ts';
|
2023-11-08 11:11:19 -05:00
|
|
|
|
2023-11-03 12:26:09 -04:00
|
|
|
export class Editor {
|
2023-11-08 11:11:19 -05:00
|
|
|
#buffer: Buffer;
|
2023-11-10 08:36:18 -05:00
|
|
|
#screen: ITerminalSize;
|
|
|
|
#cursor: IPoint;
|
2023-11-09 10:46:12 -05:00
|
|
|
constructor(terminalSize: ITerminalSize) {
|
2023-11-08 11:11:19 -05:00
|
|
|
this.#buffer = new Buffer();
|
2023-11-10 08:36:18 -05:00
|
|
|
this.#screen = terminalSize;
|
|
|
|
this.#cursor = {
|
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
};
|
2023-11-08 11:11:19 -05:00
|
|
|
}
|
|
|
|
|
2023-11-10 08:36:18 -05:00
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Command/input mapping
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2023-11-08 11:11:19 -05:00
|
|
|
/**
|
|
|
|
* Determine what to do based on input
|
|
|
|
* @param input - the decoded chunk of stdin
|
|
|
|
*/
|
|
|
|
public processKeyPress(input: string): boolean {
|
|
|
|
switch (input) {
|
|
|
|
case ctrl_key('q'):
|
2023-11-09 12:32:41 -05:00
|
|
|
this.clearScreen().then(() => {});
|
2023-11-08 11:11:19 -05:00
|
|
|
return false;
|
|
|
|
|
2023-11-10 08:36:18 -05:00
|
|
|
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;
|
2023-11-08 11:11:19 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-10 08:36:18 -05:00
|
|
|
// --------------------------------------------------------------------------
|
2023-11-09 12:05:30 -05:00
|
|
|
// Terminal Output / Drawing
|
2023-11-10 08:36:18 -05:00
|
|
|
// --------------------------------------------------------------------------
|
2023-11-09 12:05:30 -05:00
|
|
|
|
2023-11-08 11:11:19 -05:00
|
|
|
/**
|
|
|
|
* Clear the screen and write out the buffer
|
|
|
|
*/
|
|
|
|
public async refreshScreen(): Promise<void> {
|
2023-11-09 12:05:30 -05:00
|
|
|
this.#buffer.append(Ansi.HideCursor);
|
|
|
|
this.#buffer.append(Ansi.ResetCursor);
|
2023-11-08 17:02:59 -05:00
|
|
|
this.drawRows();
|
2023-11-10 08:36:18 -05:00
|
|
|
this.#buffer.append(
|
|
|
|
Ansi.moveCursor(this.#cursor.y + 1, this.#cursor.x + 1),
|
|
|
|
);
|
2023-11-09 12:05:30 -05:00
|
|
|
this.#buffer.append(Ansi.ShowCursor);
|
2023-11-08 17:02:59 -05:00
|
|
|
|
2023-11-09 12:32:41 -05:00
|
|
|
await this.writeToScreen();
|
2023-11-03 12:26:09 -04:00
|
|
|
}
|
2023-11-08 11:11:19 -05:00
|
|
|
|
2023-11-09 12:32:41 -05:00
|
|
|
private async clearScreen(): Promise<void> {
|
|
|
|
this.#buffer.append(Ansi.ClearScreen);
|
|
|
|
this.#buffer.append(Ansi.ResetCursor);
|
|
|
|
|
|
|
|
await this.writeToScreen();
|
2023-11-08 17:02:59 -05:00
|
|
|
}
|
|
|
|
|
2023-11-09 12:05:30 -05:00
|
|
|
private drawRows(): void {
|
2023-11-09 13:08:00 -05:00
|
|
|
this.drawPlaceholderRows();
|
|
|
|
}
|
|
|
|
|
|
|
|
private drawPlaceholderRows(): void {
|
2023-11-10 08:36:18 -05:00
|
|
|
for (let y = 0; y < this.#screen.rows; y++) {
|
|
|
|
if (y === Math.trunc(this.#screen.rows / 2)) {
|
2023-11-09 12:05:30 -05:00
|
|
|
const message = `Kilo editor -- version ${VERSION}`;
|
2023-11-10 08:36:18 -05:00
|
|
|
const messageLen = (message.length > this.#screen.cols)
|
|
|
|
? this.#screen.cols
|
|
|
|
: message.length;
|
|
|
|
let padding = Math.trunc((this.#screen.cols - messageLen) / 2);
|
2023-11-09 13:08:00 -05:00
|
|
|
if (padding > 0) {
|
|
|
|
this.#buffer.append('~');
|
|
|
|
padding -= 1;
|
|
|
|
|
|
|
|
this.#buffer.append(' '.repeat(padding));
|
|
|
|
}
|
|
|
|
|
|
|
|
this.#buffer.append(truncate(message, messageLen));
|
2023-11-09 12:05:30 -05:00
|
|
|
} else {
|
|
|
|
this.#buffer.append('~');
|
|
|
|
}
|
|
|
|
|
|
|
|
this.#buffer.append(Ansi.ClearLine);
|
2023-11-10 08:36:18 -05:00
|
|
|
if (y < this.#screen.rows - 1) {
|
2023-11-09 12:32:41 -05:00
|
|
|
this.#buffer.appendLine('');
|
2023-11-09 12:05:30 -05:00
|
|
|
}
|
|
|
|
}
|
2023-11-03 12:26:09 -04:00
|
|
|
}
|
2023-11-09 12:32:41 -05:00
|
|
|
|
|
|
|
private async writeToScreen(): Promise<void> {
|
|
|
|
const io = await importDefaultForRuntime('terminal_io');
|
|
|
|
|
|
|
|
await io.write(this.#buffer.getBuffer());
|
|
|
|
this.#buffer.clear();
|
|
|
|
}
|
2023-11-03 12:26:09 -04:00
|
|
|
}
|