Server Side Rendering for read-only codeblocks in CM6?

As it so happens I was working on this exact thing today.
With some pointers from Static highlighting using CM v6 - /next - discuss.CodeMirror, I implemented the following. Hope it helps as a starting point

import {Language} from "@codemirror/language";
import {Decoration} from "@codemirror/view";
import {RangeSetBuilder} from "@codemirror/rangeset";
import {defaultHighlightStyle, highlightTree} from "@codemirror/highlight";
import {Text} from "@codemirror/text";

export function getHighlights(textContent: string, language: Language): string {
    const tree = language.parser.parse(textContent);
    let markCache: { [cls: string]: Decoration } = Object.create(null)

    let builder = new RangeSetBuilder<Decoration>()
    highlightTree(tree, defaultHighlightStyle.match, (from, to, style) => {
        builder.add(from, to, markCache[style] || (markCache[style] = Decoration.mark({class: style})))
    });
    let decorationRangeSet = builder.finish();

    let html = '';
    let text = Text.of(textContent.split("\n"));
    for (let i = 1; i <= text.lines; i++) {
        let line = text.line(i), pos = line.from, cursor = decorationRangeSet.iter(line.from), lineInnerHtml = '';

        while (cursor.value && cursor.from < line.to) {
            if (cursor.from > pos) {
                lineInnerHtml += `${text.sliceString(pos, cursor.from)}`;
            }
            lineInnerHtml += `<span class="${cursor.value.spec.class}">${text.sliceString(cursor.from, Math.min(line.to, cursor.to))}</span>`
            pos = cursor.to;
            cursor.next();
        }
        if (pos < line.to) {
            lineInnerHtml += `${text.sliceString(pos, line.to)}`;
        }
        html += `<div class="cm-line">${lineInnerHtml || '<br/>'}</div>`;
    }
    return `<pre>${html}</pre>`;
}
1 Like