First output of welcome message
This commit is contained in:
parent
7eb07520ae
commit
abee0a80bf
@ -14,8 +14,8 @@ function getSizeFromTput(): ITerminalSize {
|
||||
);
|
||||
|
||||
return {
|
||||
rows: (rows > 0) ? rows : 25,
|
||||
cols: (cols > 0) ? cols : 80,
|
||||
rows: (rows > 0) ? rows + 1 : 25,
|
||||
cols: (cols > 0) ? cols + 1 : 80,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,11 @@ function esc(pieces: TemplateStringsArray): string {
|
||||
}
|
||||
|
||||
export const Ansi = {
|
||||
ClearLine: esc`K`,
|
||||
ClearScreen: esc`2J`,
|
||||
ResetCursor: esc`H`,
|
||||
HideCursor: esc`?25l`,
|
||||
ShowCursor: esc`?25h`,
|
||||
moveCursor: function moveCursor(row: number, col: number): string {
|
||||
return `\x1b${row};${col}H`;
|
||||
},
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { strlen } from '../strings.ts';
|
||||
|
||||
class Buffer {
|
||||
#b = '';
|
||||
|
||||
@ -19,6 +21,10 @@ class Buffer {
|
||||
getBuffer(): string {
|
||||
return this.#b;
|
||||
}
|
||||
|
||||
strlen(): number {
|
||||
return strlen(this.#b);
|
||||
}
|
||||
}
|
||||
|
||||
export default Buffer;
|
||||
|
@ -1,8 +1,13 @@
|
||||
import Ansi from './ansi.ts';
|
||||
import Buffer from './buffer.ts';
|
||||
import { importDefaultForRuntime } from '../runtime.ts';
|
||||
import { ctrl_key } from '../strings.ts';
|
||||
import { ITerminalSize } from '../types.ts';
|
||||
import {
|
||||
ctrl_key,
|
||||
importDefaultForRuntime,
|
||||
ITerminalSize,
|
||||
strlen,
|
||||
truncate,
|
||||
VERSION,
|
||||
} from '../mod.ts';
|
||||
|
||||
export class Editor {
|
||||
#buffer: Buffer;
|
||||
@ -29,27 +34,46 @@ export class Editor {
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
// Terminal Output / Drawing
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Clear the screen and write out the buffer
|
||||
*/
|
||||
public async refreshScreen(): Promise<void> {
|
||||
const { write } = await importDefaultForRuntime('terminal_io');
|
||||
|
||||
this.clearScreen();
|
||||
this.#buffer.append(Ansi.HideCursor);
|
||||
this.#buffer.append(Ansi.ResetCursor);
|
||||
this.drawRows();
|
||||
this.#buffer.append(Ansi.ShowCursor);
|
||||
|
||||
await write(this.#buffer.getBuffer());
|
||||
this.#buffer.clear();
|
||||
}
|
||||
|
||||
private drawRows(): void {
|
||||
for (let y = 0; y <= this.#screenRows; y++) {
|
||||
this.#buffer.appendLine('~');
|
||||
}
|
||||
private clearScreen(): void {
|
||||
importDefaultForRuntime('terminal_io').then(({ write }) => {
|
||||
this.#buffer.append(Ansi.ClearScreen);
|
||||
this.#buffer.append(Ansi.ResetCursor);
|
||||
write(this.#buffer.getBuffer()).then(() => {});
|
||||
});
|
||||
}
|
||||
|
||||
private clearScreen(): void {
|
||||
this.#buffer.append(Ansi.ClearScreen);
|
||||
this.#buffer.append(Ansi.ResetCursor);
|
||||
private drawRows(): void {
|
||||
for (let y = 0; y < this.#screenRows; y++) {
|
||||
if (y === this.#screenRows / 3) {
|
||||
const message = `Kilo editor -- version ${VERSION}`;
|
||||
this.#buffer.append(truncate(message, this.#screenCols));
|
||||
} else {
|
||||
this.#buffer.append('~');
|
||||
}
|
||||
|
||||
this.#buffer.append(Ansi.ClearLine);
|
||||
if (y < this.#screenRows - 1) {
|
||||
this.#buffer.append('\r\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,3 +3,5 @@ export * from './runtime.ts';
|
||||
export * from './strings.ts';
|
||||
export * from './termios.ts';
|
||||
export type * from './types.ts';
|
||||
|
||||
export const VERSION = '0.0.1';
|
||||
|
@ -6,19 +6,23 @@ export function chars(s: string): string[] {
|
||||
return s.split(/(?:)/u);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 'character length' of a string, not its UTF16 byte count
|
||||
* @param s - the string to check
|
||||
*/
|
||||
export function strlen(s: string): number {
|
||||
return chars(s).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the character part of ascii?
|
||||
*
|
||||
* @param char - a one character string to check
|
||||
*/
|
||||
export function is_ascii(char: string): boolean {
|
||||
if (typeof char !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return chars(char).every((char) => {
|
||||
const point = char.codePointAt(0);
|
||||
if (typeof point === 'undefined') {
|
||||
if (point === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -58,3 +62,17 @@ export function ctrl_key(char: string): string {
|
||||
// If it's not ascii, just return the input key code
|
||||
return char;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim a string to a max number of characters
|
||||
* @param s
|
||||
* @param maxLen
|
||||
*/
|
||||
export function truncate(s: string, maxLen: number): string {
|
||||
const chin = chars(s);
|
||||
if (maxLen >= chin.length) {
|
||||
return s;
|
||||
}
|
||||
|
||||
return chin.slice(0, maxLen).join('');
|
||||
}
|
||||
|
@ -1,5 +1,12 @@
|
||||
import { importDefaultForRuntime, ITestBase } from './mod.ts';
|
||||
import { chars, is_ascii } from './strings.ts';
|
||||
import {
|
||||
chars,
|
||||
ctrl_key,
|
||||
is_ascii,
|
||||
is_control,
|
||||
strlen,
|
||||
truncate,
|
||||
} from './strings.ts';
|
||||
|
||||
const t: ITestBase = await importDefaultForRuntime('test_base');
|
||||
|
||||
@ -7,7 +14,42 @@ t.test('chars fn properly splits strings into unicode characters', () => {
|
||||
t.assertEquals(chars('😺😸😹'), ['😺', '😸', '😹']);
|
||||
});
|
||||
|
||||
t.test('ctrl_key fn returns expected values', () => {
|
||||
const ctrl_a = ctrl_key('a');
|
||||
t.assertTrue(is_control(ctrl_a));
|
||||
t.assertEquals(ctrl_a, String.fromCodePoint(0x01));
|
||||
|
||||
const invalid = ctrl_key('😺');
|
||||
t.assertFalse(is_control(invalid));
|
||||
t.assertEquals(invalid, '😺');
|
||||
});
|
||||
|
||||
t.test('is_ascii properly discerns ascii chars', () => {
|
||||
t.assertTrue(is_ascii('asjyverkjhsdf1928374'));
|
||||
t.assertFalse(is_ascii('😺acalskjsdf'));
|
||||
});
|
||||
|
||||
t.test('is_control fn works as expected', () => {
|
||||
t.assertFalse(is_control('abc'));
|
||||
t.assertTrue(is_control(String.fromCodePoint(0x01)));
|
||||
t.assertFalse(is_control('😺'));
|
||||
});
|
||||
|
||||
t.test('strlen fn returns expected length for multibyte characters', () => {
|
||||
t.assertEquals(strlen('😺😸😹'), 3);
|
||||
t.assertNotEquals('😺😸😹'.length, strlen('😺😸😹'));
|
||||
|
||||
// Skin tone modifier + base character
|
||||
t.assertEquals(strlen('🤰🏼'), 2);
|
||||
t.assertNotEquals('🤰🏼'.length, strlen('🤰🏼'));
|
||||
|
||||
// This has 4 sub-characters, and 3 zero-width-joiners
|
||||
t.assertEquals(strlen('👨👩👧👦'), 7);
|
||||
t.assertNotEquals('👨👩👧👦'.length, strlen('👨👩👧👦'));
|
||||
});
|
||||
|
||||
t.test('truncate shortens strings', () => {
|
||||
t.assertEquals(truncate('😺😸😹', 1), '😺');
|
||||
t.assertEquals(truncate('😺😸😹', 5), '😺😸😹');
|
||||
t.assertEquals(truncate('👨👩👧👦', 5), '👨👩👧');
|
||||
});
|
||||
|
@ -20,12 +20,13 @@ export async function main() {
|
||||
const t = await getTermios();
|
||||
t.enableRawMode();
|
||||
onExit(() => {
|
||||
console.info('Exit handler called, disabling raw mode');
|
||||
console.log('Exit handler called, disabling raw mode\r\n');
|
||||
t.disableRawMode();
|
||||
});
|
||||
|
||||
// Create the editor itself
|
||||
const editor = new Editor(getSize());
|
||||
await editor.refreshScreen();
|
||||
|
||||
// The main event loop
|
||||
for await (const chunk of inputLoop()) {
|
||||
|
Loading…
Reference in New Issue
Block a user