From 3be211c662712fd30ac2bcd62821d43a7e4acd89 Mon Sep 17 00:00:00 2001 From: davidcforbes Date: Mon, 8 Jun 2026 12:58:18 -0700 Subject: [PATCH 1/5] rust-analyzer: lightweight profile (disable cachePriming + cargo.autoreload) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit serena's symbol tools (find_symbol, find_referencing_symbols, …) only need the symbol index, but the LSP is launched with VS Code's full heavyweight defaults. On a large, actively-built Rust workspace these drove rust-analyzer to 40+ GB RSS and triggered a full re-index on every external `cargo build` (target/ churn). Disabling the two eager/auto re-indexing drivers keeps symbol navigation working at a fraction of the memory: - cachePriming.enable: True -> False (no eager whole-workspace+deps index) - cargo.autoreload: True -> False (no re-index storm on target/ changes) checkOnSave is intentionally LEFT enabled: serena's get_diagnostics_for_file relies on flycheck (`cargo check`) for Rust diagnostics, and disabling it makes that tool return empty (test/solidlsp/rust/test_rust_diagnostics.py fails with []). checkOnSave only spawns transient `cargo check` child processes and is not a driver of rust-analyzer's resident RSS, so keeping it on preserves diagnostics at no meaningful memory cost. Observed on a rustback (multi-crate + rustic_core fork) workspace: repeated 40-44 GB rust-analyzer processes under serena with no VS Code present. Suggest upstream expose a "lightweight"/symbol-only RA profile or a config toggle. Co-Authored-By: Claude Opus 4.8 (1M context) (cherry picked from commit e09b9b623e321f72e64b6ba3de6708ff519f524f) --- src/solidlsp/language_servers/rust_analyzer.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index 1ee08896..b9327044 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -505,9 +505,19 @@ class RustAnalyzer(SolidLanguageServer): "showUnlinkedFileNotification": True, "showDependenciesExplorer": True, "assist": {"emitMustUse": False, "expressionFillDefault": "todo"}, - "cachePriming": {"enable": True, "numThreads": 0}, + # rustback lightweight patch: serena's symbol tools don't need + # rust-analyzer's eager whole-workspace+deps cache priming, nor a + # re-index on every cargo metadata / target/ change. On large, + # actively-built Rust workspaces these drove rust-analyzer to + # 40+ GB RSS and re-indexed on every external `cargo build`. + # Disabling them keeps symbol navigation working at a fraction of + # the memory. NOTE: checkOnSave is intentionally left enabled below + # because serena's get_diagnostics_for_file relies on flycheck + # (`cargo check`) for Rust diagnostics. (Upstream: expose these as + # a config knob / a "lightweight" RA profile.) + "cachePriming": {"enable": False, "numThreads": 0}, "cargo": { - "autoreload": True, + "autoreload": False, "buildScripts": { "enable": True, "invocationLocation": "workspace", From 23957a55e4b09df8881c8f42d91be0175fcd3d23 Mon Sep 17 00:00:00 2001 From: Michael Panchenko Date: Thu, 25 Jun 2026 11:46:15 +0200 Subject: [PATCH 2/5] Bump timeout for rust diagnostics (cherry picked from commit c42dcf29a9849e1fe935e5039aebd24e7b1f6cac) --- src/solidlsp/language_servers/rust_analyzer.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index b9327044..2c1d21d0 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -685,6 +685,13 @@ 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, 4.0) + def _start_server(self) -> None: """ Starts the Rust Analyzer Language Server From c839f77e26e13fa5f6baf4f3f7553b19b5aa6678 Mon Sep 17 00:00:00 2001 From: Michael Panchenko Date: Sun, 28 Jun 2026 13:12:21 +0200 Subject: [PATCH 3/5] Bump timeout (cherry picked from commit 8d0608939f1e4719cd9e12cbaab341af723d4a71) --- src/solidlsp/language_servers/rust_analyzer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index 2c1d21d0..5b48417b 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -690,7 +690,7 @@ class RustAnalyzer(SolidLanguageServer): 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, 4.0) + return max(timeout, 8.0) def _start_server(self) -> None: """ From 580a7b479ee729880b0fda4be8e53daa1416e4c4 Mon Sep 17 00:00:00 2001 From: Tyce Herrman Date: Tue, 28 Jul 2026 19:49:30 -0400 Subject: [PATCH 4/5] Rust: stabilize lightweight diagnostics --- CHANGELOG.md | 1 + .../language_servers/rust_analyzer.py | 44 ++++++++++++---- .../rust/test_rust_analyzer_settings.py | 50 +++++++++++++++++++ 3 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 test/solidlsp/rust/test_rust_analyzer_settings.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f89ae818..03ab9337 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,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 diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index 5b48417b..12f77c03 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -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,16 +511,9 @@ class RustAnalyzer(SolidLanguageServer): "showUnlinkedFileNotification": True, "showDependenciesExplorer": True, "assist": {"emitMustUse": False, "expressionFillDefault": "todo"}, - # rustback lightweight patch: serena's symbol tools don't need - # rust-analyzer's eager whole-workspace+deps cache priming, nor a - # re-index on every cargo metadata / target/ change. On large, - # actively-built Rust workspaces these drove rust-analyzer to - # 40+ GB RSS and re-indexed on every external `cargo build`. - # Disabling them keeps symbol navigation working at a fraction of - # the memory. NOTE: checkOnSave is intentionally left enabled below - # because serena's get_diagnostics_for_file relies on flycheck - # (`cargo check`) for Rust diagnostics. (Upstream: expose these as - # a config knob / a "lightweight" RA profile.) + # 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": False, @@ -692,6 +691,29 @@ class RustAnalyzer(SolidLanguageServer): # 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 diff --git a/test/solidlsp/rust/test_rust_analyzer_settings.py b/test/solidlsp/rust/test_rust_analyzer_settings.py new file mode 100644 index 00000000..01fc04af --- /dev/null +++ b/test/solidlsp/rust/test_rust_analyzer_settings.py @@ -0,0 +1,50 @@ +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["cargo"]["autoreload"] 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}}) From 1f133f62cfce4bce3af1a267c54561ffcdbe86ec Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 29 Jul 2026 23:30:25 +0200 Subject: [PATCH 5/5] Re-enable cargo.autoreload autoreload=false was likely to leave project metadata stale after manifest or workspace changes #1779 --- src/solidlsp/language_servers/rust_analyzer.py | 2 +- test/solidlsp/rust/test_rust_analyzer_settings.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index 12f77c03..5b53c17e 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -516,7 +516,7 @@ class RustAnalyzer(SolidLanguageServer): # because Rust diagnostics rely on flycheck. "cachePriming": {"enable": False, "numThreads": 0}, "cargo": { - "autoreload": False, + "autoreload": True, "buildScripts": { "enable": True, "invocationLocation": "workspace", diff --git a/test/solidlsp/rust/test_rust_analyzer_settings.py b/test/solidlsp/rust/test_rust_analyzer_settings.py index 01fc04af..3d9b46e9 100644 --- a/test/solidlsp/rust/test_rust_analyzer_settings.py +++ b/test/solidlsp/rust/test_rust_analyzer_settings.py @@ -18,7 +18,6 @@ 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["cargo"]["autoreload"] is False assert initialization_options["checkOnSave"] is True