mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 18:16:10 +00:00
Force the Nextflow server's deferred workspace scan before finding references (#1832)
The Nextflow language server does not scan the workspace when it says it does. `LanguageService.initialize` -- which the "Initializing" progress notification we wait for wraps -- only marks the workspace as unscanned and clears the AST cache. The scan itself happens in `LanguageService.update0`, behind a 1s debounce, and only on a round that finds no pending file change; a round that does find one re-defers it via `updateLater`. The first `didOpen` of a session therefore pushes the scan out by at least one further round, so a references request issued right after it sees an AST cache holding nothing but the file it just opened and answers with an empty list. Force the scan rather than waiting for it. `completion` is one of the two requests that call `updateNow` before consulting their provider, and `DebouncingExecutor.executeNow` cancels the pending debounce and runs the update synchronously on the request thread, so the response is not sent until that round has finished. Two rounds suffice whatever the workspace size: the first drains the pending file changes -- which is what re-defers the scan -- and the second finds nothing pending, so it compiles the whole workspace before replying. Polling the workspace symbol index instead would be unsound: `LanguageService.symbol` neither awaits the update nor holds the monitor that `update` synchronises on. On a generated 1050-file workspace, polling it made the server raise `java.util.ConcurrentModificationException` from inside the scan, non-deterministically (6, 3 and 0 times across three runs). The existing cross-file reference test did not catch the empty results because the `language_server` fixture is module-scoped: earlier tests in the class had already opened main.nf and forced the server to compile it, so the assertion passed for a reason that does not hold in a real session. Run alone, it failed. The added test starts its own server and opens nothing but the defining file. Verified on the 1050-file workspace, where the symbol has exactly 100 references: without the fix 0 are found, with it all 100, in 2.5s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
430fc62e72
commit
8ebb247dae
2 files changed
+53
-2
No files matched your search
@@ -88,6 +88,10 @@ class NextflowLanguageServer(SolidLanguageServer):
|
||||
self._active_progress_tokens: set[str] = set()
|
||||
self._progress_lock = threading.Lock()
|
||||
|
||||
# Whether the server's deferred workspace scan has been observed to complete
|
||||
# (see _flush_deferred_workspace_scan).
|
||||
self._workspace_scan_flushed = False
|
||||
|
||||
def _create_dependency_provider(self) -> LanguageServerDependencyProvider:
|
||||
return self.DependencyProvider(self._custom_settings, self._ls_resources_dir)
|
||||
|
||||
@@ -297,6 +301,32 @@ class NextflowLanguageServer(SolidLanguageServer):
|
||||
else:
|
||||
log.warning("Nextflow LS did not signal scan completion within %.0fs; proceeding anyway", _WORKSPACE_SCAN_TIMEOUT)
|
||||
|
||||
def _flush_deferred_workspace_scan(self, relative_file_path: str) -> None:
|
||||
"""
|
||||
Forces the workspace scan, which the server defers past the first request of a session.
|
||||
|
||||
The scan runs in ``LanguageService.update0`` behind a 1s debounce, and only on a round with no
|
||||
pending file change; the session's first ``didOpen`` re-defers it, leaving references to be
|
||||
answered from an AST cache holding just that one file.
|
||||
|
||||
``completion`` calls ``updateNow``, which runs the update synchronously before replying, so two
|
||||
such requests force the scan whatever the workspace size: the first drains the pending change,
|
||||
the second finds none and scans. Their results are discarded.
|
||||
"""
|
||||
if self._workspace_scan_flushed:
|
||||
return
|
||||
|
||||
params: lsp_types.CompletionParams = {
|
||||
"textDocument": {"uri": self._resolve_file_uri(relative_file_path)},
|
||||
"position": {"line": 0, "character": 0},
|
||||
}
|
||||
for _ in range(2):
|
||||
try:
|
||||
self.server.send.completion(params)
|
||||
except Exception as e:
|
||||
log.debug("Completion request used to flush the Nextflow workspace scan failed: %s", e)
|
||||
self._workspace_scan_flushed = True
|
||||
|
||||
@override
|
||||
def _send_references_request(self, relative_file_path: str, line: int, column: int) -> list[lsp_types.Location] | None:
|
||||
"""
|
||||
@@ -309,13 +339,14 @@ class NextflowLanguageServer(SolidLanguageServer):
|
||||
come back empty. Sending a ``documentSymbol`` request for the same file first is a cheap way to
|
||||
block until the update has been applied.
|
||||
"""
|
||||
self._flush_deferred_workspace_scan(relative_file_path)
|
||||
self.server.send.document_symbol({"textDocument": {"uri": self._resolve_file_uri(relative_file_path)}})
|
||||
return super()._send_references_request(relative_file_path, line, column)
|
||||
|
||||
@override
|
||||
def _get_wait_time_for_cross_file_referencing(self) -> float:
|
||||
"""Small safety buffer; the actual synchronisation happens in _send_references_request."""
|
||||
return 1.0
|
||||
"""No blind wait is needed: _send_references_request synchronises with the server explicitly."""
|
||||
return 0.0
|
||||
|
||||
@override
|
||||
def _document_symbols_cache_fingerprint(self) -> Hashable:
|
||||
|
||||
Reference in new issue
Block a user