mooz / js2-mode

Improved JavaScript editing mode for GNU Emacs
GNU General Public License v3.0
1.33k stars 186 forks source link

Syntax highlighting only applied after edit #572

Open ben-cellfield opened 3 years ago

ben-cellfield commented 3 years ago

When I first open a js file, no highlighting is applied. As soon as I edit a single character, suddenly the highlighting is applied.

This behaviour seems to interact with rainbow-identifiers-mode and becomes even worse. The rainbow identifiers highlighting is applied to every word in the file on open, with no javascript syntax highlighting. Editing a single character then applies syntax highlighting (overriding much of the rainbow identifiers highlighting that was applied to keywords, etc), but only to the line with the edit and following lines; unless I scroll to the top of the file and edit something in the first line, I don't get syntax highlighting for the whole file.

dgutov commented 3 years ago

Does that happen with any file, or only specific ones?

Does it happen in a new file where you write 1-2 lines of code (then save, kill the buffer and re-visit)?

Does it happen with rainbow-identifiers-mode disabled?

ben-cellfield commented 3 years ago

It happens with any file. No difference in behaviour between a small or a large file, or between an existing or a new file.

As I mentioned, it does happen without rainbow-identifiers-mode, it just gets worse with that mode. Without rainbow identifiers the highlighting is still delayed until I make an edit, but then the whole file is properly highlighted. When using rainbow identifiers syntax highlighting is delayed until I make an edit, and then it only applies from the cursor onwards.

I did just find a difference in behaviour between editing a file inside my project work tree and one outside (even if it's a copy of a file from my project, so it's not about the contents), but only without rainbow identifiers. In that case, when outside my project just moving the cursor (with the mouse or keyboard) is enough to trigger full syntax highlighting. With rainbow identifiers, it requires an actual edit to the file to trigger highlighting.

Initial load Move cursor Any edit
Inside project with rainbow-identifiers rainbow only rainbow only highlighting from cursor onwards; rainbow only before cursor
Outside project with rainbow-identifiers rainbow only rainbow only highlighting from cursor onwards; rainbow only before cursor
Inside project without rainbow identifiers no highlighting no highlighting full highlighting
Outside project without rainbow identifiers no highlighting full highlighting full highlighting

(In all of these cases I am completely killing emacs between loads)

dgutov commented 3 years ago

Can you try emacs -Q with only js2-mode loaded and no other packages?

emacs -Q -L ~/.emacs.d/elpa/js2-mode-* -l js2-mode should do that.

And evaluate (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode)) before visiting .js files.

ben-cellfield commented 3 years ago

Okay, that difference between "inside project" and "outside project" seems to be caused by tide and the presence/absence of a jsconfig.json file. If I touch jsconfig.json I get the same "inside project" behaviour in my new temp folder. And if I uninstall tide then it doesn't seem to matter; I get the same "inside project" behaviour (moving cursor isn't enough to trigger highlighting) regardless of whether I have a jsconfig.json file.

ben-cellfield commented 3 years ago

My packages aren't installed in ~/.emacs.d/elpa/ because I'm using nix, but I uninstalled all custom packages and cleared my init.el. Now syntax highlighting works immediately.

Is there a better way to figure out what's interacting with js2-mode than just adding/removing packages and customizations and seeing what happens?

dgutov commented 3 years ago

Bisecting your init script is the usual approach (e.g. cut it in half, comment out the second part, try reproducing, repeat).

You can also visit a .js file, type C-h v font-lock-keywords RET, then scroll to the bottom and see whether there are any symbolic names there that you can track down to the packages they're defined in. And try disabling those packages specifically.

ben-cellfield commented 3 years ago

Thanks.

Should we close this then? Or when I track down the interaction do you want to see if it's something that can be addressed on the js2-mode side?

dgutov commented 3 years ago

Or when I track down the interaction do you want to see if it's something that can be addressed on the js2-mode side?

Sure. Or simply knowing about the offending mode should be useful anyway.

ben-cellfield commented 3 years ago

Okay this is getting really weird.

The minimal change I have been able to find to my init.el to cure this problem requires three separate edits:

  1. remove use-package flycheck (and configuration; but just removing the configuration doesn't help)
  2. remove use-package rainbow-identifiers
  3. remove (menu-bar-mode -1)

Adding back any one of those bits of configuration independently of the other two is enough to cause the problem. (I'm really confused by the involvement of menu-bar-mode)

Going to try from the other end and see how little I have to add to a blank configuration to start seeing it.

ben-cellfield commented 3 years ago

Going minimalist, I can reproduce the problem by having js2-mode and rainbow-identifiers as the only installed emacs packages, and the contents of my init.el being:

(add-hook 'prog-mode-hook 'rainbow-identifiers-mode)
(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))

With this minimal config, (menu-bar-mode -1) and flycheck don't seem to be relevant.

But removing just rainbow-identifiers-mode from my normal config doesn't help, so there seems to be something more complicated than simply an incompatibility between js2-mode and rainbow-identifiers (or there are two separate things going on, that just happen to have similar symptoms).

ben-cellfield commented 3 years ago

Ugh, more weirdness.

Removing rainbow-identifiers-mode without also removing flycheck and (menu-bar-mode -1) does help, if I start my emacs session with emacs foo.js.

My normal workflow uses emacsclient (via a script ee), and when I start emacs with ee foo.hs that's when I get the dependency on flycheck and the menu bar.

dgutov commented 3 years ago

I'm guessing your emacsclient workflow connects to the daemon, and said daemon needs to be restarted, for certain changes for your config to be applied?

I'm seeing certain weirdness indeed when rainbow-identifiers-mode is enabled, and that's probably not surprising given that these two modes use different, competing approaches to highlight text (basically all buffer text).

But r-i-m doesn't seem to integrate with js2-mode's parser, its scopes, etc, so its usefulness seems limited here.

ben-cellfield commented 3 years ago

Yes, but I have been careful to M-x kill-emacs between each configuration change and test, so I've definitely been starting the daemon fresh every time. I'm now a bit less certain about whether I used my emacsclient script consistently or just plan emacs for some of the tests above; I wasn't keeping track at first, because how could that possibly make a difference. 🙄

I'm honestly happy to get rid of rainbow-identifiers; I've only ever been middling about whether it's actually helpful. Although it does seem to work fine with other syntax highlighting packages for every other language I use, so it obviously isn't inherently incompatible with any package that does syntax highlighting (I assumed it has some process figured out for the rainbow-identifiers highlighting to be "low-priority" so it can be overridden by whatever else the syntax highlighter wants to do).

Unfortunately that still doesn't fix my normal configuration; I still get the problem unless I also add back in the menu bar (that seems absurd) and disable flycheck (definitely not something I want to drop), or else stop using the daemon. I guess I'll remove rainbow-identifiers and go back to bisecting my config to try to find out what is breaking js2's syntax highlighting in conjunction with flycheck (since flycheck alone doesn't seem to).

dgutov commented 3 years ago

so it obviously isn't inherently incompatible with any package that does syntax highlighting

Yeah: it is js2-mode that is odd/special, since it mostly circumvents font-lock. But the conflict with rainbow-identifiers is also unusual because it's the only minor mode that basically highlights everything in the buffer.

I guess I'll remove rainbow-identifiers and go back to bisecting my config to try to find out what is breaking js2's syntax highlighting in conjunction with flycheck (since flycheck alone doesn't seem to).

Good luck! If you find some minimal configuration, maybe I could do something about it. Or it'll be just another mode to disable.

AlexChesters commented 2 years ago

@ben-cellfield did you ever make any progress with identifying what causes this? (or @dgutov are you aware of any other reports?) I've recently started having the same issue but I can't see what would cause it in my set up.

I don't have rainbow-delimiters-mode enabled and font-lock-keywords is (t nil).

Weirdly I find that the first JS file I open of a session everything is fine, but subsequent files don't have syntax highlighting applied until I edit them (see below gif).

js2-mode-bug

dgutov commented 2 years ago

Might be some other mode. If you see no errors, what would ultimately help is bisecting your config.

But try this first:

M-x toggle-debug-on-error M-: (js2-reparse t) RET in the same JS buffer where highlighting seems broken

and see if that ends up in a backtrace.

ben-cellfield commented 2 years ago

@AlexChesters No, I haven't dug out any more specific information about what's causing this. It's still happening, but it's a lot less annoying since I got rid of rainbow-identifiers. There's no longer a dependency on where my cursor is, so I can just space-backspace wherever it is when the I open the window and it's not highlighted.

Interestingly I'm not seeing that same behaviour you are where the first file is okay and subsequent ones are unhighlighted. Both cases are the same for me.

AlexChesters commented 2 years ago

hey both,

apologies for the delay - been quite busy recently.

I eventually determined by issue was caused by eglot; I'm not sure entirely how, but the offending block in my config was:

(use-package eglot
  :config
  (add-hook 'js2-mode-hook 'eglot-ensure))
dgutov commented 2 years ago

That's odd: Eglot doesn't seem to be doing anything with font-lock.

But if you are using LSP, maybe try js-mode instead? Syntax highlighting is similar enough, and you get syntax error annotations from Eglot anyway.

jordigh commented 2 years ago

I'm experiencing this problem on macOS homebrew Emacs 28.1 too with js2 version 1.8.5 (or version 20211229 according to ELPA). It seems that js2-reparse just isn't being called at the normal invocation places where it should be called (every edit?).

When upgrading to MELPA version 20220402.2211 the problem seems to have disappeared.