mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-11 12:29:04 +00:00
TypeScript: make the tsserver progress-start grace configurable, raise the default
find_referencing_symbols/request_references could return incomplete results on large TypeScript projects. _wait_for_indexing_start_or_completion waits only _INDEXING_START_GRACE_S (hardcoded 2.0s) for tsserver to *start* emitting $/progress after opening files; if nothing has appeared by then, it assumes no indexing is needed and proceeds immediately. On a large project, tsserver can take longer than that just to resolve the project graph before it even creates the first progress token, so the first cross-file reference query can race a project that is still loading. Every caller of _wait_for_indexing_start_or_completion (the base TypeScript path and the Svelte companion) always calls expect_indexing() first, so the "no progress observed" branch is reached specifically when indexing is already expected; it cannot distinguish "nothing to do" from "not started yet", because the two look identical from the client for as long as tsserver stays quiet. That ambiguity cannot be resolved from client-observable signals alone, so this makes the grace an ls_specific_settings knob (`indexing_start_grace`), the same way indexing_timeout and server_ready_timeout already are, and raises the default from 2.0s to 5.0s. Fixes #1586
This commit is contained in:
1 parent
05468d9685
commit
61ca9ba5de
4 files changed
+63
-7
No files matched your search
@@ -16,6 +16,15 @@ Status of the `main` branch. Changes prior to the next official version change w
|
||||
* JetBrains:
|
||||
- `jet_brains_find_symbol`: Disallow wildcard-only search, delegating to overview tool if request is for file
|
||||
|
||||
* Language Servers:
|
||||
- `typescript`: Fix: on large projects, the first `find_referencing_symbols`/`request_references` call
|
||||
could silently race tsserver's project load and return incomplete results, because the fixed 2s
|
||||
grace for tsserver to *start* reporting `$/progress` (distinct from the separate, already
|
||||
configurable `indexing_timeout` used to wait for it to *drain*) was hardcoded and not large enough
|
||||
for projects where the initial project-graph resolution itself takes longer than that. The grace
|
||||
is now `indexing_start_grace` (default 5.0s), configurable the same way as `indexing_timeout` and
|
||||
`server_ready_timeout` #1586
|
||||
|
||||
* Hooks:
|
||||
- Add `serena-hooks --client=grok`, including Grok-native PreToolUse allow/deny output.
|
||||
- PreToolUse remind hook: coerce non-string shell command values instead of failing, and recognize
|
||||
|
||||
@@ -1118,8 +1118,9 @@ 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. |
|
||||
| `indexing_timeout` | `30.0` | Timeout in seconds for waiting on tsserver's `$/progress` project-indexing signal to *drain* once it has started (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. |
|
||||
| `server_ready_timeout` | `10.0` | Timeout in seconds for waiting on the server-ready signal after initialization. If the signal does not arrive within this window, Serena logs a message and proceeds anyway. |
|
||||
| `indexing_start_grace` | `5.0` | Timeout in seconds to wait for tsserver to *start* reporting `$/progress` before the first cross-file reference query. tsserver must resolve the project graph before it can emit the first progress token, and that can take longer than the default on a very large project; if it takes longer than this window, Serena assumes no indexing was needed and may return incomplete cross-file references. Raising `indexing_timeout` alone does not help here, since this grace elapses first. Increase this for very large projects if `find_referencing_symbols`/`request_references` returns incomplete results shortly after project load. |
|
||||
|
||||
#### Svelte
|
||||
|
||||
|
||||
@@ -74,6 +74,8 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
- 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)
|
||||
- server_ready_timeout: float, timeout in seconds for the server-ready signal (default: 10.0)
|
||||
- indexing_start_grace: float, timeout in seconds to wait for tsserver to *start*
|
||||
reporting indexing progress before the first cross-file reference query (default: 5.0)
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@@ -84,6 +86,11 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
# well within this window; the timeout is only hit if the server never sends progress.
|
||||
INDEXING_PROGRESS_TIMEOUT = 30.0
|
||||
SERVER_READY_TIMEOUT = 10.0
|
||||
# How long to wait for tsserver to *start* emitting $/progress after opening files, before
|
||||
# assuming no indexing is needed. tsserver has to resolve the project graph before it can even
|
||||
# create the first progress token, and that resolution is proportional to project size, so a
|
||||
# large project can genuinely take longer than a small one just to begin reporting.
|
||||
INDEXING_START_GRACE = 5.0
|
||||
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
@@ -123,7 +130,7 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
:param start_grace: Maximum seconds to wait for progress to begin after opening files.
|
||||
:return: True if indexing completed or no progress began within the grace period, False on timeout.
|
||||
"""
|
||||
grace = self._INDEXING_START_GRACE_S if start_grace is None else start_grace
|
||||
grace = self.INDEXING_START_GRACE if start_grace is None else start_grace
|
||||
|
||||
# wait for progress to begin
|
||||
progress_deadline = time.monotonic() + grace
|
||||
@@ -171,6 +178,10 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
""":return: maximum seconds to wait for TypeScript project indexing."""
|
||||
return float(self._custom_settings.get("indexing_timeout", self.INDEXING_PROGRESS_TIMEOUT))
|
||||
|
||||
def _get_indexing_start_grace(self) -> float:
|
||||
""":return: maximum seconds to wait for tsserver to start reporting indexing progress."""
|
||||
return float(self._custom_settings.get("indexing_start_grace", self.INDEXING_START_GRACE))
|
||||
|
||||
def _handle_server_ready_timeout(self, timeout: float) -> None:
|
||||
"""Handle a TypeScript server-ready timeout.
|
||||
|
||||
@@ -531,17 +542,14 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
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
|
||||
|
||||
timeout = self._get_indexing_timeout()
|
||||
if self._wait_for_indexing_start_or_completion(timeout=timeout):
|
||||
start_grace = self._get_indexing_start_grace()
|
||||
if self._wait_for_indexing_start_or_completion(timeout=timeout, start_grace=start_grace):
|
||||
log.info("TypeScript cross-file indexing complete")
|
||||
else:
|
||||
log.warning(
|
||||
|
||||
@@ -63,6 +63,14 @@ class TestBaseTypeScriptTimeoutPolicy:
|
||||
assert server._get_server_ready_timeout() == 1.5
|
||||
assert server._get_indexing_timeout() == 2.5
|
||||
|
||||
def test_indexing_start_grace_default(self) -> None:
|
||||
server = _bare_ts_server(TypeScriptLanguageServer)
|
||||
assert server._get_indexing_start_grace() == 5.0
|
||||
|
||||
def test_indexing_start_grace_configurable_via_ls_specific_settings(self) -> None:
|
||||
server = _bare_ts_server(TypeScriptLanguageServer, {"indexing_start_grace": 12.0})
|
||||
assert server._get_indexing_start_grace() == 12.0
|
||||
|
||||
|
||||
class TestSvelteCompanionTimeoutPolicy:
|
||||
"""The Svelte companion must be strict: raise instead of serving from a cold/partial program."""
|
||||
@@ -141,6 +149,36 @@ class TestWaitForIndexingStartOrCompletion:
|
||||
timer.cancel()
|
||||
|
||||
|
||||
class TestWaitForCrossFileReferencesUsesConfiguredGrace:
|
||||
"""The actual find-references call path (not just the helper in isolation) must honor
|
||||
indexing_start_grace: this is the mechanism behind oraios/serena#1586, where a large
|
||||
project's tsserver takes longer than the (previously hardcoded, unconfigurable) grace to
|
||||
even start reporting progress, so the first cross-file reference query silently races a
|
||||
still-loading project and returns incomplete results.
|
||||
"""
|
||||
|
||||
def test_configured_grace_bounds_the_real_call_path(self) -> None:
|
||||
server = _bare_ts_server(TypeScriptLanguageServer, {"indexing_start_grace": 0.05})
|
||||
server._has_waited_for_cross_file_references = False
|
||||
server.expect_indexing() # mirrors _pre_open_for_cross_file_references having already run
|
||||
|
||||
start = time.monotonic()
|
||||
server._wait_for_cross_file_references_if_needed()
|
||||
elapsed = time.monotonic() - start
|
||||
|
||||
assert server._has_waited_for_cross_file_references is True
|
||||
# bounded by the configured 0.05s grace, not the 5.0s default (let alone the old 2.0s one)
|
||||
assert elapsed < 1.0
|
||||
|
||||
def test_second_call_is_a_noop_regardless_of_grace(self) -> None:
|
||||
server = _bare_ts_server(TypeScriptLanguageServer, {"indexing_start_grace": 0.05})
|
||||
server._has_waited_for_cross_file_references = True
|
||||
|
||||
start = time.monotonic()
|
||||
server._wait_for_cross_file_references_if_needed()
|
||||
assert time.monotonic() - start < 0.1
|
||||
|
||||
|
||||
class _FakeSolidLSPSettings:
|
||||
"""Minimal settings stand-in: returns a fixed TypeScript indexing_timeout so the guard tests can
|
||||
prove the svelte-key value takes precedence over the typescript-key value.
|
||||
|
||||
Reference in new issue
Block a user