Add Rust filetype, full tests for Option
Some checks failed
timw4mail/scroll/pipeline/head There was a failure building this commit

This commit is contained in:
Timothy Warren 2024-07-24 14:58:42 -04:00
parent a7fcc982fe
commit d59900a895
12 changed files with 2025 additions and 57 deletions

View File

@ -4,6 +4,3 @@ deno test --allow-all --coverage=coverage
deno coverage coverage --lcov > coverage/coverage.lcov deno coverage coverage --lcov > coverage/coverage.lcov
genhtml -o coverage coverage/coverage.lcov genhtml -o coverage coverage/coverage.lcov
rm coverage/*.json rm coverage/*.json
open coverage/index.html

1686
demo/editor.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,10 @@ default:
# Test coverage # Test coverage
coverage: deno-coverage bun-coverage coverage: deno-coverage bun-coverage
# Generate test coverage and open report in default browser
open-coverage: coverage
open coverage/index.html
# Typescript checking # Typescript checking
check: deno-check bun-check check: deno-check bun-check
@ -12,6 +16,10 @@ check: deno-check bun-check
docs: docs:
deno doc --html --name="Scroll" ./src/scroll.ts ./src/common/*.ts ./src/deno/mod.ts ./src/bun/mod.ts ./src/tsx/mod.ts deno doc --html --name="Scroll" ./src/scroll.ts ./src/common/*.ts ./src/deno/mod.ts ./src/bun/mod.ts ./src/tsx/mod.ts
# Generate source docs and open in default browser
open-docs: docs
open docs/all_symbols.html
# Reformat the code # Reformat the code
fmt: fmt:
deno fmt deno fmt

View File

@ -500,11 +500,78 @@ const OptionTest = {
assertEquivalent(Option.from(Some('foo')), Some('foo')); assertEquivalent(Option.from(Some('foo')), Some('foo'));
assertEquivalent(Some(Some('bar')), Some('bar')); assertEquivalent(Some(Some('bar')), Some('bar'));
}, },
'.isSome': () => {
assertFalse(None.isSome());
assertTrue(Option.from('foo').isSome());
assertTrue(Some('foo').isSome());
},
'.isNone': () => {
assertTrue(None.isNone());
assertFalse(Option.from('foo').isNone());
assertFalse(Some('foo').isNone());
},
'.toString': () => { '.toString': () => {
assertEquals(Some({}).toString(), 'Some ({})'); assertEquals(Some({}).toString(), 'Some ({})');
assertEquals(Some([1, 2, 3]).toString(), 'Some ([1,2,3])'); assertEquals(Some([1, 2, 3]).toString(), 'Some ([1,2,3])');
assertEquals(None.toString(), 'None'); assertEquals(None.toString(), 'None');
}, },
'.isSomeAnd': () => {
assertFalse(Option.from().isSomeAnd((_a) => true));
assertTrue(Option.from('foo').isSomeAnd((a) => typeof a === 'string'));
},
'.isNoneAnd': () => {
assertTrue(None.isNoneAnd(() => true));
assertFalse(None.isNoneAnd(() => false));
assertFalse(Some('x').isNoneAnd(() => true));
},
'.map': () => {
const fn = (_a: any) => 'bar';
assertEquivalent(Some('bar'), Some('foo').map(fn));
assertNone(None.map(fn));
},
'.mapOr': () => {
const fn = (_a: any) => 'bar';
assertEquals('bar', Some('foo').mapOr('baz', fn));
assertEquals('baz', None.mapOr('baz', fn));
},
'.mapOrElse': () => {
const fn = (_a: any) => 'bar';
const defFn = () => 'baz';
assertEquals('bar', Some('foo').mapOrElse(defFn, fn));
assertEquals('baz', None.mapOrElse(defFn, fn));
},
'.unwrapOr': () => {
assertEquals('foo', Some('foo').unwrapOr('bar'));
assertEquals('bar', None.unwrapOr('bar'));
},
'.unwrapOrElse': () => {
const fn = () => 'bar';
assertEquals('foo', Some('foo').unwrapOrElse(fn));
assertEquals('bar', None.unwrapOrElse(fn));
},
'.and': () => {
const optb = Some('bar');
assertEquivalent(optb, Some('foo').and(optb));
assertEquivalent(None, None.and(optb));
},
'.andThen': () => {
const fn = (x: any) => Some(typeof x === 'string');
assertEquivalent(Some(true), Some('foo').andThen(fn));
assertNone(None.andThen(fn));
},
'.or': () => {
const optb = Some('bar');
assertEquivalent(Some('foo'), Some('foo').or(optb));
assertEquivalent(optb, None.or(optb));
},
'.orElse': () => {
const fn = () => Some('bar');
assertEquivalent(Some('foo'), Some('foo').orElse(fn));
assertEquivalent(Some('bar'), None.orElse(fn));
},
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -132,8 +132,47 @@ export const Ansi = {
moveCursorDown, moveCursorDown,
textFormat, textFormat,
color, color,
color256, colors: {
rgb, rgb,
Black: AnsiColor.FgBlack,
Red: AnsiColor.FgRed,
Green: AnsiColor.FgGreen,
Yellow: AnsiColor.FgYellow,
Blue: AnsiColor.FgBlue,
Magenta: AnsiColor.FgMagenta,
Cyan: AnsiColor.FgCyan,
White: AnsiColor.FgWhite,
BrightBlack: AnsiColor.FgBrightBlack,
BrightRed: AnsiColor.FgBrightRed,
BrightGreen: AnsiColor.FgBrightGreen,
BrightYellow: AnsiColor.FgBrightYellow,
BrightBlue: AnsiColor.FgBrightBlue,
BrightMagenta: AnsiColor.FgBrightMagenta,
BrightCyan: AnsiColor.FgBrightCyan,
BrightWhite: AnsiColor.FgBrightWhite,
Invert: AnsiColor.Invert,
background: {
rgb: (r: number, g: number, b: number) => rgb(r, g, b, Ground.Back),
Black: AnsiColor.BgBlack,
Red: AnsiColor.BgRed,
Green: AnsiColor.BgGreen,
Yellow: AnsiColor.BgYellow,
Blue: AnsiColor.BgBlue,
Magenta: AnsiColor.BgMagenta,
Cyan: AnsiColor.BgCyan,
White: AnsiColor.BgWhite,
BrightBlack: AnsiColor.BgBrightBlack,
BrightRed: AnsiColor.BgBrightRed,
BrightGreen: AnsiColor.BgBrightGreen,
BrightYellow: AnsiColor.BgBrightYellow,
BrightBlue: AnsiColor.BgBrightBlue,
BrightMagenta: AnsiColor.BgBrightMagenta,
BrightCyan: AnsiColor.BgBrightCyan,
BrightWhite: AnsiColor.BgBrightWhite,
Invert: AnsiColor.Invert,
},
},
color256,
}; };
export default Ansi; export default Ansi;

View File

@ -1,4 +1,5 @@
import { ITerminalSize } from './types.ts'; import Ansi from './ansi.ts';
import { HighlightType, ITerminalSize } from './types.ts';
export const SCROLL_VERSION = '0.0.1'; export const SCROLL_VERSION = '0.0.1';
export const SCROLL_QUIT_TIMES = 3; export const SCROLL_QUIT_TIMES = 3;
@ -11,3 +12,16 @@ export const defaultTerminalSize: ITerminalSize = {
rows: 24, rows: 24,
cols: 80, cols: 80,
}; };
export const SCROLL_COLOR_SCHEME: Map<HighlightType, string> = new Map([
[HighlightType.Match, Ansi.colors.Invert.toString()], // Inverted color
[HighlightType.Number, Ansi.color256(196)], // Bright Red
[HighlightType.Character, Ansi.color256(207)], // Magenta
[HighlightType.String, Ansi.color256(45)], // Cyan
[HighlightType.SingleLineComment, Ansi.color256(248)], // Light Gray
[HighlightType.MultiLineComment, Ansi.color256(240)], // Medium-light Gray
[HighlightType.Keyword1, Ansi.color256(226)], // Yellow
[HighlightType.Keyword2, Ansi.color256(118)], // Green
[HighlightType.Operator, Ansi.color256(215)], // Orange/Brown
[HighlightType.None, Ansi.ResetFormatting],
]);

View File

@ -9,22 +9,16 @@ export class Document {
/** /**
* Each line of the current document * Each line of the current document
*/ */
#rows: Row[]; #rows: Row[] = [];
/** /**
* Has the document been modified? * @param dirty - Has the document been modified?
* @param type - The meta-data for the file type of the current document
*/ */
public dirty: boolean; private constructor(
public dirty: boolean = false,
/** public type: FileType = FileType.default(),
* The meta-data for the file type of the current document ) {
*/
public type: FileType;
private constructor() {
this.#rows = [];
this.dirty = false;
this.type = FileType.default();
} }
public get fileType(): string { public get fileType(): string {

View File

@ -325,7 +325,7 @@ export default class Editor {
const screenHeight = this.screen.rows; const screenHeight = this.screen.rows;
let { x, y } = this.cursor; let { x, y } = this.cursor;
const height = this.numRows; const height = this.numRows;
let width = (this.row(y).isSome()) ? this.row(y).unwrap().size : 0; let width = this.row(y).mapOr(0, (r) => r.size);
switch (char) { switch (char) {
case KeyCommand.ArrowUp: case KeyCommand.ArrowUp:
@ -370,7 +370,7 @@ export default class Editor {
break; break;
} }
width = (this.row(y).isSome()) ? this.row(y).unwrap().size : 0; width = this.row(y).mapOr(0, (r) => r.size);
if (x > width) { if (x > width) {
x = width; x = width;
@ -383,9 +383,10 @@ export default class Editor {
* Calculate the window of a file to display * 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).mapOr(
? this.row(this.cursor.y).unwrap().cxToRx(this.cursor.x) 0,
: 0; (r) => r.cxToRx(this.cursor.x),
);
const { y } = this.cursor; const { y } = this.cursor;
const offset = this.offset; const offset = this.offset;

View File

@ -3,6 +3,7 @@ import { AbstractFileType } from './base.ts';
import { CFile } from './c.ts'; import { CFile } from './c.ts';
import { CSSFile } from './css.ts'; import { CSSFile } from './css.ts';
import { JavaScriptFile, TypeScriptFile } from './javascript.ts'; import { JavaScriptFile, TypeScriptFile } from './javascript.ts';
import { RustFile } from './rust.ts';
import { ShellFile } from './shell.ts'; import { ShellFile } from './shell.ts';
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -10,14 +11,15 @@ import { ShellFile } from './shell.ts';
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
export const fileTypeMap = new Map([ export const fileTypeMap = new Map([
['.bash', ShellFile],
['.c', CFile], ['.c', CFile],
['.h', CFile],
['.css', CSSFile], ['.css', CSSFile],
['.json', JavaScriptFile], ['.h', CFile],
['.js', JavaScriptFile], ['.js', JavaScriptFile],
['.json', JavaScriptFile],
['.jsx', JavaScriptFile], ['.jsx', JavaScriptFile],
['.mjs', JavaScriptFile], ['.mjs', JavaScriptFile],
['.bash', ShellFile], ['.rs', RustFile],
['.sh', ShellFile], ['.sh', ShellFile],
['.ts', TypeScriptFile], ['.ts', TypeScriptFile],
['.tsx', TypeScriptFile], ['.tsx', TypeScriptFile],

169
src/common/filetype/rust.ts Normal file
View File

@ -0,0 +1,169 @@
import Option, { Some } from '../option.ts';
import {
AbstractFileType,
defaultHighlightOptions,
FileLang,
HighlightingOptions,
} from './base.ts';
export class RustFile extends AbstractFileType {
public readonly name: FileLang = FileLang.Rust;
public readonly singleLineComment = Some('//');
public readonly multiLineCommentStart: Option<string> = Some('/*');
public readonly multiLineCommentEnd: Option<string> = Some('*/');
public readonly keywords1 = [
'continue',
'return',
'static',
'struct',
'unsafe',
'break',
'const',
'crate',
'extern',
'match',
'super',
'trait',
'where',
'else',
'enum',
'false',
'impl',
'loop',
'move',
'self',
'type',
'while',
'for',
'let',
'mod',
'pub',
'ref',
'true',
'use',
'mut',
'as',
'fn',
'if',
'in',
];
public readonly keywords2 = [
'DoubleEndedIterator',
'ExactSizeIterator',
'IntoIterator',
'PartialOrd',
'PartialEq',
'Iterator',
'ToString',
'Default',
'ToOwned',
'Extend',
'FnOnce',
'Option',
'String',
'AsMut',
'AsRef',
'Clone',
'Debug',
'FnMut',
'Sized',
'Unpin',
'array',
'isize',
'usize',
'&str',
'Copy',
'Drop',
'From',
'Into',
'None',
'Self',
'Send',
'Some',
'Sync',
'bool',
'char',
'i128',
'u128',
'Box',
'Err',
'Ord',
'Vec',
'dyn',
'f32',
'f64',
'i16',
'i32',
'i64',
'str',
'u16',
'u32',
'u64',
'Eq',
'Fn',
'Ok',
'i8',
'u8',
'&mut self',
'&mut',
'&self',
'self',
];
public readonly operators = [
'||=',
'>>=',
'<=>',
'<<=',
'&&=',
'**=',
'..=',
'...',
'||',
'|=',
'>>',
'>=',
'=>',
'==',
'<=',
'<<',
'<-',
'+=',
'++',
'^=',
'%=',
'&=',
'&&',
'/=',
'*=',
'**',
'..',
'!=',
':=',
'::',
'->',
'-=',
'--',
'~',
'|',
'>',
'=',
'<',
'+',
'^',
'%',
'&',
'*',
'.',
'!',
':',
';',
',',
'-',
];
public readonly hlOptions: HighlightingOptions = {
...defaultHighlightOptions,
characters: true,
binNumbers: true,
hexNumbers: true,
};
}

View File

@ -1,48 +1,38 @@
import Ansi from './ansi.ts'; import Ansi from './ansi.ts';
import { SCROLL_COLOR_SCHEME } from './config.ts';
/**
* The type of Syntax being highlighted
*/
export enum HighlightType { export enum HighlightType {
/** No highlighting */
None, None,
/** Number literals */
Number, Number,
/** Search results */
Match, Match,
/** Character literals */
Character, Character,
/** String literals */
String, String,
/** Single line comments */
SingleLineComment, SingleLineComment,
/** Multi-line comments */
MultiLineComment, MultiLineComment,
/** Primary keywords */
Keyword1, Keyword1,
/** Secondary keywords */
Keyword2, Keyword2,
/** Math/logic operators */
Operator, Operator,
} }
/**
* Return the configured ANSI formatting escape codes for the
* type of syntax specified
*
* @param type The type of syntax to highlight
*/
export function highlightToColor(type: HighlightType): string { export function highlightToColor(type: HighlightType): string {
switch (type) { return SCROLL_COLOR_SCHEME.get(type) ?? Ansi.ResetFormatting;
case HighlightType.Number:
return Ansi.color256(196);
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(248);
case HighlightType.MultiLineComment:
return Ansi.color256(240);
case HighlightType.Keyword1:
return Ansi.color256(226);
case HighlightType.Keyword2:
return Ansi.color256(118);
case HighlightType.Operator:
return Ansi.color256(215);
default:
return Ansi.ResetFormatting;
}
} }

View File

@ -1,3 +1,4 @@
export { HighlightType } from './highlight.ts';
export { Position } from './position.ts'; export { Position } from './position.ts';
export type { ITestBase } from './runtime/test_base.ts'; export type { ITestBase } from './runtime/test_base.ts';
export type { export type {