Highlight single-line comments, and refactor highlighting method
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
This commit is contained in:
parent
32e3676b02
commit
359e739fe8
@ -1,4 +1,5 @@
|
|||||||
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';
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// File-related types
|
// File-related types
|
||||||
@ -18,6 +19,7 @@ export interface HighlightingOptions {
|
|||||||
|
|
||||||
interface IFileType {
|
interface IFileType {
|
||||||
readonly name: FileLang;
|
readonly name: FileLang;
|
||||||
|
readonly singleLineComment: Option<string>;
|
||||||
readonly hlOptions: HighlightingOptions;
|
readonly hlOptions: HighlightingOptions;
|
||||||
get flags(): HighlightingOptions;
|
get flags(): HighlightingOptions;
|
||||||
}
|
}
|
||||||
@ -27,6 +29,7 @@ interface IFileType {
|
|||||||
*/
|
*/
|
||||||
export abstract class AbstractFileType implements IFileType {
|
export abstract class AbstractFileType implements IFileType {
|
||||||
public readonly name: FileLang = FileLang.Plain;
|
public readonly name: FileLang = FileLang.Plain;
|
||||||
|
public readonly singleLineComment = None;
|
||||||
public readonly hlOptions: HighlightingOptions = {
|
public readonly hlOptions: HighlightingOptions = {
|
||||||
numbers: false,
|
numbers: false,
|
||||||
strings: false,
|
strings: false,
|
||||||
@ -47,6 +50,7 @@ const defaultHighlightOptions: HighlightingOptions = {
|
|||||||
|
|
||||||
class TypeScriptFile extends AbstractFileType {
|
class TypeScriptFile extends AbstractFileType {
|
||||||
public readonly name: FileLang = FileLang.TypeScript;
|
public readonly name: FileLang = FileLang.TypeScript;
|
||||||
|
public readonly singleLineComment = Some('//');
|
||||||
public readonly hlOptions: HighlightingOptions = {
|
public readonly hlOptions: HighlightingOptions = {
|
||||||
...defaultHighlightOptions,
|
...defaultHighlightOptions,
|
||||||
};
|
};
|
||||||
@ -54,6 +58,7 @@ class TypeScriptFile extends AbstractFileType {
|
|||||||
|
|
||||||
class JavaScriptFile extends AbstractFileType {
|
class JavaScriptFile extends AbstractFileType {
|
||||||
public readonly name: FileLang = FileLang.JavaScript;
|
public readonly name: FileLang = FileLang.JavaScript;
|
||||||
|
public readonly singleLineComment = Some('//');
|
||||||
public readonly hlOptions: HighlightingOptions = {
|
public readonly hlOptions: HighlightingOptions = {
|
||||||
...defaultHighlightOptions,
|
...defaultHighlightOptions,
|
||||||
};
|
};
|
||||||
@ -61,6 +66,7 @@ class JavaScriptFile extends AbstractFileType {
|
|||||||
|
|
||||||
class CSSFile extends AbstractFileType {
|
class CSSFile extends AbstractFileType {
|
||||||
public readonly name: FileLang = FileLang.CSS;
|
public readonly name: FileLang = FileLang.CSS;
|
||||||
|
public readonly singleLineComment = None;
|
||||||
public readonly hlOptions: HighlightingOptions = {
|
public readonly hlOptions: HighlightingOptions = {
|
||||||
...defaultHighlightOptions,
|
...defaultHighlightOptions,
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@ export enum HighlightType {
|
|||||||
Number,
|
Number,
|
||||||
Match,
|
Match,
|
||||||
String,
|
String,
|
||||||
|
SingleLineComment,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function highlightToColor(type: HighlightType): string {
|
export function highlightToColor(type: HighlightType): string {
|
||||||
@ -18,6 +19,9 @@ export function highlightToColor(type: HighlightType): string {
|
|||||||
case HighlightType.String:
|
case HighlightType.String:
|
||||||
return Ansi.color256(201);
|
return Ansi.color256(201);
|
||||||
|
|
||||||
|
case HighlightType.SingleLineComment:
|
||||||
|
return Ansi.color256(45);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return Ansi.ResetFormatting;
|
return Ansi.ResetFormatting;
|
||||||
}
|
}
|
||||||
|
@ -225,94 +225,141 @@ export class Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public highlight(word: Option<string>, syntax: FileType): void {
|
public highlight(word: Option<string>, syntax: FileType): void {
|
||||||
const highlighting = [];
|
this.hl = [];
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
for (; i < this.rsize;) {
|
||||||
|
const ch = this.rchars[i];
|
||||||
|
|
||||||
|
const maybeNext = this.highlightComment(i, syntax, ch)
|
||||||
|
.orElse(() => this.highlightString(i, syntax, ch))
|
||||||
|
.orElse(() => this.highlightNumber(i, syntax, ch));
|
||||||
|
|
||||||
|
if (maybeNext.isSome()) {
|
||||||
|
const next = maybeNext.unwrap();
|
||||||
|
if (next < this.rsize) {
|
||||||
|
i = maybeNext.unwrap();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.hl.push(HighlightType.None);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.highlightMatch(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected highlightMatch(word: Option<string>): void {
|
||||||
let searchIndex = 0;
|
let searchIndex = 0;
|
||||||
const matches = [];
|
|
||||||
|
|
||||||
// Find matches for the current search
|
// Find matches for the current search
|
||||||
if (word.isSome()) {
|
if (word.isSome()) {
|
||||||
while (true) {
|
while (true) {
|
||||||
const match = this.find(word.unwrap(), searchIndex);
|
const match = this.find(
|
||||||
|
word.unwrap(),
|
||||||
|
searchIndex,
|
||||||
|
SearchDirection.Forward,
|
||||||
|
);
|
||||||
if (match.isNone()) {
|
if (match.isNone()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
matches.push(match.unwrap());
|
const index = match.unwrap();
|
||||||
const nextPossible = match.unwrap() + strlen(word.unwrap());
|
const nextPossible = index + strlen(word.unwrap());
|
||||||
if (nextPossible < this.rsize) {
|
if (nextPossible < this.rsize) {
|
||||||
|
let i = index;
|
||||||
|
for (const _ in strChars(word.unwrap())) {
|
||||||
|
this.hl[i] = HighlightType.Match;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
searchIndex = nextPossible;
|
searchIndex = nextPossible;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let prevIsSeparator = true;
|
protected highlightComment(
|
||||||
let inString: string | boolean = false;
|
i: number,
|
||||||
let i = 0;
|
syntax: FileType,
|
||||||
for (; i < this.rsize;) {
|
_ch: string,
|
||||||
const ch = this.rchars[i];
|
): Option<number> {
|
||||||
const prevHighlight = (i > 0) ? highlighting[i - 1] : HighlightType.None;
|
// Highlight single-line comments
|
||||||
|
if (syntax.singleLineComment.isSome()) {
|
||||||
// Highlight search matches
|
const commentStart = syntax.singleLineComment.unwrap();
|
||||||
if (word.isSome()) {
|
if (
|
||||||
if (matches.includes(i)) {
|
this.toString().indexOf(commentStart) === this.charIndexToByteIndex(i)
|
||||||
for (const _ in strChars(word.unwrap())) {
|
) {
|
||||||
i += 1;
|
for (; i < this.rsize; i++) {
|
||||||
highlighting.push(HighlightType.Match);
|
this.hl.push(HighlightType.SingleLineComment);
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Some(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Highlight strings
|
|
||||||
if (syntax.flags.strings) {
|
|
||||||
if (inString) {
|
|
||||||
highlighting.push(HighlightType.String);
|
|
||||||
|
|
||||||
// Handle escaped characters in strings
|
|
||||||
if (ch === '\\' && i < this.rsize + 1) {
|
|
||||||
highlighting.push(HighlightType.String);
|
|
||||||
i += 2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch === inString) {
|
|
||||||
inString = false;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
prevIsSeparator = true;
|
|
||||||
continue;
|
|
||||||
} else if (prevIsSeparator && ch === '"' || ch === "'") {
|
|
||||||
highlighting.push(HighlightType.String);
|
|
||||||
inString = ch;
|
|
||||||
prevIsSeparator = true;
|
|
||||||
i += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Highlight numbers
|
|
||||||
if (syntax.flags.numbers) {
|
|
||||||
const isNumeric = isAsciiDigit(ch) && (prevIsSeparator ||
|
|
||||||
prevHighlight === HighlightType.Number);
|
|
||||||
const isDecimalNumeric = ch === '.' &&
|
|
||||||
prevHighlight === HighlightType.Number;
|
|
||||||
const isHexNumeric = ch === 'x' &&
|
|
||||||
prevHighlight === HighlightType.Number;
|
|
||||||
if (isNumeric || isDecimalNumeric || isHexNumeric) {
|
|
||||||
highlighting.push(HighlightType.Number);
|
|
||||||
} else {
|
|
||||||
highlighting.push(HighlightType.None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prevIsSeparator = isSeparator(ch);
|
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hl = highlighting;
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected highlightString(
|
||||||
|
i: number,
|
||||||
|
syntax: FileType,
|
||||||
|
ch: string,
|
||||||
|
): Option<number> {
|
||||||
|
// Highlight strings
|
||||||
|
if (syntax.flags.strings && ch === '"' || ch === "'") {
|
||||||
|
while (true) {
|
||||||
|
this.hl.push(HighlightType.String);
|
||||||
|
i += 1;
|
||||||
|
if (i === this.rsize) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextChar = this.rchars[i];
|
||||||
|
if (nextChar === ch) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.hl.push(HighlightType.String);
|
||||||
|
i += 1;
|
||||||
|
return Some(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
const nextChar = this.rchars[i];
|
||||||
|
if (nextChar !== '.' && nextChar !== 'x' && !isAsciiDigit(nextChar)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(offset: number, len: number): string {
|
public render(offset: number, len: number): string {
|
||||||
|
Loading…
Reference in New Issue
Block a user