mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
feat(coding-agent): simplify lazy syntax grammar loading
This commit is contained in:
parent
2149e32489
commit
704be487af
9 changed files with 458 additions and 880 deletions
|
|
@ -2882,12 +2882,6 @@ const typescriptLines = highlightCode("const x = 1;", "typescript");
|
|||
// Detect language from a file path
|
||||
const lang = getLanguageFromPath("/path/to/file.rs"); // "rust"
|
||||
const fileLines = highlightCode(code, lang);
|
||||
|
||||
// Uncommon grammars load lazily. In a tool renderer, request another render
|
||||
// after the grammar and its dependencies are ready.
|
||||
const lazyLines = highlightCode(code, lang, {
|
||||
onLanguageReady: context.invalidate,
|
||||
});
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
|
|
|||
|
|
@ -393,7 +393,6 @@ export {
|
|||
getMarkdownTheme,
|
||||
getSelectListTheme,
|
||||
getSettingsListTheme,
|
||||
type HighlightCodeOptions,
|
||||
highlightCode,
|
||||
initTheme,
|
||||
Theme,
|
||||
|
|
|
|||
|
|
@ -26,8 +26,6 @@ export class InteractiveThemeController {
|
|||
private autoSyncEnabled = false;
|
||||
private terminalColorSchemeUnsubscribe: (() => void) | undefined;
|
||||
private highlightLanguageLoadUnsubscribe: (() => void) | undefined;
|
||||
private highlightLanguageRefreshTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
private highlightLanguageGlobalRefreshPending = false;
|
||||
|
||||
constructor(ui: TUI, settingsManager: SettingsManager, showError: (message: string) => void, onChanged: () => void) {
|
||||
this.ui = ui;
|
||||
|
|
@ -42,14 +40,8 @@ export class InteractiveThemeController {
|
|||
this.showError(`Failed to load syntax highlighting language "${event.language}": ${message}`);
|
||||
return;
|
||||
}
|
||||
this.highlightLanguageGlobalRefreshPending ||= event.requiresGlobalRefresh;
|
||||
if (this.highlightLanguageRefreshTimer !== undefined) return;
|
||||
this.highlightLanguageRefreshTimer = setTimeout(() => {
|
||||
this.highlightLanguageRefreshTimer = undefined;
|
||||
if (this.highlightLanguageGlobalRefreshPending) this.ui.invalidate();
|
||||
this.highlightLanguageGlobalRefreshPending = false;
|
||||
this.ui.requestRender();
|
||||
}, 0);
|
||||
this.ui.invalidate();
|
||||
this.ui.requestRender();
|
||||
});
|
||||
this.bindTerminalColorSchemeListener();
|
||||
}
|
||||
|
|
@ -117,9 +109,6 @@ export class InteractiveThemeController {
|
|||
this.terminalColorSchemeUnsubscribe = undefined;
|
||||
this.highlightLanguageLoadUnsubscribe?.();
|
||||
this.highlightLanguageLoadUnsubscribe = undefined;
|
||||
if (this.highlightLanguageRefreshTimer !== undefined) clearTimeout(this.highlightLanguageRefreshTimer);
|
||||
this.highlightLanguageRefreshTimer = undefined;
|
||||
this.highlightLanguageGlobalRefreshPending = false;
|
||||
}
|
||||
|
||||
getTerminalTheme(): TerminalTheme {
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ import type { SourceInfo } from "../../../core/source-info.ts";
|
|||
import { closeWatcher, watchWithErrorHandler } from "../../../utils/fs-watch.ts";
|
||||
import { highlight, requestHighlightLanguage } from "../../../utils/syntax-highlight.ts";
|
||||
|
||||
export { getLanguageFromPath } from "../../../utils/syntax-highlight.ts";
|
||||
|
||||
// ============================================================================
|
||||
// Types & Schema
|
||||
// ============================================================================
|
||||
|
|
@ -1155,18 +1153,14 @@ function getCliHighlightTheme(t: Theme): CliHighlightTheme {
|
|||
return cachedCliHighlightTheme;
|
||||
}
|
||||
|
||||
export interface HighlightCodeOptions {
|
||||
onLanguageReady?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight code with syntax coloring based on file extension or language.
|
||||
* Returns array of highlighted lines.
|
||||
*/
|
||||
export function highlightCode(code: string, lang?: string, options: HighlightCodeOptions = {}): string[] {
|
||||
export function highlightCode(code: string, lang?: string): string[] {
|
||||
// Validate language before highlighting to avoid stderr spam from highlight.js.
|
||||
// Uncommon languages start loading in the background and render plainly once.
|
||||
const validLang = lang ? requestHighlightLanguage(lang, options.onLanguageReady) : undefined;
|
||||
const validLang = lang ? requestHighlightLanguage(lang) : undefined;
|
||||
// Skip highlighting when no valid language is specified. highlight.js's
|
||||
// auto-detection is unreliable and can misidentify prose as AppleScript,
|
||||
// LiveCodeServer, etc., coloring random English words as keywords.
|
||||
|
|
@ -1185,6 +1179,77 @@ export function highlightCode(code: string, lang?: string, options: HighlightCod
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get language identifier from file path extension.
|
||||
*/
|
||||
export function getLanguageFromPath(filePath: string): string | undefined {
|
||||
const ext = filePath.split(".").pop()?.toLowerCase();
|
||||
if (!ext) return undefined;
|
||||
|
||||
const extToLang: Record<string, string> = {
|
||||
ts: "typescript",
|
||||
tsx: "typescript",
|
||||
js: "javascript",
|
||||
jsx: "javascript",
|
||||
mjs: "javascript",
|
||||
cjs: "javascript",
|
||||
py: "python",
|
||||
rb: "ruby",
|
||||
rs: "rust",
|
||||
go: "go",
|
||||
java: "java",
|
||||
kt: "kotlin",
|
||||
swift: "swift",
|
||||
c: "c",
|
||||
h: "c",
|
||||
cpp: "cpp",
|
||||
cc: "cpp",
|
||||
cxx: "cpp",
|
||||
hpp: "cpp",
|
||||
cs: "csharp",
|
||||
php: "php",
|
||||
sh: "bash",
|
||||
bash: "bash",
|
||||
zsh: "bash",
|
||||
fish: "fish",
|
||||
ps1: "powershell",
|
||||
sql: "sql",
|
||||
html: "html",
|
||||
htm: "html",
|
||||
css: "css",
|
||||
scss: "scss",
|
||||
sass: "sass",
|
||||
less: "less",
|
||||
json: "json",
|
||||
yaml: "yaml",
|
||||
yml: "yaml",
|
||||
toml: "toml",
|
||||
xml: "xml",
|
||||
md: "markdown",
|
||||
markdown: "markdown",
|
||||
dockerfile: "dockerfile",
|
||||
makefile: "makefile",
|
||||
cmake: "cmake",
|
||||
lua: "lua",
|
||||
perl: "perl",
|
||||
r: "r",
|
||||
scala: "scala",
|
||||
clj: "clojure",
|
||||
ex: "elixir",
|
||||
exs: "elixir",
|
||||
erl: "erlang",
|
||||
hs: "haskell",
|
||||
ml: "ocaml",
|
||||
vim: "vim",
|
||||
graphql: "graphql",
|
||||
proto: "protobuf",
|
||||
tf: "hcl",
|
||||
hcl: "hcl",
|
||||
};
|
||||
|
||||
return extToLang[ext];
|
||||
}
|
||||
|
||||
export function getMarkdownTheme(): MarkdownTheme {
|
||||
return {
|
||||
heading: (text: string) => theme.fg("mdHeading", text),
|
||||
|
|
@ -1201,7 +1266,7 @@ export function getMarkdownTheme(): MarkdownTheme {
|
|||
italic: (text: string) => theme.italic(text),
|
||||
underline: (text: string) => theme.underline(text),
|
||||
strikethrough: (text: string) => chalk.strikethrough(text),
|
||||
highlightCode: (code, lang, onLanguageReady) => highlightCode(code, lang, { onLanguageReady }),
|
||||
highlightCode,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { LanguageDefinition } from "highlight.js/lib/core.js";
|
||||
import bash from "highlight.js/lib/languages/bash.js";
|
||||
import css from "highlight.js/lib/languages/css.js";
|
||||
import diff from "highlight.js/lib/languages/diff.js";
|
||||
import diffLanguage from "highlight.js/lib/languages/diff.js";
|
||||
import javascript from "highlight.js/lib/languages/javascript.js";
|
||||
import json from "highlight.js/lib/languages/json.js";
|
||||
import markdown from "highlight.js/lib/languages/markdown.js";
|
||||
|
|
@ -12,545 +12,253 @@ import xml from "highlight.js/lib/languages/xml.js";
|
|||
import yaml from "highlight.js/lib/languages/yaml.js";
|
||||
|
||||
type LanguageModule = { default: LanguageDefinition };
|
||||
type LanguageLoader = () => Promise<LanguageModule>;
|
||||
|
||||
export interface HighlightLanguage {
|
||||
name: string;
|
||||
extensions?: readonly string[];
|
||||
aliases?: readonly string[];
|
||||
dependencies?: readonly string[];
|
||||
definition?: LanguageDefinition;
|
||||
load?: () => Promise<LanguageModule>;
|
||||
}
|
||||
export const HIGHLIGHT_LANGUAGE_DEFINITIONS: Record<string, LanguageDefinition> = {
|
||||
xml: xml,
|
||||
bash: bash,
|
||||
css: css,
|
||||
markdown: markdown,
|
||||
diff: diffLanguage,
|
||||
ruby: ruby,
|
||||
javascript: javascript,
|
||||
json: json,
|
||||
python: python,
|
||||
yaml: yaml,
|
||||
typescript: typescript,
|
||||
};
|
||||
|
||||
// Registration, aliases, dependencies, lazy importers, and file-extension
|
||||
// detection live in one registry.
|
||||
//
|
||||
// Intentional exception to the no-dynamic-import rule: literal dynamic imports
|
||||
// let Bun embed every grammar in the standalone binary while keeping evaluation
|
||||
// lazy, and let Node load only the requested grammar.
|
||||
export const HIGHLIGHT_LANGUAGES: readonly HighlightLanguage[] = [
|
||||
{ name: "1c", load: () => import("highlight.js/lib/languages/1c.js") },
|
||||
{ name: "abnf", load: () => import("highlight.js/lib/languages/abnf.js") },
|
||||
{ name: "accesslog", load: () => import("highlight.js/lib/languages/accesslog.js") },
|
||||
{
|
||||
name: "actionscript",
|
||||
aliases: ["as"],
|
||||
load: () => import("highlight.js/lib/languages/actionscript.js"),
|
||||
},
|
||||
{ name: "ada", load: () => import("highlight.js/lib/languages/ada.js") },
|
||||
{
|
||||
name: "angelscript",
|
||||
aliases: ["asc"],
|
||||
load: () => import("highlight.js/lib/languages/angelscript.js"),
|
||||
},
|
||||
{
|
||||
name: "apache",
|
||||
aliases: ["apacheconf"],
|
||||
load: () => import("highlight.js/lib/languages/apache.js"),
|
||||
},
|
||||
{
|
||||
name: "applescript",
|
||||
aliases: ["osascript"],
|
||||
load: () => import("highlight.js/lib/languages/applescript.js"),
|
||||
},
|
||||
{ name: "arcade", load: () => import("highlight.js/lib/languages/arcade.js") },
|
||||
{ name: "arduino", aliases: ["ino"], load: () => import("highlight.js/lib/languages/arduino.js") },
|
||||
{ name: "armasm", aliases: ["arm"], load: () => import("highlight.js/lib/languages/armasm.js") },
|
||||
{
|
||||
name: "xml",
|
||||
extensions: ["xml"],
|
||||
aliases: ["html", "xhtml", "rss", "atom", "xjb", "xsd", "xsl", "plist", "wsf", "svg"],
|
||||
dependencies: ["css", "javascript"],
|
||||
definition: xml,
|
||||
},
|
||||
{ name: "html", extensions: ["html", "htm"], dependencies: ["xml"] },
|
||||
{
|
||||
name: "asciidoc",
|
||||
aliases: ["adoc"],
|
||||
dependencies: ["xml"],
|
||||
load: () => import("highlight.js/lib/languages/asciidoc.js"),
|
||||
},
|
||||
{ name: "aspectj", load: () => import("highlight.js/lib/languages/aspectj.js") },
|
||||
{
|
||||
name: "autohotkey",
|
||||
aliases: ["ahk"],
|
||||
load: () => import("highlight.js/lib/languages/autohotkey.js"),
|
||||
},
|
||||
{ name: "autoit", load: () => import("highlight.js/lib/languages/autoit.js") },
|
||||
{ name: "avrasm", load: () => import("highlight.js/lib/languages/avrasm.js") },
|
||||
{ name: "awk", load: () => import("highlight.js/lib/languages/awk.js") },
|
||||
{ name: "axapta", aliases: ["x++"], load: () => import("highlight.js/lib/languages/axapta.js") },
|
||||
{ name: "bash", extensions: ["sh", "bash", "zsh"], aliases: ["sh", "zsh"], definition: bash },
|
||||
{ name: "basic", load: () => import("highlight.js/lib/languages/basic.js") },
|
||||
{ name: "bnf", load: () => import("highlight.js/lib/languages/bnf.js") },
|
||||
{
|
||||
name: "brainfuck",
|
||||
aliases: ["bf"],
|
||||
load: () => import("highlight.js/lib/languages/brainfuck.js"),
|
||||
},
|
||||
{
|
||||
name: "c-like",
|
||||
aliases: ["c", "h", "cc", "c++", "h++", "hpp", "hh", "hxx", "cxx"],
|
||||
load: () => import("highlight.js/lib/languages/c-like.js"),
|
||||
},
|
||||
{ name: "c", extensions: ["c", "h"], aliases: ["h"], load: () => import("highlight.js/lib/languages/c.js") },
|
||||
{ name: "cal", load: () => import("highlight.js/lib/languages/cal.js") },
|
||||
{
|
||||
name: "capnproto",
|
||||
aliases: ["capnp"],
|
||||
load: () => import("highlight.js/lib/languages/capnproto.js"),
|
||||
},
|
||||
{ name: "ceylon", load: () => import("highlight.js/lib/languages/ceylon.js") },
|
||||
{
|
||||
name: "clean",
|
||||
aliases: ["icl", "dcl"],
|
||||
load: () => import("highlight.js/lib/languages/clean.js"),
|
||||
},
|
||||
{
|
||||
name: "clojure",
|
||||
extensions: ["clj"],
|
||||
aliases: ["clj"],
|
||||
load: () => import("highlight.js/lib/languages/clojure.js"),
|
||||
},
|
||||
{
|
||||
name: "clojure-repl",
|
||||
dependencies: ["clojure"],
|
||||
load: () => import("highlight.js/lib/languages/clojure-repl.js"),
|
||||
},
|
||||
{
|
||||
name: "cmake",
|
||||
extensions: ["cmake"],
|
||||
aliases: ["cmake.in"],
|
||||
load: () => import("highlight.js/lib/languages/cmake.js"),
|
||||
},
|
||||
{
|
||||
name: "coffeescript",
|
||||
aliases: ["coffee", "cson", "iced"],
|
||||
dependencies: ["javascript"],
|
||||
load: () => import("highlight.js/lib/languages/coffeescript.js"),
|
||||
},
|
||||
{ name: "coq", load: () => import("highlight.js/lib/languages/coq.js") },
|
||||
{
|
||||
name: "cos",
|
||||
aliases: ["cls"],
|
||||
dependencies: ["javascript", "sql", "xml"],
|
||||
load: () => import("highlight.js/lib/languages/cos.js"),
|
||||
},
|
||||
{
|
||||
name: "cpp",
|
||||
extensions: ["cpp", "cc", "cxx", "hpp"],
|
||||
aliases: ["cc", "c++", "h++", "hpp", "hh", "hxx", "cxx"],
|
||||
load: () => import("highlight.js/lib/languages/cpp.js"),
|
||||
},
|
||||
{
|
||||
name: "crmsh",
|
||||
aliases: ["crm", "pcmk"],
|
||||
load: () => import("highlight.js/lib/languages/crmsh.js"),
|
||||
},
|
||||
{ name: "crystal", aliases: ["cr"], load: () => import("highlight.js/lib/languages/crystal.js") },
|
||||
{
|
||||
name: "csharp",
|
||||
extensions: ["cs"],
|
||||
aliases: ["cs", "c#"],
|
||||
load: () => import("highlight.js/lib/languages/csharp.js"),
|
||||
},
|
||||
{ name: "csp", load: () => import("highlight.js/lib/languages/csp.js") },
|
||||
{ name: "css", extensions: ["css"], definition: css },
|
||||
{ name: "d", load: () => import("highlight.js/lib/languages/d.js") },
|
||||
{
|
||||
name: "markdown",
|
||||
extensions: ["md", "markdown"],
|
||||
aliases: ["md", "mkdown", "mkd"],
|
||||
dependencies: ["xml"],
|
||||
definition: markdown,
|
||||
},
|
||||
{
|
||||
name: "dart",
|
||||
dependencies: ["markdown"],
|
||||
load: () => import("highlight.js/lib/languages/dart.js"),
|
||||
},
|
||||
{
|
||||
name: "delphi",
|
||||
aliases: ["dpr", "dfm", "pas", "pascal", "freepascal", "lazarus", "lpr", "lfm"],
|
||||
load: () => import("highlight.js/lib/languages/delphi.js"),
|
||||
},
|
||||
{ name: "diff", aliases: ["patch"], definition: diff },
|
||||
{
|
||||
name: "django",
|
||||
aliases: ["jinja"],
|
||||
dependencies: ["xml"],
|
||||
load: () => import("highlight.js/lib/languages/django.js"),
|
||||
},
|
||||
{ name: "dns", aliases: ["bind", "zone"], load: () => import("highlight.js/lib/languages/dns.js") },
|
||||
{
|
||||
name: "dockerfile",
|
||||
extensions: ["dockerfile"],
|
||||
aliases: ["docker"],
|
||||
dependencies: ["bash"],
|
||||
load: () => import("highlight.js/lib/languages/dockerfile.js"),
|
||||
},
|
||||
{ name: "dos", aliases: ["bat", "cmd"], load: () => import("highlight.js/lib/languages/dos.js") },
|
||||
{ name: "dsconfig", load: () => import("highlight.js/lib/languages/dsconfig.js") },
|
||||
{ name: "dts", load: () => import("highlight.js/lib/languages/dts.js") },
|
||||
{
|
||||
name: "dust",
|
||||
aliases: ["dst"],
|
||||
dependencies: ["xml"],
|
||||
load: () => import("highlight.js/lib/languages/dust.js"),
|
||||
},
|
||||
{ name: "ebnf", load: () => import("highlight.js/lib/languages/ebnf.js") },
|
||||
{ name: "elixir", extensions: ["ex", "exs"], load: () => import("highlight.js/lib/languages/elixir.js") },
|
||||
{ name: "elm", load: () => import("highlight.js/lib/languages/elm.js") },
|
||||
{
|
||||
name: "ruby",
|
||||
extensions: ["rb"],
|
||||
aliases: ["rb", "gemspec", "podspec", "thor", "irb"],
|
||||
definition: ruby,
|
||||
},
|
||||
{
|
||||
name: "erb",
|
||||
dependencies: ["ruby", "xml"],
|
||||
load: () => import("highlight.js/lib/languages/erb.js"),
|
||||
},
|
||||
{ name: "erlang-repl", load: () => import("highlight.js/lib/languages/erlang-repl.js") },
|
||||
{
|
||||
name: "erlang",
|
||||
extensions: ["erl"],
|
||||
aliases: ["erl"],
|
||||
load: () => import("highlight.js/lib/languages/erlang.js"),
|
||||
},
|
||||
{
|
||||
name: "excel",
|
||||
aliases: ["xlsx", "xls"],
|
||||
load: () => import("highlight.js/lib/languages/excel.js"),
|
||||
},
|
||||
{ name: "fix", load: () => import("highlight.js/lib/languages/fix.js") },
|
||||
{ name: "fish", extensions: ["fish"] },
|
||||
{ name: "flix", load: () => import("highlight.js/lib/languages/flix.js") },
|
||||
{
|
||||
name: "fortran",
|
||||
aliases: ["f90", "f95"],
|
||||
load: () => import("highlight.js/lib/languages/fortran.js"),
|
||||
},
|
||||
{ name: "fsharp", aliases: ["fs"], load: () => import("highlight.js/lib/languages/fsharp.js") },
|
||||
{ name: "gams", aliases: ["gms"], load: () => import("highlight.js/lib/languages/gams.js") },
|
||||
{ name: "gauss", aliases: ["gss"], load: () => import("highlight.js/lib/languages/gauss.js") },
|
||||
{ name: "gcode", aliases: ["nc"], load: () => import("highlight.js/lib/languages/gcode.js") },
|
||||
{
|
||||
name: "gherkin",
|
||||
aliases: ["feature"],
|
||||
load: () => import("highlight.js/lib/languages/gherkin.js"),
|
||||
},
|
||||
{ name: "glsl", load: () => import("highlight.js/lib/languages/glsl.js") },
|
||||
{ name: "gml", load: () => import("highlight.js/lib/languages/gml.js") },
|
||||
{ name: "go", extensions: ["go"], aliases: ["golang"], load: () => import("highlight.js/lib/languages/go.js") },
|
||||
{ name: "golo", load: () => import("highlight.js/lib/languages/golo.js") },
|
||||
{ name: "gradle", load: () => import("highlight.js/lib/languages/gradle.js") },
|
||||
{ name: "graphql", extensions: ["graphql"] },
|
||||
{ name: "groovy", load: () => import("highlight.js/lib/languages/groovy.js") },
|
||||
{
|
||||
name: "haml",
|
||||
dependencies: ["ruby"],
|
||||
load: () => import("highlight.js/lib/languages/haml.js"),
|
||||
},
|
||||
{
|
||||
name: "handlebars",
|
||||
aliases: ["hbs", "html.hbs", "html.handlebars", "htmlbars"],
|
||||
dependencies: ["xml"],
|
||||
load: () => import("highlight.js/lib/languages/handlebars.js"),
|
||||
},
|
||||
{
|
||||
name: "haskell",
|
||||
extensions: ["hs"],
|
||||
aliases: ["hs"],
|
||||
load: () => import("highlight.js/lib/languages/haskell.js"),
|
||||
},
|
||||
{ name: "hcl", extensions: ["tf", "hcl"] },
|
||||
{ name: "haxe", aliases: ["hx"], load: () => import("highlight.js/lib/languages/haxe.js") },
|
||||
{ name: "hsp", load: () => import("highlight.js/lib/languages/hsp.js") },
|
||||
{
|
||||
name: "htmlbars",
|
||||
aliases: ["hbs", "html.hbs", "html.handlebars", "htmlbars"],
|
||||
dependencies: ["xml"],
|
||||
load: () => import("highlight.js/lib/languages/htmlbars.js"),
|
||||
},
|
||||
{ name: "http", aliases: ["https"], load: () => import("highlight.js/lib/languages/http.js") },
|
||||
{ name: "hy", aliases: ["hylang"], load: () => import("highlight.js/lib/languages/hy.js") },
|
||||
{ name: "inform7", aliases: ["i7"], load: () => import("highlight.js/lib/languages/inform7.js") },
|
||||
{ name: "ini", aliases: ["toml"], load: () => import("highlight.js/lib/languages/ini.js") },
|
||||
{ name: "irpf90", load: () => import("highlight.js/lib/languages/irpf90.js") },
|
||||
{ name: "isbl", load: () => import("highlight.js/lib/languages/isbl.js") },
|
||||
{ name: "java", extensions: ["java"], aliases: ["jsp"], load: () => import("highlight.js/lib/languages/java.js") },
|
||||
{
|
||||
name: "javascript",
|
||||
extensions: ["js", "jsx", "mjs", "cjs"],
|
||||
aliases: ["js", "jsx", "mjs", "cjs"],
|
||||
dependencies: ["css", "xml"],
|
||||
definition: javascript,
|
||||
},
|
||||
{
|
||||
name: "jboss-cli",
|
||||
aliases: ["wildfly-cli"],
|
||||
load: () => import("highlight.js/lib/languages/jboss-cli.js"),
|
||||
},
|
||||
{ name: "json", extensions: ["json"], definition: json },
|
||||
{ name: "julia", load: () => import("highlight.js/lib/languages/julia.js") },
|
||||
{
|
||||
name: "julia-repl",
|
||||
dependencies: ["julia"],
|
||||
load: () => import("highlight.js/lib/languages/julia-repl.js"),
|
||||
},
|
||||
{
|
||||
name: "kotlin",
|
||||
extensions: ["kt"],
|
||||
aliases: ["kt", "kts"],
|
||||
load: () => import("highlight.js/lib/languages/kotlin.js"),
|
||||
},
|
||||
{
|
||||
name: "lasso",
|
||||
aliases: ["ls", "lassoscript"],
|
||||
load: () => import("highlight.js/lib/languages/lasso.js"),
|
||||
},
|
||||
{ name: "latex", aliases: ["tex"], load: () => import("highlight.js/lib/languages/latex.js") },
|
||||
{ name: "ldif", load: () => import("highlight.js/lib/languages/ldif.js") },
|
||||
{ name: "leaf", load: () => import("highlight.js/lib/languages/leaf.js") },
|
||||
{ name: "less", extensions: ["less"], load: () => import("highlight.js/lib/languages/less.js") },
|
||||
{ name: "lisp", load: () => import("highlight.js/lib/languages/lisp.js") },
|
||||
{ name: "livecodeserver", load: () => import("highlight.js/lib/languages/livecodeserver.js") },
|
||||
{
|
||||
name: "livescript",
|
||||
aliases: ["ls"],
|
||||
dependencies: ["javascript"],
|
||||
load: () => import("highlight.js/lib/languages/livescript.js"),
|
||||
},
|
||||
{ name: "llvm", load: () => import("highlight.js/lib/languages/llvm.js") },
|
||||
{ name: "lsl", load: () => import("highlight.js/lib/languages/lsl.js") },
|
||||
{ name: "lua", extensions: ["lua"], load: () => import("highlight.js/lib/languages/lua.js") },
|
||||
{
|
||||
name: "makefile",
|
||||
extensions: ["makefile"],
|
||||
aliases: ["mk", "mak", "make"],
|
||||
load: () => import("highlight.js/lib/languages/makefile.js"),
|
||||
},
|
||||
{
|
||||
name: "mathematica",
|
||||
aliases: ["mma", "wl"],
|
||||
load: () => import("highlight.js/lib/languages/mathematica.js"),
|
||||
},
|
||||
{ name: "matlab", load: () => import("highlight.js/lib/languages/matlab.js") },
|
||||
{ name: "maxima", load: () => import("highlight.js/lib/languages/maxima.js") },
|
||||
{ name: "mel", load: () => import("highlight.js/lib/languages/mel.js") },
|
||||
{
|
||||
name: "mercury",
|
||||
aliases: ["m", "moo"],
|
||||
load: () => import("highlight.js/lib/languages/mercury.js"),
|
||||
},
|
||||
{ name: "mipsasm", aliases: ["mips"], load: () => import("highlight.js/lib/languages/mipsasm.js") },
|
||||
{ name: "mizar", load: () => import("highlight.js/lib/languages/mizar.js") },
|
||||
{
|
||||
name: "perl",
|
||||
extensions: ["perl"],
|
||||
aliases: ["pl", "pm"],
|
||||
dependencies: ["mojolicious"],
|
||||
load: () => import("highlight.js/lib/languages/perl.js"),
|
||||
},
|
||||
{
|
||||
name: "mojolicious",
|
||||
dependencies: ["perl", "xml"],
|
||||
load: () => import("highlight.js/lib/languages/mojolicious.js"),
|
||||
},
|
||||
{ name: "monkey", load: () => import("highlight.js/lib/languages/monkey.js") },
|
||||
{
|
||||
name: "moonscript",
|
||||
aliases: ["moon"],
|
||||
load: () => import("highlight.js/lib/languages/moonscript.js"),
|
||||
},
|
||||
{ name: "n1ql", load: () => import("highlight.js/lib/languages/n1ql.js") },
|
||||
{ name: "nginx", aliases: ["nginxconf"], load: () => import("highlight.js/lib/languages/nginx.js") },
|
||||
{ name: "nim", load: () => import("highlight.js/lib/languages/nim.js") },
|
||||
{ name: "nix", aliases: ["nixos"], load: () => import("highlight.js/lib/languages/nix.js") },
|
||||
{
|
||||
name: "node-repl",
|
||||
dependencies: ["javascript"],
|
||||
load: () => import("highlight.js/lib/languages/node-repl.js"),
|
||||
},
|
||||
{ name: "nsis", load: () => import("highlight.js/lib/languages/nsis.js") },
|
||||
{
|
||||
name: "objectivec",
|
||||
aliases: ["mm", "objc", "obj-c", "obj-c++", "objective-c++"],
|
||||
load: () => import("highlight.js/lib/languages/objectivec.js"),
|
||||
},
|
||||
{ name: "ocaml", extensions: ["ml"], aliases: ["ml"], load: () => import("highlight.js/lib/languages/ocaml.js") },
|
||||
{
|
||||
name: "openscad",
|
||||
aliases: ["scad"],
|
||||
load: () => import("highlight.js/lib/languages/openscad.js"),
|
||||
},
|
||||
{ name: "oxygene", load: () => import("highlight.js/lib/languages/oxygene.js") },
|
||||
{
|
||||
name: "parser3",
|
||||
dependencies: ["xml"],
|
||||
load: () => import("highlight.js/lib/languages/parser3.js"),
|
||||
},
|
||||
{ name: "pf", aliases: ["pf.conf"], load: () => import("highlight.js/lib/languages/pf.js") },
|
||||
{
|
||||
name: "pgsql",
|
||||
aliases: ["postgres", "postgresql"],
|
||||
dependencies: ["bash", "java", "json", "lua", "perl", "php", "python", "r", "ruby", "scheme", "tcl", "xml"],
|
||||
load: () => import("highlight.js/lib/languages/pgsql.js"),
|
||||
},
|
||||
{
|
||||
name: "php",
|
||||
extensions: ["php"],
|
||||
aliases: ["php3", "php4", "php5", "php6", "php7", "php8"],
|
||||
load: () => import("highlight.js/lib/languages/php.js"),
|
||||
},
|
||||
{
|
||||
name: "php-template",
|
||||
dependencies: ["php", "xml"],
|
||||
load: () => import("highlight.js/lib/languages/php-template.js"),
|
||||
},
|
||||
{
|
||||
name: "plaintext",
|
||||
aliases: ["text", "txt"],
|
||||
load: () => import("highlight.js/lib/languages/plaintext.js"),
|
||||
},
|
||||
{ name: "pony", load: () => import("highlight.js/lib/languages/pony.js") },
|
||||
{
|
||||
name: "powershell",
|
||||
extensions: ["ps1"],
|
||||
aliases: ["ps", "ps1"],
|
||||
load: () => import("highlight.js/lib/languages/powershell.js"),
|
||||
},
|
||||
{ name: "processing", load: () => import("highlight.js/lib/languages/processing.js") },
|
||||
{ name: "profile", load: () => import("highlight.js/lib/languages/profile.js") },
|
||||
{ name: "prolog", load: () => import("highlight.js/lib/languages/prolog.js") },
|
||||
{ name: "properties", load: () => import("highlight.js/lib/languages/properties.js") },
|
||||
{ name: "protobuf", extensions: ["proto"], load: () => import("highlight.js/lib/languages/protobuf.js") },
|
||||
{ name: "puppet", aliases: ["pp"], load: () => import("highlight.js/lib/languages/puppet.js") },
|
||||
{
|
||||
name: "purebasic",
|
||||
aliases: ["pb", "pbi"],
|
||||
load: () => import("highlight.js/lib/languages/purebasic.js"),
|
||||
},
|
||||
{ name: "python", extensions: ["py"], aliases: ["py", "gyp", "ipython"], definition: python },
|
||||
{
|
||||
name: "python-repl",
|
||||
aliases: ["pycon"],
|
||||
dependencies: ["python"],
|
||||
load: () => import("highlight.js/lib/languages/python-repl.js"),
|
||||
},
|
||||
{ name: "q", aliases: ["k", "kdb"], load: () => import("highlight.js/lib/languages/q.js") },
|
||||
{
|
||||
name: "qml",
|
||||
aliases: ["qt"],
|
||||
dependencies: ["xml"],
|
||||
load: () => import("highlight.js/lib/languages/qml.js"),
|
||||
},
|
||||
{ name: "r", extensions: ["r"], load: () => import("highlight.js/lib/languages/r.js") },
|
||||
{ name: "reasonml", aliases: ["re"], load: () => import("highlight.js/lib/languages/reasonml.js") },
|
||||
{ name: "rib", load: () => import("highlight.js/lib/languages/rib.js") },
|
||||
{
|
||||
name: "roboconf",
|
||||
aliases: ["graph", "instances"],
|
||||
load: () => import("highlight.js/lib/languages/roboconf.js"),
|
||||
},
|
||||
{
|
||||
name: "routeros",
|
||||
aliases: ["mikrotik"],
|
||||
load: () => import("highlight.js/lib/languages/routeros.js"),
|
||||
},
|
||||
{ name: "rsl", load: () => import("highlight.js/lib/languages/rsl.js") },
|
||||
{ name: "ruleslanguage", load: () => import("highlight.js/lib/languages/ruleslanguage.js") },
|
||||
{ name: "rust", extensions: ["rs"], aliases: ["rs"], load: () => import("highlight.js/lib/languages/rust.js") },
|
||||
{ name: "sas", load: () => import("highlight.js/lib/languages/sas.js") },
|
||||
{ name: "sass", extensions: ["sass"] },
|
||||
{ name: "scala", extensions: ["scala"], load: () => import("highlight.js/lib/languages/scala.js") },
|
||||
{ name: "scheme", load: () => import("highlight.js/lib/languages/scheme.js") },
|
||||
{ name: "scilab", aliases: ["sci"], load: () => import("highlight.js/lib/languages/scilab.js") },
|
||||
{ name: "scss", extensions: ["scss"], load: () => import("highlight.js/lib/languages/scss.js") },
|
||||
{
|
||||
name: "shell",
|
||||
aliases: ["console"],
|
||||
dependencies: ["bash"],
|
||||
load: () => import("highlight.js/lib/languages/shell.js"),
|
||||
},
|
||||
{ name: "smali", load: () => import("highlight.js/lib/languages/smali.js") },
|
||||
{
|
||||
name: "smalltalk",
|
||||
aliases: ["st"],
|
||||
load: () => import("highlight.js/lib/languages/smalltalk.js"),
|
||||
},
|
||||
{ name: "sml", aliases: ["ml"], load: () => import("highlight.js/lib/languages/sml.js") },
|
||||
{ name: "sqf", load: () => import("highlight.js/lib/languages/sqf.js") },
|
||||
{
|
||||
name: "sql_more",
|
||||
aliases: ["mysql", "oracle"],
|
||||
load: () => import("highlight.js/lib/languages/sql_more.js"),
|
||||
},
|
||||
{ name: "sql", extensions: ["sql"], load: () => import("highlight.js/lib/languages/sql.js") },
|
||||
{ name: "stan", aliases: ["stanfuncs"], load: () => import("highlight.js/lib/languages/stan.js") },
|
||||
{ name: "stata", aliases: ["do", "ado"], load: () => import("highlight.js/lib/languages/stata.js") },
|
||||
{
|
||||
name: "step21",
|
||||
aliases: ["p21", "step", "stp"],
|
||||
load: () => import("highlight.js/lib/languages/step21.js"),
|
||||
},
|
||||
{ name: "stylus", aliases: ["styl"], load: () => import("highlight.js/lib/languages/stylus.js") },
|
||||
{ name: "subunit", load: () => import("highlight.js/lib/languages/subunit.js") },
|
||||
{ name: "swift", extensions: ["swift"], load: () => import("highlight.js/lib/languages/swift.js") },
|
||||
{ name: "taggerscript", load: () => import("highlight.js/lib/languages/taggerscript.js") },
|
||||
{
|
||||
name: "yaml",
|
||||
extensions: ["yaml", "yml"],
|
||||
aliases: ["yml"],
|
||||
dependencies: ["ruby"],
|
||||
definition: yaml,
|
||||
},
|
||||
{
|
||||
name: "tap",
|
||||
dependencies: ["yaml"],
|
||||
load: () => import("highlight.js/lib/languages/tap.js"),
|
||||
},
|
||||
{ name: "tcl", aliases: ["tk"], load: () => import("highlight.js/lib/languages/tcl.js") },
|
||||
{ name: "thrift", load: () => import("highlight.js/lib/languages/thrift.js") },
|
||||
{ name: "toml", extensions: ["toml"], dependencies: ["ini"] },
|
||||
{ name: "tp", load: () => import("highlight.js/lib/languages/tp.js") },
|
||||
{
|
||||
name: "twig",
|
||||
aliases: ["craftcms"],
|
||||
dependencies: ["xml"],
|
||||
load: () => import("highlight.js/lib/languages/twig.js"),
|
||||
},
|
||||
{
|
||||
name: "typescript",
|
||||
extensions: ["ts", "tsx"],
|
||||
aliases: ["ts", "tsx"],
|
||||
dependencies: ["css", "xml"],
|
||||
definition: typescript,
|
||||
},
|
||||
{ name: "vala", load: () => import("highlight.js/lib/languages/vala.js") },
|
||||
{ name: "vbnet", aliases: ["vb"], load: () => import("highlight.js/lib/languages/vbnet.js") },
|
||||
{ name: "vbscript", aliases: ["vbs"], load: () => import("highlight.js/lib/languages/vbscript.js") },
|
||||
{
|
||||
name: "vbscript-html",
|
||||
dependencies: ["vbscript", "xml"],
|
||||
load: () => import("highlight.js/lib/languages/vbscript-html.js"),
|
||||
},
|
||||
{
|
||||
name: "verilog",
|
||||
aliases: ["v", "sv", "svh"],
|
||||
load: () => import("highlight.js/lib/languages/verilog.js"),
|
||||
},
|
||||
{ name: "vhdl", load: () => import("highlight.js/lib/languages/vhdl.js") },
|
||||
{ name: "vim", extensions: ["vim"], load: () => import("highlight.js/lib/languages/vim.js") },
|
||||
{ name: "x86asm", load: () => import("highlight.js/lib/languages/x86asm.js") },
|
||||
{ name: "xl", aliases: ["tao"], load: () => import("highlight.js/lib/languages/xl.js") },
|
||||
{
|
||||
name: "xquery",
|
||||
aliases: ["xpath", "xq"],
|
||||
dependencies: ["xml"],
|
||||
load: () => import("highlight.js/lib/languages/xquery.js"),
|
||||
},
|
||||
{ name: "zephir", aliases: ["zep"], load: () => import("highlight.js/lib/languages/zephir.js") },
|
||||
];
|
||||
export const HIGHLIGHT_LANGUAGE_LOADERS: Record<string, LanguageLoader> = {
|
||||
"1c": () => import("highlight.js/lib/languages/1c.js"),
|
||||
abnf: () => import("highlight.js/lib/languages/abnf.js"),
|
||||
accesslog: () => import("highlight.js/lib/languages/accesslog.js"),
|
||||
actionscript: () => import("highlight.js/lib/languages/actionscript.js"),
|
||||
ada: () => import("highlight.js/lib/languages/ada.js"),
|
||||
angelscript: () => import("highlight.js/lib/languages/angelscript.js"),
|
||||
apache: () => import("highlight.js/lib/languages/apache.js"),
|
||||
applescript: () => import("highlight.js/lib/languages/applescript.js"),
|
||||
arcade: () => import("highlight.js/lib/languages/arcade.js"),
|
||||
arduino: () => import("highlight.js/lib/languages/arduino.js"),
|
||||
armasm: () => import("highlight.js/lib/languages/armasm.js"),
|
||||
asciidoc: () => import("highlight.js/lib/languages/asciidoc.js"),
|
||||
aspectj: () => import("highlight.js/lib/languages/aspectj.js"),
|
||||
autohotkey: () => import("highlight.js/lib/languages/autohotkey.js"),
|
||||
autoit: () => import("highlight.js/lib/languages/autoit.js"),
|
||||
avrasm: () => import("highlight.js/lib/languages/avrasm.js"),
|
||||
awk: () => import("highlight.js/lib/languages/awk.js"),
|
||||
axapta: () => import("highlight.js/lib/languages/axapta.js"),
|
||||
basic: () => import("highlight.js/lib/languages/basic.js"),
|
||||
bnf: () => import("highlight.js/lib/languages/bnf.js"),
|
||||
brainfuck: () => import("highlight.js/lib/languages/brainfuck.js"),
|
||||
"c-like": () => import("highlight.js/lib/languages/c-like.js"),
|
||||
c: () => import("highlight.js/lib/languages/c.js"),
|
||||
cal: () => import("highlight.js/lib/languages/cal.js"),
|
||||
capnproto: () => import("highlight.js/lib/languages/capnproto.js"),
|
||||
ceylon: () => import("highlight.js/lib/languages/ceylon.js"),
|
||||
clean: () => import("highlight.js/lib/languages/clean.js"),
|
||||
clojure: () => import("highlight.js/lib/languages/clojure.js"),
|
||||
"clojure-repl": () => import("highlight.js/lib/languages/clojure-repl.js"),
|
||||
cmake: () => import("highlight.js/lib/languages/cmake.js"),
|
||||
coffeescript: () => import("highlight.js/lib/languages/coffeescript.js"),
|
||||
coq: () => import("highlight.js/lib/languages/coq.js"),
|
||||
cos: () => import("highlight.js/lib/languages/cos.js"),
|
||||
cpp: () => import("highlight.js/lib/languages/cpp.js"),
|
||||
crmsh: () => import("highlight.js/lib/languages/crmsh.js"),
|
||||
crystal: () => import("highlight.js/lib/languages/crystal.js"),
|
||||
csharp: () => import("highlight.js/lib/languages/csharp.js"),
|
||||
csp: () => import("highlight.js/lib/languages/csp.js"),
|
||||
d: () => import("highlight.js/lib/languages/d.js"),
|
||||
dart: () => import("highlight.js/lib/languages/dart.js"),
|
||||
delphi: () => import("highlight.js/lib/languages/delphi.js"),
|
||||
django: () => import("highlight.js/lib/languages/django.js"),
|
||||
dns: () => import("highlight.js/lib/languages/dns.js"),
|
||||
dockerfile: () => import("highlight.js/lib/languages/dockerfile.js"),
|
||||
dos: () => import("highlight.js/lib/languages/dos.js"),
|
||||
dsconfig: () => import("highlight.js/lib/languages/dsconfig.js"),
|
||||
dts: () => import("highlight.js/lib/languages/dts.js"),
|
||||
dust: () => import("highlight.js/lib/languages/dust.js"),
|
||||
ebnf: () => import("highlight.js/lib/languages/ebnf.js"),
|
||||
elixir: () => import("highlight.js/lib/languages/elixir.js"),
|
||||
elm: () => import("highlight.js/lib/languages/elm.js"),
|
||||
erb: () => import("highlight.js/lib/languages/erb.js"),
|
||||
"erlang-repl": () => import("highlight.js/lib/languages/erlang-repl.js"),
|
||||
erlang: () => import("highlight.js/lib/languages/erlang.js"),
|
||||
excel: () => import("highlight.js/lib/languages/excel.js"),
|
||||
fix: () => import("highlight.js/lib/languages/fix.js"),
|
||||
flix: () => import("highlight.js/lib/languages/flix.js"),
|
||||
fortran: () => import("highlight.js/lib/languages/fortran.js"),
|
||||
fsharp: () => import("highlight.js/lib/languages/fsharp.js"),
|
||||
gams: () => import("highlight.js/lib/languages/gams.js"),
|
||||
gauss: () => import("highlight.js/lib/languages/gauss.js"),
|
||||
gcode: () => import("highlight.js/lib/languages/gcode.js"),
|
||||
gherkin: () => import("highlight.js/lib/languages/gherkin.js"),
|
||||
glsl: () => import("highlight.js/lib/languages/glsl.js"),
|
||||
gml: () => import("highlight.js/lib/languages/gml.js"),
|
||||
go: () => import("highlight.js/lib/languages/go.js"),
|
||||
golo: () => import("highlight.js/lib/languages/golo.js"),
|
||||
gradle: () => import("highlight.js/lib/languages/gradle.js"),
|
||||
groovy: () => import("highlight.js/lib/languages/groovy.js"),
|
||||
haml: () => import("highlight.js/lib/languages/haml.js"),
|
||||
handlebars: () => import("highlight.js/lib/languages/handlebars.js"),
|
||||
haskell: () => import("highlight.js/lib/languages/haskell.js"),
|
||||
haxe: () => import("highlight.js/lib/languages/haxe.js"),
|
||||
hsp: () => import("highlight.js/lib/languages/hsp.js"),
|
||||
htmlbars: () => import("highlight.js/lib/languages/htmlbars.js"),
|
||||
http: () => import("highlight.js/lib/languages/http.js"),
|
||||
hy: () => import("highlight.js/lib/languages/hy.js"),
|
||||
inform7: () => import("highlight.js/lib/languages/inform7.js"),
|
||||
ini: () => import("highlight.js/lib/languages/ini.js"),
|
||||
irpf90: () => import("highlight.js/lib/languages/irpf90.js"),
|
||||
isbl: () => import("highlight.js/lib/languages/isbl.js"),
|
||||
java: () => import("highlight.js/lib/languages/java.js"),
|
||||
"jboss-cli": () => import("highlight.js/lib/languages/jboss-cli.js"),
|
||||
julia: () => import("highlight.js/lib/languages/julia.js"),
|
||||
"julia-repl": () => import("highlight.js/lib/languages/julia-repl.js"),
|
||||
kotlin: () => import("highlight.js/lib/languages/kotlin.js"),
|
||||
lasso: () => import("highlight.js/lib/languages/lasso.js"),
|
||||
latex: () => import("highlight.js/lib/languages/latex.js"),
|
||||
ldif: () => import("highlight.js/lib/languages/ldif.js"),
|
||||
leaf: () => import("highlight.js/lib/languages/leaf.js"),
|
||||
less: () => import("highlight.js/lib/languages/less.js"),
|
||||
lisp: () => import("highlight.js/lib/languages/lisp.js"),
|
||||
livecodeserver: () => import("highlight.js/lib/languages/livecodeserver.js"),
|
||||
livescript: () => import("highlight.js/lib/languages/livescript.js"),
|
||||
llvm: () => import("highlight.js/lib/languages/llvm.js"),
|
||||
lsl: () => import("highlight.js/lib/languages/lsl.js"),
|
||||
lua: () => import("highlight.js/lib/languages/lua.js"),
|
||||
makefile: () => import("highlight.js/lib/languages/makefile.js"),
|
||||
mathematica: () => import("highlight.js/lib/languages/mathematica.js"),
|
||||
matlab: () => import("highlight.js/lib/languages/matlab.js"),
|
||||
maxima: () => import("highlight.js/lib/languages/maxima.js"),
|
||||
mel: () => import("highlight.js/lib/languages/mel.js"),
|
||||
mercury: () => import("highlight.js/lib/languages/mercury.js"),
|
||||
mipsasm: () => import("highlight.js/lib/languages/mipsasm.js"),
|
||||
mizar: () => import("highlight.js/lib/languages/mizar.js"),
|
||||
perl: () => import("highlight.js/lib/languages/perl.js"),
|
||||
mojolicious: () => import("highlight.js/lib/languages/mojolicious.js"),
|
||||
monkey: () => import("highlight.js/lib/languages/monkey.js"),
|
||||
moonscript: () => import("highlight.js/lib/languages/moonscript.js"),
|
||||
n1ql: () => import("highlight.js/lib/languages/n1ql.js"),
|
||||
nginx: () => import("highlight.js/lib/languages/nginx.js"),
|
||||
nim: () => import("highlight.js/lib/languages/nim.js"),
|
||||
nix: () => import("highlight.js/lib/languages/nix.js"),
|
||||
"node-repl": () => import("highlight.js/lib/languages/node-repl.js"),
|
||||
nsis: () => import("highlight.js/lib/languages/nsis.js"),
|
||||
objectivec: () => import("highlight.js/lib/languages/objectivec.js"),
|
||||
ocaml: () => import("highlight.js/lib/languages/ocaml.js"),
|
||||
openscad: () => import("highlight.js/lib/languages/openscad.js"),
|
||||
oxygene: () => import("highlight.js/lib/languages/oxygene.js"),
|
||||
parser3: () => import("highlight.js/lib/languages/parser3.js"),
|
||||
pf: () => import("highlight.js/lib/languages/pf.js"),
|
||||
pgsql: () => import("highlight.js/lib/languages/pgsql.js"),
|
||||
php: () => import("highlight.js/lib/languages/php.js"),
|
||||
"php-template": () => import("highlight.js/lib/languages/php-template.js"),
|
||||
plaintext: () => import("highlight.js/lib/languages/plaintext.js"),
|
||||
pony: () => import("highlight.js/lib/languages/pony.js"),
|
||||
powershell: () => import("highlight.js/lib/languages/powershell.js"),
|
||||
processing: () => import("highlight.js/lib/languages/processing.js"),
|
||||
profile: () => import("highlight.js/lib/languages/profile.js"),
|
||||
prolog: () => import("highlight.js/lib/languages/prolog.js"),
|
||||
properties: () => import("highlight.js/lib/languages/properties.js"),
|
||||
protobuf: () => import("highlight.js/lib/languages/protobuf.js"),
|
||||
puppet: () => import("highlight.js/lib/languages/puppet.js"),
|
||||
purebasic: () => import("highlight.js/lib/languages/purebasic.js"),
|
||||
"python-repl": () => import("highlight.js/lib/languages/python-repl.js"),
|
||||
q: () => import("highlight.js/lib/languages/q.js"),
|
||||
qml: () => import("highlight.js/lib/languages/qml.js"),
|
||||
r: () => import("highlight.js/lib/languages/r.js"),
|
||||
reasonml: () => import("highlight.js/lib/languages/reasonml.js"),
|
||||
rib: () => import("highlight.js/lib/languages/rib.js"),
|
||||
roboconf: () => import("highlight.js/lib/languages/roboconf.js"),
|
||||
routeros: () => import("highlight.js/lib/languages/routeros.js"),
|
||||
rsl: () => import("highlight.js/lib/languages/rsl.js"),
|
||||
ruleslanguage: () => import("highlight.js/lib/languages/ruleslanguage.js"),
|
||||
rust: () => import("highlight.js/lib/languages/rust.js"),
|
||||
sas: () => import("highlight.js/lib/languages/sas.js"),
|
||||
scala: () => import("highlight.js/lib/languages/scala.js"),
|
||||
scheme: () => import("highlight.js/lib/languages/scheme.js"),
|
||||
scilab: () => import("highlight.js/lib/languages/scilab.js"),
|
||||
scss: () => import("highlight.js/lib/languages/scss.js"),
|
||||
shell: () => import("highlight.js/lib/languages/shell.js"),
|
||||
smali: () => import("highlight.js/lib/languages/smali.js"),
|
||||
smalltalk: () => import("highlight.js/lib/languages/smalltalk.js"),
|
||||
sml: () => import("highlight.js/lib/languages/sml.js"),
|
||||
sqf: () => import("highlight.js/lib/languages/sqf.js"),
|
||||
sql_more: () => import("highlight.js/lib/languages/sql_more.js"),
|
||||
sql: () => import("highlight.js/lib/languages/sql.js"),
|
||||
stan: () => import("highlight.js/lib/languages/stan.js"),
|
||||
stata: () => import("highlight.js/lib/languages/stata.js"),
|
||||
step21: () => import("highlight.js/lib/languages/step21.js"),
|
||||
stylus: () => import("highlight.js/lib/languages/stylus.js"),
|
||||
subunit: () => import("highlight.js/lib/languages/subunit.js"),
|
||||
swift: () => import("highlight.js/lib/languages/swift.js"),
|
||||
taggerscript: () => import("highlight.js/lib/languages/taggerscript.js"),
|
||||
tap: () => import("highlight.js/lib/languages/tap.js"),
|
||||
tcl: () => import("highlight.js/lib/languages/tcl.js"),
|
||||
thrift: () => import("highlight.js/lib/languages/thrift.js"),
|
||||
tp: () => import("highlight.js/lib/languages/tp.js"),
|
||||
twig: () => import("highlight.js/lib/languages/twig.js"),
|
||||
vala: () => import("highlight.js/lib/languages/vala.js"),
|
||||
vbnet: () => import("highlight.js/lib/languages/vbnet.js"),
|
||||
vbscript: () => import("highlight.js/lib/languages/vbscript.js"),
|
||||
"vbscript-html": () => import("highlight.js/lib/languages/vbscript-html.js"),
|
||||
verilog: () => import("highlight.js/lib/languages/verilog.js"),
|
||||
vhdl: () => import("highlight.js/lib/languages/vhdl.js"),
|
||||
vim: () => import("highlight.js/lib/languages/vim.js"),
|
||||
x86asm: () => import("highlight.js/lib/languages/x86asm.js"),
|
||||
xl: () => import("highlight.js/lib/languages/xl.js"),
|
||||
xquery: () => import("highlight.js/lib/languages/xquery.js"),
|
||||
zephir: () => import("highlight.js/lib/languages/zephir.js"),
|
||||
};
|
||||
|
||||
const aliasPairs = `
|
||||
as=actionscript asc=angelscript apacheconf=apache osascript=applescript ino=arduino arm=armasm html=xml
|
||||
xhtml=xml rss=xml atom=xml xjb=xml xsd=xml xsl=xml plist=xml wsf=xml svg=xml adoc=asciidoc ahk=autohotkey
|
||||
x++=axapta sh=bash zsh=bash bf=brainfuck c=c-like h=c cc=cpp c++=cpp h++=cpp hpp=cpp hh=cpp hxx=cpp cxx=cpp
|
||||
capnp=capnproto icl=clean dcl=clean clj=clojure cmake.in=cmake coffee=coffeescript cson=coffeescript
|
||||
iced=coffeescript cls=cos crm=crmsh pcmk=crmsh cr=crystal cs=csharp c#=csharp md=markdown mkdown=markdown
|
||||
mkd=markdown dpr=delphi dfm=delphi pas=delphi pascal=delphi freepascal=delphi lazarus=delphi lpr=delphi
|
||||
lfm=delphi patch=diff jinja=django bind=dns zone=dns docker=dockerfile bat=dos cmd=dos dst=dust rb=ruby
|
||||
gemspec=ruby podspec=ruby thor=ruby irb=ruby erl=erlang xlsx=excel xls=excel f90=fortran f95=fortran fs=fsharp
|
||||
gms=gams gss=gauss nc=gcode feature=gherkin golang=go hbs=htmlbars html.hbs=htmlbars html.handlebars=htmlbars
|
||||
htmlbars=htmlbars hs=haskell hx=haxe https=http hylang=hy i7=inform7 toml=ini jsp=java js=javascript
|
||||
jsx=javascript mjs=javascript cjs=javascript wildfly-cli=jboss-cli kt=kotlin kts=kotlin ls=livescript
|
||||
lassoscript=lasso tex=latex mk=makefile mak=makefile make=makefile mma=mathematica wl=mathematica m=mercury
|
||||
moo=mercury mips=mipsasm pl=perl pm=perl moon=moonscript nginxconf=nginx nixos=nix mm=objectivec
|
||||
objc=objectivec obj-c=objectivec obj-c++=objectivec objective-c++=objectivec ml=sml scad=openscad pf.conf=pf
|
||||
postgres=pgsql postgresql=pgsql php3=php php4=php php5=php php6=php php7=php php8=php text=plaintext
|
||||
txt=plaintext ps=powershell ps1=powershell pp=puppet pb=purebasic pbi=purebasic py=python gyp=python
|
||||
ipython=python pycon=python-repl k=q kdb=q qt=qml re=reasonml graph=roboconf instances=roboconf
|
||||
mikrotik=routeros rs=rust sci=scilab console=shell st=smalltalk mysql=sql_more oracle=sql_more stanfuncs=stan
|
||||
do=stata ado=stata p21=step21 step=step21 stp=step21 styl=stylus yml=yaml tk=tcl craftcms=twig ts=typescript
|
||||
tsx=typescript vb=vbnet vbs=vbscript v=verilog sv=verilog svh=verilog tao=xl xpath=xquery xq=xquery zep=zephir
|
||||
`
|
||||
.trim()
|
||||
.split(/\s+/);
|
||||
|
||||
export const HIGHLIGHT_LANGUAGE_ALIASES = new Map(
|
||||
aliasPairs.map((pair) => {
|
||||
const separator = pair.indexOf("=");
|
||||
return [pair.slice(0, separator), pair.slice(separator + 1)] as const;
|
||||
}),
|
||||
);
|
||||
|
||||
const dependencyPairs = `
|
||||
xml=css,javascript html=xml asciidoc=xml clojure-repl=clojure coffeescript=javascript cos=javascript,sql,xml
|
||||
markdown=xml dart=markdown django=xml dockerfile=bash dust=xml erb=ruby,xml haml=ruby handlebars=xml htmlbars=xml
|
||||
javascript=css,xml julia-repl=julia livescript=javascript perl=mojolicious mojolicious=perl,xml node-repl=javascript
|
||||
parser3=xml pgsql=bash,java,json,lua,perl,php,python,r,ruby,scheme,tcl,xml php-template=php,xml python-repl=python
|
||||
qml=xml shell=bash yaml=ruby tap=yaml toml=ini twig=xml typescript=css,xml vbscript-html=vbscript,xml xquery=xml
|
||||
`
|
||||
.trim()
|
||||
.split(/\s+/);
|
||||
|
||||
export const HIGHLIGHT_LANGUAGE_DEPENDENCIES = new Map(
|
||||
dependencyPairs.map((pair) => {
|
||||
const separator = pair.indexOf("=");
|
||||
return [pair.slice(0, separator), pair.slice(separator + 1).split(",")] as const;
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import hljs, { type LanguageDefinition } from "highlight.js/lib/core.js";
|
||||
import { HIGHLIGHT_LANGUAGES, type HighlightLanguage } from "./highlight-languages.ts";
|
||||
import {
|
||||
HIGHLIGHT_LANGUAGE_ALIASES,
|
||||
HIGHLIGHT_LANGUAGE_DEFINITIONS,
|
||||
HIGHLIGHT_LANGUAGE_DEPENDENCIES,
|
||||
HIGHLIGHT_LANGUAGE_LOADERS,
|
||||
} from "./highlight-languages.ts";
|
||||
import { decodeHtmlEntityAt } from "./html.ts";
|
||||
|
||||
export type HighlightFormatter = (text: string) => string;
|
||||
|
|
@ -12,64 +17,45 @@ export interface HighlightOptions {
|
|||
theme?: HighlightTheme;
|
||||
}
|
||||
|
||||
const LANGUAGE_BY_NAME = new Map<string, HighlightLanguage>();
|
||||
for (const language of HIGHLIGHT_LANGUAGES) {
|
||||
LANGUAGE_BY_NAME.set(language.name, language);
|
||||
for (const alias of language.aliases ?? []) LANGUAGE_BY_NAME.set(alias.toLowerCase(), language);
|
||||
}
|
||||
|
||||
const LANGUAGE_BY_EXTENSION = new Map(
|
||||
HIGHLIGHT_LANGUAGES.flatMap(({ name, extensions = [] }) =>
|
||||
extensions.map((extension) => [extension, name] as const),
|
||||
),
|
||||
);
|
||||
|
||||
const registeredLanguages = new Set<string>();
|
||||
|
||||
function registerLanguage(language: HighlightLanguage, definition: LanguageDefinition): void {
|
||||
if (registeredLanguages.has(language.name)) return;
|
||||
hljs.registerLanguage(language.name, definition);
|
||||
registeredLanguages.add(language.name);
|
||||
}
|
||||
|
||||
for (const language of HIGHLIGHT_LANGUAGES) {
|
||||
if (language.definition) registerLanguage(language, language.definition);
|
||||
interface HighlightLanguage {
|
||||
name: string;
|
||||
definition: LanguageDefinition;
|
||||
}
|
||||
|
||||
type HighlightLanguageLoadEvent =
|
||||
| { language: string; requiresGlobalRefresh: boolean }
|
||||
| { language: string; languages: HighlightLanguage[] }
|
||||
| { language: string; error: unknown };
|
||||
type HighlightLanguageLoadListener = (event: HighlightLanguageLoadEvent) => void;
|
||||
|
||||
interface LanguageLoadState {
|
||||
definition?: LanguageDefinition;
|
||||
promise: Promise<LanguageDefinition>;
|
||||
}
|
||||
|
||||
interface LanguageRequestState {
|
||||
promise: Promise<boolean>;
|
||||
callbacks: Set<() => void>;
|
||||
requiresGlobalRefresh: boolean;
|
||||
status: "pending" | "ready" | "failed";
|
||||
}
|
||||
|
||||
interface LanguageRegistryState {
|
||||
loads: Map<string, LanguageLoadState>;
|
||||
definitions: Map<string, Promise<LanguageDefinition>>;
|
||||
requests: Map<string, Promise<HighlightLanguage[]>>;
|
||||
listeners: Set<HighlightLanguageLoadListener>;
|
||||
requests?: Map<string, LanguageRequestState>;
|
||||
}
|
||||
|
||||
// jiti can evaluate this module more than once. Sharing imported definitions
|
||||
// and listeners lets extension-triggered loads refresh the host TUI.
|
||||
const LANGUAGE_REGISTRY_KEY = Symbol.for("@earendil-works/pi-coding-agent:highlight-language-loaders-v3");
|
||||
// jiti can evaluate this module more than once. Share imported definitions and
|
||||
// listeners so extension-triggered loads still refresh the host TUI.
|
||||
const LANGUAGE_REGISTRY_KEY = Symbol.for("@earendil-works/pi-coding-agent:highlight-language-loaders-v4");
|
||||
const globalRegistry = globalThis as Record<symbol, LanguageRegistryState | undefined>;
|
||||
const languageRegistry: LanguageRegistryState = globalRegistry[LANGUAGE_REGISTRY_KEY] ?? {
|
||||
loads: new Map(),
|
||||
definitions: new Map(),
|
||||
requests: new Map(),
|
||||
listeners: new Set(),
|
||||
};
|
||||
globalRegistry[LANGUAGE_REGISTRY_KEY] = languageRegistry;
|
||||
if (!languageRegistry.requests) languageRegistry.requests = new Map();
|
||||
const languageRequests = languageRegistry.requests;
|
||||
|
||||
const registeredLanguages = new Set<string>();
|
||||
for (const [name, definition] of Object.entries(HIGHLIGHT_LANGUAGE_DEFINITIONS)) {
|
||||
hljs.registerLanguage(name, definition);
|
||||
registeredLanguages.add(name);
|
||||
}
|
||||
|
||||
function getCanonicalLanguageName(name: string): string | undefined {
|
||||
if (Object.hasOwn(HIGHLIGHT_LANGUAGE_DEFINITIONS, name) || Object.hasOwn(HIGHLIGHT_LANGUAGE_LOADERS, name)) {
|
||||
return name;
|
||||
}
|
||||
return HIGHLIGHT_LANGUAGE_ALIASES.get(name);
|
||||
}
|
||||
|
||||
function notifyLanguageLoad(event: HighlightLanguageLoadEvent): void {
|
||||
if (languageRegistry.listeners.size > 0) {
|
||||
|
|
@ -80,131 +66,93 @@ function notifyLanguageLoad(event: HighlightLanguageLoadEvent): void {
|
|||
}
|
||||
}
|
||||
|
||||
function getLanguageClosure(language: HighlightLanguage): HighlightLanguage[] {
|
||||
const result: HighlightLanguage[] = [];
|
||||
const visited = new Set<string>();
|
||||
const visit = (current: HighlightLanguage) => {
|
||||
if (visited.has(current.name)) return;
|
||||
visited.add(current.name);
|
||||
for (const dependencyName of current.dependencies ?? []) {
|
||||
const dependency = LANGUAGE_BY_NAME.get(dependencyName);
|
||||
if (dependency) visit(dependency);
|
||||
}
|
||||
result.push(current);
|
||||
};
|
||||
visit(language);
|
||||
return result;
|
||||
}
|
||||
function loadLanguageDefinition(name: string): Promise<LanguageDefinition> {
|
||||
const eagerDefinition = HIGHLIGHT_LANGUAGE_DEFINITIONS[name];
|
||||
if (eagerDefinition) return Promise.resolve(eagerDefinition);
|
||||
|
||||
function ensureLanguagesRegistered(languages: HighlightLanguage[], requestedName: string): boolean {
|
||||
for (const language of languages) {
|
||||
const definition = language.definition ?? languageRegistry.loads.get(language.name)?.definition;
|
||||
if (definition) registerLanguage(language, definition);
|
||||
}
|
||||
return (
|
||||
languages.every(
|
||||
(language) =>
|
||||
registeredLanguages.has(language.name) ||
|
||||
(!language.definition && !language.load && supportsLanguage(language.name)),
|
||||
) && supportsLanguage(requestedName)
|
||||
);
|
||||
}
|
||||
|
||||
function loadLanguageDefinition(language: HighlightLanguage): Promise<LanguageDefinition> | undefined {
|
||||
if (language.definition) return Promise.resolve(language.definition);
|
||||
if (!language.load) return undefined;
|
||||
|
||||
let state = languageRegistry.loads.get(language.name);
|
||||
if (!state) {
|
||||
const promise = Promise.resolve()
|
||||
.then(language.load)
|
||||
let promise = languageRegistry.definitions.get(name);
|
||||
if (!promise) {
|
||||
const loader = HIGHLIGHT_LANGUAGE_LOADERS[name];
|
||||
promise = Promise.resolve()
|
||||
.then(loader)
|
||||
.then(
|
||||
({ default: definition }) => {
|
||||
const currentState = languageRegistry.loads.get(language.name);
|
||||
if (currentState) currentState.definition = definition;
|
||||
return definition;
|
||||
},
|
||||
({ default: definition }) => definition,
|
||||
(error: unknown) => {
|
||||
notifyLanguageLoad({ language: language.name, error });
|
||||
notifyLanguageLoad({ language: name, error });
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
state = { promise };
|
||||
languageRegistry.loads.set(language.name, state);
|
||||
languageRegistry.definitions.set(name, promise);
|
||||
}
|
||||
return state.promise;
|
||||
return promise;
|
||||
}
|
||||
|
||||
function getLanguageRequest(language: HighlightLanguage, languages: HighlightLanguage[]): LanguageRequestState {
|
||||
let request = languageRequests.get(language.name);
|
||||
if (request) return request;
|
||||
|
||||
const callbacks = new Set<() => void>();
|
||||
const promises: Promise<LanguageDefinition>[] = [];
|
||||
for (const current of languages) {
|
||||
const promise = loadLanguageDefinition(current);
|
||||
if (promise) promises.push(promise);
|
||||
async function loadLanguageClosure(name: string, visited = new Set<string>()): Promise<HighlightLanguage[]> {
|
||||
if (visited.has(name)) return [];
|
||||
visited.add(name);
|
||||
const definition = await loadLanguageDefinition(name);
|
||||
const languages: HighlightLanguage[] = [];
|
||||
for (const dependency of HIGHLIGHT_LANGUAGE_DEPENDENCIES.get(name) ?? []) {
|
||||
languages.push(...(await loadLanguageClosure(dependency, visited)));
|
||||
}
|
||||
languages.push({ name, definition });
|
||||
return languages;
|
||||
}
|
||||
|
||||
function registerLanguages(languages: HighlightLanguage[], requestedName: string): boolean {
|
||||
for (const { name, definition } of languages) {
|
||||
if (registeredLanguages.has(name)) continue;
|
||||
hljs.registerLanguage(name, definition);
|
||||
registeredLanguages.add(name);
|
||||
}
|
||||
return registeredLanguages.has(requestedName);
|
||||
}
|
||||
|
||||
function getLanguageRequest(name: string): Promise<HighlightLanguage[]> {
|
||||
let request = languageRegistry.requests.get(name);
|
||||
if (!request) {
|
||||
request = loadLanguageClosure(name);
|
||||
languageRegistry.requests.set(name, request);
|
||||
void request.then(
|
||||
(languages) => notifyLanguageLoad({ language: name, languages }),
|
||||
() => {},
|
||||
);
|
||||
}
|
||||
const promise = Promise.all(promises).then(() => ensureLanguagesRegistered(languages, language.name));
|
||||
request = { promise, callbacks, requiresGlobalRefresh: false, status: "pending" };
|
||||
languageRequests.set(language.name, request);
|
||||
void promise.then(
|
||||
(ready) => {
|
||||
request.status = ready ? "ready" : "failed";
|
||||
if (ready) {
|
||||
for (const callback of callbacks) callback();
|
||||
notifyLanguageLoad({ language: language.name, requiresGlobalRefresh: request.requiresGlobalRefresh });
|
||||
}
|
||||
callbacks.clear();
|
||||
},
|
||||
() => {
|
||||
request.status = "failed";
|
||||
callbacks.clear();
|
||||
},
|
||||
);
|
||||
return request;
|
||||
}
|
||||
|
||||
export function onHighlightLanguageLoad(listener: HighlightLanguageLoadListener): () => void {
|
||||
languageRegistry.listeners.add(listener);
|
||||
return () => languageRegistry.listeners.delete(listener);
|
||||
const localListener: HighlightLanguageLoadListener = (event) => {
|
||||
if ("languages" in event) registerLanguages(event.languages, event.language);
|
||||
listener(event);
|
||||
};
|
||||
languageRegistry.listeners.add(localListener);
|
||||
return () => languageRegistry.listeners.delete(localListener);
|
||||
}
|
||||
|
||||
export async function loadHighlightLanguage(name: string): Promise<boolean> {
|
||||
const normalizedName = name.toLowerCase();
|
||||
const language = LANGUAGE_BY_NAME.get(normalizedName);
|
||||
if (!language) return supportsLanguage(normalizedName);
|
||||
|
||||
const languages = getLanguageClosure(language);
|
||||
if (ensureLanguagesRegistered(languages, language.name)) return true;
|
||||
const request = getLanguageRequest(language, languages);
|
||||
request.requiresGlobalRefresh = true;
|
||||
return request.promise;
|
||||
const canonicalName = getCanonicalLanguageName(normalizedName);
|
||||
if (!canonicalName) return supportsLanguage(normalizedName);
|
||||
if (registeredLanguages.has(canonicalName)) return true;
|
||||
return registerLanguages(await getLanguageRequest(canonicalName), canonicalName);
|
||||
}
|
||||
|
||||
/** Return the canonical grammar when ready, starting a background load when needed. */
|
||||
export function requestHighlightLanguage(name: string, onReady?: () => void): string | undefined {
|
||||
export function requestHighlightLanguage(name: string): string | undefined {
|
||||
const normalizedName = name.toLowerCase();
|
||||
const language = LANGUAGE_BY_NAME.get(normalizedName);
|
||||
if (!language) return supportsLanguage(normalizedName) ? normalizedName : undefined;
|
||||
|
||||
const languages = getLanguageClosure(language);
|
||||
if (ensureLanguagesRegistered(languages, language.name)) return language.name;
|
||||
if (!languages.some((current) => current.load)) return undefined;
|
||||
|
||||
const request = getLanguageRequest(language, languages);
|
||||
if (request.status === "pending") {
|
||||
if (onReady) request.callbacks.add(onReady);
|
||||
else request.requiresGlobalRefresh = true;
|
||||
const canonicalName = getCanonicalLanguageName(normalizedName);
|
||||
if (!canonicalName) return supportsLanguage(normalizedName) ? normalizedName : undefined;
|
||||
if (registeredLanguages.has(canonicalName)) {
|
||||
return normalizedName === "html" || normalizedName === "toml" ? normalizedName : canonicalName;
|
||||
}
|
||||
void getLanguageRequest(canonicalName).then(
|
||||
(languages) => registerLanguages(languages, canonicalName),
|
||||
() => {},
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getLanguageFromPath(filePath: string): string | undefined {
|
||||
const extension = filePath.split(".").pop()?.toLowerCase();
|
||||
return extension ? LANGUAGE_BY_EXTENSION.get(extension) : undefined;
|
||||
}
|
||||
|
||||
const SPAN_CLOSE = "</span>";
|
||||
const HIGHLIGHT_CLASS_PREFIX = "hljs-";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { resetCapabilitiesCache, setCapabilities, type TUI } from "@earendil-works/pi-tui";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
import { highlightCode, initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
import { getLanguageFromPath, highlightCode, initTheme } from "../src/modes/interactive/theme/theme.ts";
|
||||
import { InteractiveThemeController } from "../src/modes/interactive/theme/theme-controller.ts";
|
||||
import { HIGHLIGHT_LANGUAGES } from "../src/utils/highlight-languages.ts";
|
||||
import { HIGHLIGHT_LANGUAGE_LOADERS } from "../src/utils/highlight-languages.ts";
|
||||
import {
|
||||
getLanguageFromPath,
|
||||
highlight,
|
||||
loadHighlightLanguage,
|
||||
onHighlightLanguageLoad,
|
||||
|
|
@ -60,72 +59,31 @@ describe("syntax highlight renderer", () => {
|
|||
});
|
||||
|
||||
it("maps file extensions and preloads only common languages", () => {
|
||||
expect(getLanguageFromPath("src/main.ts")).toBe("typescript");
|
||||
expect(getLanguageFromPath("public/index.html")).toBe("html");
|
||||
expect(getLanguageFromPath("Dockerfile")).toBe("dockerfile");
|
||||
expect(getLanguageFromPath("build.ps1")).toBe("powershell");
|
||||
expect(getLanguageFromPath("config.toml")).toBe("toml");
|
||||
expect(getLanguageFromPath("shell.fish")).toBe("fish");
|
||||
expect(getLanguageFromPath("style.sass")).toBe("sass");
|
||||
expect(getLanguageFromPath("schema.graphql")).toBe("graphql");
|
||||
expect(getLanguageFromPath("main.tf")).toBe("hcl");
|
||||
expect(getLanguageFromPath("config.hcl")).toBe("hcl");
|
||||
expect(supportsLanguage(getLanguageFromPath("src/main.ts")!)).toBe(true);
|
||||
expect(supportsLanguage(getLanguageFromPath("public/index.html")!)).toBe(true);
|
||||
expect(supportsLanguage(getLanguageFromPath("Dockerfile")!)).toBe(false);
|
||||
expect(supportsLanguage(getLanguageFromPath("build.ps1")!)).toBe(false);
|
||||
const handlebars = HIGHLIGHT_LANGUAGES.find(({ name }) => name === "handlebars");
|
||||
expect(handlebars?.definition).toBeUndefined();
|
||||
expect(handlebars?.load).toBeTypeOf("function");
|
||||
expect(["main.ts", "index.html", "Dockerfile", "build.ps1", "main.tf"].map(getLanguageFromPath)).toEqual([
|
||||
"typescript",
|
||||
"html",
|
||||
"dockerfile",
|
||||
"powershell",
|
||||
"hcl",
|
||||
]);
|
||||
for (const common of ["typescript", "html"]) expect(supportsLanguage(common)).toBe(true);
|
||||
for (const lazy of ["dockerfile", "powershell"]) expect(supportsLanguage(lazy)).toBe(false);
|
||||
for (const unsupported of ["fish", "graphql", "hcl", "sass"]) {
|
||||
expect(requestHighlightLanguage(unsupported)).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it("normalizes names and loads aliases through their canonical grammar", async () => {
|
||||
expect(requestHighlightLanguage("ToMl")).toBeUndefined();
|
||||
expect(await loadHighlightLanguage("TOML")).toBe(true);
|
||||
expect(supportsLanguage("ini")).toBe(true);
|
||||
expect(supportsLanguage("toml")).toBe(true);
|
||||
expect(requestHighlightLanguage("ToMl")).toBe("toml");
|
||||
it("loads one uncommon grammar with its aliases and dependencies", async () => {
|
||||
const loaded: string[] = [];
|
||||
const unsubscribe = onHighlightLanguageLoad((event) => {
|
||||
if (!("error" in event)) loaded.push(event.language);
|
||||
});
|
||||
|
||||
expect(requestHighlightLanguage("Rust")).toBeUndefined();
|
||||
expect(await loadHighlightLanguage("RUST")).toBe(true);
|
||||
expect(supportsLanguage("rust")).toBe(true);
|
||||
expect(requestHighlightLanguage("Rust")).toBe("rust");
|
||||
expect(requestHighlightLanguage("BF")).toBeUndefined();
|
||||
expect(await loadHighlightLanguage("BF")).toBe(true);
|
||||
expect(requestHighlightLanguage("bf")).toBe("brainfuck");
|
||||
expect(supportsLanguage("abnf")).toBe(false);
|
||||
|
||||
expect(await loadHighlightLanguage("PowerShell")).toBe(true);
|
||||
expect(supportsLanguage("POWERSHELL")).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps duplicate aliases bound to their canonical grammar", async () => {
|
||||
expect(await loadHighlightLanguage("ls")).toBe(true);
|
||||
expect(requestHighlightLanguage("ls")).toBe("livescript");
|
||||
expect(await loadHighlightLanguage("lasso")).toBe(true);
|
||||
expect(requestHighlightLanguage("ls")).toBe("livescript");
|
||||
|
||||
expect(await loadHighlightLanguage("ml")).toBe(true);
|
||||
expect(requestHighlightLanguage("ml")).toBe("sml");
|
||||
expect(await loadHighlightLanguage("ocaml")).toBe(true);
|
||||
expect(requestHighlightLanguage("ml")).toBe("sml");
|
||||
|
||||
expect(await loadHighlightLanguage("hbs")).toBe(true);
|
||||
expect(requestHighlightLanguage("hbs")).toBe("htmlbars");
|
||||
expect(await loadHighlightLanguage("handlebars")).toBe(true);
|
||||
expect(requestHighlightLanguage("hbs")).toBe("htmlbars");
|
||||
});
|
||||
|
||||
it("loads required sublanguage grammars", async () => {
|
||||
expect(supportsLanguage("ruby")).toBe(true);
|
||||
expect(await loadHighlightLanguage("ERB")).toBe(true);
|
||||
expect(
|
||||
highlight("<%= User.find(1) %>", {
|
||||
language: "erb",
|
||||
theme: { number: (text) => `[number:${text}]` },
|
||||
}),
|
||||
).toContain("[number:1]");
|
||||
|
||||
expect(supportsLanguage("php")).toBe(false);
|
||||
expect(await loadHighlightLanguage("php-template")).toBe(true);
|
||||
expect(supportsLanguage("php")).toBe(true);
|
||||
expect(
|
||||
|
|
@ -134,37 +92,24 @@ describe("syntax highlight renderer", () => {
|
|||
theme: { keyword: (text) => `[keyword:${text}]` },
|
||||
}),
|
||||
).toContain("[keyword:echo]");
|
||||
expect(loaded).toEqual(["brainfuck", "php-template"]);
|
||||
unsubscribe();
|
||||
|
||||
expect(await loadHighlightLanguage("TOML")).toBe(true);
|
||||
expect(requestHighlightLanguage("ToMl")).toBe("toml");
|
||||
expect(await loadHighlightLanguage("mojolicious")).toBe(true);
|
||||
expect(supportsLanguage("perl")).toBe(true);
|
||||
});
|
||||
|
||||
it("loads only the requested uncommon language", async () => {
|
||||
expect(supportsLanguage("brainfuck")).toBe(false);
|
||||
expect(supportsLanguage("abnf")).toBe(false);
|
||||
let notifications = 0;
|
||||
const unsubscribe = onHighlightLanguageLoad(() => notifications++);
|
||||
|
||||
expect(requestHighlightLanguage("bf")).toBeUndefined();
|
||||
expect(await loadHighlightLanguage("bf")).toBe(true);
|
||||
expect(supportsLanguage("brainfuck")).toBe(true);
|
||||
expect(supportsLanguage("bf")).toBe(true);
|
||||
expect(supportsLanguage("abnf")).toBe(false);
|
||||
expect(notifications).toBe(1);
|
||||
|
||||
expect(await loadHighlightLanguage("abnf")).toBe(true);
|
||||
expect(supportsLanguage("abnf")).toBe(true);
|
||||
expect(notifications).toBe(2);
|
||||
unsubscribe();
|
||||
expect(await loadHighlightLanguage("ls")).toBe(true);
|
||||
expect(await loadHighlightLanguage("lasso")).toBe(true);
|
||||
expect(requestHighlightLanguage("ls")).toBe("livescript");
|
||||
});
|
||||
|
||||
it("reports background load failures once without retrying", async () => {
|
||||
const language = HIGHLIGHT_LANGUAGES.find(({ name }) => name === "accesslog");
|
||||
if (!language?.load) throw new Error("accesslog test grammar is missing");
|
||||
const originalLoad = language.load;
|
||||
const originalLoad = HIGHLIGHT_LANGUAGE_LOADERS.accesslog;
|
||||
if (!originalLoad) throw new Error("accesslog test grammar is missing");
|
||||
const loadError = new Error("test grammar load failed");
|
||||
let attempts = 0;
|
||||
language.load = () => {
|
||||
HIGHLIGHT_LANGUAGE_LOADERS.accesslog = () => {
|
||||
attempts++;
|
||||
return Promise.reject(loadError);
|
||||
};
|
||||
|
|
@ -185,14 +130,14 @@ describe("syntax highlight renderer", () => {
|
|||
expect(attempts).toBe(1);
|
||||
await expect(loadHighlightLanguage("accesslog")).rejects.toThrow("test grammar load failed");
|
||||
} finally {
|
||||
language.load = originalLoad;
|
||||
HIGHLIGHT_LANGUAGE_LOADERS.accesslog = originalLoad;
|
||||
unsubscribe();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("highlight language load lifecycle", () => {
|
||||
it("invalidates only affected targets while retaining a global fallback", async () => {
|
||||
it("refreshes the TUI when a grammar loads", async () => {
|
||||
const invalidate = vi.fn();
|
||||
const requestRender = vi.fn();
|
||||
const unsubscribeTerminalTheme = vi.fn();
|
||||
|
|
@ -203,62 +148,19 @@ describe("highlight language load lifecycle", () => {
|
|||
} as unknown as TUI;
|
||||
const settingsManager = { getThemeSetting: () => "dark" } as unknown as SettingsManager;
|
||||
const controller = new InteractiveThemeController(ui, settingsManager, vi.fn(), vi.fn());
|
||||
const arcade = HIGHLIGHT_LANGUAGES.find(({ name }) => name === "arcade");
|
||||
const oneC = HIGHLIGHT_LANGUAGES.find(({ name }) => name === "1c");
|
||||
const eager = HIGHLIGHT_LANGUAGES.find(({ name }) => name === "bash");
|
||||
if (!arcade?.load || !oneC?.load || !eager?.definition) throw new Error("highlight test grammars are missing");
|
||||
const originalArcadeLoad = arcade.load;
|
||||
const originalOneCLoad = oneC.load;
|
||||
arcade.load = () => Promise.resolve({ default: eager.definition! });
|
||||
oneC.load = () => Promise.resolve({ default: eager.definition! });
|
||||
|
||||
try {
|
||||
const loaded = new Set<string>();
|
||||
let resolveLoads = (): void => {};
|
||||
const loadsFinished = new Promise<void>((resolve) => {
|
||||
resolveLoads = resolve;
|
||||
});
|
||||
const unsubscribeLoads = onHighlightLanguageLoad((event) => {
|
||||
if ("error" in event) return;
|
||||
loaded.add(event.language);
|
||||
if (loaded.has("arcade") && loaded.has("1c")) resolveLoads();
|
||||
});
|
||||
const invalidateArcade = vi.fn();
|
||||
const invalidateOneC = vi.fn();
|
||||
highlightCode("let value = 1", "arcade");
|
||||
await loadHighlightLanguage("arcade");
|
||||
expect(invalidate).toHaveBeenCalledOnce();
|
||||
expect(requestRender).toHaveBeenCalledOnce();
|
||||
|
||||
highlightCode("let value = 1", "arcade", { onLanguageReady: invalidateArcade });
|
||||
highlightCode("let value = 1", "1c", { onLanguageReady: invalidateOneC });
|
||||
await loadsFinished;
|
||||
unsubscribeLoads();
|
||||
|
||||
expect(invalidateArcade).toHaveBeenCalledOnce();
|
||||
expect(invalidateOneC).toHaveBeenCalledOnce();
|
||||
expect(invalidate).not.toHaveBeenCalled();
|
||||
expect(requestRender).not.toHaveBeenCalled();
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(invalidate).not.toHaveBeenCalled();
|
||||
expect(requestRender).toHaveBeenCalledOnce();
|
||||
requestRender.mockClear();
|
||||
|
||||
highlightCode("package main", "ada");
|
||||
await loadHighlightLanguage("ada");
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(invalidate).toHaveBeenCalledOnce();
|
||||
expect(requestRender).toHaveBeenCalledOnce();
|
||||
invalidate.mockClear();
|
||||
requestRender.mockClear();
|
||||
|
||||
controller.dispose();
|
||||
await loadHighlightLanguage("apache");
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(unsubscribeTerminalTheme).toHaveBeenCalledOnce();
|
||||
expect(invalidate).not.toHaveBeenCalled();
|
||||
expect(requestRender).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
arcade.load = originalArcadeLoad;
|
||||
oneC.load = originalOneCLoad;
|
||||
controller.dispose();
|
||||
}
|
||||
invalidate.mockClear();
|
||||
requestRender.mockClear();
|
||||
controller.dispose();
|
||||
await loadHighlightLanguage("apache");
|
||||
expect(unsubscribeTerminalTheme).toHaveBeenCalledOnce();
|
||||
expect(invalidate).not.toHaveBeenCalled();
|
||||
expect(requestRender).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ export interface MarkdownTheme {
|
|||
italic: (text: string) => string;
|
||||
strikethrough: (text: string) => string;
|
||||
underline: (text: string) => string;
|
||||
highlightCode?: (code: string, lang?: string, onLanguageReady?: () => void) => string[];
|
||||
highlightCode?: (code: string, lang?: string) => string[];
|
||||
/** Prefix applied to each rendered code block line (default: " ") */
|
||||
codeBlockIndent?: string;
|
||||
}
|
||||
|
|
@ -241,7 +241,6 @@ export class Markdown implements Component {
|
|||
private theme: MarkdownTheme;
|
||||
private options: MarkdownOptions;
|
||||
private defaultStylePrefix?: string;
|
||||
private readonly handleHighlightLanguageReady = (): void => this.invalidate();
|
||||
|
||||
// Cache for rendered output
|
||||
private cachedText?: string;
|
||||
|
|
@ -522,11 +521,7 @@ export class Markdown implements Component {
|
|||
const indent = this.theme.codeBlockIndent ?? " ";
|
||||
lines.push(this.theme.codeBlockBorder(`\`\`\`${token.lang || ""}`));
|
||||
if (this.theme.highlightCode) {
|
||||
const highlightedLines = this.theme.highlightCode(
|
||||
token.text,
|
||||
token.lang,
|
||||
this.handleHighlightLanguageReady,
|
||||
);
|
||||
const highlightedLines = this.theme.highlightCode(token.text, token.lang);
|
||||
for (const hlLine of highlightedLines) {
|
||||
lines.push(`${indent}${hlLine}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,28 +38,6 @@ function stripAnsi(line: string): string {
|
|||
|
||||
describe("Markdown component", () => {
|
||||
describe("Transforms", () => {
|
||||
it("invalidates its cache when a code language finishes loading", () => {
|
||||
const callbacks: Array<() => void> = [];
|
||||
let highlightCalls = 0;
|
||||
const markdown = new Markdown("```example\ncode\n```", 0, 0, {
|
||||
...defaultMarkdownTheme,
|
||||
highlightCode: (_code, _lang, onLanguageReady) => {
|
||||
highlightCalls++;
|
||||
if (onLanguageReady) callbacks.push(onLanguageReady);
|
||||
return ["code"];
|
||||
},
|
||||
});
|
||||
|
||||
markdown.render(80);
|
||||
markdown.render(80);
|
||||
assert.strictEqual(highlightCalls, 1);
|
||||
|
||||
callbacks[0]?.();
|
||||
markdown.render(80);
|
||||
assert.strictEqual(highlightCalls, 2);
|
||||
assert.strictEqual(callbacks[0], callbacks[1]);
|
||||
});
|
||||
|
||||
it("caches transformed Markdown by source and available width", () => {
|
||||
const calls: Array<{ source: string; availableWidth: number }> = [];
|
||||
const markdown = new Markdown("source", 2, 0, defaultMarkdownTheme, undefined, {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue