fix(clojure): declare didChangeWatchedFiles client capability (#1884)

Serena notifies language servers about files changed outside its own edit tools (a git checkout, another editor, a build step) by sending workspace/didChangeWatchedFiles from LanguageServerManager.poll_and_notify, whose docstring names this exact case: such edits are 'otherwise invisible to a warm language server, causing symbolic queries to answer from a stale index'.

ClojureLSP never declared the corresponding client capability, so clojure-lsp was not told those notifications are sent and need not treat them as a reason to invalidate its analysis. DefaultInitializeParamsBuilder does not supply a default capabilities block, so nothing filled this in on the server's behalf.

Relates to #1593, where find_symbol returned a body from a comment mentioning the symbol rather than its definition, at a position the symbol had previously occupied.
This commit is contained in:
Michael Panchenko authored and GitHub committed 2026-08-18 09:47:19 +02:00
commit 2561478a70
3 files changed
+52

No files matched your search

+4
View File
@@ -8,6 +8,10 @@ Status of the `main` branch. Changes prior to the next official version change w
* Language Servers:
- Fix: Dart's `$/analyzerStatus` notifications were logged as unhandled-method warnings during analysis (#1855)
- Fix: clojure-lsp was not told that Serena sends `workspace/didChangeWatchedFiles`, so changes made
outside Serena's own edit tools (a git checkout, another editor, a build step) need not invalidate
its analysis; symbol queries could then answer from a stale index, e.g. `find_symbol` returning a
body from the position the symbol used to occupy (#1593)
- Fix: Scala cross-file queries waited a fixed 5s after the first file was opened, which on a cold
Metals is long before its build import, indexing and compilation have finished; the first
`find_referencing_symbols` of a session could return a fraction of the references with nothing to
@@ -325,6 +325,12 @@ class ClojureLSP(SolidLanguageServer):
"workspace": {
"applyEdit": True,
"workspaceEdit": {"documentChanges": True},
# Serena notifies language servers about files changed outside its own
# edit tools (git checkout, another editor, a build step) via
# workspace/didChangeWatchedFiles; see LanguageServerManager.poll_and_notify.
# Without declaring the capability, clojure-lsp is not told the client
# sends those notifications and may keep answering from its stale analysis.
"didChangeWatchedFiles": {"dynamicRegistration": True},
"symbol": {"symbolKind": {"valueSet": list(range(1, 27))}},
"workspaceFolders": True,
},
@@ -0,0 +1,42 @@
import pytest
from solidlsp.language_servers.clojure_lsp import ClojureLSP
pytestmark = pytest.mark.clojure
def _make_server(monkeypatch: pytest.MonkeyPatch) -> ClojureLSP:
"""Build a ClojureLSP without running __init__ (no clojure-lsp binary needed)."""
server = object.__new__(ClojureLSP)
monkeypatch.setattr(ClojureLSP, "_resolve_source_paths", lambda _self: None)
return server
def test_declares_did_change_watched_files_capability(monkeypatch: pytest.MonkeyPatch) -> None:
"""clojure-lsp must announce that the client sends workspace/didChangeWatchedFiles.
Serena notifies language servers about files changed outside its own edit tools
(git checkout, another editor, a build step) via that notification
(LanguageServerManager.poll_and_notify). A server that was never told the client
supports it may keep answering symbol queries from its own stale analysis, which
surfaces as find_symbol returning a body from the wrong location.
"""
params = _make_server(monkeypatch)._create_base_initialize_params()
workspace = params["capabilities"]["workspace"]
assert "didChangeWatchedFiles" in workspace, (
"clojure-lsp does not declare the didChangeWatchedFiles client capability, so external file changes may not invalidate its analysis"
)
assert workspace["didChangeWatchedFiles"]["dynamicRegistration"] is True
def test_base_initialize_params_keep_existing_workspace_capabilities(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""The added capability must not displace the ones already relied upon."""
workspace = _make_server(monkeypatch)._create_base_initialize_params()["capabilities"]["workspace"]
assert workspace["applyEdit"] is True
assert workspace["workspaceEdit"] == {"documentChanges": True}
assert workspace["workspaceFolders"] is True
assert workspace["symbol"]["symbolKind"]["valueSet"] == list(range(1, 27))