mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 00:15:05 +00:00
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:
1 parent
f581cf642d
commit
cf54869af8
3 files changed
+56
-5
No files matched your search
@@ -141,6 +141,10 @@ Status of the `main` branch. Changes prior to the next official version change w
|
||||
`(int X, string Y)`, had their name corrupted to include a trailing `:` because the
|
||||
parenthesis in the type was mistaken for a method's parameter list; `find_symbol` on
|
||||
the real name then returned nothing
|
||||
- Fix: TypeScript's `_has_waited_for_cross_file_references` latch was set after the first
|
||||
cross-file query and never reset, so a later query that opened a file from a project tsserver
|
||||
had not loaded yet (e.g. a monorepo package) skipped the indexing wait even while that
|
||||
project's own `$/progress` indexing was still in flight (#1937)
|
||||
- Fix: Nextflow's `_flush_deferred_workspace_scan` marked the workspace scan flushed even when both
|
||||
of its `completion` probes failed, permanently skipping the flush (and silencing retries) for the
|
||||
rest of the session (#1871)
|
||||
|
||||
@@ -575,18 +575,29 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
|
||||
@override
|
||||
def _wait_for_cross_file_references_if_needed(self) -> None:
|
||||
if self._has_waited_for_cross_file_references:
|
||||
timeout = self._get_indexing_timeout()
|
||||
if not self._has_waited_for_cross_file_references:
|
||||
start_grace = self._get_indexing_start_grace()
|
||||
self._log_cross_file_indexing_wait_outcome(
|
||||
self._wait_for_indexing_start_or_completion(timeout=timeout, start_grace=start_grace), timeout
|
||||
)
|
||||
self._has_waited_for_cross_file_references = True
|
||||
return
|
||||
|
||||
timeout = self._get_indexing_timeout()
|
||||
start_grace = self._get_indexing_start_grace()
|
||||
if self._wait_for_indexing_start_or_completion(timeout=timeout, start_grace=start_grace):
|
||||
# The latch above only covers the first query; a later one can still open a file from a
|
||||
# project tsserver has not loaded before, starting a fresh $/progress cycle to drain.
|
||||
with self._progress_lock:
|
||||
indexing_in_progress = bool(self._active_progress_tokens)
|
||||
if indexing_in_progress:
|
||||
self._log_cross_file_indexing_wait_outcome(self.wait_for_indexing(timeout=timeout), timeout)
|
||||
|
||||
def _log_cross_file_indexing_wait_outcome(self, completed: bool, timeout: float) -> None:
|
||||
if completed:
|
||||
log.info("TypeScript cross-file indexing complete")
|
||||
else:
|
||||
log.warning(
|
||||
"TypeScript cross-file indexing did not complete within %.0fs; proceeding (%s)", timeout, self.describe_indexing_state()
|
||||
)
|
||||
self._has_waited_for_cross_file_references = True
|
||||
|
||||
@override
|
||||
def _get_preferred_definition(self, definitions: list[ls_types.Location]) -> ls_types.Location:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user