fix(typescript): drain in-flight indexing on later cross-file queries (#1978)

_wait_for_cross_file_references_if_needed latched after the first cross-file
query and never waited again, even when a later query opened a file from a
project tsserver had not loaded yet and started a fresh $/progress cycle.
find_referencing_symbols and find_references would then return whatever
tsserver had indexed so far, silently partial, with nothing distinguishing it
from a complete answer.

The latch still covers the first query's start-grace wait unchanged. A later
query now also checks whether a progress token is currently active and, if
so, waits for it to drain before returning.

Fixes #1937
This commit is contained in:
Amir Fathi authored and GitHub committed 2026-09-23 15:09:02 +02:00
1 parent f581cf642d
commit cf54869af8
3 files changed
+56 -5

No files matched your search

@@ -284,6 +284,42 @@ class TestWaitForCrossFileReferencesUsesConfiguredGrace:
server._wait_for_cross_file_references_if_needed()
assert time.monotonic() - start < 0.1
def test_second_call_drains_a_progress_token_already_in_flight(self) -> None:
"""oraios/serena#1937: a later cross-file query can open a file from a project tsserver
has not loaded yet (e.g. a monorepo package), starting a fresh $/progress cycle. The
once-only latch must not let that query return while the fresh token is still active.
"""
server = _bare_ts_server(TypeScriptLanguageServer, {"indexing_start_grace": 0.05})
server._has_waited_for_cross_file_references = True
server._active_progress_tokens.add("tsserver/project-b-load")
server._indexing_complete.clear()
def _drain_shortly() -> None:
time.sleep(0.2)
with server._progress_lock:
server._active_progress_tokens.discard("tsserver/project-b-load")
server._indexing_complete.set()
threading.Thread(target=_drain_shortly, daemon=True).start()
start = time.monotonic()
server._wait_for_cross_file_references_if_needed()
elapsed = time.monotonic() - start
# proves the wait actually blocked for the still-active token, not a fast no-op return
assert elapsed >= 0.15
assert server._active_progress_tokens == set()
def test_second_call_proceeds_on_timeout_while_a_token_never_drains(self) -> None:
server = _bare_ts_server(TypeScriptLanguageServer, {"indexing_timeout": 0.05})
server._has_waited_for_cross_file_references = True
server._active_progress_tokens.add("tsserver/project-b-load")
server._indexing_complete.clear()
server._wait_for_cross_file_references_if_needed() # must log and return, not raise or hang
assert server._active_progress_tokens == {"tsserver/project-b-load"}
class _FakeSolidLSPSettings:
"""Minimal settings stand-in: returns a fixed TypeScript indexing_timeout so the guard tests can