From 81b1bc804d95d67ed14e27b8561f56e4bedb1594 Mon Sep 17 00:00:00 2001 From: bymebyu Date: Fri, 3 Jul 2026 18:38:53 +0900 Subject: [PATCH] TypeScript/VTS: event-based indexing wait and no automatic typing acquisition (#1639) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * TypeScript/VTS: event-based indexing wait, no automatic typing acquisition - Set disableAutomaticTypingAcquisition in initializationOptions (TS and VTS) so tsserver does not download type packages from the network during language server initialization. - Replace the fixed 2-second wait before cross-file reference requests with $/progress-based indexing tracking: a pre-open hook arms the tracking (expect_indexing) before didOpen, then the request waits until indexing progress drains (wait_for_indexing), bounded by a configurable 'indexing_timeout' (ls_specific_settings, default 30s). If tsserver does not start reporting progress within a 2-second grace period (the previous fixed wait), the request proceeds — so the fast path is never slower than before, while slow indexing is actually awaited instead of guessed at. --- CHANGELOG.md | 3 ++ docs/02-usage/050_configuration.md | 1 + .../typescript_language_server.py | 46 +++++++++++++++++-- .../language_servers/vts_language_server.py | 5 ++ src/solidlsp/ls.py | 11 +++++ 5 files changed, 61 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 831b4824..0c34aae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,9 @@ Status of the `main` branch. Changes prior to the next official version change w - C/C++ (clangd): improve support and documentation for Unreal Engine 5 projects. - `typescript_vts`: Add `initialization_options` setting in `ls_specific_settings.typescript_vts`. Enables Yarn PnP setups with `typescript.tsdk` pointing at the Yarn-generated SDK. + - TypeScript/VTS: disable automatic typing acquisition during initialization (no network + downloads at startup) and replace the fixed 2-second cross-file reference wait with + event-based `$/progress` indexing tracking (configurable `indexing_timeout`, default 30s) - C#: minor fixes in Omnisharp and Roslyn that prevented startup on some systems #1617 - `SvelteLanguageServer`: Fix diagnostics requests for TypeScript/JavaScript files incorrectly being processed by the Svelte LS instead of the TypeScript LS. diff --git a/docs/02-usage/050_configuration.md b/docs/02-usage/050_configuration.md index c69faaaf..dd8aa138 100644 --- a/docs/02-usage/050_configuration.md +++ b/docs/02-usage/050_configuration.md @@ -1000,6 +1000,7 @@ Supported settings: | `typescript_version` | `5.9.3` | Override the bundled `typescript` npm package version Serena installs when `ls_path` is not set. | | `typescript_language_server_version` | `5.1.3` | Override the bundled `typescript-language-server` npm package version Serena installs when `ls_path` is not set. | | `npm_registry` | `null` | Override the npm registry Serena uses for the managed install. | +| `indexing_timeout` | `30.0` | Timeout in seconds for waiting on tsserver's `$/progress` project-indexing signal (both at startup and before the first cross-file reference query). If indexing does not complete within this window, Serena logs a warning and proceeds anyway. Increase it for very large projects. | TypeScript supports [additional workspace folders](additional-workspace-folders) for cross-package reference discovery. Configure `additional_workspace_folders` in `project.yml` to enable this feature. diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index 892843f5..e056e5de 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -7,6 +7,7 @@ import os import pathlib import shutil import threading +import time from typing import Any, cast from overrides import override @@ -73,6 +74,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): You can pass the following entries in ls_specific_settings["typescript"]: - typescript_version: Version of TypeScript to install (default: "5.9.3") - typescript_language_server_version: Version of typescript-language-server to install (default: "5.1.3") + - indexing_timeout: float, timeout in seconds for project indexing (default: 30.0) """ @classmethod @@ -81,7 +83,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): # Safety timeout for $/progress-based indexing wait. Normally the event fires # well within this window; the timeout is only hit if the server never sends progress. - INDEXING_PROGRESS_TIMEOUT = 15.0 if os.name == "nt" else 10.0 + INDEXING_PROGRESS_TIMEOUT = 30.0 def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings): """ @@ -231,6 +233,15 @@ class TypeScriptLanguageServer(SolidLanguageServer): root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "locale": "en", + # Disable Automatic Type Acquisition (ATA): with ATA enabled, tsserver fetches + # @types/* packages from npm in the background during indexing, which makes startup + # slow, network-dependent, and nondeterministic (and can hang on offline/locked-down + # machines). Serena relies on the types already installed in the project instead. + "initializationOptions": { + "preferences": { + "disableAutomaticTypingAcquisition": True, + }, + }, "capabilities": { "textDocument": { "synchronization": {"didSave": True, "dynamicRegistration": True}, @@ -393,13 +404,14 @@ class TypeScriptLanguageServer(SolidLanguageServer): # typescript-language-server may send $/progress for "Initializing JS/TS # language features…" after initialized. If no progress is sent, # _indexing_complete stays SET and wait() returns immediately. + indexing_timeout = self._custom_settings.get("indexing_timeout", self.INDEXING_PROGRESS_TIMEOUT) log.info("Waiting for TypeScript project indexing to complete (if async)...") - if self.wait_for_indexing(timeout=self.INDEXING_PROGRESS_TIMEOUT): + if self.wait_for_indexing(timeout=indexing_timeout): log.info("TypeScript project indexing complete") else: log.warning( "TypeScript project indexing did not complete within %.0fs; proceeding anyway", - self.INDEXING_PROGRESS_TIMEOUT, + indexing_timeout, ) self._activate_additional_workspaces() @@ -456,8 +468,32 @@ class TypeScriptLanguageServer(SolidLanguageServer): return self._published_diagnostics_timeout @override - def _get_wait_time_for_cross_file_referencing(self) -> float: - return 2 + def _pre_open_for_cross_file_references(self) -> None: + if not self._has_waited_for_cross_file_references: + self.expect_indexing() + + #: how long to wait for tsserver to *start* reporting indexing progress after didOpen; + #: matches the fixed wait the base implementation previously used + _INDEXING_START_GRACE_S = 2.0 + + @override + def _wait_for_cross_file_references_if_needed(self) -> None: + if self._has_waited_for_cross_file_references: + return + # Give tsserver a short grace period to start reporting $/progress after the file + # was opened. If progress starts, wait until it drains (bounded by indexing_timeout); + # if it never starts, tsserver considers the project loaded and we proceed after the + # grace period — which is no worse than the previous fixed 2-second wait. + grace_deadline = time.monotonic() + self._INDEXING_START_GRACE_S + while time.monotonic() < grace_deadline and not self._indexing_complete.is_set() and not self._active_progress_tokens: + time.sleep(0.05) + if self._active_progress_tokens: + timeout = self._custom_settings.get("indexing_timeout", self.INDEXING_PROGRESS_TIMEOUT) + if self.wait_for_indexing(timeout=timeout): + log.info("TypeScript cross-file indexing complete") + else: + log.warning("TypeScript cross-file indexing did not complete within %.0fs; proceeding", timeout) + self._has_waited_for_cross_file_references = True @override def _get_preferred_definition(self, definitions: list[ls_types.Location]) -> ls_types.Location: diff --git a/src/solidlsp/language_servers/vts_language_server.py b/src/solidlsp/language_servers/vts_language_server.py index 5525809b..6cd092bb 100644 --- a/src/solidlsp/language_servers/vts_language_server.py +++ b/src/solidlsp/language_servers/vts_language_server.py @@ -151,6 +151,11 @@ class VtsLanguageServer(SolidLanguageServer): root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params: dict = { "locale": "en", + "initializationOptions": { + "preferences": { + "disableAutomaticTypingAcquisition": True, + }, + }, "capabilities": { "textDocument": { "synchronization": {"didSave": True, "dynamicRegistration": True}, diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index a769a3b3..af3e5f07 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -1582,6 +1582,9 @@ class SolidLanguageServer(ABC): self._ensure_server_started() t0 = perf_counter() if _debug_enabled else None + # arm indexing tracking before didOpen so that the subsequent wait can + # observe the indexing progress triggered by opening the file + self.language_server._pre_open_for_cross_file_references() with self.language_server.open_file(self.relative_file_path): self.language_server._wait_for_cross_file_references_if_needed() try: @@ -1733,6 +1736,14 @@ class SolidLanguageServer(ABC): }, ) + def _pre_open_for_cross_file_references(self) -> None: + """Called just before open_file() for requests that may need cross-file indexing. + + Override to pre-arm async indexing tracking before sending didOpen (e.g. clear + a threading.Event so a subsequent wait for indexing blocks correctly). + The base implementation is a no-op. + """ + def _wait_for_cross_file_references_if_needed(self) -> None: if not self._has_waited_for_cross_file_references: # Some LS require waiting for a while before they can return accurate cross-file results.