Basic file opening and display. Off-by-one bug skipping first line, though

This commit is contained in:
Timothy Warren 2023-11-14 15:53:45 -05:00
parent 816295ff9c
commit d5ce04fe8b
8 changed files with 80 additions and 34 deletions

View File

@ -3,7 +3,7 @@
"include": ["src/"], "include": ["src/"],
"rules": { "rules": {
"tags": ["recommended"], "tags": ["recommended"],
"exclude": ["no-explicit-any"] "exclude": ["no-explicit-any", "no-inferrable-types"]
} }
}, },
"fmt": { "fmt": {

View File

@ -34,8 +34,8 @@ bun-test:
bun test --coverage bun test --coverage
# Run with bun # Run with bun
bun-run: bun-run file="":
bun run ./src/scroll.ts bun run ./src/scroll.ts {{file}}
######################################################################################################################## ########################################################################################################################
# Deno-specific commands # Deno-specific commands
@ -56,5 +56,5 @@ deno-coverage:
deno coverage --unstable-ffi .deno-cover deno coverage --unstable-ffi .deno-cover
# Run with deno # Run with deno
deno-run: deno-run file="":
deno run --allow-all --allow-ffi --deny-net --deny-hrtime --unstable ./src/scroll.ts deno run --allow-all --allow-ffi --deny-net --deny-hrtime --unstable ./src/scroll.ts {{file}}

View File

@ -8,7 +8,7 @@ const BunFileIO: IFIO = {
return await file.text(); return await file.text();
}, },
openFileSync: (path: string): string => { openFileSync: (path: string): string => {
return readFileSync(path); return readFileSync(path).toString();
}, },
}; };

View File

@ -5,7 +5,10 @@ import { ITerminal, ITerminalSize } from '../common/mod.ts';
import Ansi from '../common/ansi.ts'; import Ansi from '../common/ansi.ts';
const BunTerminalIO: ITerminal = { const BunTerminalIO: ITerminal = {
argv: Bun.argv, // Deno only returns arguments passed to the script, so
// remove the bun runtime executable, and entry script arguments
// to have consistent argument lists
argv: (Bun.argv.length > 2) ? Bun.argv.slice(2) : [],
inputLoop: async function* inputLoop() { inputLoop: async function* inputLoop() {
for await (const chunk of Bun.stdin.stream()) { for await (const chunk of Bun.stdin.stream()) {
yield chunk; yield chunk;

View File

@ -1,4 +1,4 @@
import { strlen } from './utils.ts'; import { strlen, truncate } from './utils.ts';
import { getRuntime } from './runtime.ts'; import { getRuntime } from './runtime.ts';
class Buffer { class Buffer {
@ -7,12 +7,12 @@ class Buffer {
constructor() { constructor() {
} }
public append(s: string): void { public append(s: string, maxLen?: number): void {
this.#b += s; this.#b += (maxLen === undefined) ? s : truncate(s, maxLen);
} }
public appendLine(s: string): void { public appendLine(s = ''): void {
this.#b += s + '\r\n'; this.#b += (s ?? '') + '\r\n';
} }
public clear(): void { public clear(): void {

View File

@ -1,4 +1,5 @@
import { chars } from './utils.ts'; import { chars } from './utils.ts';
import { getRuntime } from './runtime.ts';
export class Row { export class Row {
chars: string[] = []; chars: string[] = [];
@ -27,23 +28,36 @@ export class Document {
return new Document(); return new Document();
} }
public static open(_filename: string): Document { public isEmpty(): boolean {
const doc = new Document(); return this.#rows.length === 0;
const line = 'Hello, World!';
const row = new Row(line);
doc.#rows.push(row);
return doc;
} }
public getRow(i: number): Row | null { public async open(filename: string): Promise<Document> {
const { file } = await getRuntime();
// Clear any existing rows
if (!this.isEmpty()) {
this.#rows = [];
}
const rawFile = await file.openFile(filename);
rawFile.split(/\r?\n/)
.forEach((row) => this.appendRow(row));
return this;
}
public row(i: number): Row | null {
if (this.#rows[i] !== undefined) { if (this.#rows[i] !== undefined) {
return this.#rows[i]; return this.#rows[i];
} }
return null; return null;
} }
protected appendRow(s: string): void {
this.#rows.push(new Row(s));
}
} }
export default Document; export default Document;

View File

@ -1,23 +1,45 @@
import Ansi, { KeyCommand } from './ansi.ts'; import Ansi, { KeyCommand } from './ansi.ts';
import Buffer from './buffer.ts'; import Buffer from './buffer.ts';
import Document from './document.ts'; import Document from './document.ts';
import { ctrl_key, IPoint, ITerminalSize, truncate, VERSION } from './mod.ts'; import { IPoint, ITerminalSize, VERSION } from './mod.ts';
import { ctrl_key } from './utils.ts';
export class Editor { export class Editor {
/**
* The output buffer for the terminal
* @private
*/
#buffer: Buffer; #buffer: Buffer;
/**
* The size of the screen in rows/columns
* @private
*/
#screen: ITerminalSize; #screen: ITerminalSize;
/**
* The current location of the mouse cursor
* @private
*/
#cursor: IPoint; #cursor: IPoint;
/**
* The document being edited
* @private
*/
#document: Document; #document: Document;
constructor(terminalSize: ITerminalSize, args: string[]) { constructor(terminalSize: ITerminalSize) {
this.#buffer = new Buffer(); this.#buffer = new Buffer();
this.#screen = terminalSize; this.#screen = terminalSize;
this.#cursor = { this.#cursor = {
x: 0, x: 0,
y: 0, y: 0,
}; };
this.#document = (args.length >= 2)
? Document.open(args[1]) this.#document = Document.empty();
: Document.empty(); }
public async open(filename: string): Promise<Editor> {
await this.#document.open(filename);
return this;
} }
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@ -120,7 +142,7 @@ export class Editor {
private drawRows(): void { private drawRows(): void {
for (let y = 0; y < this.#screen.rows; y++) { for (let y = 0; y < this.#screen.rows; y++) {
if (y >= this.#document.numrows) { if (this.#document.numrows < y) {
this.drawPlaceholderRow(y); this.drawPlaceholderRow(y);
} else { } else {
this.drawFileRow(y); this.drawFileRow(y);
@ -128,18 +150,19 @@ export class Editor {
} }
} }
private drawFileRow(_y: number): void { private drawFileRow(y: number): void {
const row = this.#document.getRow(0); const row = this.#document.row(y);
let len = row?.chars.length ?? 0; let len = row?.chars.length ?? 0;
if (len > this.#screen.cols) { if (len > this.#screen.cols) {
len = this.#screen.cols; len = this.#screen.cols;
} }
this.#buffer.appendLine(truncate(row!.toString(), len)); this.#buffer.append(row!.toString(), len);
this.#buffer.appendLine(Ansi.ClearLine);
} }
private drawPlaceholderRow(y: number): void { private drawPlaceholderRow(y: number): void {
if (y === Math.trunc(this.#screen.rows / 2)) { if (y === Math.trunc(this.#screen.rows / 2) && this.#document.isEmpty()) {
const message = `Kilo editor -- version ${VERSION}`; const message = `Kilo editor -- version ${VERSION}`;
const messageLen = (message.length > this.#screen.cols) const messageLen = (message.length > this.#screen.cols)
? this.#screen.cols ? this.#screen.cols
@ -152,14 +175,14 @@ export class Editor {
this.#buffer.append(' '.repeat(padding)); this.#buffer.append(' '.repeat(padding));
} }
this.#buffer.append(truncate(message, messageLen)); this.#buffer.append(message, messageLen);
} else { } else {
this.#buffer.append('~'); this.#buffer.append('~');
} }
this.#buffer.append(Ansi.ClearLine); this.#buffer.append(Ansi.ClearLine);
if (y < this.#screen.rows - 1) { if (y < this.#screen.rows - 1) {
this.#buffer.appendLine(''); this.#buffer.appendLine();
} }
} }
} }

View File

@ -47,7 +47,13 @@ export async function main() {
const terminalSize = await term.getTerminalSize(); const terminalSize = await term.getTerminalSize();
// Create the editor itself // Create the editor itself
const editor = new Editor(terminalSize, term.argv); const editor = new Editor(terminalSize);
if (term.argv.length > 0) {
const filename = term.argv[0];
if (filename.trim() !== '') {
await editor.open(filename);
}
}
await editor.refreshScreen(); await editor.refreshScreen();
// The main event loop // The main event loop