mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-01 09:40:28 +08:00
Changes which were needed after the upgrade: - Replace vite-plugin-virtual (vite 8 incompatible) with vite's built-in `define` for the buildTimeConfig injection - tsconfig: drop deprecated `baseUrl` (TS 5+ resolves `paths` relative to tsconfig) - Vitest 4: import `defineConfig` from `vitest/config` so `test` is valid in vite configs - Zod 4: rename `.errors` to `.issues`, make `ZodSchema` a type-only import, drop the removed `SafeParseReturnType` - Tighten/loosen generic casts in localStore to match stricter zod 4 inference - ESLint 9 `no-useless-assignment`: remove redundant initializers and dead reassignments; disable the rule where Svelte's reactive blocks confuse the analyser - Svelte/eslint `prefer-svelte-reactivity`: disable for legacy Svelte usages of URL / Set - Misc naming-convention and prefer-const fixes
239 lines
6.6 KiB
TypeScript
239 lines
6.6 KiB
TypeScript
import type { ElementContent, Root } from "hast";
|
|
|
|
import onigurumaWASMUrl from "vscode-oniguruma/release/onig.wasm?url";
|
|
import sourceAsciiDoc from "@wooorm/starry-night/text.html.asciidoc";
|
|
import sourceDockerfile from "@wooorm/starry-night/source.dockerfile";
|
|
import sourceErlang from "@wooorm/starry-night/source.erlang";
|
|
import sourceSolidity from "@wooorm/starry-night/source.solidity";
|
|
import sourceSvelte from "@wooorm/starry-night/source.svelte";
|
|
import sourceSass from "@wooorm/starry-night/source.sass";
|
|
import sourceToml from "@wooorm/starry-night/source.toml";
|
|
import sourceTsx from "@wooorm/starry-night/source.tsx";
|
|
import sourceNix from "@wooorm/starry-night/source.nix";
|
|
import sourceGitconfig from "@wooorm/starry-night/source.gitconfig";
|
|
import sourceGitignore from "@wooorm/starry-night/source.gitignore";
|
|
import sourceGitrevlist from "@wooorm/starry-night/source.git-revlist";
|
|
import sourceGitattributes from "@wooorm/starry-night/source.gitattributes";
|
|
import sourceJson from "@wooorm/starry-night/source.json";
|
|
import sourceNpmrc from "@wooorm/starry-night/source.ini.npmrc";
|
|
import sourceGradle from "@wooorm/starry-night/source.groovy.gradle";
|
|
import sourceBatchfile from "@wooorm/starry-night/source.batchfile";
|
|
import sourceEditorconfig from "@wooorm/starry-night/source.editorconfig";
|
|
import sourceHaproxyConfig from "@wooorm/starry-night/source.haproxy-config";
|
|
import sourceDotenv from "@wooorm/starry-night/source.dotenv";
|
|
import sourceZig from "@wooorm/starry-night/source.zig";
|
|
import textHtmlVue from "@wooorm/starry-night/text.html.vue";
|
|
import textHtmlDjango from "@wooorm/starry-night/text.html.django";
|
|
import textRobotsTxt from "@wooorm/starry-night/text.robots-txt";
|
|
import textZoneFile from "@wooorm/starry-night/text.zone_file";
|
|
import etc from "@wooorm/starry-night/etc";
|
|
import goMod from "@wooorm/starry-night/go.mod";
|
|
import goSum from "@wooorm/starry-night/go.sum";
|
|
|
|
import { createStarryNight, common, type Grammar } from "@wooorm/starry-night";
|
|
|
|
export { type Root };
|
|
|
|
export const grammars = [
|
|
...common,
|
|
sourceAsciiDoc,
|
|
sourceToml,
|
|
sourceErlang,
|
|
sourceSolidity,
|
|
sourceSvelte,
|
|
sourceSass,
|
|
sourceTsx,
|
|
sourceDockerfile,
|
|
sourceNix,
|
|
sourceGitconfig,
|
|
sourceGitignore,
|
|
sourceGitrevlist,
|
|
sourceGitattributes,
|
|
sourceNpmrc,
|
|
sourceGradle,
|
|
sourceBatchfile,
|
|
sourceEditorconfig,
|
|
sourceHaproxyConfig,
|
|
sourceDotenv,
|
|
sourceZig,
|
|
textHtmlVue,
|
|
textHtmlDjango,
|
|
textRobotsTxt,
|
|
textZoneFile,
|
|
etc,
|
|
goMod,
|
|
goSum,
|
|
{
|
|
extensions: [".hintrc"],
|
|
names: ["json"],
|
|
patterns: [sourceJson],
|
|
scopeName: "source.json",
|
|
},
|
|
{
|
|
extensions: [
|
|
".npmignore",
|
|
".eslintignore",
|
|
".dockerignore",
|
|
".nuxtignore",
|
|
".vscodeignore",
|
|
],
|
|
names: ["ignore"],
|
|
patterns: [sourceGitignore],
|
|
scopeName: "source.gitignore",
|
|
},
|
|
{
|
|
extensions: [".sample", ".example", ".template"],
|
|
names: [".env.sample", ".env.example", ".env.template"],
|
|
patterns: [sourceDotenv],
|
|
scopeName: "source.dotenv",
|
|
},
|
|
{
|
|
extensions: [".mod"],
|
|
names: ["go.mod"],
|
|
patterns: [goMod],
|
|
scopeName: "go.mode",
|
|
},
|
|
{
|
|
extensions: [".sum"],
|
|
names: ["go.sum"],
|
|
patterns: [goSum],
|
|
scopeName: "go.sum",
|
|
},
|
|
// A grammar that doesn't do any parsing, but needed for files without a known filetype.
|
|
{
|
|
extensions: [""],
|
|
names: ["raw-format"],
|
|
patterns: [],
|
|
scopeName: "text.raw",
|
|
},
|
|
] satisfies Grammar[];
|
|
|
|
let starryNight: Awaited<ReturnType<typeof createStarryNight>>;
|
|
|
|
export async function highlight(
|
|
content: string,
|
|
grammar: string,
|
|
): Promise<Root> {
|
|
if (starryNight === undefined) {
|
|
starryNight = await createStarryNight(grammars, {
|
|
getOnigurumaUrlFetch: () => new URL(onigurumaWASMUrl, import.meta.url),
|
|
});
|
|
}
|
|
const scope = starryNight.flagToScope(grammar);
|
|
return starryNight.highlight(content, scope ?? "text.raw");
|
|
}
|
|
|
|
export function lineNumbersGutter(tree: Root) {
|
|
const replacement: ElementContent[] = [];
|
|
const search = /\r?\n|\r/g;
|
|
let index = -1;
|
|
let start = 0;
|
|
let startTextRemainder = "";
|
|
let lineNumber = 0;
|
|
|
|
while (++index < tree.children.length) {
|
|
const child = tree.children[index];
|
|
|
|
if (child.type === "text") {
|
|
let textStart = 0;
|
|
let match = search.exec(child.value);
|
|
|
|
while (match) {
|
|
// Nodes in this line.
|
|
const line = tree.children.slice(start, index) as ElementContent[];
|
|
|
|
// Prepend text from a partial matched earlier text.
|
|
if (startTextRemainder) {
|
|
line.unshift({ type: "text", value: startTextRemainder });
|
|
startTextRemainder = "";
|
|
}
|
|
|
|
// Append text from this text.
|
|
if (match.index > textStart) {
|
|
line.push({
|
|
type: "text",
|
|
value: child.value.slice(textStart, match.index),
|
|
});
|
|
}
|
|
|
|
// Add a line, and the eol.
|
|
lineNumber += 1;
|
|
replacement.push(createLine(line, lineNumber), {
|
|
type: "text",
|
|
value: match[0],
|
|
});
|
|
|
|
start = index + 1;
|
|
textStart = match.index + match[0].length;
|
|
match = search.exec(child.value);
|
|
}
|
|
|
|
// If we matched, make sure to not drop the text after the last line ending.
|
|
if (start === index + 1) {
|
|
startTextRemainder = child.value.slice(textStart);
|
|
}
|
|
}
|
|
}
|
|
|
|
const line = tree.children.slice(start) as ElementContent[];
|
|
// Prepend text from a partial matched earlier text.
|
|
if (startTextRemainder) {
|
|
line.unshift({ type: "text", value: startTextRemainder });
|
|
}
|
|
|
|
if (line.length > 0) {
|
|
lineNumber += 1;
|
|
replacement.push(createLine(line, lineNumber));
|
|
}
|
|
|
|
// Replace children with new array.
|
|
tree.children = replacement;
|
|
|
|
return tree;
|
|
}
|
|
|
|
function createLine(children: ElementContent[], line: number): ElementContent {
|
|
return {
|
|
type: "element",
|
|
tagName: "tr",
|
|
properties: {
|
|
class: "line",
|
|
id: "L" + line,
|
|
},
|
|
children: [
|
|
{
|
|
type: "element",
|
|
tagName: "td",
|
|
properties: {
|
|
className: "line-number",
|
|
},
|
|
children: [
|
|
{
|
|
type: "element",
|
|
tagName: "a",
|
|
properties: { href: "#L" + line },
|
|
children: [{ type: "text", value: line.toString() }],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: "element",
|
|
tagName: "td",
|
|
properties: {
|
|
className: "line-content",
|
|
},
|
|
children: [
|
|
{
|
|
type: "element",
|
|
tagName: "pre",
|
|
properties: {
|
|
className: "content",
|
|
},
|
|
children,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
}
|