char/sorcery

static-files based git repo viewer

git clone https://git.t4t.associates/char/sorcery

Charlotte Somexperiment: soft line wrapping on source code rendersf92b8f4

main
1.7 KiB46 linesraw
1import { availableLanguages, detectLanguage as sniffLanguage, highlight } from "@arborium/arborium";
2import { detectLanguage } from "./languages.ts";
3
4const available = new Set<string>(availableLanguages);
5
6function languageOf(path: string, source: string): string | null {
7  const candidate = detectLanguage(path);
8  if (candidate && available.has(candidate)) return candidate;
9  const detected = sniffLanguage(source);
10  return detected && available.has(detected) ? detected : null;
11}
12
13export async function highlightedHtml(path: string, source: string): Promise<string | null> {
14  const language = languageOf(path, source);
15  return language ? await highlight(language, source) : null;
16}
17
18function splitHighlighted(nodes: Node[]): Node[][] {
19  const lines: Node[][] = [[]];
20  for (const node of nodes) {
21    let parts: Node[][];
22    if (node instanceof Text) {
23      parts = node.data.split("\n").map(text => text ? [document.createTextNode(text)] : []);
24    } else if (node instanceof Element) {
25      parts = splitHighlighted([...node.childNodes]).map(children => {
26        if (children.length === 0) return [];
27        const clone = node.cloneNode(false) as Element;
28        clone.append(...children);
29        return [clone];
30      });
31    } else {
32      parts = splitHighlighted([...node.childNodes]);
33    }
34    lines.at(-1)!.push(...parts[0]);
35    for (const part of parts.slice(1)) lines.push(part);
36  }
37  return lines;
38}
39
40export async function highlightedLines(path: string, source: string): Promise<Node[][] | null> {
41  const html = await highlightedHtml(path, source);
42  if (html === null) return null;
43  const template = document.createElement("template");
44  template.innerHTML = html;
45  return splitHighlighted([...template.content.childNodes]);
46}