// Token-based highlighter for blog code blocks. // Used by markdown-fenced `
` produced by
// site/blog/build_blog.py. The hero + playground now use CodeMirror — this
// file no longer ships an editable variant.
//
// Keyword/type sets come from site/files/cm/daslang-keywords.js when loaded;
// otherwise the local fallback (smaller, but always present) takes over so
// pre-generated blog pages keep working if the keywords script is unavailable.

(function (global) {
    const FALLBACK_KEYWORDS = new Set([
        'def', 'let', 'var', 'for', 'in', 'if', 'else', 'elif', 'while', 'return',
        'struct', 'class', 'require', 'module', 'options', 'export', 'typeinfo',
        'static_if', 'true', 'false', 'null', 'as', 'is', 'cast',
        'yield',
    ]);
    const FALLBACK_TYPES = new Set([
        'int', 'int2', 'int3', 'int4', 'uint', 'uint2', 'uint3', 'uint4',
        'float', 'float2', 'float3', 'float4', 'double', 'bool', 'string',
        'void', 'auto', 'array', 'table', 'range', 'tuple', 'variant',
        'lambda', 'generator', 'block', 'iterator',
    ]);

    function resolveSets() {
        const ext = global.daslangKeywords;
        if (ext) {
            // Atoms collapse into 'kw' so they get amber via .tok-kw.
            const KW = new Set([...ext.KEYWORDS, ...ext.ATOMS]);
            return { KEYWORDS: KW, TYPES: ext.TYPES };
        }
        return { KEYWORDS: FALLBACK_KEYWORDS, TYPES: FALLBACK_TYPES };
    }

    function tokenize(src) {
        const { KEYWORDS, TYPES } = resolveSets();
        const out = [];
        let i = 0;
        const push = (t, k) => out.push({ t, k });
        while (i < src.length) {
            const c = src[i];
            if (c === '/' && src[i + 1] === '/') {
                let j = i;
                while (j < src.length && src[j] !== '\n') j++;
                push(src.slice(i, j), 'c'); i = j; continue;
            }
            if (c === '"') {
                let j = i + 1;
                while (j < src.length && src[j] !== '"') {
                    if (src[j] === '\\') j++;
                    j++;
                }
                j = Math.min(j + 1, src.length);
                push(src.slice(i, j), 's'); i = j; continue;
            }
            if (c === '[') {
                let j = i;
                while (j < src.length && src[j] !== ']') j++;
                j = Math.min(j + 1, src.length);
                push(src.slice(i, j), 'a'); i = j; continue;
            }
            if (/[A-Za-z_]/.test(c)) {
                let j = i;
                while (j < src.length && /[A-Za-z0-9_]/.test(src[j])) j++;
                const w = src.slice(i, j);
                const k = KEYWORDS.has(w) ? 'kw' : TYPES.has(w) ? 'ty' : 'id';
                push(w, k); i = j; continue;
            }
            if (/[0-9]/.test(c)) {
                let j = i;
                while (j < src.length && /[0-9.]/.test(src[j])) j++;
                push(src.slice(i, j), 'n'); i = j; continue;
            }
            if (c === '<') {
                let j = i + 1;
                while (j < src.length && src[j] !== '>' && src[j] !== '\n') j++;
                if (src[j] === '>') { push(src.slice(i, j + 1), 'ty'); i = j + 1; continue; }
            }
            push(c, /\s/.test(c) ? 'w' : 'p');
            i++;
        }
        return out;
    }

    function esc(s) {
        return s.replace(/&/g, '&').replace(//g, '>');
    }

    // Walk all `
` (or "language-das")
    // and replace their contents with tokenized HTML. Idempotent.
    function highlightAll() {
        const blocks = document.querySelectorAll(
            'code.language-daslang, code.language-das, pre code:not(.tok-id):not(.tok-kw)');
        blocks.forEach(code => {
            const cls = code.className || '';
            if (cls && !/language-(das|daslang)/.test(cls)) return;
            const src = code.textContent;
            const tokens = tokenize(src);
            code.innerHTML = tokens.map(t =>
                `${esc(t.t)}`
            ).join('');
        });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', highlightAll);
    } else {
        setTimeout(highlightAll, 0);
    }

    global.dasHighlight = { tokenize, highlightAll };
})(window);