diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b1a34c1..7c951222 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 22a8872d..3429de33 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. | #### Svelte diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index 0cab8c87..278cd10c 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -6,6 +6,7 @@ import logging import os import shutil import threading +import time from typing import Any from overrides import override @@ -71,6 +72,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 @@ -79,7 +81,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): """ @@ -225,6 +227,15 @@ class TypeScriptLanguageServer(SolidLanguageServer): def _create_base_initialize_params(self) -> dict: 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}, @@ -383,13 +394,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() @@ -446,8 +458,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 23f8fe76..d0c5dd5f 100644 --- a/src/solidlsp/language_servers/vts_language_server.py +++ b/src/solidlsp/language_servers/vts_language_server.py @@ -147,6 +147,11 @@ class VtsLanguageServer(SolidLanguageServer): """ 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 11443129..65793bec 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -1586,6 +1586,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: @@ -1737,6 +1740,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.