Fix some issues with line splitting/merging
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
This commit is contained in:
parent
4436a8a783
commit
4be7be09a7
7
.gitignore
vendored
7
.gitignore
vendored
@ -335,8 +335,13 @@ $RECYCLE.BIN/
|
|||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/jetbrains+all,vim,node,deno,macos,windows,linux
|
# End of https://www.toptal.com/developers/gitignore/api/jetbrains+all,vim,node,deno,macos,windows,linux
|
||||||
|
|
||||||
|
# Other editors
|
||||||
|
.nova/
|
||||||
|
.zed/
|
||||||
|
|
||||||
## Misc generated files
|
## Misc generated files
|
||||||
scroll.err
|
scroll*.log
|
||||||
|
docs
|
||||||
deno.lock
|
deno.lock
|
||||||
cov_profile/
|
cov_profile/
|
||||||
coverage/
|
coverage/
|
||||||
|
@ -14,6 +14,9 @@ To simplify running, I'm using [Just](https://github.com/casey/just).
|
|||||||
- Deno: `just deno-run [filename]`
|
- Deno: `just deno-run [filename]`
|
||||||
- TSX: `just tsx-run [filename`
|
- TSX: `just tsx-run [filename`
|
||||||
|
|
||||||
|
Deno is generally used for dev tools, but each runtime should be functionally
|
||||||
|
equivalent running the text editor.
|
||||||
|
|
||||||
## Development Notes
|
## Development Notes
|
||||||
|
|
||||||
- Implementation is based on [Kilo](https://viewsourcecode.org/snaptoken/kilo/)
|
- Implementation is based on [Kilo](https://viewsourcecode.org/snaptoken/kilo/)
|
||||||
|
19
justfile
19
justfile
@ -6,7 +6,7 @@ default:
|
|||||||
coverage: bun-test deno-coverage
|
coverage: bun-test deno-coverage
|
||||||
|
|
||||||
# Typescript checking
|
# Typescript checking
|
||||||
check: deno-check bun-check
|
check: deno-check bun-check tsx-check
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
deno doc --html --unstable-ffi --name="Scroll" ./src/scroll.ts ./src/common/*.ts ./src/deno/mod.ts ./src/bun/mod.ts
|
deno doc --html --unstable-ffi --name="Scroll" ./src/scroll.ts ./src/common/*.ts ./src/deno/mod.ts ./src/bun/mod.ts
|
||||||
@ -27,8 +27,8 @@ clean:
|
|||||||
rm -rf .deno-cover
|
rm -rf .deno-cover
|
||||||
rm -rf coverage
|
rm -rf coverage
|
||||||
rm -rf docs
|
rm -rf docs
|
||||||
rm -f scroll.log
|
rm -f scroll*.log
|
||||||
rm -f scroll.err
|
rm -f test.file
|
||||||
rm -f tsconfig.tsbuildinfo
|
rm -f tsconfig.tsbuildinfo
|
||||||
|
|
||||||
##########################################################################################
|
##########################################################################################
|
||||||
@ -53,8 +53,8 @@ bun-run file="":
|
|||||||
|
|
||||||
# Lint code and check types
|
# Lint code and check types
|
||||||
deno-check:
|
deno-check:
|
||||||
deno lint
|
deno task deno-lint
|
||||||
deno check --unstable-ffi --all -c deno.jsonc ./src/deno/*.ts ./src/common/*.ts
|
deno task deno-check
|
||||||
|
|
||||||
# Test with deno
|
# Test with deno
|
||||||
deno-test:
|
deno-test:
|
||||||
@ -66,15 +66,20 @@ deno-coverage:
|
|||||||
|
|
||||||
# Run with deno
|
# Run with deno
|
||||||
deno-run file="":
|
deno-run file="":
|
||||||
deno run --allow-all --allow-ffi --deny-hrtime --unstable-ffi ./src/scroll.ts {{file}}
|
deno task deno-run {{file}}
|
||||||
|
#deno run --allow-all --allow-ffi --deny-hrtime --unstable-ffi ./src/scroll.ts {{file}}
|
||||||
|
|
||||||
##########################################################################################
|
##########################################################################################
|
||||||
# tsx(Node JS)-specific commands
|
# tsx(Node JS)-specific commands
|
||||||
##########################################################################################
|
##########################################################################################
|
||||||
|
|
||||||
|
# Check code with actual Typescript compiler
|
||||||
|
tsx-check:
|
||||||
|
npm run tsx-check
|
||||||
|
|
||||||
# Test with tsx (NodeJS)
|
# Test with tsx (NodeJS)
|
||||||
tsx-test:
|
tsx-test:
|
||||||
npx tsx --test './src/common/all_test.ts'
|
npm run tsx-test
|
||||||
|
|
||||||
# Run with tsx (NodeJS)
|
# Run with tsx (NodeJS)
|
||||||
tsx-run file="":
|
tsx-run file="":
|
||||||
|
10
package.json
10
package.json
@ -4,6 +4,16 @@
|
|||||||
"bun-types": "^1.0.11"
|
"bun-types": "^1.0.11"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"bun-check": "bunx tsc",
|
||||||
|
"bun-coverage": "bun test --coverage",
|
||||||
|
"bun-test": "bun test",
|
||||||
|
"deno-lint": "deno lint",
|
||||||
|
"deno-check": "deno check --unstable-ffi --all -c deno.jsonc ./src/deno/*.ts ./src/common/*.ts ./src/tsx/*.ts",
|
||||||
|
"deno-coverage": "./coverage.sh",
|
||||||
|
"deno-run": "deno run --allow-all --allow-ffi --deny-hrtime --unstable-ffi ./src/scroll.ts",
|
||||||
|
"deno-test": "deno test --allow-all --unstable-ffi",
|
||||||
|
"tsx-check": "npx tsc",
|
||||||
|
"tsx-test": "npx tsx --test './src/common/all_test.ts'"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
@ -381,25 +381,18 @@ const DocumentTest = {
|
|||||||
doc.delete(Position.at(3, 0));
|
doc.delete(Position.at(3, 0));
|
||||||
assertEquals(doc.row(0).unwrap().toString(), 'fooar');
|
assertEquals(doc.row(0).unwrap().toString(), 'fooar');
|
||||||
|
|
||||||
// Merge previous row
|
// Merge next row
|
||||||
const doc2 = Document.default();
|
const doc2 = Document.default();
|
||||||
doc2.insertNewline(Position.default());
|
doc2.insertNewline(Position.default());
|
||||||
doc2.insert(Position.at(0, 1), 'foobar');
|
doc2.insert(Position.at(0, 1), 'foobar');
|
||||||
doc2.delete(Position.at(0, 1));
|
doc2.delete(Position.at(0, 0));
|
||||||
assertEquals(doc2.row(0).unwrap().toString(), 'foobar');
|
assertEquals(doc2.row(0).unwrap().toString(), 'foobar');
|
||||||
|
|
||||||
// Merge next row
|
|
||||||
const doc3 = Document.default();
|
|
||||||
doc3.insertNewline(Position.default());
|
|
||||||
doc3.insert(Position.at(0, 1), 'foobar');
|
|
||||||
doc3.delete(Position.at(0, 0));
|
|
||||||
assertEquals(doc3.row(0).unwrap().toString(), 'foobar');
|
|
||||||
|
|
||||||
// Invalid delete location
|
// Invalid delete location
|
||||||
const doc4 = Document.default();
|
const doc3 = Document.default();
|
||||||
doc4.insert(Position.default(), 'foobar');
|
doc3.insert(Position.default(), 'foobar');
|
||||||
doc4.delete(Position.at(0, 1));
|
doc3.delete(Position.at(0, 3));
|
||||||
assertEquals(doc4.row(0).unwrap().toString(), 'foobar');
|
assertEquals(doc3.row(0).unwrap().toString(), 'foobar');
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ export const SCROLL_VERSION = '0.0.1';
|
|||||||
export const SCROLL_QUIT_TIMES = 3;
|
export const SCROLL_QUIT_TIMES = 3;
|
||||||
export const SCROLL_TAB_SIZE = 4;
|
export const SCROLL_TAB_SIZE = 4;
|
||||||
|
|
||||||
export const SCROLL_LOG_FILE = './scroll.log';
|
export const SCROLL_LOG_FILE_PREFIX = './scroll';
|
||||||
export const SCROLL_ERR_FILE = './scroll.err';
|
export const SCROLL_LOG_FILE_SUFFIX = '.log';
|
||||||
|
|
||||||
export const defaultTerminalSize: ITerminalSize = {
|
export const defaultTerminalSize: ITerminalSize = {
|
||||||
rows: 24,
|
rows: 24,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Row from './row.ts';
|
import Row from './row.ts';
|
||||||
import { arrayInsert, maxAdd, minSub } from './fns.ts';
|
import { arrayInsert, maxAdd, minSub } from './fns.ts';
|
||||||
import Option, { None, Some } from './option.ts';
|
import Option, { None, Some } from './option.ts';
|
||||||
import { getRuntime, log, LogLevel } from './runtime.ts';
|
import { getRuntime, logDebug } from './runtime.ts';
|
||||||
import { Position, SearchDirection } from './types.ts';
|
import { Position, SearchDirection } from './types.ts';
|
||||||
|
|
||||||
export class Document {
|
export class Document {
|
||||||
@ -153,6 +153,8 @@ export class Document {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.dirty = true;
|
||||||
|
|
||||||
const maybeRow = this.row(at.y);
|
const maybeRow = this.row(at.y);
|
||||||
if (maybeRow.isNone()) {
|
if (maybeRow.isNone()) {
|
||||||
return;
|
return;
|
||||||
@ -161,38 +163,29 @@ export class Document {
|
|||||||
const row = maybeRow.unwrap();
|
const row = maybeRow.unwrap();
|
||||||
|
|
||||||
const mergeNextRow = at.x === row.size && this.row(at.y + 1).isSome();
|
const mergeNextRow = at.x === row.size && this.row(at.y + 1).isSome();
|
||||||
const mergeIntoPrevRow = at.x === 0 && this.row(at.y - 1).isSome() &&
|
|
||||||
this.row(at.y).isSome();
|
|
||||||
|
|
||||||
log({
|
logDebug('Document.delete', {
|
||||||
method: 'Document.delete',
|
method: 'Document.delete',
|
||||||
at,
|
at,
|
||||||
mergeNextRow,
|
mergeNextRow,
|
||||||
mergeIntoPrevRow,
|
});
|
||||||
}, LogLevel.Debug);
|
|
||||||
|
|
||||||
// If we are at the end of a line, and press delete,
|
// If we are at the end of a line, and press delete,
|
||||||
// add the contents of the next row, and delete
|
// add the contents of the next row, and delete
|
||||||
// the merged row object
|
// the merged row object (This also works for pressing
|
||||||
|
// backspace at the beginning of a line: the cursor is
|
||||||
|
// moved to the end of the previous line)
|
||||||
if (mergeNextRow) {
|
if (mergeNextRow) {
|
||||||
// At the end of a line, pressing delete will merge
|
// At the end of a line, pressing delete will merge
|
||||||
// the next line into the current on
|
// the next line into the current one
|
||||||
const rowToAppend = this.#rows.at(at.y + 1)!.toString();
|
const rowToAppend = this.#rows[at.y + 1].toString();
|
||||||
row.append(rowToAppend);
|
row.append(rowToAppend);
|
||||||
this.deleteRow(at.y + 1);
|
this.deleteRow(at.y + 1);
|
||||||
} else if (mergeIntoPrevRow) {
|
|
||||||
// At the beginning of a line, merge the current line
|
|
||||||
// into the previous Row
|
|
||||||
const rowToAppend = row.toString();
|
|
||||||
this.#rows[at.y - 1].append(rowToAppend);
|
|
||||||
this.deleteRow(at.y);
|
|
||||||
} else {
|
} else {
|
||||||
row.delete(at.x);
|
row.delete(at.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
row.update(None);
|
row.update(None);
|
||||||
|
|
||||||
this.dirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public row(i: number): Option<Row> {
|
public row(i: number): Option<Row> {
|
||||||
@ -200,7 +193,7 @@ export class Document {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Option.from(this.#rows[i]);
|
return Option.from(this.#rows.at(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
public insertRow(at: number = this.numRows, s: string = ''): void {
|
public insertRow(at: number = this.numRows, s: string = ''): void {
|
||||||
@ -219,17 +212,15 @@ export class Document {
|
|||||||
/**
|
/**
|
||||||
* Delete the specified row
|
* Delete the specified row
|
||||||
* @param at - the index of the row to delete
|
* @param at - the index of the row to delete
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
private deleteRow(at: number): void {
|
protected deleteRow(at: number): void {
|
||||||
this.#rows.splice(at, 1);
|
this.#rows.splice(at, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the array of row objects into one string
|
* Convert the array of row objects into one string
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
private rowsToString(): string {
|
protected rowsToString(): string {
|
||||||
return this.#rows.map((r) => r.toString()).join('\n');
|
return this.#rows.map((r) => r.toString()).join('\n');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
truncate,
|
truncate,
|
||||||
} from './fns.ts';
|
} from './fns.ts';
|
||||||
import Option, { None, Some } from './option.ts';
|
import Option, { None, Some } from './option.ts';
|
||||||
import { getRuntime, log, LogLevel } from './runtime.ts';
|
import { getRuntime, logDebug, logWarning } from './runtime.ts';
|
||||||
import { ITerminalSize, Position, SearchDirection } from './types.ts';
|
import { ITerminalSize, Position, SearchDirection } from './types.ts';
|
||||||
|
|
||||||
export default class Editor {
|
export default class Editor {
|
||||||
@ -72,12 +72,16 @@ export default class Editor {
|
|||||||
this.document = Document.default();
|
this.document = Document.default();
|
||||||
}
|
}
|
||||||
|
|
||||||
private get numRows(): number {
|
protected get numRows(): number {
|
||||||
return this.document.numRows;
|
return this.document.numRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get currentRow(): Option<Row> {
|
protected row(at: number): Option<Row> {
|
||||||
return this.document.row(this.cursor.y);
|
return this.document.row(at);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get currentRow(): Option<Row> {
|
||||||
|
return this.row(this.cursor.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async open(filename: string): Promise<Editor> {
|
public async open(filename: string): Promise<Editor> {
|
||||||
@ -150,11 +154,9 @@ export default class Editor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case KeyCommand.Backspace:
|
case KeyCommand.Backspace:
|
||||||
{
|
if (this.cursor.x > 0 || this.cursor.y > 0) {
|
||||||
if (this.cursor.x > 0 || this.cursor.y > 0) {
|
this.moveCursor(KeyCommand.ArrowLeft);
|
||||||
this.moveCursor(KeyCommand.ArrowLeft);
|
this.document.delete(this.cursor);
|
||||||
this.document.delete(this.cursor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -317,20 +319,19 @@ export default class Editor {
|
|||||||
* Filter out any additional unwanted keyboard input
|
* Filter out any additional unwanted keyboard input
|
||||||
*
|
*
|
||||||
* @param input
|
* @param input
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
private shouldFilter(input: string): boolean {
|
protected shouldFilter(input: string): boolean {
|
||||||
const isEscapeSequence = input[0] === KeyCommand.Escape;
|
const isEscapeSequence = input[0] === KeyCommand.Escape;
|
||||||
const isCtrl = isControl(input);
|
const isCtrl = isControl(input);
|
||||||
const shouldFilter = isEscapeSequence || isCtrl;
|
const shouldFilter = isEscapeSequence || isCtrl;
|
||||||
const whitelist = ['\t'];
|
const whitelist = ['\t'];
|
||||||
|
|
||||||
if (shouldFilter && !whitelist.includes(input)) {
|
if (shouldFilter && !whitelist.includes(input)) {
|
||||||
log({
|
logDebug('Ignoring input:', {
|
||||||
'msg': `Ignoring input: ${input}`,
|
input,
|
||||||
isEscapeSequence,
|
isEscapeSequence,
|
||||||
isCtrl,
|
isCtrl,
|
||||||
}, LogLevel.Debug);
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -338,22 +339,20 @@ export default class Editor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private moveCursor(char: string): void {
|
protected moveCursor(char: string): void {
|
||||||
const screenHeight = this.screen.rows;
|
const screenHeight = this.screen.rows;
|
||||||
let { x, y } = this.cursor;
|
let { x, y } = this.cursor;
|
||||||
const height = this.numRows;
|
const height = this.numRows;
|
||||||
let width = (this.document.row(y).isSome())
|
let width = (this.row(y).isSome()) ? this.row(y).unwrap().size : 0;
|
||||||
? this.currentRow.unwrap().size
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
log({
|
logDebug('Editor.moveCursor - start', {
|
||||||
method: 'Editor.moveCursor - start',
|
char,
|
||||||
cursor: this.cursor,
|
cursor: this.cursor,
|
||||||
renderX: this.renderX,
|
renderX: this.renderX,
|
||||||
screen: this.screen,
|
screen: this.screen,
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
}, LogLevel.Debug);
|
});
|
||||||
|
|
||||||
switch (char) {
|
switch (char) {
|
||||||
case KeyCommand.ArrowUp:
|
case KeyCommand.ArrowUp:
|
||||||
@ -371,12 +370,12 @@ export default class Editor {
|
|||||||
x -= 1;
|
x -= 1;
|
||||||
} else if (y > 0) {
|
} else if (y > 0) {
|
||||||
y -= 1;
|
y -= 1;
|
||||||
x = (this.currentRow.isSome()) ? this.currentRow.unwrap().rsize : 0;
|
x = (this.row(y).isSome()) ? this.row(y).unwrap().size : 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case KeyCommand.ArrowRight:
|
case KeyCommand.ArrowRight:
|
||||||
if (
|
if (
|
||||||
this.currentRow.isSome() && x < width
|
this.row(y).isSome() && x < width
|
||||||
) {
|
) {
|
||||||
x += 1;
|
x += 1;
|
||||||
} else if (y < height) {
|
} else if (y < height) {
|
||||||
@ -398,7 +397,7 @@ export default class Editor {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
width = (this.currentRow.isSome()) ? this.currentRow.unwrap().size : 0;
|
width = (this.row(y).isSome()) ? this.row(y).unwrap().size : 0;
|
||||||
|
|
||||||
if (x > width) {
|
if (x > width) {
|
||||||
x = width;
|
x = width;
|
||||||
@ -406,27 +405,25 @@ export default class Editor {
|
|||||||
|
|
||||||
this.cursor = Position.at(x, y);
|
this.cursor = Position.at(x, y);
|
||||||
|
|
||||||
log({
|
logDebug('Editor.moveCursor - end', {
|
||||||
method: 'Editor.moveCursor - end',
|
|
||||||
cursor: this.cursor,
|
cursor: this.cursor,
|
||||||
renderX: this.renderX,
|
renderX: this.renderX,
|
||||||
screen: this.screen,
|
screen: this.screen,
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
}, LogLevel.Debug);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private scroll(): void {
|
protected scroll(): void {
|
||||||
this.renderX = (this.currentRow.isSome())
|
this.renderX = (this.row(this.cursor.y).isSome())
|
||||||
? this.currentRow.unwrap().cxToRx(this.cursor.x)
|
? this.document.row(this.cursor.y).unwrap().cxToRx(this.cursor.x)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
log({
|
logDebug('Editor.scroll - start', {
|
||||||
method: 'Editor.scroll - start',
|
|
||||||
cursor: this.cursor,
|
cursor: this.cursor,
|
||||||
renderX: this.renderX,
|
renderX: this.renderX,
|
||||||
offset: this.offset,
|
offset: this.offset,
|
||||||
}, LogLevel.Debug);
|
});
|
||||||
|
|
||||||
const { y } = this.cursor;
|
const { y } = this.cursor;
|
||||||
const offset = this.offset;
|
const offset = this.offset;
|
||||||
@ -445,12 +442,11 @@ export default class Editor {
|
|||||||
offset.x = this.renderX - width + 1;
|
offset.x = this.renderX - width + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
log({
|
logDebug('Editor.scroll - end', {
|
||||||
method: 'Editor.scroll - end',
|
|
||||||
cursor: this.cursor,
|
cursor: this.cursor,
|
||||||
renderX: this.renderX,
|
renderX: this.renderX,
|
||||||
offset: this.offset,
|
offset: this.offset,
|
||||||
}, LogLevel.Debug);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
@ -484,14 +480,14 @@ export default class Editor {
|
|||||||
await this.buffer.flush();
|
await this.buffer.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async clearScreen(): Promise<void> {
|
protected async clearScreen(): Promise<void> {
|
||||||
this.buffer.append(Ansi.ClearScreen);
|
this.buffer.append(Ansi.ClearScreen);
|
||||||
this.buffer.append(Ansi.ResetCursor);
|
this.buffer.append(Ansi.ResetCursor);
|
||||||
|
|
||||||
await this.buffer.flush();
|
await this.buffer.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private drawRows(): void {
|
protected drawRows(): void {
|
||||||
for (let y = 0; y < this.screen.rows; y++) {
|
for (let y = 0; y < this.screen.rows; y++) {
|
||||||
this.buffer.append(Ansi.ClearLine);
|
this.buffer.append(Ansi.ClearLine);
|
||||||
const fileRow = y + this.offset.y;
|
const fileRow = y + this.offset.y;
|
||||||
@ -505,10 +501,10 @@ export default class Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private drawFileRow(y: number): void {
|
protected drawFileRow(y: number): void {
|
||||||
const maybeRow = this.document.row(y);
|
const maybeRow = this.document.row(y);
|
||||||
if (maybeRow.isNone()) {
|
if (maybeRow.isNone()) {
|
||||||
log(`Trying to draw non-existent row '${y}'`, LogLevel.Warning);
|
logWarning(`Trying to draw non-existent row '${y}'`);
|
||||||
return this.drawPlaceholderRow(y);
|
return this.drawPlaceholderRow(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,7 +518,7 @@ export default class Editor {
|
|||||||
this.buffer.append(row.render(this.offset.x, len));
|
this.buffer.append(row.render(this.offset.x, len));
|
||||||
}
|
}
|
||||||
|
|
||||||
private drawPlaceholderRow(y: number): void {
|
protected drawPlaceholderRow(y: number): void {
|
||||||
if (y === Math.trunc(this.screen.rows / 2) && this.document.isEmpty()) {
|
if (y === Math.trunc(this.screen.rows / 2) && this.document.isEmpty()) {
|
||||||
const message = `Scroll editor -- version ${SCROLL_VERSION}`;
|
const message = `Scroll editor -- version ${SCROLL_VERSION}`;
|
||||||
const messageLen = (message.length > this.screen.cols)
|
const messageLen = (message.length > this.screen.cols)
|
||||||
@ -542,7 +538,7 @@ export default class Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private drawStatusBar(): void {
|
protected drawStatusBar(): void {
|
||||||
this.buffer.append(Ansi.InvertColor);
|
this.buffer.append(Ansi.InvertColor);
|
||||||
const name = (this.filename !== '') ? this.filename : '[No Name]';
|
const name = (this.filename !== '') ? this.filename : '[No Name]';
|
||||||
const modified = (this.document.dirty) ? '(modified)' : '';
|
const modified = (this.document.dirty) ? '(modified)' : '';
|
||||||
@ -563,7 +559,7 @@ export default class Editor {
|
|||||||
this.buffer.appendLine(Ansi.ResetFormatting);
|
this.buffer.appendLine(Ansi.ResetFormatting);
|
||||||
}
|
}
|
||||||
|
|
||||||
private drawMessageBar(): void {
|
protected drawMessageBar(): void {
|
||||||
this.buffer.append(Ansi.ClearLine);
|
this.buffer.append(Ansi.ClearLine);
|
||||||
const msgLen = this.statusMessage.length;
|
const msgLen = this.statusMessage.length;
|
||||||
if (msgLen > 0 && (Date.now() - this.statusTimeout < 5000)) {
|
if (msgLen > 0 && (Date.now() - this.statusTimeout < 5000)) {
|
||||||
|
@ -7,8 +7,8 @@ import { IRuntime, ITerminalSize, ITestBase } from './types.ts';
|
|||||||
import { noop } from './fns.ts';
|
import { noop } from './fns.ts';
|
||||||
import {
|
import {
|
||||||
defaultTerminalSize,
|
defaultTerminalSize,
|
||||||
SCROLL_ERR_FILE,
|
SCROLL_LOG_FILE_PREFIX,
|
||||||
SCROLL_LOG_FILE,
|
SCROLL_LOG_FILE_SUFFIX,
|
||||||
} from './config.ts';
|
} from './config.ts';
|
||||||
|
|
||||||
export type { IFileIO, IRuntime, ITerminal } from './types.ts';
|
export type { IFileIO, IRuntime, ITerminal } from './types.ts';
|
||||||
@ -79,24 +79,36 @@ async function _getTerminalSizeFromAnsi(): Promise<ITerminalSize> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function log(s: unknown, level: LogLevel = LogLevel.Notice): void {
|
/**
|
||||||
|
* Basic logging -
|
||||||
|
*/
|
||||||
|
export function log(
|
||||||
|
s: unknown,
|
||||||
|
level: LogLevel = LogLevel.Notice,
|
||||||
|
data?: any,
|
||||||
|
): void {
|
||||||
getRuntime().then(({ file }) => {
|
getRuntime().then(({ file }) => {
|
||||||
const raw = JSON.stringify(s, null, 2);
|
const rawS = JSON.stringify(s, null, 2);
|
||||||
const output = `${level}: ${raw}\n`;
|
const rawData = JSON.stringify(data, null, 2);
|
||||||
|
const output = (typeof data !== 'undefined')
|
||||||
|
? `${rawS}\n${rawData}\n\n`
|
||||||
|
: `${rawS}\n`;
|
||||||
|
|
||||||
const outputFile = (level === LogLevel.Error)
|
const outputFile =
|
||||||
? SCROLL_ERR_FILE
|
`${SCROLL_LOG_FILE_PREFIX}-${level.toLowerCase()}${SCROLL_LOG_FILE_SUFFIX}`;
|
||||||
: SCROLL_LOG_FILE;
|
|
||||||
file.appendFile(outputFile, output).then(noop);
|
file.appendFile(outputFile, output).then(noop);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export const logDebug = (s: unknown, data?: any) =>
|
||||||
* Append information to the scroll.err logfile
|
log(s, LogLevel.Debug, data);
|
||||||
*/
|
export const logInfo = (s: unknown, data?: any) => log(s, LogLevel.Info, data);
|
||||||
export function logError(s: unknown): void {
|
export const logNotice = (s: unknown, data?: any) =>
|
||||||
log(s, LogLevel.Error);
|
log(s, LogLevel.Notice, data);
|
||||||
}
|
export const logWarning = (s: unknown, data?: any) =>
|
||||||
|
log(s, LogLevel.Warning, data);
|
||||||
|
export const logError = (s: unknown, data?: any) =>
|
||||||
|
log(s, LogLevel.Warning, data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kill program, displaying an error message
|
* Kill program, displaying an error message
|
||||||
|
Loading…
Reference in New Issue
Block a user