emacs-tree-sitter / elisp-tree-sitter

Emacs Lisp bindings for tree-sitter
https://emacs-tree-sitter.github.io
MIT License
822 stars 74 forks source link

Incremental highlighting constantly changes color of unrelated lines #200

Open guillaumebrunerie opened 2 years ago

guillaumebrunerie commented 2 years ago

I’m not sure if it's a tree-sitter, elisp-tree-sitter, or tree-sitter-langs problem, but while typing new code the highlighting changes way too often. For instance consider the following Javascript code with tree-sitter-hl-mode enabled

const f = () => {
    // <- Point here before the comment
    const n = 3;
}

and type const a = new Array(); in order to obtain

const f = () => {
    const a = new Array(); // <- Point here before the comment
    const n = 3;
}

Note that while typing, the line below the point (const n = 3;) changes color a total of 8 times (!), which is pretty distracting for the eyes. In comparison, with the built-in js-mode that same line does not change color at all.

I guess this is due to the fact that the tree is constantly being parsed and new (weird/erroneous) interpretations are being found, but I wonder if there is a way it could be fixed somehow?

guillaumebrunerie commented 2 years ago

I checked js2-mode (which also does highlighting based on an AST), and it seems that the difference there is that it waits for 0.2 s of inactivity before updating the highlighting. So if you type fast enough, the line below does not have time to keep changing colors and it’s much less annoying. On the other hand, it also means that the current line does not get any highlighting until you're finished typing it (unless with js-mode). Maybe it would be worth having such a (configurable) delay here as well?

ubolonton commented 2 years ago

Maybe it would be worth having such a (configurable) delay here as well?

I think you can try setting jit-lock-defer-time (locally in buffers that need it), since tree-sitter-hl-mode reuses font-lock and jit-lock underneath.

You can even make it "smarter", by setting it only when typing (i.e. self-insert-command) and not when pasting/formatting code. I'm open to adding that behavior behind a customizable option.

I guess this is due to the fact that the tree is constantly being parsed and new (weird/erroneous) interpretations are being found, but I wonder if there is a way it could be fixed somehow?

Yes, this is how tree-sitter does error recovery. When a piece of code cannot be fully parsed, it will pick the "most reasonable" parse tree (and mark where parsing fails). How it scores the candidate trees depends on multiple factors, including precedence rules in the grammar. I think you can report these "unexpected parse results" to the js grammar repo.

guillaumebrunerie commented 2 years ago

It looks like the unexpected parse results cannot be fixed at the moment, unfortunately (already reported as https://github.com/tree-sitter/tree-sitter-javascript/issues/204, and it will hopefully be possible to fix them once https://github.com/tree-sitter/tree-sitter/pull/246 is merged)

Setting jit-lock-defer-time to 0.2 works, but unfortunately it also defers highlighting while scrolling. Making it smarter might be a bit tricky as well given that it doesn’t seem possible to change jit-lock-defer-time while font-lock is running (it doesn’t restart the timer…).