This commit is contained in:
parent
5b40d16999
commit
21d26ede6c
@ -63,6 +63,7 @@ function print16colorTable(): void {
|
|||||||
|
|
||||||
console.log(colorTable);
|
console.log(colorTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
function print256colorTable(): void {
|
function print256colorTable(): void {
|
||||||
let colorTable = '';
|
let colorTable = '';
|
||||||
// deno-fmt-ignore
|
// deno-fmt-ignore
|
||||||
|
@ -2,7 +2,7 @@ import Ansi, * as _Ansi from './ansi.ts';
|
|||||||
import Buffer from './buffer.ts';
|
import Buffer from './buffer.ts';
|
||||||
import Document from './document.ts';
|
import Document from './document.ts';
|
||||||
import Editor from './editor.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 { highlightToColor, HighlightType } from './highlight.ts';
|
||||||
import Option, { None, Some } from './option.ts';
|
import Option, { None, Some } from './option.ts';
|
||||||
import Position from './position.ts';
|
import Position from './position.ts';
|
||||||
@ -202,9 +202,19 @@ const readKeyTest = () => {
|
|||||||
|
|
||||||
const highlightToColorTest = {
|
const highlightToColorTest = {
|
||||||
'highlightToColor()': () => {
|
'highlightToColor()': () => {
|
||||||
assertTrue(highlightToColor(HighlightType.Number).length > 0);
|
[
|
||||||
assertTrue(highlightToColor(HighlightType.Match).length > 0);
|
HighlightType.Number,
|
||||||
assertTrue(highlightToColor(HighlightType.None).length > 0);
|
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 = {
|
const BufferTest = {
|
||||||
'new Buffer': () => {
|
'new Buffer': () => {
|
||||||
const b = new Buffer();
|
const b = Buffer.default();
|
||||||
assertInstanceOf(b, Buffer);
|
assertInstanceOf(b, Buffer);
|
||||||
assertEquals(b.strlen(), 0);
|
assertEquals(b.strlen(), 0);
|
||||||
},
|
},
|
||||||
'.appendLine': () => {
|
'.appendLine': () => {
|
||||||
const b = new Buffer();
|
const b = Buffer.default();
|
||||||
|
|
||||||
// Carriage return and line feed
|
// Carriage return and line feed
|
||||||
b.appendLine();
|
b.appendLine();
|
||||||
@ -261,7 +271,7 @@ const BufferTest = {
|
|||||||
assertEquals(b.strlen(), 5);
|
assertEquals(b.strlen(), 5);
|
||||||
},
|
},
|
||||||
'.append': () => {
|
'.append': () => {
|
||||||
const b = new Buffer();
|
const b = Buffer.default();
|
||||||
|
|
||||||
b.append('foobar');
|
b.append('foobar');
|
||||||
assertEquals(b.strlen(), 6);
|
assertEquals(b.strlen(), 6);
|
||||||
@ -271,7 +281,7 @@ const BufferTest = {
|
|||||||
assertEquals(b.strlen(), 3);
|
assertEquals(b.strlen(), 3);
|
||||||
},
|
},
|
||||||
'.flush': async () => {
|
'.flush': async () => {
|
||||||
const b = new Buffer();
|
const b = Buffer.default();
|
||||||
b.appendLine('foobarbaz' + Ansi.ClearLine);
|
b.appendLine('foobarbaz' + Ansi.ClearLine);
|
||||||
assertEquals(b.strlen(), 14);
|
assertEquals(b.strlen(), 14);
|
||||||
|
|
||||||
@ -297,6 +307,7 @@ const DocumentTest = {
|
|||||||
assertEquals(oldDoc.numRows, 1);
|
assertEquals(oldDoc.numRows, 1);
|
||||||
|
|
||||||
const doc = await oldDoc.open(THIS_FILE);
|
const doc = await oldDoc.open(THIS_FILE);
|
||||||
|
assertEquals(FileLang.TypeScript, doc.fileType);
|
||||||
assertFalse(doc.dirty);
|
assertFalse(doc.dirty);
|
||||||
assertFalse(doc.isEmpty());
|
assertFalse(doc.isEmpty());
|
||||||
assertTrue(doc.numRows > 1);
|
assertTrue(doc.numRows > 1);
|
||||||
@ -347,6 +358,16 @@ const DocumentTest = {
|
|||||||
const pos3 = query3.unwrap();
|
const pos3 = query3.unwrap();
|
||||||
assertEquivalent(pos3, Position.at(5, 328));
|
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': () => {
|
'.insert': () => {
|
||||||
const doc = Document.default();
|
const doc = Document.default();
|
||||||
assertFalse(doc.dirty);
|
assertFalse(doc.dirty);
|
||||||
@ -420,22 +441,22 @@ const DocumentTest = {
|
|||||||
|
|
||||||
const EditorTest = {
|
const EditorTest = {
|
||||||
'new Editor': () => {
|
'new Editor': () => {
|
||||||
const e = new Editor(defaultTerminalSize);
|
const e = Editor.create(defaultTerminalSize);
|
||||||
assertInstanceOf(e, Editor);
|
assertInstanceOf(e, Editor);
|
||||||
},
|
},
|
||||||
'.open': async () => {
|
'.open': async () => {
|
||||||
const e = new Editor(defaultTerminalSize);
|
const e = Editor.create(defaultTerminalSize);
|
||||||
await e.open(THIS_FILE);
|
await e.open(THIS_FILE);
|
||||||
assertInstanceOf(e, Editor);
|
assertInstanceOf(e, Editor);
|
||||||
},
|
},
|
||||||
'.processKeyPress - letters': async () => {
|
'.processKeyPress - letters': async () => {
|
||||||
const e = new Editor(defaultTerminalSize);
|
const e = Editor.create(defaultTerminalSize);
|
||||||
const res = await e.processKeyPress('a');
|
const res = await e.processKeyPress('a');
|
||||||
assertTrue(res);
|
assertTrue(res);
|
||||||
},
|
},
|
||||||
'.processKeyPress - ctrl-q': async () => {
|
'.processKeyPress - ctrl-q': async () => {
|
||||||
// Dirty file (Need to clear confirmation messages)
|
// Dirty file (Need to clear confirmation messages)
|
||||||
const e = new Editor(defaultTerminalSize);
|
const e = Editor.create(defaultTerminalSize);
|
||||||
await e.processKeyPress('d');
|
await e.processKeyPress('d');
|
||||||
assertTrue(await e.processKeyPress(Fn.ctrlKey('q')));
|
assertTrue(await e.processKeyPress(Fn.ctrlKey('q')));
|
||||||
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')));
|
assertFalse(await e.processKeyPress(Fn.ctrlKey('q')));
|
||||||
|
|
||||||
// Clean file
|
// Clean file
|
||||||
const e2 = new Editor(defaultTerminalSize);
|
const e2 = Editor.create(defaultTerminalSize);
|
||||||
const res = await e2.processKeyPress(Fn.ctrlKey('q'));
|
const res = await e2.processKeyPress(Fn.ctrlKey('q'));
|
||||||
assertFalse(res);
|
assertFalse(res);
|
||||||
},
|
},
|
||||||
|
@ -42,6 +42,7 @@ export enum AnsiColor {
|
|||||||
FgMagenta,
|
FgMagenta,
|
||||||
FgCyan,
|
FgCyan,
|
||||||
FgWhite,
|
FgWhite,
|
||||||
|
ForegroundColor,
|
||||||
FgDefault,
|
FgDefault,
|
||||||
|
|
||||||
// Background Colors
|
// Background Colors
|
||||||
@ -53,6 +54,7 @@ export enum AnsiColor {
|
|||||||
BgMagenta,
|
BgMagenta,
|
||||||
BgCyan,
|
BgCyan,
|
||||||
BgWhite,
|
BgWhite,
|
||||||
|
BackgroundColor,
|
||||||
BgDefault,
|
BgDefault,
|
||||||
|
|
||||||
// Bright Foreground Colors
|
// Bright Foreground Colors
|
||||||
@ -77,8 +79,8 @@ export enum AnsiColor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum Ground {
|
export enum Ground {
|
||||||
Fore = AnsiColor.FgDefault,
|
Fore = AnsiColor.ForegroundColor,
|
||||||
Back = AnsiColor.BgDefault,
|
Back = AnsiColor.BackgroundColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -4,7 +4,11 @@ import { getRuntime } from './runtime/mod.ts';
|
|||||||
class Buffer {
|
class Buffer {
|
||||||
#b = '';
|
#b = '';
|
||||||
|
|
||||||
constructor() {
|
private constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static default(): Buffer {
|
||||||
|
return new Buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public append(s: string, maxLen?: number): void {
|
public append(s: string, maxLen?: number): void {
|
||||||
|
@ -102,15 +102,6 @@ export class Document {
|
|||||||
const position = Position.from(at);
|
const position = Position.from(at);
|
||||||
|
|
||||||
for (let y = at.y; y >= 0 && y < this.numRows; y += direction) {
|
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);
|
const maybeMatch = this.#rows[y].find(q, position.x, direction);
|
||||||
if (maybeMatch.isSome()) {
|
if (maybeMatch.isSome()) {
|
||||||
position.x = this.#rows[y].rxToCx(maybeMatch.unwrap());
|
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 { getRuntime, logDebug, logWarning } from './runtime/mod.ts';
|
||||||
import { ITerminalSize, Position, SearchDirection } from './types.ts';
|
import { ITerminalSize, Position, SearchDirection } from './types.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main Editor interface
|
||||||
|
*/
|
||||||
export default class Editor {
|
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;
|
private constructor(
|
||||||
/**
|
protected screen: ITerminalSize,
|
||||||
* The output buffer for the terminal
|
protected document: Document = Document.default(),
|
||||||
*/
|
protected buffer: Buffer = Buffer.default(),
|
||||||
protected buffer: Buffer;
|
protected cursor: Position = Position.default(),
|
||||||
/**
|
protected offset: Position = Position.default(),
|
||||||
* The size of the screen in rows/columns
|
protected renderX: number = 0,
|
||||||
*/
|
protected filename: string = '',
|
||||||
protected screen: ITerminalSize;
|
protected statusMessage: string = '',
|
||||||
/**
|
protected statusTimeout: number = 0,
|
||||||
* The current location of the mouse cursor
|
protected quitTimes: number = SCROLL_QUIT_TIMES,
|
||||||
*/
|
protected highlightedWord: Option<string> = None,
|
||||||
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();
|
|
||||||
|
|
||||||
// Subtract two rows from the terminal size
|
// Subtract two rows from the terminal size
|
||||||
// for displaying the status bar
|
// for displaying the status bar
|
||||||
// and message bar
|
// and message bar
|
||||||
this.screen = terminalSize;
|
|
||||||
this.screen.rows -= 2;
|
this.screen.rows -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
this.cursor = Position.default();
|
public static create(terminalSize: ITerminalSize) {
|
||||||
this.offset = Position.default();
|
return new Editor(terminalSize);
|
||||||
this.document = Document.default();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected get numRows(): number {
|
protected get numRows(): number {
|
||||||
@ -82,10 +65,6 @@ export default class Editor {
|
|||||||
return this.document.row(at);
|
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> {
|
||||||
await this.document.open(filename);
|
await this.document.open(filename);
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
@ -400,9 +379,12 @@ export default class Editor {
|
|||||||
this.cursor = Position.at(x, y);
|
this.cursor = Position.at(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the window of a file to display
|
||||||
|
*/
|
||||||
protected scroll(): void {
|
protected scroll(): void {
|
||||||
this.renderX = (this.row(this.cursor.y).isSome())
|
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;
|
: 0;
|
||||||
|
|
||||||
const { y } = this.cursor;
|
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 { node_path as path } from '../runtime/mod.ts';
|
||||||
import Option, { None, Some } from '../option.ts';
|
import { AbstractFileType } from './base.ts';
|
||||||
|
import { CSSFile } from './css.ts';
|
||||||
// ----------------------------------------------------------------------------
|
import { JavaScriptFile, TypeScriptFile } from './javascript.ts';
|
||||||
// File-related types
|
import { ShellFile } from './shell.ts';
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// External interface
|
// 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';
|
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));
|
logError(JSON.stringify(error, null, 2));
|
||||||
});
|
});
|
||||||
|
|
||||||
const terminalSize = await term.getTerminalSize();
|
|
||||||
|
|
||||||
// Create the editor itself
|
// Create the editor itself
|
||||||
const editor = new Editor(terminalSize);
|
const editor = Editor.create(await term.getTerminalSize());
|
||||||
|
|
||||||
// Process cli arguments
|
// Process cli arguments
|
||||||
if (term.argv.length > 0) {
|
if (term.argv.length > 0) {
|
||||||
|
@ -46,22 +46,37 @@ export class Row {
|
|||||||
this.rchars = [];
|
this.rchars = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of 'characters' in this row
|
||||||
|
*/
|
||||||
public get size(): number {
|
public get size(): number {
|
||||||
return this.chars.length;
|
return this.chars.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of 'characters' in the 'render' array
|
||||||
|
*/
|
||||||
public get rsize(): number {
|
public get rsize(): number {
|
||||||
return this.rchars.length;
|
return this.rchars.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the 'render' string
|
||||||
|
*/
|
||||||
public rstring(offset: number = 0): string {
|
public rstring(offset: number = 0): string {
|
||||||
return this.rchars.slice(offset).join('');
|
return this.rchars.slice(offset).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new empty Row
|
||||||
|
*/
|
||||||
public static default(): Row {
|
public static default(): Row {
|
||||||
return new Row();
|
return new Row();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Row
|
||||||
|
*/
|
||||||
public static from(s: string | string[] | Row): Row {
|
public static from(s: string | string[] | Row): Row {
|
||||||
if (s instanceof Row) {
|
if (s instanceof Row) {
|
||||||
return s;
|
return s;
|
||||||
@ -70,11 +85,17 @@ export class Row {
|
|||||||
return new Row(s);
|
return new Row(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a character to the end of the current row
|
||||||
|
*/
|
||||||
public append(s: string, syntax: FileType): void {
|
public append(s: string, syntax: FileType): void {
|
||||||
this.chars = this.chars.concat(strChars(s));
|
this.chars = this.chars.concat(strChars(s));
|
||||||
this.update(None, syntax);
|
this.update(None, syntax);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a character to the current row at the specified location
|
||||||
|
*/
|
||||||
public insertChar(at: number, c: string): void {
|
public insertChar(at: number, c: string): void {
|
||||||
const newSlice = strChars(c);
|
const newSlice = strChars(c);
|
||||||
if (at >= this.size) {
|
if (at >= this.size) {
|
||||||
@ -141,6 +162,10 @@ export class Row {
|
|||||||
return Some(this.cxToRx(this.byteIndexToCharIndex(byteIndex)));
|
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> {
|
public rIndexOf(s: string, offset: number = 0): Option<number> {
|
||||||
const rstring = this.rchars.join('');
|
const rstring = this.rchars.join('');
|
||||||
const byteIndex = rstring.indexOf(s, this.charIndexToByteIndex(offset));
|
const byteIndex = rstring.indexOf(s, this.charIndexToByteIndex(offset));
|
||||||
@ -223,10 +248,17 @@ export class Row {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output the contents of the row
|
||||||
|
*/
|
||||||
public toString(): string {
|
public toString(): string {
|
||||||
return this.chars.join('');
|
return this.chars.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup up the row by converting tabs to spaces for rendering,
|
||||||
|
* then setup syntax highlighting
|
||||||
|
*/
|
||||||
public update(
|
public update(
|
||||||
word: Option<string>,
|
word: Option<string>,
|
||||||
syntax: FileType,
|
syntax: FileType,
|
||||||
@ -241,17 +273,24 @@ export class Row {
|
|||||||
return this.highlight(word, syntax, startWithComment);
|
return this.highlight(word, syntax, startWithComment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the syntax types of the current Row
|
||||||
|
*/
|
||||||
public highlight(
|
public highlight(
|
||||||
word: Option<string>,
|
word: Option<string>,
|
||||||
syntax: FileType,
|
syntax: FileType,
|
||||||
startWithComment: boolean,
|
startWithComment: boolean,
|
||||||
): boolean {
|
): boolean {
|
||||||
|
// When the highlighting is already up-to-date
|
||||||
if (this.isHighlighted && word.isNone()) {
|
if (this.isHighlighted && word.isNone()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hl = [];
|
this.hl = [];
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
|
// Handle the case where we are in a multi-line
|
||||||
|
// comment from a previous row
|
||||||
let inMlComment = startWithComment;
|
let inMlComment = startWithComment;
|
||||||
if (inMlComment && syntax.hasMultilineComments()) {
|
if (inMlComment && syntax.hasMultilineComments()) {
|
||||||
const maybeEnd = this.rIndexOf(syntax.multiLineCommentEnd.unwrap(), i);
|
const maybeEnd = this.rIndexOf(syntax.multiLineCommentEnd.unwrap(), i);
|
||||||
@ -266,8 +305,7 @@ export class Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (; i < this.rsize;) {
|
for (; i < this.rsize;) {
|
||||||
const ch = this.rchars[i];
|
const maybeMultiline = this.highlightMultilineComment(i, syntax);
|
||||||
const maybeMultiline = this.highlightMultilineComment(i, syntax, ch);
|
|
||||||
if (maybeMultiline.isSome()) {
|
if (maybeMultiline.isSome()) {
|
||||||
inMlComment = true;
|
inMlComment = true;
|
||||||
i = maybeMultiline.unwrap();
|
i = maybeMultiline.unwrap();
|
||||||
@ -276,11 +314,14 @@ export class Row {
|
|||||||
|
|
||||||
inMlComment = false;
|
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.highlightPrimaryKeywords(i, syntax))
|
||||||
.orElse(() => this.highlightSecondaryKeywords(i, syntax))
|
.orElse(() => this.highlightSecondaryKeywords(i, syntax))
|
||||||
.orElse(() => this.highlightString(i, syntax, ch))
|
.orElse(() => this.highlightString(i, syntax))
|
||||||
.orElse(() => this.highlightNumber(i, syntax, ch))
|
.orElse(() => this.highlightNumber(i, syntax))
|
||||||
.orElse(() => this.highlightOperators(i, syntax));
|
.orElse(() => this.highlightOperators(i, syntax));
|
||||||
|
|
||||||
if (maybeNext.isSome()) {
|
if (maybeNext.isSome()) {
|
||||||
@ -346,7 +387,6 @@ export class Row {
|
|||||||
protected highlightComment(
|
protected highlightComment(
|
||||||
i: number,
|
i: number,
|
||||||
syntax: FileType,
|
syntax: FileType,
|
||||||
_ch: string,
|
|
||||||
): Option<number> {
|
): Option<number> {
|
||||||
// Highlight single-line comments
|
// Highlight single-line comments
|
||||||
if (syntax.singleLineComment.isSome()) {
|
if (syntax.singleLineComment.isSome()) {
|
||||||
@ -365,7 +405,7 @@ export class Row {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected highlightStr(
|
private highlightStr(
|
||||||
i: number,
|
i: number,
|
||||||
substring: string,
|
substring: string,
|
||||||
hl_type: HighlightType,
|
hl_type: HighlightType,
|
||||||
@ -390,7 +430,7 @@ export class Row {
|
|||||||
return Some(i);
|
return Some(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected highlightKeywords(
|
private highlightKeywords(
|
||||||
i: number,
|
i: number,
|
||||||
keywords: string[],
|
keywords: string[],
|
||||||
hl_type: HighlightType,
|
hl_type: HighlightType,
|
||||||
@ -473,9 +513,9 @@ export class Row {
|
|||||||
protected highlightString(
|
protected highlightString(
|
||||||
i: number,
|
i: number,
|
||||||
syntax: FileType,
|
syntax: FileType,
|
||||||
ch: string,
|
|
||||||
): Option<number> {
|
): Option<number> {
|
||||||
// Highlight strings
|
// Highlight strings
|
||||||
|
const ch = this.rchars[i];
|
||||||
if (syntax.flags.strings && ch === '"' || ch === "'") {
|
if (syntax.flags.strings && ch === '"' || ch === "'") {
|
||||||
while (true) {
|
while (true) {
|
||||||
this.hl.push(HighlightType.String);
|
this.hl.push(HighlightType.String);
|
||||||
@ -500,12 +540,13 @@ export class Row {
|
|||||||
protected highlightMultilineComment(
|
protected highlightMultilineComment(
|
||||||
i: number,
|
i: number,
|
||||||
syntax: FileType,
|
syntax: FileType,
|
||||||
ch: string,
|
|
||||||
): Option<number> {
|
): Option<number> {
|
||||||
if (!syntax.hasMultilineComments()) {
|
if (!syntax.hasMultilineComments()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ch = this.rchars[i];
|
||||||
|
|
||||||
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]) {
|
||||||
@ -526,50 +567,65 @@ export class Row {
|
|||||||
protected highlightNumber(
|
protected highlightNumber(
|
||||||
i: number,
|
i: number,
|
||||||
syntax: FileType,
|
syntax: FileType,
|
||||||
ch: string,
|
|
||||||
): Option<number> {
|
): Option<number> {
|
||||||
// Highlight numbers
|
// Exit early
|
||||||
if (syntax.flags.numbers && isAsciiDigit(ch)) {
|
const ch = this.rchars[i];
|
||||||
if (i > 0 && !isSeparator(this.rchars[i - 1])) {
|
if (!(syntax.flags.numbers && isAsciiDigit(ch))) {
|
||||||
return None;
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
public render(offset: number, len: number): string {
|
||||||
const end = Math.min(len, this.rsize);
|
const end = Math.min(len, this.rsize);
|
||||||
const start = Math.min(offset, len);
|
const start = Math.min(offset, len);
|
||||||
|
Loading…
Reference in New Issue
Block a user