lsp

lassemaatta 2026-03-15T06:06:42.413529Z

is it normal for lsp-mode to show stale diagnostics until I modify/save the file?

lassemaatta 2026-03-15T07:23:56.756969Z

perhaps 🤔 I think I have file watchers enabled and the log lists them at startup. When I modify buffer B in my example, I can look at the lsp io log and see a textDocument/didChange event and a bunch of other events. But those seem to affect only buffer B and not buffer A, even if A is visible.

ericdallo 2026-03-15T13:21:45.610419Z

lsp-file-watchers is for when file is changed outside the editor when the file is/was not opened, so from your steps I'd say clojure-lsp should send the diagnostics update and we do have a logic for that but there is a high chance it's a flychceck issue updating it in emacs

ericdallo 2026-03-15T13:22:08.009319Z

the best way to debug is enable lsp-log-io , do the steps and see if clojure-lsp is sending it properly, then we would debug on emacs side

lassemaatta 2026-03-15T14:43:29.891819Z

I figured out what it was: :notify-references-on-file-change was false. I had previously altered that because I had performance issues (https://clojurians.slack.com/archives/CPABC1H61/p1756474783539759?thread_ts=1756213182.089459&cid=CPABC1H61) when switching branches and setting that to false helped a lot.

👍 1
lassemaatta 2026-03-17T07:21:01.867439Z

unfortunately it seems the performance issues are still there. So • :notify-references-on-file-change is false: buffers may show stale data, but switching branches is fast • :notify-references-on-file-change is true: other buffers are (immediately) updated when changes occur elsewhere and code lenses etc. are (always) up-to-date. But switching git branches causes my cpu usage to go 800-1000% (= several cpu cores hitting 100%) for a while (maybe a minute or two, much longer than opening the project with a hot cache), even if the branches only differ slightly.

ericdallo 2026-03-17T11:58:06.136719Z

Interesting, it's been a while nobody complains about performance in that, is your project huge?

lassemaatta 2026-03-17T12:01:57.907799Z

just to be clear: I'm not complaining, merely providing a point of data 🙂

ericdallo 2026-03-17T12:02:20.766229Z

sure :)

lassemaatta 2026-03-17T12:03:14.169429Z

but yes, this is a relatively largeish project; about 2k namespaces and 300+ k loc.

ericdallo 2026-03-17T12:09:01.087579Z

wow , yeah, we can probably tweak that

lassemaatta 2026-03-17T12:09:04.321609Z

I had no memory we already discussed this years ago https://github.com/clojure-lsp/clojure-lsp/issues/1205 🙂

😅 1
ericdallo 2026-03-17T12:09:52.606779Z

me too haha

lassemaatta 2026-03-17T12:22:53.169639Z

it did a brief test, where I made a tiny change to a common utilitity namespace and then switched branches to the previous commit. Lots of cpu usage for a while, after which the logs were filled with several thousands of lines of :lsp/publish-diagnostics 0ms. I only had like 2-3 files open in emacs. I could also spot a few interesting lines: • [clojure-lsp.feature.file-management:157] - :reference-files/analyze 47169ms[clojure-lsp.feature.file-management:151] - :internal/notify-references 48438ms

lassemaatta 2026-03-17T12:23:27.948799Z

also, the following pattern repeated maybe 5 times


2026-03-17T12:20:15.405Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:15.405Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:15.405Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:15.406Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:15.406Z  INFO [clojure-lsp.feature.file-management:151] - :internal/notify-references 48735ms
2026-03-17T12:20:15.406Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:15.589Z  INFO [clojure-lsp.handlers:443] - :lsp/code-lens 1ms
2026-03-17T12:20:15.635Z  INFO [clojure-lsp.handlers:450] - :lsp/code-lens-resolve 30ms
2026-03-17T12:20:15.635Z  INFO [clojure-lsp.handlers:450] - :lsp/code-lens-resolve 30ms
2026-03-17T12:20:15.640Z  INFO [clojure-lsp.handlers:450] - :lsp/code-lens-resolve 35ms
2026-03-17T12:20:15.648Z  INFO [clojure-lsp.handlers:450] - :lsp/code-lens-resolve 43ms
2026-03-17T12:20:15.648Z  INFO [clojure-lsp.handlers:450] - :lsp/code-lens-resolve 43ms
2026-03-17T12:20:15.673Z  INFO [clojure-lsp.handlers:450] - :lsp/code-lens-resolve 68ms
2026-03-17T12:20:15.689Z  INFO [clojure-lsp.handlers:450] - :lsp/code-lens-resolve 84ms
2026-03-17T12:20:15.940Z  INFO [clojure-lsp.feature.diagnostics.built-in:324] - :internal/built-in-linters.unused-public-vars 1312ms
2026-03-17T12:20:15.960Z  INFO [clojure-lsp.feature.diagnostics.built-in:315] - :internal/built-in-linters 1339ms
2026-03-17T12:20:16.020Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 59ms
2026-03-17T12:20:16.026Z  INFO [clojure-lsp.feature.diagnostics.built-in:327] - :internal/built-in-linters.different-aliases 0ms
2026-03-17T12:20:16.027Z  INFO [clojure-lsp.feature.diagnostics.built-in:330] - :internal/built-in-linters.cyclic-dependencies 0ms
2026-03-17T12:20:16.160Z  INFO [clojure-lsp.feature.diagnostics.built-in:324] - :internal/built-in-linters.unused-public-vars 1339ms
2026-03-17T12:20:16.180Z  INFO [clojure-lsp.feature.diagnostics.built-in:315] - :internal/built-in-linters 1365ms
2026-03-17T12:20:16.242Z  INFO [clojure-lsp.dep-graph:280] - :internal/maintain-dep-graph 62ms
2026-03-17T12:20:16.250Z  INFO [clojure-lsp.feature.diagnostics.built-in:327] - :internal/built-in-linters.different-aliases 0ms
2026-03-17T12:20:16.250Z  INFO [clojure-lsp.feature.diagnostics.built-in:330] - :internal/built-in-linters.cyclic-dependencies 0ms
2026-03-17T12:20:16.307Z  INFO [clojure-lsp.feature.diagnostics.built-in:321] - :internal/built-in-linters.find-linter-ignore-comments 281ms
2026-03-17T12:20:16.504Z  INFO [clojure-lsp.feature.diagnostics.built-in:321] - :internal/built-in-linters.find-linter-ignore-comments 255ms
2026-03-17T12:20:17.498Z  INFO [clojure-lsp.feature.diagnostics.built-in:324] - :internal/built-in-linters.unused-public-vars 1472ms
2026-03-17T12:20:17.521Z  INFO [clojure-lsp.feature.diagnostics.built-in:315] - :internal/built-in-linters 1501ms
2026-03-17T12:20:17.521Z  INFO [clojure-lsp.feature.file-management:157] - :reference-files/analyze 50827ms
2026-03-17T12:20:17.522Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:17.522Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:17.522Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:17.522Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:17.523Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:17.523Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:17.523Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms
2026-03-17T12:20:17.523Z  INFO [clojure-lsp.server:133] - :lsp/publish-diagnostics 0ms

ericdallo 2026-03-17T12:36:33.226329Z

yeah the problem here is the analyze, which will in the end become a clj-kondo call which might be where this will become a issue :/ it's similar if you clean .lsp/.cache and restart lsp, I suppose this takes a while right?

lassemaatta 2026-03-17T12:40:05.947089Z

yeah, with a clean cache => :lsp/initialize 66174ms

ericdallo 2026-03-17T12:44:08.399139Z

even so 47s for a utils functin reference vs 66 for whole project seems a lot

lassemaatta 2026-03-17T12:53:31.322699Z

also, switching branches seems a lot more intensive compared to the initialization. At times all my cpu's are hitting 100%.

lassemaatta 2026-03-17T12:54:08.785799Z

I guess the good news is that clojure-lsp is efficiently utilizing all my cores 😅

ericdallo 2026-03-17T12:57:29.531489Z

yes, that's is what I see we can improve, we limit the parallelism somehow

ericdallo 2026-03-17T12:57:38.573309Z

it should be slower but smoother for user

lassemaatta 2026-03-15T06:09:15.561809Z

what I typically see is • I open file A and look at some clojure function. The code lens shows "2 references" (from some other buffers) • I navigate to one of the call sites in some other file (file B) • I remove the function call. Maybe I'll save file B or maybe not, doesn't matter. • I switch back to buffer A • buffer A still lists "2 references" in the code lens for the function • I make some trivial change to file A (e.g. add an empty line) => The code lens is updated and now correctly shows "1 references"

lassemaatta 2026-03-15T06:14:01.870449Z

I tried to hack together some "refresh LSP diagnostics when the buffer in the window changes" using window-buffer-change-functions but didn't get very far. I started thinking maybe there's something wrong with my LPS setup 🤷

borkdude 2026-03-15T06:49:47.318039Z

I think this is what watchers is for