export interface HighlightResult { html: string; toc: { name: string; anchor: string }[]; refs: string[]; }
const esc = (s: string) => s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
export function highlightMustache(src: string, resolveHref: (p: string) => string | undefined): HighlightResult {
let out = "";
const toc: { name: string; anchor: string }[] = [];
const refs: string[] = [];
let depth = 0;
let i = 0;
while (i < src.length) {
const open = src.indexOf("{{", i);
if (open === -1) { out += esc(src.slice(i)); break; }
out += esc(src.slice(i, open));
const triple = src.startsWith("{{{", open);
const close = src.indexOf(triple ? "}}}" : "}}", open);
if (close === -1) { out += esc(src.slice(open)); break; }
const end = close + (triple ? 3 : 2);
const rawTok = src.slice(open, end);
const inner = src.slice(open + (triple ? 3 : 2), close).trim();
const sigil = triple ? "{" : inner[0] ?? "";
const name = triple ? inner : inner.replace(/^[#^\/>!&]\s*/, "");
const span = (cls: string, extra = "") => `${esc(rawTok)}`;
if (!triple && sigil === "!") out += span("mu-com");
else if (!triple && sigil === ">") out += span("mu-par");
else if (!triple && (sigil === "#" || sigil === "^")) {
refs.push(name);
const href = resolveHref(name);
if (depth === 0 && sigil === "#") { toc.push({ name, anchor: `sec-${name}` }); }
const idAttr = depth === 0 && sigil === "#" ? ` id="sec-${esc(name)}"` : "";
out += href ? `${esc(rawTok)}` : span("mu-sec mu-unresolved", idAttr);
depth++;
} else if (!triple && sigil === "/") { depth = Math.max(0, depth - 1); out += span("mu-sec"); }
else {
refs.push(name);
const cls = triple || sigil === "&" ? "mu-raw" : "mu-var";
const href = resolveHref(name);
out += href ? `${esc(rawTok)}` : span(`${cls} mu-unresolved`);
}
i = end;
}
return { html: out, toc, refs };
}