Merge pull request #1779 from TyceHerrman/agent/rust-analyzer-lightweight

Rust: reduce rust-analyzer memory churn
This commit is contained in:
Dr. Dominik Jain authored and GitHub committed 2026-07-30 10:43:59 +02:00
commit dd12fa032f
3 files changed
+91 -2

No files matched your search

+1
View File
@@ -39,6 +39,7 @@ Status of the `main` branch. Changes prior to the next official version change w
- `jet_brains_find_symbol`: Disallow wildcard-only search, delegating to overview tool if request is for file
* Language Servers:
- Rust: reduce rust-analyzer memory usage and reload churn by disabling cache priming and Cargo autoreload while preserving diagnostics.
- `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
+41 -2
View File
@@ -12,7 +12,13 @@ import threading
from overrides import override
from solidlsp.ls import LanguageServerDependencyProvider, LanguageServerDependencyProviderSinglePath, SolidLanguageServer
from solidlsp import ls_types
from solidlsp.ls import (
LanguageServerDependencyProvider,
LanguageServerDependencyProviderSinglePath,
LSPConstants,
SolidLanguageServer,
)
from solidlsp.ls_config import LanguageServerConfig
from solidlsp.settings import SolidLSPSettings
from solidlsp.util.subprocess_util import subprocess_run
@@ -505,7 +511,10 @@ class RustAnalyzer(SolidLanguageServer):
"showUnlinkedFileNotification": True,
"showDependenciesExplorer": True,
"assist": {"emitMustUse": False, "expressionFillDefault": "todo"},
"cachePriming": {"enable": True, "numThreads": 0},
# Eager cache priming and automatic Cargo reloads can repeatedly
# index large, actively-built workspaces. Keep checkOnSave enabled
# because Rust diagnostics rely on flycheck.
"cachePriming": {"enable": False, "numThreads": 0},
"cargo": {
"autoreload": True,
"buildScripts": {
@@ -675,6 +684,36 @@ class RustAnalyzer(SolidLanguageServer):
}
return initialize_params
@override
def _get_published_diagnostics_wait_timeout(self, pull_diagnostics_failed: bool) -> float:
timeout = super()._get_published_diagnostics_wait_timeout(pull_diagnostics_failed)
# Rust diagnostics are often published asynchronously after the pull-diagnostics request,
# so keep a wider fallback wait window across all platforms.
return max(timeout, 8.0)
@override
def request_text_document_diagnostics(
self,
relative_file_path: str,
start_line: int = 0,
end_line: int = -1,
min_severity: int = 4,
) -> list[ls_types.Diagnostic]:
uri = self._validate_text_document_diagnostics_request(relative_file_path, start_line, end_line, min_severity)
# With cache priming disabled, startup can finish before rust-analyzer
# schedules its initial flycheck. Trigger the retained checkOnSave path
# explicitly whenever diagnostics are requested.
with self.open_file(relative_file_path):
self.server.notify.did_save_text_document(
{ # ty: ignore[invalid-argument-type] # dict built from LSPConstants keys; shape matches the TypedDict
LSPConstants.TEXT_DOCUMENT: {
LSPConstants.URI: uri,
}
}
)
return super().request_text_document_diagnostics(relative_file_path, start_line, end_line, min_severity)
def _start_server(self) -> None:
"""
Starts the Rust Analyzer Language Server
@@ -0,0 +1,49 @@
from contextlib import nullcontext
from typing import Any, cast
from unittest.mock import MagicMock
import pytest
from solidlsp import SolidLanguageServer
from solidlsp.language_servers.rust_analyzer import RustAnalyzer
pytestmark = pytest.mark.rust
def _make_server() -> RustAnalyzer:
return object.__new__(RustAnalyzer)
def test_initialization_uses_lightweight_indexing_settings() -> None:
initialization_options = _make_server()._create_base_initialize_params()["initializationOptions"]
assert initialization_options["cachePriming"]["enable"] is False
assert initialization_options["checkOnSave"] is True
@pytest.mark.parametrize("pull_diagnostics_failed", [False, True])
def test_published_diagnostics_wait_has_eight_second_minimum(pull_diagnostics_failed: bool) -> None:
assert _make_server()._get_published_diagnostics_wait_timeout(pull_diagnostics_failed) == 8.0
def test_published_diagnostics_wait_preserves_larger_base_timeout(monkeypatch: pytest.MonkeyPatch) -> None:
def wider_base_timeout(_server: SolidLanguageServer, _pull_diagnostics_failed: bool) -> float:
return 12.0
monkeypatch.setattr(SolidLanguageServer, "_get_published_diagnostics_wait_timeout", wider_base_timeout)
assert _make_server()._get_published_diagnostics_wait_timeout(False) == 12.0
def test_diagnostics_request_triggers_check_on_save(monkeypatch: pytest.MonkeyPatch) -> None:
server = _make_server()
server_interface = MagicMock()
server.server = cast(Any, server_interface)
uri = "file:///workspace/src/lib.rs"
monkeypatch.setattr(server, "_validate_text_document_diagnostics_request", lambda *_args: uri)
monkeypatch.setattr(server, "open_file", lambda _relative_file_path: nullcontext())
monkeypatch.setattr(SolidLanguageServer, "request_text_document_diagnostics", lambda *_args, **_kwargs: [])
assert server.request_text_document_diagnostics("src/lib.rs") == []
server_interface.notify.did_save_text_document.assert_called_once_with({"textDocument": {"uri": uri}})