This commit is contained in:
parent
5b40d16999
commit
21d26ede6c
@ -63,6 +63,7 @@ function print16colorTable(): void {
|
||||
|
||||
console.log(colorTable);
|
||||
}
|
||||
|
||||
function print256colorTable(): void {
|
||||
let colorTable = '';
|
||||
// deno-fmt-ignore
|
||||
|
@ -2,7 +2,7 @@ import Ansi, * as _Ansi from './ansi.ts';
|
||||
import Buffer from './buffer.ts';
|
||||
import Document from './document.ts';
|
||||
import Editor from './editor.ts';
|
||||
import { FileType } from './filetype/mod.ts';
|
||||
import { FileLang, FileType } from './filetype/mod.ts';
|
||||
import { highlightToColor, HighlightType } from './highlight.ts';
|
||||
import Option, { None, Some } from './option.ts';
|
||||
import Position from './position.ts';
|
||||
@ -202,9 +202,19 @@ const readKeyTest = () => {
|
||||
|
||||
const highlightToColorTest = {
|
||||
'highlightToColor()': () => {
|
||||
assertTrue(highlightToColor(HighlightType.Number).length > 0);
|
||||
assertTrue(highlightToColor(HighlightType.Match).length > 0);
|
||||
assertTrue(highlightToColor(HighlightType.None).length > 0);
|
||||
[
|
||||
HighlightType.Number,
|
||||
HighlightType.Match,
|
||||
HighlightType.String,
|
||||
HighlightType.SingleLineComment,
|
||||
HighlightType.MultiLineComment,
|
||||
HighlightType.Keyword1,
|
||||
HighlightType.Keyword2,
|
||||
HighlightType.Operator,
|
||||
HighlightType.None,
|
||||
].forEach((type) => {
|
||||
assertTrue(highlightToColor(type).length > 0);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@ -243,12 +253,12 @@ const ANSITest = () => {
|
||||
|
||||
const BufferTest = {
|
||||
'new Buffer': () => {
|
||||
const b = new Buffer();
|
||||
const b = Buffer.default();
|
||||
assertInstanceOf(b, Buffer);
|
||||
assertEquals(b.strlen(), 0);
|
||||
},
|
||||
'.appendLine': () => {
|
||||
const b = new Buffer();
|
||||
const b = Buffer.default();
|
||||
|
||||
// Carriage return and line feed
|
||||
b.appendLine();
|
||||
@ -261,7 +271,7 @@ const BufferTest = {
|
||||
assertEquals(b.strlen(), 5);
|
||||
},
|
||||
'.append': () => {
|
||||
const b = new Buffer();
|
||||
const b = Buffer.default();
|
||||
|
||||
b.append('foobar');
|
||||
assertEquals(b.strlen(), 6);
|
||||
@ -271,7 +281,7 @@ const BufferTest = {
|
||||
assertEquals(b.strlen(), 3);
|
||||
},
|
||||
'.flush': async () => {
|
||||
const b = new Buffer();
|
||||
const b = Buffer.default();
|
||||
b.appendLine('foobarbaz' + Ansi.ClearLine);
|
||||
assertEquals(b.strlen(), 14);
|
||||
|
||||
@ -297,6 +307,7 @@ const DocumentTest = {
|
||||
assertEquals(oldDoc.numRows, 1);
|
||||
|
||||
const doc = await oldDoc.open(THIS_FILE);
|
||||
assertEquals(FileLang.TypeScript, doc.fileType);
|
||||
assertFalse(doc.dirty);
|
||||
assertFalse(doc.isEmpty());
|
||||
assertTrue(doc.numRows > 1);
|
||||
@ -347,6 +358,16 @@ const DocumentTest = {
|
||||
const pos3 = query3.unwrap();
|
||||
assertEquivalent(pos3, Position.at(5, 328));
|
||||
},
|
||||
'.find - empty result': () => {
|
||||
const doc = Document.default();
|
||||
doc.insertNewline(Position.default());
|
||||
|
||||
const query = doc.find('foo', Position.default(), SearchDirection.Forward);
|
||||
assertNone(query);
|
||||
|
||||
const query2 = doc.find('bar', Position.at(0, 5), SearchDirection.Forward);
|
||||
assertNone(query2);
|
||||
},
|
||||
'.insert': () => {
|
||||
const doc = Document.default();
|
||||
assertFalse(doc.dirty);
|
||||
@ -420,22 +441,22 @@ const DocumentTest = {
|
||||
|
||||
const EditorTest = {
|
||||
'new Editor': () => {
|
||||
const e = new Editor(defaultTerminalSize);
|
||||
const e = Editor.create(defaultTerminalSize);
|
||||
assertInstanceOf(e, Editor);
|
||||
},
|
||||
'.open': async () => {
|
||||
const e = new Editor(defaultTerminalSize);
|
||||
const e = Editor.create(defaultTerminalSize);
|
||||
await e.open(THIS_FILE);
|
||||
assertInstanceOf(e, Editor);
|
||||
},
|
||||
'.processKeyPress - letters': async () => {
|
||||
const e = new Editor(defaultTerminalSize);
|
||||
const e = Editor.create(defaultTerminalSize);
|
||||
const res = await e.processKeyPress('a');
|
||||
assertTrue(res);
|
||||
},
|
||||
'.processKeyPress - ctrl-q': async () => {
|
||||
// Dirty file (Need to clear confirmation messages)
|
||||
const e = new Editor(defaultTerminalSize);
|
||||
const e = Editor.create(defaultTerminalSize);
|
||||
await e.processKeyPress('d');
|
||||
assertTrue(await e.processKeyPress(Fn.ctrlKey('q')));
|
||||
assertTrue(await e.processKeyPress(Fn.ctrlKey('q')));
|
||||
@ -443,7 +464,7 @@ const EditorTest = {
|
||||
assertFalse(await e.processKeyPress(Fn.ctrlKey('q')));
|
||||
|
||||
// Clean file
|
||||
const e2 = new Editor(defaultTerminalSize);
|
||||
const e2 = Editor.create(defaultTerminalSize);
|
||||
const res = await e2.processKeyPress(Fn.ctrlKey('q'));
|
||||
assertFalse(res);
|
||||
},
|
||||
|
@ -42,6 +42,7 @@ export enum AnsiColor {
|
||||
FgMagenta,
|
||||
FgCyan,
|
||||
FgWhite,
|
||||
ForegroundColor,
|
||||
FgDefault,
|
||||
|
||||
// Background Colors
|
||||
@ -53,6 +54,7 @@ export enum AnsiColor {
|
||||
BgMagenta,
|
||||
BgCyan,
|
||||
BgWhite,
|
||||
BackgroundColor,
|
||||
BgDefault,
|
||||
|
||||
// Bright Foreground Colors
|
||||
@ -77,8 +79,8 @@ export enum AnsiColor {
|
||||
}
|
||||
|
||||
export enum Ground {
|
||||
Fore = AnsiColor.FgDefault,
|
||||
Back = AnsiColor.BgDefault,
|
||||
Fore = AnsiColor.ForegroundColor,
|
||||
Back = AnsiColor.BackgroundColor,
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -4,7 +4,11 @@ import { getRuntime } from './runtime/mod.ts';
|
||||
class Buffer {
|
||||
#b = '';
|
||||
|
||||
constructor() {
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
public static default(): Buffer {
|
||||
return new Buffer();
|
||||
}
|
||||
|
||||
public append(s: string, maxLen?: number): void {
|
||||
|
@ -102,15 +102,6 @@ export class Document {
|
||||
const position = Position.from(at);
|
||||
|
||||
for (let y = at.y; y >= 0 && y < this.numRows; y += direction) {
|
||||
if (this.row(position.y).isNone()) {
|
||||
logWarning('Invalid Search location', {
|
||||
position,
|
||||
document: this,
|
||||
});
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
const maybeMatch = this.#rows[y].find(q, position.x, direction);
|
||||
if (maybeMatch.isSome()) {
|
||||
position.x = this.#rows[y].rxToCx(maybeMatch.unwrap());
|
||||
|
@ -16,62 +16,45 @@ import Option, { None, Some } from './option.ts';
|
||||
import { getRuntime, logDebug, logWarning } from './runtime/mod.ts';
|
||||
import { ITerminalSize, Position, SearchDirection } from './types.ts';
|
||||
|
||||
/**
|
||||
* The main Editor interface
|
||||
*/
|
||||
export default class Editor {
|
||||
/**
|
||||
* The document being edited
|
||||
* @param screen - The size of the screen in rows/columns
|
||||
* @param document - The document being edited
|
||||
* @param buffer - The output buffer for the terminal
|
||||
* @param cursor - The current location of the mouse cursor
|
||||
* @param offset - The current scrolling offset
|
||||
* @param renderX - The scrolling offset for the rendered row
|
||||
* @param filename - The name of the currently open file
|
||||
* @param statusMessage - A message to display at the bottom of the screen
|
||||
* @param statusTimeout - Timeout for status messages
|
||||
* @param quitTimes - The number of times required to quit a dirty document
|
||||
* @param highlightedWord - The current search term, if there is one
|
||||
* @private
|
||||
*/
|
||||
protected document: Document;
|
||||
/**
|
||||
* The output buffer for the terminal
|
||||
*/
|
||||
protected buffer: Buffer;
|
||||
/**
|
||||
* The size of the screen in rows/columns
|
||||
*/
|
||||
protected screen: ITerminalSize;
|
||||
/**
|
||||
* The current location of the mouse cursor
|
||||
*/
|
||||
protected cursor: Position;
|
||||
/**
|
||||
* The current scrolling offset
|
||||
*/
|
||||
protected offset: Position;
|
||||
/**
|
||||
* The scrolling offset for the rendered row
|
||||
*/
|
||||
protected renderX: number = 0;
|
||||
/**
|
||||
* The name of the currently open file
|
||||
*/
|
||||
protected filename: string = '';
|
||||
/**
|
||||
* A message to display at the bottom of the screen
|
||||
*/
|
||||
protected statusMessage: string = '';
|
||||
/**
|
||||
* Timeout for status messages
|
||||
*/
|
||||
protected statusTimeout: number = 0;
|
||||
/**
|
||||
* The number of times required to quit a dirty document
|
||||
*/
|
||||
protected quitTimes: number = SCROLL_QUIT_TIMES;
|
||||
|
||||
protected highlightedWord: Option<string> = None;
|
||||
|
||||
constructor(terminalSize: ITerminalSize) {
|
||||
this.buffer = new Buffer();
|
||||
|
||||
private constructor(
|
||||
protected screen: ITerminalSize,
|
||||
protected document: Document = Document.default(),
|
||||
protected buffer: Buffer = Buffer.default(),
|
||||
protected cursor: Position = Position.default(),
|
||||
protected offset: Position = Position.default(),
|
||||
protected renderX: number = 0,
|
||||
protected filename: string = '',
|
||||
protected statusMessage: string = '',
|
||||
protected statusTimeout: number = 0,
|
||||
protected quitTimes: number = SCROLL_QUIT_TIMES,
|
||||
protected highlightedWord: Option<string> = None,
|
||||
) {
|
||||
// Subtract two rows from the terminal size
|
||||
// for displaying the status bar
|
||||
// and message bar
|
||||
this.screen = terminalSize;
|
||||
this.screen.rows -= 2;
|
||||
}
|
||||
|
||||
this.cursor = Position.default();
|
||||
this.offset = Position.default();
|
||||
this.document = Document.default();
|
||||
public static create(terminalSize: ITerminalSize) {
|
||||
return new Editor(terminalSize);
|
||||
}
|
||||
|
||||
protected get numRows(): number {
|
||||
@ -82,10 +65,6 @@ export default class Editor {
|
||||
return this.document.row(at);
|
||||
}
|
||||
|
||||
protected get currentRow(): Option<Row> {
|
||||
return this.row(this.cursor.y);
|
||||
}
|
||||
|
||||
public async open(filename: string): Promise<Editor> {
|
||||
await this.document.open(filename);
|
||||
this.filename = filename;
|
||||
@ -400,9 +379,12 @@ export default class Editor {
|
||||
this.cursor = Position.at(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the window of a file to display
|
||||
*/
|
||||
protected scroll(): void {
|
||||
this.renderX = (this.row(this.cursor.y).isSome())
|
||||
? this.document.row(this.cursor.y).unwrap().cxToRx(this.cursor.x)
|
||||
? this.row(this.cursor.y).unwrap().cxToRx(this.cursor.x)
|
||||
: 0;
|
||||
|
||||
const { y } = this.cursor;
|
||||
|
87
src/common/filetype/base.ts
Normal file
87
src/common/filetype/base.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import Option, { None } from '../option.ts';
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// File-related types
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
export enum FileLang {
|
||||
TypeScript = 'TypeScript',
|
||||
JavaScript = 'JavaScript',
|
||||
PHP = 'PHP',
|
||||
Go = 'Golang',
|
||||
Rust = 'Rust',
|
||||
CSS = 'CSS',
|
||||
Shell = 'Shell',
|
||||
Plain = 'Plain Text',
|
||||
}
|
||||
|
||||
export interface HighlightingOptions {
|
||||
numbers: boolean;
|
||||
octalNumbers: boolean;
|
||||
hexNumbers: boolean;
|
||||
binNumbers: boolean;
|
||||
jsBigInt: boolean;
|
||||
strings: boolean;
|
||||
}
|
||||
|
||||
interface IFileType {
|
||||
readonly name: FileLang;
|
||||
readonly singleLineComment: Option<string>;
|
||||
readonly multiLineCommentStart: Option<string>;
|
||||
readonly multiLineCommentEnd: Option<string>;
|
||||
readonly keywords1: string[];
|
||||
readonly keywords2: string[];
|
||||
readonly operators: string[];
|
||||
readonly hlOptions: HighlightingOptions;
|
||||
get flags(): HighlightingOptions;
|
||||
get primaryKeywords(): string[];
|
||||
get secondaryKeywords(): string[];
|
||||
hasMultilineComments(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base class for File Types
|
||||
*/
|
||||
export abstract class AbstractFileType implements IFileType {
|
||||
public readonly name: FileLang = FileLang.Plain;
|
||||
public readonly singleLineComment = None;
|
||||
public readonly multiLineCommentStart: Option<string> = None;
|
||||
public readonly multiLineCommentEnd: Option<string> = None;
|
||||
public readonly keywords1: string[] = [];
|
||||
public readonly keywords2: string[] = [];
|
||||
public readonly operators: string[] = [];
|
||||
public readonly hlOptions: HighlightingOptions = {
|
||||
numbers: false,
|
||||
octalNumbers: false,
|
||||
hexNumbers: false,
|
||||
binNumbers: false,
|
||||
jsBigInt: false,
|
||||
strings: false,
|
||||
};
|
||||
|
||||
get flags(): HighlightingOptions {
|
||||
return this.hlOptions;
|
||||
}
|
||||
|
||||
get primaryKeywords(): string[] {
|
||||
return this.keywords1;
|
||||
}
|
||||
|
||||
get secondaryKeywords(): string[] {
|
||||
return this.keywords2;
|
||||
}
|
||||
|
||||
public hasMultilineComments(): boolean {
|
||||
return this.multiLineCommentStart.isSome() &&
|
||||
this.multiLineCommentEnd.isSome();
|
||||
}
|
||||
}
|
||||
|
||||
export const defaultHighlightOptions: HighlightingOptions = {
|
||||
numbers: true,
|
||||
octalNumbers: false,
|
||||
hexNumbers: false,
|
||||
binNumbers: false,
|
||||
jsBigInt: false,
|
||||
strings: true,
|
||||
};
|
393
src/common/filetype/css.ts
Normal file
393
src/common/filetype/css.ts
Normal file
@ -0,0 +1,393 @@
|
||||
import Option, { None, Some } from '../option.ts';
|
||||
import {
|
||||
AbstractFileType,
|
||||
defaultHighlightOptions,
|
||||
FileLang,
|
||||
HighlightingOptions,
|
||||
} from './base.ts';
|
||||
|
||||
export class CSSFile extends AbstractFileType {
|
||||
public readonly name: FileLang = FileLang.CSS;
|
||||
public readonly singleLineComment = None;
|
||||
public readonly multiLineCommentStart: Option<string> = Some('/*');
|
||||
public readonly multiLineCommentEnd: Option<string> = Some('*/');
|
||||
public readonly keywords1 = [
|
||||
':active',
|
||||
':any-link',
|
||||
':autofill',
|
||||
':checked',
|
||||
':default',
|
||||
':disabled',
|
||||
':empty',
|
||||
':enabled',
|
||||
':first-child',
|
||||
':first-of-type',
|
||||
':focus-visible',
|
||||
':focus-within',
|
||||
':focus',
|
||||
':fullscreen',
|
||||
':hover',
|
||||
':in-range',
|
||||
':indeterminate',
|
||||
':invalid',
|
||||
':last-child',
|
||||
':last-of-type',
|
||||
':link',
|
||||
':modal',
|
||||
':nth-child',
|
||||
':nth-last-child',
|
||||
':nth-last-of-type',
|
||||
':nth-of-type',
|
||||
':only-child',
|
||||
':only-of-type',
|
||||
':optional',
|
||||
':out-of-range',
|
||||
':paused',
|
||||
':picture-in-picture',
|
||||
':placeholder-shown',
|
||||
':playing',
|
||||
':read-only',
|
||||
':read-write',
|
||||
':required',
|
||||
':root',
|
||||
':scope',
|
||||
':target',
|
||||
':user-valid',
|
||||
':valid',
|
||||
':visited',
|
||||
'::after',
|
||||
'::backdrop',
|
||||
'::before',
|
||||
'::cue',
|
||||
'::file-selector-button',
|
||||
'::first-letter',
|
||||
'::first-line',
|
||||
'::grammar-error',
|
||||
'::marker',
|
||||
'::placeholder',
|
||||
'::selection',
|
||||
'::spelling-error',
|
||||
'@charset',
|
||||
'@color-profile',
|
||||
'@container',
|
||||
'@counter-style',
|
||||
'@font-face',
|
||||
'@font-feature-values',
|
||||
'@font-palette-values',
|
||||
'@import',
|
||||
'@keyframes',
|
||||
'@layer',
|
||||
'@media',
|
||||
'@namespace',
|
||||
'@page',
|
||||
'@position-try',
|
||||
'@property',
|
||||
'@scope',
|
||||
'@starting-style',
|
||||
'@supports',
|
||||
'@view-transition',
|
||||
];
|
||||
public readonly keywords2 = [
|
||||
'animation-range-end',
|
||||
'animation-range-start',
|
||||
'accent-color',
|
||||
'animation-timeline',
|
||||
'animation',
|
||||
'animation-timing-function',
|
||||
'animation-composition',
|
||||
'animation-delay',
|
||||
'animation-direction',
|
||||
'appearance',
|
||||
'align-content',
|
||||
'animation-duration',
|
||||
'align-items',
|
||||
'animation-fill-mode',
|
||||
'align-self',
|
||||
'animation-iteration-count',
|
||||
'aspect-ratio',
|
||||
'align-tracks',
|
||||
'animation-name',
|
||||
'all',
|
||||
'animation-play-state',
|
||||
'animation-name',
|
||||
'anchor-name',
|
||||
'border-block-start-color',
|
||||
'border-inline-style',
|
||||
'backdrop-filter',
|
||||
'border-block-start-style',
|
||||
'border-inline-width',
|
||||
'backface-visibility',
|
||||
'border-block-start-width',
|
||||
'border-left',
|
||||
'background',
|
||||
'border-block-style',
|
||||
'border-left-color',
|
||||
'background-attachment',
|
||||
'border-block-width',
|
||||
'border-left-style',
|
||||
'background-blend-mode',
|
||||
'border-bottom',
|
||||
'border-left-width',
|
||||
'background-clip',
|
||||
'border-bottom-color',
|
||||
'border-radius',
|
||||
'background-color',
|
||||
'border-bottom-left-radius',
|
||||
'border-right',
|
||||
'background-image',
|
||||
'border-bottom-right-radius',
|
||||
'border-right-color',
|
||||
'background-origin',
|
||||
'border-bottom-style',
|
||||
'border-right-style',
|
||||
'background-position',
|
||||
'border-bottom-width',
|
||||
'border-right-width',
|
||||
'background-position-x',
|
||||
'border-collapse',
|
||||
'border-spacing',
|
||||
'background-position-y',
|
||||
'border-color',
|
||||
'border-start-end-radius',
|
||||
'background-repeat',
|
||||
'border-end-end-radius',
|
||||
'border-start-start-radius',
|
||||
'background-size',
|
||||
'border-end-start-radius',
|
||||
'border-style',
|
||||
'border-image',
|
||||
'border-top',
|
||||
'border-image-outset',
|
||||
'border-top-color',
|
||||
'border-image-repeat',
|
||||
'border-top-left-radius',
|
||||
'border-image-slice',
|
||||
'border-top-right-radius',
|
||||
'border-image-source',
|
||||
'border-top-style',
|
||||
'border-image-width',
|
||||
'border-top-width',
|
||||
'border-inline',
|
||||
'border-width',
|
||||
'block-size',
|
||||
'border-inline-color',
|
||||
'bottom',
|
||||
'border-inline-end',
|
||||
'border',
|
||||
'border-inline-end-color',
|
||||
'box-decoration-break',
|
||||
'border-block',
|
||||
'border-inline-end-style',
|
||||
'box-shadow',
|
||||
'border-block-color',
|
||||
'border-inline-end-width',
|
||||
'box-sizing',
|
||||
'border-block-end',
|
||||
'border-inline-start',
|
||||
'break-after',
|
||||
'border-block-end-color',
|
||||
'border-inline-start-color',
|
||||
'break-before',
|
||||
'border-block-end-style',
|
||||
'border-inline-start-style',
|
||||
'break-inside',
|
||||
'border-block-end-width',
|
||||
'border-inline-start-width',
|
||||
'border-block-start',
|
||||
'column-rule',
|
||||
'content-visibility',
|
||||
'caption-side',
|
||||
'column-rule-color',
|
||||
'column-rule-style',
|
||||
'caret-color',
|
||||
'column-rule-width',
|
||||
'column-span',
|
||||
'counter-increment',
|
||||
'column-width',
|
||||
'counter-reset',
|
||||
'columns',
|
||||
'counter-set',
|
||||
'contain',
|
||||
'contain-intrinsic-block-size',
|
||||
'clear',
|
||||
'contain-intrinsic-height',
|
||||
'clip',
|
||||
'contain-intrinsic-inline-size',
|
||||
'clip-path',
|
||||
'color',
|
||||
'contain-intrinsic-width',
|
||||
'cursor',
|
||||
'color-scheme',
|
||||
'container',
|
||||
'column-count',
|
||||
'container-name',
|
||||
'column-fill',
|
||||
'container-type',
|
||||
'column-gap',
|
||||
'content',
|
||||
'direction',
|
||||
'display',
|
||||
'empty-cells',
|
||||
'font-synthesis-position',
|
||||
'field-sizing',
|
||||
'font',
|
||||
'font-synthesis-small-caps',
|
||||
'filter',
|
||||
'font-synthesis-style',
|
||||
'font-synthesis-weight',
|
||||
'font-family',
|
||||
'font-variant',
|
||||
'font-variant-alternates',
|
||||
'font-variant-caps',
|
||||
'font-feature-settings',
|
||||
'font-variant-east-asian',
|
||||
'font-variant-emoji',
|
||||
'font-variant-ligatures',
|
||||
'font-variant-numeric',
|
||||
'font-variant-position',
|
||||
'flex',
|
||||
'font-kerning',
|
||||
'font-variation-settings',
|
||||
'flex-basis',
|
||||
'font-language-override',
|
||||
'flex-direction',
|
||||
'font-optical-sizing',
|
||||
'flex-flow',
|
||||
'font-palette',
|
||||
'font-weight',
|
||||
'flex-grow',
|
||||
'flex-shrink',
|
||||
'font-size',
|
||||
'forced-color-adjust',
|
||||
'flex-wrap',
|
||||
'font-size-adjust',
|
||||
'font-stretch',
|
||||
'float',
|
||||
'font-style',
|
||||
'font-synthesis',
|
||||
'grid-auto-columns',
|
||||
'grid-row-end',
|
||||
'gap',
|
||||
'grid-auto-flow',
|
||||
'grid-row-start',
|
||||
'grid-auto-rows',
|
||||
'grid-template',
|
||||
'grid-column',
|
||||
'grid-template-areas',
|
||||
'grid-column-end',
|
||||
'grid-template-columns',
|
||||
'grid',
|
||||
'grid-column-start',
|
||||
'grid-template-rows',
|
||||
'grid-area',
|
||||
'grid-row',
|
||||
'hanging-punctuation',
|
||||
'hyphenate-character',
|
||||
'hyphenate-limit-chars',
|
||||
'height',
|
||||
'hyphens',
|
||||
'initial',
|
||||
'inset-inline',
|
||||
'initial-letter',
|
||||
'inset-inline-end',
|
||||
'image-orientation',
|
||||
'image-rendering',
|
||||
'inline-size',
|
||||
'image-resolution',
|
||||
'inset',
|
||||
'isolation',
|
||||
'inset-area',
|
||||
'inset-block',
|
||||
'inherit',
|
||||
'inset-block-end',
|
||||
'inset-block-start',
|
||||
'justify-content',
|
||||
'justify-self',
|
||||
'justify-items',
|
||||
'justify-tracks',
|
||||
'letter-spacing',
|
||||
'list-style',
|
||||
'list-style-image',
|
||||
'line-break',
|
||||
'list-style-position',
|
||||
'line-clamp',
|
||||
'list-style-type',
|
||||
'line-height',
|
||||
'left',
|
||||
'line-height-step',
|
||||
'mask-border-outset',
|
||||
'margin',
|
||||
'mask-border-repeat',
|
||||
'margin-block',
|
||||
'mask-border-slice',
|
||||
'margin-block-end',
|
||||
'mask-border-source',
|
||||
'margin-block-start',
|
||||
'mask-border-width',
|
||||
'max-height',
|
||||
'margin-bottom',
|
||||
'mask-clip',
|
||||
'max-inline-size',
|
||||
'margin-inline',
|
||||
'mask-composite',
|
||||
'margin-inline-end',
|
||||
'mask-image',
|
||||
'max-width',
|
||||
'margin-inline-start',
|
||||
'mask-mode',
|
||||
'margin-left',
|
||||
'mask-origin',
|
||||
'margin-right',
|
||||
'mask-position',
|
||||
'min-block-size',
|
||||
'margin-top',
|
||||
'mask-repeat',
|
||||
'min-height',
|
||||
'margin-trim',
|
||||
'mask-size',
|
||||
'min-inline-size',
|
||||
'mask-type',
|
||||
'min-width',
|
||||
'masonry-auto-flow',
|
||||
'mask',
|
||||
'math-depth',
|
||||
'mix-blend-mode',
|
||||
'mask-border',
|
||||
'math-shift',
|
||||
'mask-border-mode',
|
||||
'math-style',
|
||||
'object-fit',
|
||||
'order',
|
||||
'overflow-inline',
|
||||
'object-position',
|
||||
'overflow-wrap',
|
||||
'offset',
|
||||
'orphans',
|
||||
'overflow-x',
|
||||
'offset-anchor',
|
||||
'overflow-y',
|
||||
'offset-distance',
|
||||
'outline',
|
||||
'overlay',
|
||||
'offset-path',
|
||||
'outline-color',
|
||||
'offset-position',
|
||||
'outline-offset',
|
||||
'offset-rotate',
|
||||
'outline-style',
|
||||
'overscroll-behavior',
|
||||
'outline-width',
|
||||
'overscroll-behavior-block',
|
||||
'overscroll-behavior-inline',
|
||||
'opacity',
|
||||
'overflow-anchor',
|
||||
'overscroll-behavior-x',
|
||||
'overflow-block',
|
||||
'overscroll-behavior-y',
|
||||
'overflow-clip-margin',
|
||||
];
|
||||
public readonly operators = ['::', ':', ',', '+', '>', '~', '-'];
|
||||
public readonly hlOptions: HighlightingOptions = {
|
||||
...defaultHighlightOptions,
|
||||
};
|
||||
}
|
@ -1,259 +1,8 @@
|
||||
import { node_path as path } from '../runtime/mod.ts';
|
||||
import Option, { None, Some } from '../option.ts';
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// File-related types
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
export enum FileLang {
|
||||
TypeScript = 'TypeScript',
|
||||
JavaScript = 'JavaScript',
|
||||
CSS = 'CSS',
|
||||
Shell = 'Shell',
|
||||
Plain = 'Plain Text',
|
||||
}
|
||||
|
||||
export interface HighlightingOptions {
|
||||
numbers: boolean;
|
||||
strings: boolean;
|
||||
}
|
||||
|
||||
interface IFileType {
|
||||
readonly name: FileLang;
|
||||
readonly singleLineComment: Option<string>;
|
||||
readonly multiLineCommentStart: Option<string>;
|
||||
readonly multiLineCommentEnd: Option<string>;
|
||||
readonly keywords1: string[];
|
||||
readonly keywords2: string[];
|
||||
readonly operators: string[];
|
||||
readonly hlOptions: HighlightingOptions;
|
||||
get flags(): HighlightingOptions;
|
||||
get primaryKeywords(): string[];
|
||||
get secondaryKeywords(): string[];
|
||||
hasMultilineComments(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base class for File Types
|
||||
*/
|
||||
export abstract class AbstractFileType implements IFileType {
|
||||
public readonly name: FileLang = FileLang.Plain;
|
||||
public readonly singleLineComment = None;
|
||||
public readonly multiLineCommentStart: Option<string> = None;
|
||||
public readonly multiLineCommentEnd: Option<string> = None;
|
||||
public readonly keywords1: string[] = [];
|
||||
public readonly keywords2: string[] = [];
|
||||
public readonly operators: string[] = [];
|
||||
public readonly hlOptions: HighlightingOptions = {
|
||||
numbers: false,
|
||||
strings: false,
|
||||
};
|
||||
|
||||
get flags(): HighlightingOptions {
|
||||
return this.hlOptions;
|
||||
}
|
||||
|
||||
get primaryKeywords(): string[] {
|
||||
return this.keywords1;
|
||||
}
|
||||
|
||||
get secondaryKeywords(): string[] {
|
||||
return this.keywords2;
|
||||
}
|
||||
|
||||
public hasMultilineComments(): boolean {
|
||||
return this.multiLineCommentStart.isSome() &&
|
||||
this.multiLineCommentEnd.isSome();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// FileType implementations
|
||||
// ----------------------------------------------------------------------------
|
||||
const defaultHighlightOptions: HighlightingOptions = {
|
||||
numbers: true,
|
||||
strings: true,
|
||||
};
|
||||
|
||||
class JavaScriptFile extends AbstractFileType {
|
||||
public readonly name: FileLang = FileLang.JavaScript;
|
||||
public readonly singleLineComment = Some('//');
|
||||
public readonly multiLineCommentStart: Option<string> = Some('/*');
|
||||
public readonly multiLineCommentEnd: Option<string> = Some('*/');
|
||||
public readonly keywords1 = [
|
||||
'=>',
|
||||
'await',
|
||||
'break',
|
||||
'case',
|
||||
'catch',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'default',
|
||||
'delete',
|
||||
'do',
|
||||
'else',
|
||||
'export',
|
||||
'extends',
|
||||
'false',
|
||||
'finally',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'import',
|
||||
'in',
|
||||
'instanceof',
|
||||
'let',
|
||||
'new',
|
||||
'null',
|
||||
'return',
|
||||
'static',
|
||||
'super',
|
||||
'switch',
|
||||
'this',
|
||||
'throw',
|
||||
'true',
|
||||
'try',
|
||||
'typeof',
|
||||
'var',
|
||||
'void',
|
||||
'while',
|
||||
'with',
|
||||
'yield',
|
||||
];
|
||||
public readonly keywords2 = [
|
||||
'arguments',
|
||||
'as',
|
||||
'async',
|
||||
'BigInt',
|
||||
'Boolean',
|
||||
'eval',
|
||||
'from',
|
||||
'get',
|
||||
'JSON',
|
||||
'Math',
|
||||
'Number',
|
||||
'Object',
|
||||
'of',
|
||||
'set',
|
||||
'String',
|
||||
'Symbol',
|
||||
'undefined',
|
||||
];
|
||||
public readonly operators = [
|
||||
'>>>=',
|
||||
'**=',
|
||||
'<<=',
|
||||
'>>=',
|
||||
'&&=',
|
||||
'||=',
|
||||
'??=',
|
||||
'===',
|
||||
'!==',
|
||||
'>>>',
|
||||
'+=',
|
||||
'-=',
|
||||
'*=',
|
||||
'/=',
|
||||
'%=',
|
||||
'&=',
|
||||
'^=',
|
||||
'|=',
|
||||
'==',
|
||||
'!=',
|
||||
'>=',
|
||||
'<=',
|
||||
'++',
|
||||
'--',
|
||||
'**',
|
||||
'<<',
|
||||
'>>',
|
||||
'&&',
|
||||
'||',
|
||||
'??',
|
||||
'?.',
|
||||
'?',
|
||||
':',
|
||||
'=',
|
||||
'>',
|
||||
'<',
|
||||
'%',
|
||||
'-',
|
||||
'+',
|
||||
'&',
|
||||
'|',
|
||||
'^',
|
||||
'~',
|
||||
'!',
|
||||
];
|
||||
public readonly hlOptions: HighlightingOptions = {
|
||||
...defaultHighlightOptions,
|
||||
};
|
||||
}
|
||||
|
||||
class TypeScriptFile extends JavaScriptFile {
|
||||
public readonly name: FileLang = FileLang.TypeScript;
|
||||
public readonly multiLineCommentStart: Option<string> = Some('/*');
|
||||
public readonly multiLineCommentEnd: Option<string> = Some('*/');
|
||||
public readonly keywords2 = [
|
||||
...super.secondaryKeywords,
|
||||
// Typescript-specific
|
||||
'any',
|
||||
'bigint',
|
||||
'boolean',
|
||||
'enum',
|
||||
'interface',
|
||||
'keyof',
|
||||
'number',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'string',
|
||||
'type',
|
||||
'unknown',
|
||||
];
|
||||
}
|
||||
|
||||
class ShellFile extends AbstractFileType {
|
||||
public readonly name: FileLang = FileLang.Shell;
|
||||
public readonly singleLineComment = Some('#');
|
||||
public readonly keywords1 = [
|
||||
'case',
|
||||
'do',
|
||||
'done',
|
||||
'elif',
|
||||
'else',
|
||||
'esac',
|
||||
'fi',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'in',
|
||||
'select',
|
||||
'then',
|
||||
'time',
|
||||
'until',
|
||||
'while',
|
||||
'declare',
|
||||
];
|
||||
public readonly keywords2 = ['set'];
|
||||
public readonly operators = ['[[', ']]'];
|
||||
public readonly hlOptions: HighlightingOptions = {
|
||||
...defaultHighlightOptions,
|
||||
numbers: false,
|
||||
};
|
||||
}
|
||||
|
||||
class CSSFile extends AbstractFileType {
|
||||
public readonly name: FileLang = FileLang.CSS;
|
||||
public readonly singleLineComment = None;
|
||||
public readonly multiLineCommentStart: Option<string> = Some('/*');
|
||||
public readonly multiLineCommentEnd: Option<string> = Some('*/');
|
||||
public readonly hlOptions: HighlightingOptions = {
|
||||
...defaultHighlightOptions,
|
||||
};
|
||||
}
|
||||
import { AbstractFileType } from './base.ts';
|
||||
import { CSSFile } from './css.ts';
|
||||
import { JavaScriptFile, TypeScriptFile } from './javascript.ts';
|
||||
import { ShellFile } from './shell.ts';
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// External interface
|
||||
|
149
src/common/filetype/javascript.ts
Normal file
149
src/common/filetype/javascript.ts
Normal file
@ -0,0 +1,149 @@
|
||||
import Option, { Some } from '../option.ts';
|
||||
import {
|
||||
AbstractFileType,
|
||||
defaultHighlightOptions,
|
||||
FileLang,
|
||||
HighlightingOptions,
|
||||
} from './base.ts';
|
||||
|
||||
export class JavaScriptFile extends AbstractFileType {
|
||||
public readonly name: FileLang = FileLang.JavaScript;
|
||||
public readonly singleLineComment = Some('//');
|
||||
public readonly multiLineCommentStart: Option<string> = Some('/*');
|
||||
public readonly multiLineCommentEnd: Option<string> = Some('*/');
|
||||
public readonly keywords1 = [
|
||||
'=>',
|
||||
'await',
|
||||
'break',
|
||||
'case',
|
||||
'catch',
|
||||
'class',
|
||||
'const',
|
||||
'continue',
|
||||
'debugger',
|
||||
'default',
|
||||
'delete',
|
||||
'do',
|
||||
'else',
|
||||
'export',
|
||||
'extends',
|
||||
'false',
|
||||
'finally',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'import',
|
||||
'in',
|
||||
'instanceof',
|
||||
'let',
|
||||
'new',
|
||||
'null',
|
||||
'return',
|
||||
'static',
|
||||
'super',
|
||||
'switch',
|
||||
'this',
|
||||
'throw',
|
||||
'true',
|
||||
'try',
|
||||
'typeof',
|
||||
'var',
|
||||
'void',
|
||||
'while',
|
||||
'with',
|
||||
'yield',
|
||||
];
|
||||
public readonly keywords2 = [
|
||||
'arguments',
|
||||
'as',
|
||||
'async',
|
||||
'BigInt',
|
||||
'Boolean',
|
||||
'eval',
|
||||
'from',
|
||||
'get',
|
||||
'JSON',
|
||||
'Math',
|
||||
'Number',
|
||||
'Object',
|
||||
'of',
|
||||
'set',
|
||||
'String',
|
||||
'Symbol',
|
||||
'undefined',
|
||||
];
|
||||
public readonly operators = [
|
||||
'>>>=',
|
||||
'**=',
|
||||
'<<=',
|
||||
'>>=',
|
||||
'&&=',
|
||||
'||=',
|
||||
'??=',
|
||||
'===',
|
||||
'!==',
|
||||
'>>>',
|
||||
'+=',
|
||||
'-=',
|
||||
'*=',
|
||||
'/=',
|
||||
'%=',
|
||||
'&=',
|
||||
'^=',
|
||||
'|=',
|
||||
'==',
|
||||
'!=',
|
||||
'>=',
|
||||
'<=',
|
||||
'++',
|
||||
'--',
|
||||
'**',
|
||||
'<<',
|
||||
'>>',
|
||||
'&&',
|
||||
'||',
|
||||
'??',
|
||||
'?.',
|
||||
'?',
|
||||
':',
|
||||
'=',
|
||||
'>',
|
||||
'<',
|
||||
'%',
|
||||
'-',
|
||||
'+',
|
||||
'&',
|
||||
'|',
|
||||
'^',
|
||||
'~',
|
||||
'!',
|
||||
];
|
||||
public readonly hlOptions: HighlightingOptions = {
|
||||
...defaultHighlightOptions,
|
||||
octalNumbers: true,
|
||||
hexNumbers: true,
|
||||
binNumbers: true,
|
||||
jsBigInt: true,
|
||||
};
|
||||
}
|
||||
|
||||
export class TypeScriptFile extends JavaScriptFile {
|
||||
public readonly name: FileLang = FileLang.TypeScript;
|
||||
public readonly keywords2 = [
|
||||
...super.secondaryKeywords,
|
||||
// Typescript-specific
|
||||
'any',
|
||||
'bigint',
|
||||
'boolean',
|
||||
'enum',
|
||||
'interface',
|
||||
'keyof',
|
||||
'number',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'string',
|
||||
'type',
|
||||
'unknown',
|
||||
];
|
||||
}
|
@ -1 +1,2 @@
|
||||
export * from './base.ts';
|
||||
export * from './filetype.ts';
|
||||
|
37
src/common/filetype/shell.ts
Normal file
37
src/common/filetype/shell.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { Some } from '../option.ts';
|
||||
import {
|
||||
AbstractFileType,
|
||||
defaultHighlightOptions,
|
||||
FileLang,
|
||||
HighlightingOptions,
|
||||
} from './base.ts';
|
||||
|
||||
export class ShellFile extends AbstractFileType {
|
||||
public readonly name: FileLang = FileLang.Shell;
|
||||
public readonly singleLineComment = Some('#');
|
||||
public readonly keywords1 = [
|
||||
'case',
|
||||
'do',
|
||||
'done',
|
||||
'elif',
|
||||
'else',
|
||||
'esac',
|
||||
'fi',
|
||||
'for',
|
||||
'function',
|
||||
'if',
|
||||
'in',
|
||||
'select',
|
||||
'then',
|
||||
'time',
|
||||
'until',
|
||||
'while',
|
||||
'declare',
|
||||
];
|
||||
public readonly keywords2 = ['set'];
|
||||
public readonly operators = ['[[', ']]'];
|
||||
public readonly hlOptions: HighlightingOptions = {
|
||||
...defaultHighlightOptions,
|
||||
numbers: false,
|
||||
};
|
||||
}
|
@ -23,10 +23,8 @@ export async function main() {
|
||||
logError(JSON.stringify(error, null, 2));
|
||||
});
|
||||
|
||||
const terminalSize = await term.getTerminalSize();
|
||||
|
||||
// Create the editor itself
|
||||
const editor = new Editor(terminalSize);
|
||||
const editor = Editor.create(await term.getTerminalSize());
|
||||
|
||||
// Process cli arguments
|
||||
if (term.argv.length > 0) {
|
||||
|
@ -46,22 +46,37 @@ export class Row {
|
||||
this.rchars = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of 'characters' in this row
|
||||
*/
|
||||
public get size(): number {
|
||||
return this.chars.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of 'characters' in the 'render' array
|
||||
*/
|
||||
public get rsize(): number {
|
||||
return this.rchars.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 'render' string
|
||||
*/
|
||||
public rstring(offset: number = 0): string {
|
||||
return this.rchars.slice(offset).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new empty Row
|
||||
*/
|
||||
public static default(): Row {
|
||||
return new Row();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Row
|
||||
*/
|
||||
public static from(s: string | string[] | Row): Row {
|
||||
if (s instanceof Row) {
|
||||
return s;
|
||||
@ -70,11 +85,17 @@ export class Row {
|
||||
return new Row(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a character to the end of the current row
|
||||
*/
|
||||
public append(s: string, syntax: FileType): void {
|
||||
this.chars = this.chars.concat(strChars(s));
|
||||
this.update(None, syntax);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a character to the current row at the specified location
|
||||
*/
|
||||
public insertChar(at: number, c: string): void {
|
||||
const newSlice = strChars(c);
|
||||
if (at >= this.size) {
|
||||
@ -141,6 +162,10 @@ export class Row {
|
||||
return Some(this.cxToRx(this.byteIndexToCharIndex(byteIndex)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the current Row for the given string, returning the index in
|
||||
* the 'render' version
|
||||
*/
|
||||
public rIndexOf(s: string, offset: number = 0): Option<number> {
|
||||
const rstring = this.rchars.join('');
|
||||
const byteIndex = rstring.indexOf(s, this.charIndexToByteIndex(offset));
|
||||
@ -223,10 +248,17 @@ export class Row {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the contents of the row
|
||||
*/
|
||||
public toString(): string {
|
||||
return this.chars.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup up the row by converting tabs to spaces for rendering,
|
||||
* then setup syntax highlighting
|
||||
*/
|
||||
public update(
|
||||
word: Option<string>,
|
||||
syntax: FileType,
|
||||
@ -241,17 +273,24 @@ export class Row {
|
||||
return this.highlight(word, syntax, startWithComment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the syntax types of the current Row
|
||||
*/
|
||||
public highlight(
|
||||
word: Option<string>,
|
||||
syntax: FileType,
|
||||
startWithComment: boolean,
|
||||
): boolean {
|
||||
// When the highlighting is already up-to-date
|
||||
if (this.isHighlighted && word.isNone()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.hl = [];
|
||||
let i = 0;
|
||||
|
||||
// Handle the case where we are in a multi-line
|
||||
// comment from a previous row
|
||||
let inMlComment = startWithComment;
|
||||
if (inMlComment && syntax.hasMultilineComments()) {
|
||||
const maybeEnd = this.rIndexOf(syntax.multiLineCommentEnd.unwrap(), i);
|
||||
@ -266,8 +305,7 @@ export class Row {
|
||||
}
|
||||
|
||||
for (; i < this.rsize;) {
|
||||
const ch = this.rchars[i];
|
||||
const maybeMultiline = this.highlightMultilineComment(i, syntax, ch);
|
||||
const maybeMultiline = this.highlightMultilineComment(i, syntax);
|
||||
if (maybeMultiline.isSome()) {
|
||||
inMlComment = true;
|
||||
i = maybeMultiline.unwrap();
|
||||
@ -276,11 +314,14 @@ export class Row {
|
||||
|
||||
inMlComment = false;
|
||||
|
||||
const maybeNext = this.highlightComment(i, syntax, ch)
|
||||
// Go through the syntax highlighting types in order:
|
||||
// If there is a match, we end the chain of syntax types
|
||||
// and 'consume' the number of characters that matched
|
||||
const maybeNext = this.highlightComment(i, syntax)
|
||||
.orElse(() => this.highlightPrimaryKeywords(i, syntax))
|
||||
.orElse(() => this.highlightSecondaryKeywords(i, syntax))
|
||||
.orElse(() => this.highlightString(i, syntax, ch))
|
||||
.orElse(() => this.highlightNumber(i, syntax, ch))
|
||||
.orElse(() => this.highlightString(i, syntax))
|
||||
.orElse(() => this.highlightNumber(i, syntax))
|
||||
.orElse(() => this.highlightOperators(i, syntax));
|
||||
|
||||
if (maybeNext.isSome()) {
|
||||
@ -346,7 +387,6 @@ export class Row {
|
||||
protected highlightComment(
|
||||
i: number,
|
||||
syntax: FileType,
|
||||
_ch: string,
|
||||
): Option<number> {
|
||||
// Highlight single-line comments
|
||||
if (syntax.singleLineComment.isSome()) {
|
||||
@ -365,7 +405,7 @@ export class Row {
|
||||
return None;
|
||||
}
|
||||
|
||||
protected highlightStr(
|
||||
private highlightStr(
|
||||
i: number,
|
||||
substring: string,
|
||||
hl_type: HighlightType,
|
||||
@ -390,7 +430,7 @@ export class Row {
|
||||
return Some(i);
|
||||
}
|
||||
|
||||
protected highlightKeywords(
|
||||
private highlightKeywords(
|
||||
i: number,
|
||||
keywords: string[],
|
||||
hl_type: HighlightType,
|
||||
@ -473,9 +513,9 @@ export class Row {
|
||||
protected highlightString(
|
||||
i: number,
|
||||
syntax: FileType,
|
||||
ch: string,
|
||||
): Option<number> {
|
||||
// Highlight strings
|
||||
const ch = this.rchars[i];
|
||||
if (syntax.flags.strings && ch === '"' || ch === "'") {
|
||||
while (true) {
|
||||
this.hl.push(HighlightType.String);
|
||||
@ -500,12 +540,13 @@ export class Row {
|
||||
protected highlightMultilineComment(
|
||||
i: number,
|
||||
syntax: FileType,
|
||||
ch: string,
|
||||
): Option<number> {
|
||||
if (!syntax.hasMultilineComments()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
const ch = this.rchars[i];
|
||||
|
||||
const startChars = syntax.multiLineCommentStart.unwrap();
|
||||
const endChars = syntax.multiLineCommentEnd.unwrap();
|
||||
if (ch === startChars[0] && this.rchars[i + 1] == startChars[1]) {
|
||||
@ -526,50 +567,65 @@ export class Row {
|
||||
protected highlightNumber(
|
||||
i: number,
|
||||
syntax: FileType,
|
||||
ch: string,
|
||||
): Option<number> {
|
||||
// Highlight numbers
|
||||
if (syntax.flags.numbers && isAsciiDigit(ch)) {
|
||||
if (i > 0 && !isSeparator(this.rchars[i - 1])) {
|
||||
return None;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
this.hl.push(HighlightType.Number);
|
||||
i += 1;
|
||||
if (i >= this.rsize) {
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return Some(i);
|
||||
// Exit early
|
||||
const ch = this.rchars[i];
|
||||
if (!(syntax.flags.numbers && isAsciiDigit(ch))) {
|
||||
return None;
|
||||
}
|
||||
|
||||
return None;
|
||||
// Configure which characters are valid
|
||||
// for numbers in the current FileType
|
||||
let validChars = ['.'];
|
||||
if (syntax.flags.binNumbers) {
|
||||
validChars = validChars.concat(['b', 'B']);
|
||||
}
|
||||
if (syntax.flags.octalNumbers) {
|
||||
validChars = validChars.concat(['o', 'O']);
|
||||
}
|
||||
if (syntax.flags.hexNumbers) {
|
||||
// deno-fmt-ignore
|
||||
validChars = validChars.concat([
|
||||
'a','A',
|
||||
'b','B',
|
||||
'c','C',
|
||||
'd','D',
|
||||
'e','E',
|
||||
'f','F',
|
||||
'x','X',
|
||||
]);
|
||||
}
|
||||
if (syntax.flags.jsBigInt) {
|
||||
validChars.push('n');
|
||||
}
|
||||
|
||||
// Number literals are not attached to other syntax
|
||||
if (i > 0 && !isSeparator(this.rchars[i - 1])) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Match until the end of the number literal
|
||||
while (true) {
|
||||
this.hl.push(HighlightType.Number);
|
||||
i += 1;
|
||||
if (i >= this.rsize) {
|
||||
break;
|
||||
}
|
||||
|
||||
const nextChar = this.rchars[i];
|
||||
if (
|
||||
!(validChars.includes(nextChar) || isAsciiDigit(nextChar))
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Some(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a terminal-formatted version of the current row
|
||||
*/
|
||||
public render(offset: number, len: number): string {
|
||||
const end = Math.min(len, this.rsize);
|
||||
const start = Math.min(offset, len);
|
||||
|
Loading…
Reference in New Issue
Block a user