Add operator highlighting, partially fix search
Some checks failed
timw4mail/scroll/pipeline/head There was a failure building this commit
Some checks failed
timw4mail/scroll/pipeline/head There was a failure building this commit
This commit is contained in:
parent
65ff7e5b79
commit
1a8d9f5469
@ -13,6 +13,8 @@ import { defaultTerminalSize, SCROLL_TAB_SIZE } from './config.ts';
|
|||||||
import { getTestRunner } from './runtime/mod.ts';
|
import { getTestRunner } from './runtime/mod.ts';
|
||||||
import { SearchDirection } from './types.ts';
|
import { SearchDirection } from './types.ts';
|
||||||
|
|
||||||
|
import fs from 'node:fs';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
assertEquals,
|
assertEquals,
|
||||||
assertEquivalent,
|
assertEquivalent,
|
||||||
@ -27,6 +29,7 @@ const {
|
|||||||
} = await getTestRunner();
|
} = await getTestRunner();
|
||||||
|
|
||||||
const THIS_FILE = './src/common/all_test.ts';
|
const THIS_FILE = './src/common/all_test.ts';
|
||||||
|
const KILO_FILE = './demo/kilo.c';
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helper Function Tests
|
// Helper Function Tests
|
||||||
@ -304,35 +307,45 @@ const DocumentTest = {
|
|||||||
assertTrue(doc.dirty);
|
assertTrue(doc.dirty);
|
||||||
|
|
||||||
await doc.save('test.file');
|
await doc.save('test.file');
|
||||||
|
|
||||||
|
fs.rm('test.file', (err: any) => {
|
||||||
|
assertNone(Option.from(err));
|
||||||
|
});
|
||||||
|
|
||||||
assertFalse(doc.dirty);
|
assertFalse(doc.dirty);
|
||||||
},
|
},
|
||||||
'.find': async () => {
|
'.find': async () => {
|
||||||
const doc = await Document.default().open(THIS_FILE);
|
const doc = await Document.default().open(KILO_FILE);
|
||||||
|
|
||||||
|
// First search forward from the beginning of the file
|
||||||
const query1 = doc.find(
|
const query1 = doc.find(
|
||||||
'dessert',
|
'editor',
|
||||||
Position.default(),
|
Position.default(),
|
||||||
SearchDirection.Forward,
|
SearchDirection.Forward,
|
||||||
);
|
);
|
||||||
assertTrue(query1.isSome());
|
assertTrue(query1.isSome());
|
||||||
// const pos1 = query1.unwrap();
|
const pos1 = query1.unwrap();
|
||||||
//
|
assertEquivalent(pos1, Position.at(5, 27));
|
||||||
// const query2 = doc.find(
|
|
||||||
// 'dessert',
|
|
||||||
// Position.at(pos1.x, 400),
|
|
||||||
// SearchDirection.Backward,
|
|
||||||
// );
|
|
||||||
// assertTrue(query2.isSome());
|
|
||||||
// const pos2 = query2.unwrap();
|
|
||||||
|
|
||||||
// assertEquivalent(pos2, pos1);
|
// Now search backwards from line 400
|
||||||
},
|
const query2 = doc.find(
|
||||||
'.insertRow': () => {
|
'realloc',
|
||||||
const doc = Document.default();
|
Position.at(44, 400),
|
||||||
doc.insertRow(undefined, 'foobar');
|
SearchDirection.Backward,
|
||||||
assertEquals(doc.numRows, 1);
|
);
|
||||||
assertFalse(doc.isEmpty());
|
assertTrue(query2.isSome());
|
||||||
assertInstanceOf(doc.row(0).unwrap(), Row);
|
const pos2 = query2.unwrap();
|
||||||
|
assertEquivalent(pos2, Position.at(11, 330));
|
||||||
|
|
||||||
|
// And backwards again
|
||||||
|
const query3 = doc.find(
|
||||||
|
'editor',
|
||||||
|
Position.from(pos2),
|
||||||
|
SearchDirection.Backward,
|
||||||
|
);
|
||||||
|
assertTrue(query3.isSome());
|
||||||
|
const pos3 = query3.unwrap();
|
||||||
|
assertEquivalent(pos3, Position.at(5, 328));
|
||||||
},
|
},
|
||||||
'.insert': () => {
|
'.insert': () => {
|
||||||
const doc = Document.default();
|
const doc = Document.default();
|
||||||
@ -522,15 +535,15 @@ const RowTest = {
|
|||||||
assertEquals(row.split(3, FileType.default()).toString(), row2.toString());
|
assertEquals(row.split(3, FileType.default()).toString(), row2.toString());
|
||||||
},
|
},
|
||||||
'.find': () => {
|
'.find': () => {
|
||||||
const normalRow = Row.from('For whom the bell tolls');
|
const normalRow = Row.from('\tFor whom the bell tolls');
|
||||||
assertEquivalent(
|
assertEquivalent(
|
||||||
normalRow.find('who', 0, SearchDirection.Forward),
|
normalRow.find('who', 0, SearchDirection.Forward),
|
||||||
Some(4),
|
Some(8),
|
||||||
);
|
);
|
||||||
assertEquals(normalRow.find('foo', 0, SearchDirection.Forward), None);
|
assertEquals(normalRow.find('foo', 0, SearchDirection.Forward), None);
|
||||||
|
|
||||||
const emojiRow = Row.from('😺😸😹');
|
const emojiRow = Row.from('\t😺😸😹');
|
||||||
assertEquivalent(emojiRow.find('😹', 0, SearchDirection.Forward), Some(2));
|
assertEquivalent(emojiRow.find('😹', 0, SearchDirection.Forward), Some(6));
|
||||||
assertEquals(emojiRow.find('🤰🏼', 10, SearchDirection.Forward), None);
|
assertEquals(emojiRow.find('🤰🏼', 10, SearchDirection.Forward), None);
|
||||||
},
|
},
|
||||||
'.find backwards': () => {
|
'.find backwards': () => {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import Row from './row.ts';
|
import Row from './row.ts';
|
||||||
import { FileType } from './filetype/mod.ts';
|
import { FileType } from './filetype/mod.ts';
|
||||||
import { arrayInsert, maxAdd, minSub } from './fns.ts';
|
import { arrayInsert } from './fns.ts';
|
||||||
import Option, { None, Some } from './option.ts';
|
import Option, { None, Some } from './option.ts';
|
||||||
import { getRuntime, logDebug, logWarning } from './runtime/mod.ts';
|
import { getRuntime, logWarning } from './runtime/mod.ts';
|
||||||
import { Position, SearchDirection } from './types.ts';
|
import { Position, SearchDirection } from './types.ts';
|
||||||
|
|
||||||
export class Document {
|
export class Document {
|
||||||
@ -55,13 +55,12 @@ export class Document {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.type = FileType.from(filename);
|
this.type = FileType.from(filename);
|
||||||
let startWithComment = false;
|
|
||||||
|
|
||||||
const rawFile = await file.openFile(filename);
|
const rawFile = await file.openFile(filename);
|
||||||
rawFile.split(/\r?\n/)
|
rawFile.split(/\r?\n/)
|
||||||
.forEach((row) =>
|
.forEach((row) => this.insertRow(this.numRows, row));
|
||||||
startWithComment = this.insertRow(this.numRows, row, startWithComment)
|
|
||||||
);
|
this.highlight(None);
|
||||||
|
|
||||||
this.dirty = false;
|
this.dirty = false;
|
||||||
|
|
||||||
@ -75,13 +74,10 @@ export class Document {
|
|||||||
const { file } = await getRuntime();
|
const { file } = await getRuntime();
|
||||||
|
|
||||||
await file.saveFile(filename, this.rowsToString());
|
await file.saveFile(filename, this.rowsToString());
|
||||||
let startWithComment = false;
|
|
||||||
this.type = FileType.from(filename);
|
this.type = FileType.from(filename);
|
||||||
|
|
||||||
// Re-highlight the file
|
// Re-highlight the file
|
||||||
this.#rows.forEach((row) => {
|
this.highlight(None);
|
||||||
startWithComment = row.update(None, this.type, startWithComment);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.dirty = false;
|
this.dirty = false;
|
||||||
}
|
}
|
||||||
@ -101,12 +97,7 @@ export class Document {
|
|||||||
|
|
||||||
const position = Position.from(at);
|
const position = Position.from(at);
|
||||||
|
|
||||||
const start = (direction === SearchDirection.Forward) ? at.y : 0;
|
for (let y = at.y; y >= 0 && y < this.numRows; y += direction) {
|
||||||
const end = (direction === SearchDirection.Forward)
|
|
||||||
? this.numRows
|
|
||||||
: maxAdd(at.y, 1, this.numRows);
|
|
||||||
|
|
||||||
for (let y = start; y < end; y++) {
|
|
||||||
if (this.row(position.y).isNone()) {
|
if (this.row(position.y).isNone()) {
|
||||||
logWarning('Invalid Search location', {
|
logWarning('Invalid Search location', {
|
||||||
position,
|
position,
|
||||||
@ -123,14 +114,13 @@ export class Document {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (direction === SearchDirection.Forward) {
|
if (direction === SearchDirection.Forward) {
|
||||||
position.y = maxAdd(position.y, 1, this.numRows - 1);
|
position.y += 1;
|
||||||
position.x = 0;
|
position.x = 0;
|
||||||
} else {
|
} else if (direction === SearchDirection.Backward) {
|
||||||
position.y = minSub(position.y, 1, 0);
|
position.y -= 1;
|
||||||
|
position.x = this.#rows[position.y].size;
|
||||||
|
|
||||||
console.assert(position.y < this.numRows);
|
console.assert(position.y < this.numRows);
|
||||||
|
|
||||||
position.x = this.#rows[position.y].size - 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,8 +138,10 @@ export class Document {
|
|||||||
this.insertRow(this.numRows, c);
|
this.insertRow(this.numRows, c);
|
||||||
} else {
|
} else {
|
||||||
this.#rows[at.y].insertChar(at.x, c);
|
this.#rows[at.y].insertChar(at.x, c);
|
||||||
this.#rows[at.y].update(None, this.type, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-highlight the file
|
||||||
|
this.highlight(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,9 +165,10 @@ export class Document {
|
|||||||
// row with the leftovers
|
// row with the leftovers
|
||||||
const currentRow = this.#rows[at.y];
|
const currentRow = this.#rows[at.y];
|
||||||
const newRow = currentRow.split(at.x, this.type);
|
const newRow = currentRow.split(at.x, this.type);
|
||||||
currentRow.update(None, this.type, false);
|
|
||||||
newRow.update(None, this.type, false);
|
|
||||||
this.#rows = arrayInsert(this.#rows, at.y + 1, newRow);
|
this.#rows = arrayInsert(this.#rows, at.y + 1, newRow);
|
||||||
|
|
||||||
|
// Re-highlight the file
|
||||||
|
this.highlight(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -199,12 +192,6 @@ export class Document {
|
|||||||
|
|
||||||
const mergeNextRow = at.x === row.size && this.row(at.y + 1).isSome();
|
const mergeNextRow = at.x === row.size && this.row(at.y + 1).isSome();
|
||||||
|
|
||||||
logDebug('Document.delete', {
|
|
||||||
method: 'Document.delete',
|
|
||||||
at,
|
|
||||||
mergeNextRow,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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 (This also works for pressing
|
// the merged row object (This also works for pressing
|
||||||
@ -220,7 +207,8 @@ export class Document {
|
|||||||
row.delete(at.x);
|
row.delete(at.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
row.update(None, this.type, false);
|
// Re-highlight the file
|
||||||
|
this.highlight(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public row(i: number): Option<Row> {
|
public row(i: number): Option<Row> {
|
||||||
@ -231,18 +219,6 @@ export class Document {
|
|||||||
return Option.from(this.#rows.at(i));
|
return Option.from(this.#rows.at(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
public insertRow(
|
|
||||||
at: number = this.numRows,
|
|
||||||
s: string = '',
|
|
||||||
startWithComment: boolean = false,
|
|
||||||
): boolean {
|
|
||||||
this.#rows = arrayInsert(this.#rows, at, Row.from(s));
|
|
||||||
|
|
||||||
this.dirty = true;
|
|
||||||
|
|
||||||
return this.#rows[at].update(None, this.type, startWithComment);
|
|
||||||
}
|
|
||||||
|
|
||||||
public highlight(searchMatch: Option<string>): void {
|
public highlight(searchMatch: Option<string>): void {
|
||||||
let startWithComment = false;
|
let startWithComment = false;
|
||||||
this.#rows.forEach((row) => {
|
this.#rows.forEach((row) => {
|
||||||
@ -250,6 +226,14 @@ export class Document {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected insertRow(
|
||||||
|
at: number = this.numRows,
|
||||||
|
s: string = '',
|
||||||
|
): void {
|
||||||
|
this.#rows = arrayInsert(this.#rows, at, Row.from(s));
|
||||||
|
this.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
@ -265,6 +265,7 @@ export default class Editor {
|
|||||||
*/
|
*/
|
||||||
public async find(): Promise<void> {
|
public async find(): Promise<void> {
|
||||||
const savedCursor = Position.from(this.cursor);
|
const savedCursor = Position.from(this.cursor);
|
||||||
|
const savedOffset = Position.from(this.offset);
|
||||||
let direction = SearchDirection.Forward;
|
let direction = SearchDirection.Forward;
|
||||||
|
|
||||||
const result = await this.prompt(
|
const result = await this.prompt(
|
||||||
@ -308,7 +309,7 @@ export default class Editor {
|
|||||||
// when you cancel the search (press the escape key)
|
// when you cancel the search (press the escape key)
|
||||||
if (result.isNone()) {
|
if (result.isNone()) {
|
||||||
this.cursor = Position.from(savedCursor);
|
this.cursor = Position.from(savedCursor);
|
||||||
// this.offset = Position.from(savedOffset);
|
this.offset = Position.from(savedOffset);
|
||||||
this.scroll();
|
this.scroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,15 +346,6 @@ export default class Editor {
|
|||||||
const height = this.numRows;
|
const height = this.numRows;
|
||||||
let width = (this.row(y).isSome()) ? this.row(y).unwrap().size : 0;
|
let width = (this.row(y).isSome()) ? this.row(y).unwrap().size : 0;
|
||||||
|
|
||||||
logDebug('Editor.moveCursor - start', {
|
|
||||||
char,
|
|
||||||
cursor: this.cursor,
|
|
||||||
renderX: this.renderX,
|
|
||||||
screen: this.screen,
|
|
||||||
height,
|
|
||||||
width,
|
|
||||||
});
|
|
||||||
|
|
||||||
switch (char) {
|
switch (char) {
|
||||||
case KeyCommand.ArrowUp:
|
case KeyCommand.ArrowUp:
|
||||||
if (y > 0) {
|
if (y > 0) {
|
||||||
@ -404,14 +396,6 @@ export default class Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.cursor = Position.at(x, y);
|
this.cursor = Position.at(x, y);
|
||||||
|
|
||||||
logDebug('Editor.moveCursor - end', {
|
|
||||||
cursor: this.cursor,
|
|
||||||
renderX: this.renderX,
|
|
||||||
screen: this.screen,
|
|
||||||
height,
|
|
||||||
width,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected scroll(): void {
|
protected scroll(): void {
|
||||||
|
@ -25,6 +25,7 @@ interface IFileType {
|
|||||||
readonly multiLineCommentEnd: Option<string>;
|
readonly multiLineCommentEnd: Option<string>;
|
||||||
readonly keywords1: string[];
|
readonly keywords1: string[];
|
||||||
readonly keywords2: string[];
|
readonly keywords2: string[];
|
||||||
|
readonly operators: string[];
|
||||||
readonly hlOptions: HighlightingOptions;
|
readonly hlOptions: HighlightingOptions;
|
||||||
get flags(): HighlightingOptions;
|
get flags(): HighlightingOptions;
|
||||||
get primaryKeywords(): string[];
|
get primaryKeywords(): string[];
|
||||||
@ -42,6 +43,7 @@ export abstract class AbstractFileType implements IFileType {
|
|||||||
public readonly multiLineCommentEnd: Option<string> = None;
|
public readonly multiLineCommentEnd: Option<string> = None;
|
||||||
public readonly keywords1: string[] = [];
|
public readonly keywords1: string[] = [];
|
||||||
public readonly keywords2: string[] = [];
|
public readonly keywords2: string[] = [];
|
||||||
|
public readonly operators: string[] = [];
|
||||||
public readonly hlOptions: HighlightingOptions = {
|
public readonly hlOptions: HighlightingOptions = {
|
||||||
numbers: false,
|
numbers: false,
|
||||||
strings: false,
|
strings: false,
|
||||||
@ -79,6 +81,8 @@ class JavaScriptFile extends AbstractFileType {
|
|||||||
public readonly multiLineCommentStart: Option<string> = Some('/*');
|
public readonly multiLineCommentStart: Option<string> = Some('/*');
|
||||||
public readonly multiLineCommentEnd: Option<string> = Some('*/');
|
public readonly multiLineCommentEnd: Option<string> = Some('*/');
|
||||||
public readonly keywords1 = [
|
public readonly keywords1 = [
|
||||||
|
'=>',
|
||||||
|
'await',
|
||||||
'break',
|
'break',
|
||||||
'case',
|
'case',
|
||||||
'catch',
|
'catch',
|
||||||
@ -100,9 +104,11 @@ class JavaScriptFile extends AbstractFileType {
|
|||||||
'import',
|
'import',
|
||||||
'in',
|
'in',
|
||||||
'instanceof',
|
'instanceof',
|
||||||
|
'let',
|
||||||
'new',
|
'new',
|
||||||
'null',
|
'null',
|
||||||
'return',
|
'return',
|
||||||
|
'static',
|
||||||
'super',
|
'super',
|
||||||
'switch',
|
'switch',
|
||||||
'this',
|
'this',
|
||||||
@ -114,27 +120,72 @@ class JavaScriptFile extends AbstractFileType {
|
|||||||
'void',
|
'void',
|
||||||
'while',
|
'while',
|
||||||
'with',
|
'with',
|
||||||
'let',
|
|
||||||
'static',
|
|
||||||
'yield',
|
'yield',
|
||||||
'await',
|
|
||||||
];
|
];
|
||||||
public readonly keywords2 = [
|
public readonly keywords2 = [
|
||||||
'arguments',
|
'arguments',
|
||||||
'as',
|
'as',
|
||||||
'async',
|
'async',
|
||||||
|
'BigInt',
|
||||||
|
'Boolean',
|
||||||
'eval',
|
'eval',
|
||||||
'from',
|
'from',
|
||||||
'get',
|
'get',
|
||||||
|
'JSON',
|
||||||
|
'Math',
|
||||||
|
'Number',
|
||||||
|
'Object',
|
||||||
'of',
|
'of',
|
||||||
'set',
|
'set',
|
||||||
'=>',
|
|
||||||
'Number',
|
|
||||||
'String',
|
'String',
|
||||||
'Object',
|
'Symbol',
|
||||||
'Math',
|
'undefined',
|
||||||
'JSON',
|
];
|
||||||
'Boolean',
|
public readonly operators = [
|
||||||
|
'>>>=',
|
||||||
|
'**=',
|
||||||
|
'<<=',
|
||||||
|
'>>=',
|
||||||
|
'&&=',
|
||||||
|
'||=',
|
||||||
|
'??=',
|
||||||
|
'===',
|
||||||
|
'!==',
|
||||||
|
'>>>',
|
||||||
|
'+=',
|
||||||
|
'-=',
|
||||||
|
'*=',
|
||||||
|
'/=',
|
||||||
|
'%=',
|
||||||
|
'&=',
|
||||||
|
'^=',
|
||||||
|
'|=',
|
||||||
|
'==',
|
||||||
|
'!=',
|
||||||
|
'>=',
|
||||||
|
'<=',
|
||||||
|
'++',
|
||||||
|
'--',
|
||||||
|
'**',
|
||||||
|
'<<',
|
||||||
|
'>>',
|
||||||
|
'&&',
|
||||||
|
'||',
|
||||||
|
'??',
|
||||||
|
'?.',
|
||||||
|
'?',
|
||||||
|
':',
|
||||||
|
'=',
|
||||||
|
'>',
|
||||||
|
'<',
|
||||||
|
'%',
|
||||||
|
'-',
|
||||||
|
'+',
|
||||||
|
'&',
|
||||||
|
'|',
|
||||||
|
'^',
|
||||||
|
'~',
|
||||||
|
'!',
|
||||||
];
|
];
|
||||||
public readonly hlOptions: HighlightingOptions = {
|
public readonly hlOptions: HighlightingOptions = {
|
||||||
...defaultHighlightOptions,
|
...defaultHighlightOptions,
|
||||||
@ -148,18 +199,19 @@ class TypeScriptFile extends JavaScriptFile {
|
|||||||
public readonly keywords2 = [
|
public readonly keywords2 = [
|
||||||
...super.secondaryKeywords,
|
...super.secondaryKeywords,
|
||||||
// Typescript-specific
|
// Typescript-specific
|
||||||
'keyof',
|
|
||||||
'interface',
|
|
||||||
'enum',
|
|
||||||
'public',
|
|
||||||
'protected',
|
|
||||||
'private',
|
|
||||||
'string',
|
|
||||||
'number',
|
|
||||||
'boolean',
|
|
||||||
'any',
|
'any',
|
||||||
'unknown',
|
'bigint',
|
||||||
|
'boolean',
|
||||||
|
'enum',
|
||||||
|
'interface',
|
||||||
|
'keyof',
|
||||||
|
'number',
|
||||||
|
'private',
|
||||||
|
'protected',
|
||||||
|
'public',
|
||||||
|
'string',
|
||||||
'type',
|
'type',
|
||||||
|
'unknown',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,6 +238,7 @@ class ShellFile extends AbstractFileType {
|
|||||||
'declare',
|
'declare',
|
||||||
];
|
];
|
||||||
public readonly keywords2 = ['set'];
|
public readonly keywords2 = ['set'];
|
||||||
|
public readonly operators = ['[[', ']]'];
|
||||||
public readonly hlOptions: HighlightingOptions = {
|
public readonly hlOptions: HighlightingOptions = {
|
||||||
...defaultHighlightOptions,
|
...defaultHighlightOptions,
|
||||||
numbers: false,
|
numbers: false,
|
||||||
|
@ -63,6 +63,7 @@ export function readKey(raw: Uint8Array): string {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a value into an array at the specified index
|
* Insert a value into an array at the specified index
|
||||||
|
*
|
||||||
* @param arr - the array
|
* @param arr - the array
|
||||||
* @param at - the index to insert at
|
* @param at - the index to insert at
|
||||||
* @param value - what to add into the array
|
* @param value - what to add into the array
|
||||||
@ -87,6 +88,7 @@ export function arrayInsert<T>(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Subtract two numbers, returning a zero if the result is negative
|
* Subtract two numbers, returning a zero if the result is negative
|
||||||
|
*
|
||||||
* @param l
|
* @param l
|
||||||
* @param s
|
* @param s
|
||||||
*/
|
*/
|
||||||
|
@ -9,6 +9,7 @@ export enum HighlightType {
|
|||||||
MultiLineComment,
|
MultiLineComment,
|
||||||
Keyword1,
|
Keyword1,
|
||||||
Keyword2,
|
Keyword2,
|
||||||
|
Operator,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function highlightToColor(type: HighlightType): string {
|
export function highlightToColor(type: HighlightType): string {
|
||||||
@ -23,15 +24,20 @@ export function highlightToColor(type: HighlightType): string {
|
|||||||
return Ansi.color256(45);
|
return Ansi.color256(45);
|
||||||
|
|
||||||
case HighlightType.SingleLineComment:
|
case HighlightType.SingleLineComment:
|
||||||
case HighlightType.MultiLineComment:
|
|
||||||
return Ansi.color256(201);
|
return Ansi.color256(201);
|
||||||
|
|
||||||
|
case HighlightType.MultiLineComment:
|
||||||
|
return Ansi.color256(240);
|
||||||
|
|
||||||
case HighlightType.Keyword1:
|
case HighlightType.Keyword1:
|
||||||
return Ansi.color256(226);
|
return Ansi.color256(226);
|
||||||
|
|
||||||
case HighlightType.Keyword2:
|
case HighlightType.Keyword2:
|
||||||
return Ansi.color256(118);
|
return Ansi.color256(118);
|
||||||
|
|
||||||
|
case HighlightType.Operator:
|
||||||
|
return Ansi.color256(215);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return Ansi.ResetFormatting;
|
return Ansi.ResetFormatting;
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,8 @@ export class Row {
|
|||||||
.orElse(() => this.highlightPrimaryKeywords(i, syntax))
|
.orElse(() => this.highlightPrimaryKeywords(i, syntax))
|
||||||
.orElse(() => this.highlightSecondaryKeywords(i, syntax))
|
.orElse(() => this.highlightSecondaryKeywords(i, syntax))
|
||||||
.orElse(() => this.highlightString(i, syntax, ch))
|
.orElse(() => this.highlightString(i, syntax, ch))
|
||||||
.orElse(() => this.highlightNumber(i, syntax, ch));
|
.orElse(() => this.highlightNumber(i, syntax, ch))
|
||||||
|
.orElse(() => this.highlightOperators(i, syntax));
|
||||||
|
|
||||||
if (maybeNext.isSome()) {
|
if (maybeNext.isSome()) {
|
||||||
const next = maybeNext.unwrap();
|
const next = maybeNext.unwrap();
|
||||||
@ -313,9 +314,10 @@ export class Row {
|
|||||||
|
|
||||||
// Find matches for the current search
|
// Find matches for the current search
|
||||||
if (word.isSome()) {
|
if (word.isSome()) {
|
||||||
|
const query = word.unwrap();
|
||||||
while (true) {
|
while (true) {
|
||||||
const match = this.find(
|
const match = this.find(
|
||||||
word.unwrap(),
|
query,
|
||||||
searchIndex,
|
searchIndex,
|
||||||
SearchDirection.Forward,
|
SearchDirection.Forward,
|
||||||
);
|
);
|
||||||
@ -324,7 +326,8 @@ export class Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const index = match.unwrap();
|
const index = match.unwrap();
|
||||||
const nextPossible = index + strlen(word.unwrap());
|
const matchSize = strlen(query);
|
||||||
|
const nextPossible = index + matchSize;
|
||||||
if (nextPossible < this.rsize) {
|
if (nextPossible < this.rsize) {
|
||||||
let i = index;
|
let i = index;
|
||||||
for (const _ in strChars(word.unwrap())) {
|
for (const _ in strChars(word.unwrap())) {
|
||||||
@ -438,6 +441,35 @@ export class Row {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected highlightOperators(
|
||||||
|
i: number,
|
||||||
|
syntax: FileType,
|
||||||
|
): Option<number> {
|
||||||
|
// Search the list of operators
|
||||||
|
outer: for (const op of syntax.operators) {
|
||||||
|
const chars = strChars(op);
|
||||||
|
|
||||||
|
// See if this operator (chars[j]) exists at this index
|
||||||
|
for (const [j, ch] of chars.entries()) {
|
||||||
|
// Make sure the next character of this operator matches too
|
||||||
|
const nextChar = this.rchars[i + j];
|
||||||
|
if (nextChar !== ch) {
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This operator matches, highlight it
|
||||||
|
for (const _ of chars) {
|
||||||
|
this.hl.push(HighlightType.Operator);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
protected highlightString(
|
protected highlightString(
|
||||||
i: number,
|
i: number,
|
||||||
syntax: FileType,
|
syntax: FileType,
|
||||||
@ -470,16 +502,19 @@ export class Row {
|
|||||||
syntax: FileType,
|
syntax: FileType,
|
||||||
ch: string,
|
ch: string,
|
||||||
): Option<number> {
|
): Option<number> {
|
||||||
if (syntax.hasMultilineComments()) {
|
if (!syntax.hasMultilineComments()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
const startChars = syntax.multiLineCommentStart.unwrap();
|
const startChars = syntax.multiLineCommentStart.unwrap();
|
||||||
const endChars = syntax.multiLineCommentEnd.unwrap();
|
const endChars = syntax.multiLineCommentEnd.unwrap();
|
||||||
if (ch === startChars[0] && this.rchars[i + 1] == startChars[1]) {
|
if (ch === startChars[0] && this.rchars[i + 1] == startChars[1]) {
|
||||||
const maybeEnd = this.rIndexOf(endChars, i);
|
const maybeEnd = this.rIndexOf(endChars, i);
|
||||||
const end = (maybeEnd.isSome())
|
const end = (maybeEnd.isSome())
|
||||||
? maybeEnd.unwrap() + strlen(endChars) + 1
|
? maybeEnd.unwrap() + strlen(endChars) + 2
|
||||||
: this.rsize;
|
: this.rsize;
|
||||||
|
|
||||||
for (; i < end; i++) {
|
for (; i <= end; i++) {
|
||||||
this.hl.push(HighlightType.MultiLineComment);
|
this.hl.push(HighlightType.MultiLineComment);
|
||||||
}
|
}
|
||||||
return Some(i);
|
return Some(i);
|
||||||
@ -488,9 +523,6 @@ export class Row {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected highlightNumber(
|
protected highlightNumber(
|
||||||
i: number,
|
i: number,
|
||||||
syntax: FileType,
|
syntax: FileType,
|
||||||
@ -505,12 +537,29 @@ export class Row {
|
|||||||
while (true) {
|
while (true) {
|
||||||
this.hl.push(HighlightType.Number);
|
this.hl.push(HighlightType.Number);
|
||||||
i += 1;
|
i += 1;
|
||||||
if (i < this.rsize) {
|
if (i >= this.rsize) {
|
||||||
const nextChar = this.rchars[i];
|
|
||||||
if (nextChar !== '.' && nextChar !== 'x' && !isAsciiDigit(nextChar)) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
const nextChar = this.rchars[i];
|
||||||
|
// deno-fmt-ignore
|
||||||
|
const validChars = [
|
||||||
|
// Decimal
|
||||||
|
'.',
|
||||||
|
// Octal Notation
|
||||||
|
'o','O',
|
||||||
|
// Hex Notation
|
||||||
|
'x','X',
|
||||||
|
// Hex digits
|
||||||
|
'a','A','c','C','d','D','e','E','f','F',
|
||||||
|
// Binary Notation/Hex digit
|
||||||
|
'b','B',
|
||||||
|
// BigInt
|
||||||
|
'n',
|
||||||
|
];
|
||||||
|
if (
|
||||||
|
!(validChars.includes(nextChar) || isAsciiDigit(nextChar))
|
||||||
|
) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user