Basic file opening and display. Off-by-one bug skipping first line, though
This commit is contained in:
parent
816295ff9c
commit
d5ce04fe8b
@ -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": {
|
||||||
|
8
justfile
8
justfile
@ -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}}
|
||||||
|
@ -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();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user