Syntax highlighting disappears if large documents are scrolled fast

I can actually reproduce this issue with the official demo for large documents here: CodeMirror Huge Doc Demo

Just drag the scroll indicator down real fast, and the highlighting gets lost somewhere on the way. I have this problem with much smaller — yet still large — documents, as well. (Firefox 109)

On the demo, it states this is on purpose to consume resources. Which is fine. However, in my use-case, highlighting doesn’t reappear. Even if I scroll back to the very top which has been initially highlighted.

So you end up in a situation where the content at the top is not highlighted at all? Can you set up a reproduction of this?

While preparing a repro, I identified the cause of the problem. I was binding a scroll event handler which called editor.elementAtHeight() on every scroll event. Debouncing the call of the listener solved the problem. Sorry for the trouble. Nothing wrong here with CM6!

I just started using CodeMirror v6 in my Angular app and have this scrolling problem. Scrolling from the top slowly works fine, but scrolling fast doesn’t highlight the visible content. Is there a fix? I have been trying to find a fix since yesterday but cannot. Is there a fix for this behavior?

This is by design—eagerly parsing a huge document would waste a lot of cpu time (and battery, etc), so the editor doesn’t parse ahead very aggressively, and if you scroll down fast, you may have to wait a bit for it to catch up.

First of all, thank you for your reply. Secondly, I understand the concept of not parsing the whole document at once due to performance concerns. I tried using forceParsing, but when I have around 3 million lines, it freezes for almost 3 minutes, which aligns with what you mentioned earlier. Could we move the parser to only process the visible portion of the text instead of the entire document? For example, in VS Code, if we have 3 million lines and scroll quickly to the end of the editor, it highlights only the visible text and not the portion above it. This approach ensures better performance and avoids unnecessary processing.

The thing with that approach is that it will often produce very wrong results, when it skips a block comment marker, or some indicator of rough structure, such as a language change indicator (say, <script>) in mixed language modes. The resulting highlighting, folding, and auto-indentation will be entirely wrong, which seems worse than taking longer to provide any.

1 Like

That makes sense. Is it possible to pre-parse the text and add it to the editor in one go? For instance, when opening the editor with a large document, I don’t mind if the initialization takes longer, as long as the entire text is fully highlighted upfront. Partially highlighted content could affect user expectations, especially since I’ve implemented parsing on scroll.

Currently, when a user scrolls to the end of a very large file, the browser freezes momentarily due to the parsing, which is understandable. However, if the entire document were parsed during initialization, the user would only experience a single wait at the start, and scrolling behavior afterward would remain smooth.

Would it be possible to implement this approach?

You could create your editor state before showing it in an actual view, and use ensureSyntaxTree to run the parser (preferably in small steps so as to not freeze the thread) until enough is parsed, at which point you could create a view for that state.

1 Like

This is what I am currently doing. What changes should I make to implement the approach you suggested?

public ngAfterViewInit() {
    const extensions = [
        basicSetup,
        java(),
        keymap.of(defaultKeymap),
    ];

    const state = EditorState.create({
        doc: this.element.values != null ? this.element.values[0] : null,
        extensions: extensions,
    });

    this.editorView = new EditorView({
        state,
        parent: this.myEditor.nativeElement,
    });

    this.ensureParsing();
}

private ensureParsing() {
    const scrollContainer = this.myEditor.nativeElement.querySelector('.cm-scroller');
    const view = this.editorView;
    const timeout = 100000;

    scrollContainer.addEventListener('scroll', (event: Event) => {
        const upto = view.viewport.to;
        const parsed = forceParsing(view, upto, timeout);
        if (!parsed) {
            console.warn(`Parsing could not reach position ${upto} within ${timeout}ms.`);
        }
    });
}