Highlight keywords
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good

This commit is contained in:
Timothy Warren 2024-07-17 10:32:00 -04:00
parent 359e739fe8
commit e84dfa9ba9
3 changed files with 219 additions and 8 deletions

View File

@ -20,8 +20,12 @@ export interface HighlightingOptions {
interface IFileType {
readonly name: FileLang;
readonly singleLineComment: Option<string>;
readonly keywords1: string[];
readonly keywords2: string[];
readonly hlOptions: HighlightingOptions;
get flags(): HighlightingOptions;
get primaryKeywords(): string[];
get secondaryKeywords(): string[];
}
/**
@ -30,6 +34,8 @@ interface IFileType {
export abstract class AbstractFileType implements IFileType {
public readonly name: FileLang = FileLang.Plain;
public readonly singleLineComment = None;
public readonly keywords1: string[] = [];
public readonly keywords2: string[] = [];
public readonly hlOptions: HighlightingOptions = {
numbers: false,
strings: false,
@ -38,6 +44,14 @@ export abstract class AbstractFileType implements IFileType {
get flags(): HighlightingOptions {
return this.hlOptions;
}
get primaryKeywords(): string[] {
return this.keywords1;
}
get secondaryKeywords(): string[] {
return this.keywords2;
}
}
// ----------------------------------------------------------------------------
@ -48,20 +62,131 @@ const defaultHighlightOptions: HighlightingOptions = {
strings: true,
};
class TypeScriptFile extends AbstractFileType {
public readonly name: FileLang = FileLang.TypeScript;
class JavaScriptFile extends AbstractFileType {
public readonly name: FileLang = FileLang.JavaScript;
public readonly singleLineComment = Some('//');
public readonly keywords1 = [
'break',
'case',
'catch',
'class',
'const',
'continue',
'debugger',
'default',
'delete',
'do',
'else',
'export',
'extends',
'false',
'finally',
'for',
'function',
'if',
'import',
'in',
'instanceof',
'new',
'null',
'return',
'super',
'switch',
'this',
'throw',
'true',
'try',
'typeof',
'var',
'void',
'while',
'with',
'let',
'static',
'yield',
'await',
];
public readonly keywords2 = [
'arguments',
'as',
'async',
'eval',
'from',
'get',
'of',
'set',
];
public readonly hlOptions: HighlightingOptions = {
...defaultHighlightOptions,
};
}
class JavaScriptFile extends AbstractFileType {
public readonly name: FileLang = FileLang.JavaScript;
public readonly singleLineComment = Some('//');
public readonly hlOptions: HighlightingOptions = {
...defaultHighlightOptions,
};
class TypeScriptFile extends JavaScriptFile {
public readonly name: FileLang = FileLang.TypeScript;
public readonly keywords1 = [
'break',
'case',
'catch',
'class',
'const',
'continue',
'debugger',
'default',
'delete',
'do',
'else',
'export',
'extends',
'false',
'finally',
'for',
'function',
'if',
'import',
'in',
'instanceof',
'new',
'null',
'return',
'super',
'switch',
'this',
'throw',
'true',
'try',
'typeof',
'var',
'void',
'while',
'with',
'let',
'static',
'yield',
'await',
];
public readonly keywords2 = [
'arguments',
'as',
'async',
'eval',
'from',
'get',
'of',
'set',
// Typescript-specific
'keyof',
'interface',
'enum',
'public',
'protected',
'private',
'string',
'number',
'boolean',
'any',
'unknown',
'type',
];
}
class CSSFile extends AbstractFileType {

View File

@ -6,6 +6,8 @@ export enum HighlightType {
Match,
String,
SingleLineComment,
Keyword1,
Keyword2,
}
export function highlightToColor(type: HighlightType): string {
@ -22,6 +24,12 @@ export function highlightToColor(type: HighlightType): string {
case HighlightType.SingleLineComment:
return Ansi.color256(45);
case HighlightType.Keyword1:
return Ansi.color256(226);
case HighlightType.Keyword2:
return Ansi.color256(118);
default:
return Ansi.ResetFormatting;
}

View File

@ -232,6 +232,8 @@ export class Row {
const ch = this.rchars[i];
const maybeNext = this.highlightComment(i, syntax, ch)
.orElse(() => this.highlightPrimaryKeywords(i, syntax))
.orElse(() => this.highlightSecondaryKeywords(i, syntax))
.orElse(() => this.highlightString(i, syntax, ch))
.orElse(() => this.highlightNumber(i, syntax, ch));
@ -305,6 +307,82 @@ export class Row {
return None;
}
protected highlightStr(
i: number,
substring: string,
hl_type: HighlightType,
): Option<number> {
if (strlen(substring) === 0) {
return None;
}
const substringChars = strChars(substring);
for (const [j, ch] of substringChars.entries()) {
const nextChar = this.rchars[i + j];
if (nextChar !== ch) {
return None;
}
}
for (const _ of substringChars) {
this.hl.push(hl_type);
i += 1;
}
return Some(i);
}
protected highlightKeywords(
i: number,
keywords: string[],
hl_type: HighlightType,
): Option<number> {
if (i > 0) {
const prevChar = this.rchars[i - 1];
if (!isSeparator(prevChar)) {
return None;
}
}
for (const keyword of keywords) {
if (i + strlen(keyword) < this.rsize) {
const nextChar = this.rchars[i + strlen(keyword)];
if (!isSeparator(nextChar)) {
continue;
}
}
const maybeHighlight = this.highlightStr(i, keyword, hl_type);
if (maybeHighlight.isSome()) {
return maybeHighlight;
}
}
return None;
}
protected highlightPrimaryKeywords(
i: number,
syntax: FileType,
): Option<number> {
return this.highlightKeywords(
i,
syntax.primaryKeywords,
HighlightType.Keyword1,
);
}
protected highlightSecondaryKeywords(
i: number,
syntax: FileType,
): Option<number> {
return this.highlightKeywords(
i,
syntax.secondaryKeywords,
HighlightType.Keyword2,
);
}
protected highlightString(
i: number,
syntax: FileType,