diff --git a/src/common/filetype/c.ts b/src/common/filetype/c.ts index b77e887..0cc0a15 100644 --- a/src/common/filetype/c.ts +++ b/src/common/filetype/c.ts @@ -77,13 +77,9 @@ export class CFile extends AbstractFileType { public readonly operators = [ '>>>=', '**=', - '<<=', - '>>=', '&&=', '||=', '??=', - '===', - '!==', '>>>', '<=>', '<<=', @@ -137,6 +133,7 @@ export class CFile extends AbstractFileType { '%', '-', '+', + '*', '&', '|', '^', diff --git a/src/common/filetype/filetype.ts b/src/common/filetype/filetype.ts index 1a9307d..9d946ee 100644 --- a/src/common/filetype/filetype.ts +++ b/src/common/filetype/filetype.ts @@ -1,6 +1,6 @@ import { node_path as path } from '../runtime/mod.ts'; import { AbstractFileType } from './base.ts'; -import { CFile } from './c.ts' +import { CFile } from './c.ts'; import { CSSFile } from './css.ts'; import { JavaScriptFile, TypeScriptFile } from './javascript.ts'; import { ShellFile } from './shell.ts'; diff --git a/src/common/highlight.ts b/src/common/highlight.ts index 7c154ac..4f6e9d4 100644 --- a/src/common/highlight.ts +++ b/src/common/highlight.ts @@ -4,6 +4,7 @@ export enum HighlightType { None, Number, Match, + Character, String, SingleLineComment, MultiLineComment, @@ -20,11 +21,14 @@ export function highlightToColor(type: HighlightType): string { case HighlightType.Match: return Ansi.color256(21); + case HighlightType.Character: + return Ansi.color256(207); + case HighlightType.String: return Ansi.color256(45); case HighlightType.SingleLineComment: - return Ansi.color256(201); + return Ansi.color256(248); case HighlightType.MultiLineComment: return Ansi.color256(240); diff --git a/src/common/option.ts b/src/common/option.ts index 1a904e0..21716e3 100644 --- a/src/common/option.ts +++ b/src/common/option.ts @@ -88,14 +88,14 @@ export class Option { /** * The wrapped value is not null or undefined */ - isSome(): boolean { + public isSome(): boolean { return isSome(this.inner); } /** * The wrapped value is null or undefined */ - isNone(): boolean { + public isNone(): boolean { return !this.isSome(); } @@ -107,7 +107,7 @@ export class Option { * * @param fn A boolean check to run on the wrapped value */ - isSomeAnd(fn: (a: T) => boolean): boolean { + public isSomeAnd(fn: (a: T) => boolean): boolean { return isSome(this.inner) ? fn(this.inner.value) : false; } @@ -119,7 +119,7 @@ export class Option { * * @param fn A function returning a boolean value */ - isNoneAnd(fn: () => boolean): boolean { + public isNoneAnd(fn: () => boolean): boolean { return this.isNone() ? fn() : false; } @@ -130,7 +130,7 @@ export class Option { * * @param fn A function that takes the inner value of the `Option` and returns a new one */ - map(fn: (a: T) => U): Option { + public map(fn: (a: T) => U): Option { return isSome(this.inner) ? Option.from(fn(this.inner.value)) : Option.None; } @@ -142,7 +142,7 @@ export class Option { * @param def The default value to return if this `Option` is `None` * @param fn A function that takes the inner value of this `Option` and returns a new value */ - mapOr(def: U, fn: (a: T) => U): U { + public mapOr(def: U, fn: (a: T) => U): U { return isSome(this.inner) ? fn(this.inner.value) : def; } @@ -154,7 +154,7 @@ export class Option { * @param def A function to return a value if this `Option` is `None` * @param fn A function that takes the inner value of this `Option` and returns a new value */ - mapOrElse(def: () => U, fn: (a: T) => U): U { + public mapOrElse(def: () => U, fn: (a: T) => U): U { return isSome(this.inner) ? fn(this.inner.value) : def(); } @@ -164,7 +164,7 @@ export class Option { * * @param err */ - assert(err: string): T | never { + public assert(err: string): T | never { if (isSome(this.inner)) { return this.inner.value; } @@ -177,7 +177,7 @@ export class Option { * * If `None`, throws an exception. */ - unwrap(): T | never { + public unwrap(): T | never { return this.assert("Called unwrap on a 'None'"); } @@ -187,7 +187,7 @@ export class Option { * * @param def Value to return on `None` value */ - unwrapOr(def: T): T { + public unwrapOr(def: T): T { return isSome(this.inner) ? this.inner.value : def; } @@ -197,7 +197,7 @@ export class Option { * * @param f Function to run on `None` value */ - unwrapOrElse(f: () => T): T { + public unwrapOrElse(f: () => T): T { return isSome(this.inner) ? this.inner.value : f(); } @@ -207,7 +207,7 @@ export class Option { * * @param optb Another `Option` to check */ - and(optb: Option): Option { + public and(optb: Option): Option { return isSome(this.inner) ? optb : Option.None; } @@ -219,7 +219,7 @@ export class Option { * * @param f function to run on the wrapped value */ - andThen(f: (a: T) => Option): Option { + public andThen(f: (a: T) => Option): Option { return isSome(this.inner) ? f(this.inner.value) : Option.None; } @@ -228,7 +228,7 @@ export class Option { * * @param optb The `Option` to return if this `Option` is `None` */ - or(optb: Option): Option { + public or(optb: Option): Option { return this.isNone() ? optb : this; } @@ -239,7 +239,7 @@ export class Option { * * @param f A function to return a different `Option` */ - orElse(f: () => Option): Option { + public orElse(f: () => Option): Option { return this.isNone() ? f() : this; } @@ -247,7 +247,7 @@ export class Option { * Create a string representation of the `Option`, * mostly for debugging */ - toString(): string { + public toString(): string { const innerValue = (isSome(this.inner)) ? JSON.stringify(this.inner.value) : ''; diff --git a/src/common/row.ts b/src/common/row.ts index 4ff6408..b46e370 100644 --- a/src/common/row.ts +++ b/src/common/row.ts @@ -14,6 +14,9 @@ import { highlightToColor, HighlightType } from './highlight.ts'; import Option, { None, Some } from './option.ts'; import { SearchDirection } from './types.ts'; +const SINGLE_QUOTE = "'"; +const DOUBLE_QUOTE = '"'; + /** * One row of text in the current document. In order to handle * multi-byte graphemes, all operations are done on an @@ -321,20 +324,22 @@ export class Row { .orElse(() => this.highlightPrimaryKeywords(i, syntax)) .orElse(() => this.highlightSecondaryKeywords(i, syntax)) .orElse(() => this.highlightString(i, syntax)) + .orElse(() => this.highlightCharacter(i, syntax)) .orElse(() => this.highlightNumber(i, syntax)) .orElse(() => this.highlightOperators(i, syntax)); - if (maybeNext.isSome()) { - const next = maybeNext.unwrap(); - if (next >= this.rsize) { - break; - } - - i = next; + if (maybeNext.isNone()) { + this.hl.push(HighlightType.None); + i += 1; continue; } - this.hl.push(HighlightType.None); - i += 1; + + const next = maybeNext.unwrap(); + if (next >= this.rsize) { + break; + } + + i = next; } this.highlightMatch(word); @@ -391,8 +396,9 @@ export class Row { // Highlight single-line comments if (syntax.singleLineComment.isSome()) { const commentStart = syntax.singleLineComment.unwrap(); + const hasCommentStart = this.rIndexOf(commentStart).isSome(); if ( - this.toString().indexOf(commentStart) === this.charIndexToByteIndex(i) + hasCommentStart && this.rIndexOf(commentStart).unwrap() === i ) { for (; i < this.rsize; i++) { this.hl.push(HighlightType.SingleLineComment); @@ -516,13 +522,58 @@ export class Row { return None; } + protected highlightCharacter( + i: number, + syntax: FileType, + ): Option { + if (!syntax.flags.characters) { + return None; + } + + // Highlight character literals + const ch = this.rchars[i]; + if (ch === SINGLE_QUOTE) { + while (true) { + this.hl.push(HighlightType.Character); + i += 1; + if (i === this.rsize) { + break; + } + + const nextChar = this.rchars[i]; + // Make sure to continue highlighting if + // you have an escaped character delimeter + if (nextChar === '\\') { + this.hl.push(HighlightType.Character); + i += 1; + continue; + } + if (nextChar === ch) { + break; + } + } + this.hl.push(HighlightType.Character); + i += 1; + return Some(i); + } + + return None; + } + protected highlightString( i: number, syntax: FileType, ): Option { + if (!syntax.flags.strings) { + return None; + } + // Highlight strings const ch = this.rchars[i]; - if (syntax.flags.strings && ch === '"' || ch === "'") { + if ( + ch === DOUBLE_QUOTE || + ((!syntax.flags.characters) && ch === SINGLE_QUOTE) + ) { while (true) { this.hl.push(HighlightType.String); i += 1;