import { chars } from './utils.ts'; import Row from './row.ts'; import { getRuntime } from './runtime.ts'; import { Position, TAB_SIZE } from './mod.ts'; export class Document { #filename: string | null; #rows: Row[]; dirty: boolean; private constructor() { this.#rows = []; this.#filename = null; this.dirty = false; } get numRows(): number { return this.#rows.length; } public static empty(): Document { return new Document(); } public isEmpty(): boolean { return this.#rows.length === 0; } public async open(filename: string): Promise { 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)); this.#filename = filename; this.dirty = false; return this; } public async save() { if (this.#filename === null) { return; } const { file } = await getRuntime(); await file.saveFile(this.#filename, this.rowsToString()); this.dirty = false; } public insert(at: Position, c: string): void { if (at.y === this.numRows) { this.appendRow(c); } else { this.#rows[at.y].insertChar(at.x, c); this.updateRow(this.#rows[at.y]); } this.dirty = true; } public row(i: number): Row | null { return this.#rows[i] ?? null; } public appendRow(s: string): void { const at = this.numRows; this.#rows[at] = new Row(s); this.updateRow(this.#rows[at]); this.dirty = true; } private updateRow(r: Row): void { r.render = chars(r.toString().replace('\t', ' '.repeat(TAB_SIZE))); } private rowsToString(): string { return this.#rows.map((r) => r.toString()).join('\n'); } } export default Document;