Refactor again
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good

This commit is contained in:
Timothy Warren 2024-07-25 11:47:44 -04:00
parent 501f5e10d5
commit 8b44e250e2
29 changed files with 172 additions and 120 deletions

View File

@ -13,5 +13,6 @@
"semiColons": true, "semiColons": true,
"singleQuote": true "singleQuote": true
}, },
"nodeModulesDir": true "nodeModulesDir": true,
"exclude": ["src/bun/"]
} }

View File

@ -14,7 +14,7 @@ check: deno-check bun-check
# Generate source docs # Generate source docs
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/common/*.ts ./src/common/**/*.ts
# Generate source docs and open in default browser # Generate source docs and open in default browser
open-docs: docs open-docs: docs

View File

@ -1,7 +1,7 @@
/** /**
* The main entrypoint when using Bun as the runtime * The main entrypoint when using Bun as the runtime
*/ */
import { CommonRuntime, IRuntime, RunTimeType } from '../common/runtime/mod.ts'; import { CommonRuntime, IRuntime, RunTimeType } from '../common/runtime.ts';
/** /**
* The Bun Runtime implementation * The Bun Runtime implementation

View File

@ -2,17 +2,16 @@ 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 { FileLang } from './filetype/mod.ts'; import { FileLang } from './filetype.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';
import Row from './row.ts'; import Row from './row.ts';
import FileType, * as FT from './filetype/mod.ts'; import FileType, * as FT from './filetype.ts';
import * as Fn from './fns.ts'; import * as Fn from './fns.ts';
import { defaultTerminalSize, SCROLL_TAB_SIZE } from './config.ts'; import { defaultTerminalSize, SCROLL_TAB_SIZE } from './config.ts';
import { getTestRunner } from './runtime/mod.ts'; import { getTestRunner } from './runtime.ts';
import { SearchDirection } from './types.ts'; import { HighlightType, SearchDirection } from './types.ts';
import fs from 'node:fs'; import fs from 'node:fs';
@ -51,6 +50,7 @@ const fnTest = () => {
isAsciiDigit, isAsciiDigit,
strlen, strlen,
truncate, truncate,
highlightToColor,
} = Fn; } = Fn;
return { return {
@ -78,6 +78,21 @@ const fnTest = () => {
assertExists(noop); assertExists(noop);
assertEquals(noop(), undefined); assertEquals(noop(), undefined);
}, },
'highlightToColor()': () => {
[
HighlightType.Number,
HighlightType.Match,
HighlightType.String,
HighlightType.SingleLineComment,
HighlightType.MultiLineComment,
HighlightType.Keyword1,
HighlightType.Keyword2,
HighlightType.Operator,
HighlightType.None,
].forEach((type) => {
assertTrue(highlightToColor(type).length > 0);
});
},
'posSub()': () => { 'posSub()': () => {
assertEquals(posSub(14, 15), 0); assertEquals(posSub(14, 15), 0);
assertEquals(posSub(15, 1), 14); assertEquals(posSub(15, 1), 14);
@ -201,24 +216,6 @@ const readKeyTest = () => {
}; };
}; };
const highlightToColorTest = {
'highlightToColor()': () => {
[
HighlightType.Number,
HighlightType.Match,
HighlightType.String,
HighlightType.SingleLineComment,
HighlightType.MultiLineComment,
HighlightType.Keyword1,
HighlightType.Keyword2,
HighlightType.Operator,
HighlightType.None,
].forEach((type) => {
assertTrue(highlightToColor(type).length > 0);
});
},
};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Tests by module // Tests by module
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -708,7 +705,6 @@ const RowTest = {
testSuite({ testSuite({
fns: fnTest(), fns: fnTest(),
highlightToColorTest,
'readKey()': readKeyTest(), 'readKey()': readKeyTest(),
'ANSI utils': ANSITest(), 'ANSI utils': ANSITest(),
Buffer: BufferTest, Buffer: BufferTest,

View File

@ -117,6 +117,9 @@ const rgb = (
ground: Ground = Ground.Fore, ground: Ground = Ground.Fore,
): string => textFormat([ground, AnsiColor.TypeRGB, r, g, b]); ): string => textFormat([ground, AnsiColor.TypeRGB, r, g, b]);
/**
* Ansi terminal codes and helper functions
*/
export const Ansi = { export const Ansi = {
ClearLine: code('K'), ClearLine: code('K'),
ClearScreen: code('2J'), ClearScreen: code('2J'),

View File

@ -1,6 +1,9 @@
import { strlen, truncate } from './fns.ts'; import { strlen, truncate } from './fns.ts';
import { getRuntime } from './runtime/mod.ts'; import { getRuntime } from './runtime.ts';
/**
* A simple string buffer
*/
class Buffer { class Buffer {
#b = ''; #b = '';

View File

@ -1,7 +1,7 @@
import Ansi from './ansi.ts'; import Ansi from './ansi.ts';
import { HighlightType, ITerminalSize } from './types.ts'; import { HighlightType, ITerminalSize } from './types.ts';
export const SCROLL_VERSION = '0.0.1'; export const SCROLL_VERSION = '0.1.0';
export const SCROLL_QUIT_TIMES = 3; export const SCROLL_QUIT_TIMES = 3;
export const SCROLL_TAB_SIZE = 4; export const SCROLL_TAB_SIZE = 4;

View File

@ -1,8 +1,8 @@
import Row from './row.ts'; import Row from './row.ts';
import { FileType } from './filetype/mod.ts'; import { FileType } from './filetype.ts';
import { arrayInsert } from './fns.ts'; import { arrayInsert } from './fns.ts';
import Option, { None, Some } from './option.ts'; import Option, { None, Some } from './option.ts';
import { getRuntime, logWarning } from './runtime/mod.ts'; import { getRuntime, logWarning } from './runtime.ts';
import { Position, SearchDirection } from './types.ts'; import { Position, SearchDirection } from './types.ts';
export class Document { export class Document {
@ -52,7 +52,7 @@ export class Document {
const rawFile = await file.openFile(filename); const rawFile = await file.openFile(filename);
rawFile.split(/\r?\n/) rawFile.split(/\r?\n/)
.forEach((row) => { .forEach((row: string) => {
this.#rows.push(Row.from(row)); this.#rows.push(Row.from(row));
}); });

View File

@ -13,7 +13,7 @@ import {
truncate, truncate,
} from './fns.ts'; } from './fns.ts';
import Option, { None, Some } from './option.ts'; import Option, { None, Some } from './option.ts';
import { getRuntime, logDebug, logWarning } from './runtime/mod.ts'; import { getRuntime, logDebug, logWarning } from './runtime.ts';
import { ITerminalSize, Position, SearchDirection } from './types.ts'; import { ITerminalSize, Position, SearchDirection } from './types.ts';
/** /**

5
src/common/filetype.ts Normal file
View File

@ -0,0 +1,5 @@
export * from './filetype/base.ts';
export * from './filetype/filetype.ts';
import FileType from './filetype/filetype.ts';
export default FileType;

View File

@ -1,4 +1,4 @@
import { node_path as path } from '../runtime/mod.ts'; import { node_path as path } from '../runtime.ts';
import { AbstractFileType } from './base.ts'; 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';

View File

@ -1,5 +0,0 @@
export * from './base.ts';
export * from './filetype.ts';
import FileType from './filetype.ts';
export default FileType;

View File

@ -1,4 +1,6 @@
import { KeyCommand } from './ansi.ts'; import Ansi, { KeyCommand } from './ansi.ts';
import { SCROLL_COLOR_SCHEME } from './config.ts';
import { HighlightType } from './types.ts';
const decoder = new TextDecoder(); const decoder = new TextDecoder();
@ -57,6 +59,16 @@ export function readKey(raw: Uint8Array): string {
} }
} }
/**
* 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 {
return SCROLL_COLOR_SCHEME.get(type) ?? Ansi.ResetFormatting;
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Array manipulation // Array manipulation
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -1,38 +0,0 @@
import Ansi from './ansi.ts';
import { SCROLL_COLOR_SCHEME } from './config.ts';
/**
* The type of Syntax being highlighted
*/
export enum HighlightType {
/** No highlighting */
None,
/** Number literals */
Number,
/** Search results */
Match,
/** Character literals */
Character,
/** String literals */
String,
/** Single line comments */
SingleLineComment,
/** Multi-line comments */
MultiLineComment,
/** Primary keywords */
Keyword1,
/** Secondary keywords */
Keyword2,
/** Math/logic operators */
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 {
return SCROLL_COLOR_SCHEME.get(type) ?? Ansi.ResetFormatting;
}

View File

@ -4,10 +4,15 @@ import {
logError, logError,
logWarning, logWarning,
node_process as process, node_process as process,
} from './runtime/mod.ts'; } from './runtime.ts';
import Editor from './editor.ts'; import Editor from './editor.ts';
export async function main() { /**
* The main runtime loop
*
* Only returns on error or quit
*/
export async function main(): Promise<void> {
const rt = await getRuntime(); const rt = await getRuntime();
const { term } = rt; const { term } = rt;
@ -18,7 +23,7 @@ export async function main() {
}); });
// Setup error handler to log to file // Setup error handler to log to file
rt.onEvent('error', (error) => { rt.onEvent('error', (error: any) => {
process.stdin.setRawMode(false); process.stdin.setRawMode(false);
logError(JSON.stringify(error, null, 2)); logError(JSON.stringify(error, null, 2));
}); });

View File

@ -4,14 +4,23 @@
export class Position { export class Position {
private constructor(public x: number = 0, public y: number = 0) {} private constructor(public x: number = 0, public y: number = 0) {}
/**
* Create a new `Position` at the specified location
*/
public static at(x: number, y: number): Position { public static at(x: number, y: number): Position {
return new Position(x, y); return new Position(x, y);
} }
/**
* Create a new `Position` from an existing one
*/
public static from(p: Position): Position { public static from(p: Position): Position {
return new Position(p.x, p.y); return new Position(p.x, p.y);
} }
/**
* Create a new `Position` at the origin (0, 0)
*/
public static default(): Position { public static default(): Position {
return new Position(); return new Position();
} }

View File

@ -3,16 +3,16 @@ import Ansi from './ansi.ts';
import { SCROLL_TAB_SIZE } from './config.ts'; import { SCROLL_TAB_SIZE } from './config.ts';
import { import {
arrayInsert, arrayInsert,
highlightToColor,
isAsciiDigit, isAsciiDigit,
isSeparator, isSeparator,
strChars, strChars,
strlen, strlen,
substr, substr,
} from './fns.ts'; } from './fns.ts';
import { FileType } from './filetype/mod.ts'; import { FileType } from './filetype.ts';
import { highlightToColor, HighlightType } from './highlight.ts';
import Option, { None, Some } from './option.ts'; import Option, { None, Some } from './option.ts';
import { SearchDirection } from './types.ts'; import { HighlightType, SearchDirection } from './types.ts';
const SINGLE_QUOTE = "'"; const SINGLE_QUOTE = "'";
const DOUBLE_QUOTE = '"'; const DOUBLE_QUOTE = '"';

11
src/common/runtime.ts Normal file
View File

@ -0,0 +1,11 @@
export * from './runtime/file_io.ts';
export * from './runtime/helpers.ts';
export * from './runtime/log.ts';
export * from './runtime/node.ts';
export * from './runtime/runtime.ts';
export * from './runtime/terminal_io.ts';
export * from './runtime/test_base.ts';
export { RunTimeType } from './types.ts';
import { CommonRuntime } from './runtime/runtime.ts';
export default CommonRuntime;

View File

@ -1,8 +1,11 @@
import { appendFile, readFile, writeFile } from 'node:fs/promises'; import { appendFile, readFile, writeFile } from 'node:fs/promises';
import { resolve } from 'node:path'; import { resolve } from 'node:path';
import { IFileIO } from './mod.ts'; import { IFileIO } from './runtime.ts';
/**
* File IO implementation using shared node APIs
*/
export const CommonFileIO: IFileIO = { export const CommonFileIO: IFileIO = {
openFile: async function (path: string): Promise<string> { openFile: async function (path: string): Promise<string> {
const filePath = resolve(path); const filePath = resolve(path);

View File

@ -3,8 +3,9 @@
*/ */
import { logError, logWarning } from './log.ts'; import { logError, logWarning } from './log.ts';
import { node_path as path, node_process as process } from './node.ts'; import { node_path as path, node_process as process } from './node.ts';
import { RunTimeType } from './runtime.ts'; import { IRuntime } from './runtime.ts';
import { IRuntime, ITestBase } from '../types.ts'; import { ITestBase } from './test_base.ts';
import { RunTimeType } from '../types.ts';
let scrollRuntime: IRuntime | null = null; let scrollRuntime: IRuntime | null = null;

View File

@ -1,6 +1,6 @@
import { noop } from '../fns.ts'; import { noop } from '../fns.ts';
import { SCROLL_LOG_FILE_PREFIX, SCROLL_LOG_FILE_SUFFIX } from '../config.ts'; import { SCROLL_LOG_FILE_PREFIX, SCROLL_LOG_FILE_SUFFIX } from '../config.ts';
import { getRuntime } from './mod.ts'; import { getRuntime } from './helpers.ts';
/** /**
* The label for type/severity of the log entry * The label for type/severity of the log entry
@ -14,7 +14,11 @@ export enum LogLevel {
} }
/** /**
* Basic logging - * Basic logging
*
* @param s The string or data to display first
* @param level The log severity
* @param data Any additional data to display with the log entry
*/ */
export function log( export function log(
s: unknown, s: unknown,
@ -34,12 +38,31 @@ export function log(
}); });
} }
/**
* Make a log entry of `LogLevel.Debug` severity
*/
export const logDebug = (s: unknown, data?: any) => export const logDebug = (s: unknown, data?: any) =>
log(s, LogLevel.Debug, data); log(s, LogLevel.Debug, data);
/**
* Make a log entry of `LogLevel.Info` severity
*/
export const logInfo = (s: unknown, data?: any) => log(s, LogLevel.Info, data); export const logInfo = (s: unknown, data?: any) => log(s, LogLevel.Info, data);
/**
* Make a log entry of `LogLevel.Notice` severity
*/
export const logNotice = (s: unknown, data?: any) => export const logNotice = (s: unknown, data?: any) =>
log(s, LogLevel.Notice, data); log(s, LogLevel.Notice, data);
/**
* Make a log entry of `LogLevel.Warning` severity
*/
export const logWarning = (s: unknown, data?: any) => export const logWarning = (s: unknown, data?: any) =>
log(s, LogLevel.Warning, data); log(s, LogLevel.Warning, data);
/**
* Make a log entry of `LogLevel.Error` severity
*/
export const logError = (s: unknown, data?: any) => export const logError = (s: unknown, data?: any) =>
log(s, LogLevel.Warning, data); log(s, LogLevel.Warning, data);

View File

@ -1,10 +0,0 @@
export * from './file_io.ts';
export * from './helpers.ts';
export * from './log.ts';
export * from './node.ts';
export * from './runtime.ts';
export * from './terminal_io.ts';
export * from './test_base.ts';
import { CommonRuntime } from './runtime.ts';
export default CommonRuntime;

View File

@ -1,6 +1,7 @@
import { node_process as process } from './node.ts'; import { node_process as process } from './node.ts';
import CommonFileIO from './file_io.ts'; import CommonFileIO from './file_io.ts';
import CommonTerminalIO from './terminal_io.ts'; import CommonTerminalIO from './terminal_io.ts';
import { RunTimeType } from '../types.ts';
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Runtime adapter interfaces // Runtime adapter interfaces
@ -14,16 +15,6 @@ export interface ITerminalSize {
cols: number; cols: number;
} }
/**
* Which Typescript runtime is currently being used
*/
export enum RunTimeType {
Bun = 'bun',
Deno = 'deno',
Tsx = 'tsx',
Unknown = 'common',
}
/** /**
* Runtime-specific terminal functionality * Runtime-specific terminal functionality
*/ */
@ -131,6 +122,9 @@ export interface IRuntime {
exit(code?: number): void; exit(code?: number): void;
} }
/**
* Base runtime using shared Node APIs
*/
export const CommonRuntime: IRuntime = { export const CommonRuntime: IRuntime = {
name: RunTimeType.Unknown, name: RunTimeType.Unknown,
term: CommonTerminalIO, term: CommonTerminalIO,

View File

@ -2,6 +2,9 @@ import { node_process as process } from './node.ts';
import { readKey } from '../fns.ts'; import { readKey } from '../fns.ts';
import { ITerminal, ITerminalSize } from '../types.ts'; import { ITerminal, ITerminalSize } from '../types.ts';
/**
* Terminal IO using shared Node APIs
*/
export const CommonTerminalIO: ITerminal = { export const CommonTerminalIO: ITerminal = {
argv: (process.argv.length > 2) ? process.argv.slice(2) : [], argv: (process.argv.length > 2) ? process.argv.slice(2) : [],
inputLoop: async function* (): AsyncGenerator<Uint8Array, null> { inputLoop: async function* (): AsyncGenerator<Uint8Array, null> {

View File

@ -4,6 +4,10 @@
import { deepStrictEqual, notStrictEqual, strictEqual } from 'node:assert'; import { deepStrictEqual, notStrictEqual, strictEqual } from 'node:assert';
import Option from '../option.ts'; import Option from '../option.ts';
/**
* The shared interface for tests, running on a different test
* runner for each runtime
*/
export interface ITestBase { export interface ITestBase {
assertEquivalent(actual: unknown, expected: unknown): void; assertEquivalent(actual: unknown, expected: unknown): void;
assertExists(actual: unknown): void; assertExists(actual: unknown): void;
@ -20,6 +24,9 @@ export interface ITestBase {
testSuite(testObj: any): void; testSuite(testObj: any): void;
} }
/**
* The base testing implementation using Node assert API
*/
export abstract class AbstractTestBase implements Partial<ITestBase> { export abstract class AbstractTestBase implements Partial<ITestBase> {
/** /**
* The values (often objects) have all the same property values * The values (often objects) have all the same property values

View File

@ -1,13 +1,42 @@
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 { IFileIO, IRuntime, ITerminal, ITerminalSize } from './runtime.ts';
IFileIO,
IRuntime, /**
ITerminal, * Which Typescript runtime is currently being used
ITerminalSize, */
RunTimeType, export enum RunTimeType {
} from './runtime/mod.ts'; Bun = 'bun',
Deno = 'deno',
Tsx = 'tsx',
Unknown = 'unknown',
}
/**
* The type of Syntax being highlighted
*/
export enum HighlightType {
/** No highlighting */
None,
/** Number literals */
Number,
/** Search results */
Match,
/** Character literals */
Character,
/** String literals */
String,
/** Single line comments */
SingleLineComment,
/** Multi-line comments */
MultiLineComment,
/** Primary keywords */
Keyword1,
/** Secondary keywords */
Keyword2,
/** Math/logic operators */
Operator,
}
/** /**
* Which direction to search in the current document * Which direction to search in the current document

View File

@ -1,4 +1,4 @@
import { IFileIO } from '../common/runtime/mod.ts'; import { IFileIO } from '../common/runtime.ts';
const DenoFileIO: IFileIO = { const DenoFileIO: IFileIO = {
openFile: async function (path: string): Promise<string> { openFile: async function (path: string): Promise<string> {

View File

@ -1,7 +1,7 @@
/** /**
* The main entrypoint when using Deno as the runtime * The main entrypoint when using Deno as the runtime
*/ */
import { CommonRuntime, IRuntime, RunTimeType } from '../common/runtime/mod.ts'; import { CommonRuntime, IRuntime, RunTimeType } from '../common/runtime.ts';
import DenoTerminalIO from './terminal_io.ts'; import DenoTerminalIO from './terminal_io.ts';
import DenoFileIO from './file_io.ts'; import DenoFileIO from './file_io.ts';

View File

@ -1,7 +1,7 @@
/** /**
* The main entrypoint when using Tsx as the runtime * The main entrypoint when using Tsx as the runtime
*/ */
import { CommonRuntime, IRuntime, RunTimeType } from '../common/runtime/mod.ts'; import { CommonRuntime, IRuntime, RunTimeType } from '../common/runtime.ts';
/** /**
* The Tsx Runtime implementation * The Tsx Runtime implementation