mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 20:17:42 +00:00
The Dart analysis server treats rootUri as an additional analysis root on top of workspaceFolders, with no de-duplication, so on a monorepo root the whole tree is analysed and the server burns CPU at idle. Always omit rootUri/rootPath and rely on workspaceFolders alone.
This commit is contained in:
5 files changed
+102
-3
No files matched your search
@@ -104,6 +104,7 @@ Status of the `main` branch. Changes prior to the next official version change w
|
||||
thread (#2038)
|
||||
|
||||
* Language Servers:
|
||||
- Fix: Dart analysis server no longer receives rootUri/rootPath, which added the monorepo root as an extra analysis root and could pin a CPU core at idle (#2045)
|
||||
- Fix: The C# language server opened every `.csproj` found anywhere under the repository root,
|
||||
without consulting the project's ignore settings. On repositories that vendor third-party or
|
||||
sample C# projects, this loads projects the server cannot restore on every start, and their
|
||||
|
||||
@@ -40,10 +40,11 @@ class InitializeParamsBuilder(ABC):
|
||||
|
||||
|
||||
class DefaultInitializeParamsBuilder(InitializeParamsBuilder):
|
||||
def __init__(self, ls: "SolidLanguageServer", set_workspace_folders: bool = True):
|
||||
def __init__(self, ls: "SolidLanguageServer", set_workspace_folders: bool = True, set_root_uri: bool = True):
|
||||
super().__init__()
|
||||
self._ls = ls
|
||||
self._set_workspace_folders = set_workspace_folders
|
||||
self._set_root_uri = set_root_uri
|
||||
|
||||
@staticmethod
|
||||
def _create_workspace_folder_entry(path: str) -> WorkspaceFolder:
|
||||
@@ -54,10 +55,22 @@ class DefaultInitializeParamsBuilder(InitializeParamsBuilder):
|
||||
root_abs_path = self._ls.repository_root_path
|
||||
|
||||
self._set("processId", os.getpid())
|
||||
self._set("rootPath", root_abs_path)
|
||||
self._set("rootUri", pathlib.Path(root_abs_path).as_uri())
|
||||
self._set("clientInfo", {"name": "Serena"})
|
||||
|
||||
# Some language servers treat rootUri as an additional analysis root on top of
|
||||
# workspaceFolders, with no de-duplication, which can cause unbounded indexing.
|
||||
# When set_root_uri is False, rootUri/rootPath are omitted so that workspaceFolders
|
||||
# alone determine the analysis roots.
|
||||
if self._set_root_uri:
|
||||
self._set("rootPath", root_abs_path)
|
||||
self._set("rootUri", pathlib.Path(root_abs_path).as_uri())
|
||||
else:
|
||||
# Some servers reject initialize when the key is absent
|
||||
# ("params.rootUri must not be undefined"). Send explicit null so the
|
||||
# field is present but not used as an analysis root (#2045).
|
||||
self._set("rootPath", None)
|
||||
self._set("rootUri", None)
|
||||
|
||||
if self._set_workspace_folders:
|
||||
abs_workspace_paths = self._ls.config.get_absolute_workspace_folders(root_abs_path)
|
||||
log.info("Workspace folders: %s", abs_workspace_paths)
|
||||
|
||||
@@ -7,6 +7,7 @@ from collections.abc import Hashable
|
||||
|
||||
from overrides import override
|
||||
|
||||
from solidlsp.initialize_params import DefaultInitializeParamsBuilder, InitializeParamsBuilder
|
||||
from solidlsp.ls import RawDocumentSymbol, SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
@@ -73,6 +74,13 @@ class DartLanguageServer(SolidLanguageServer):
|
||||
# via either notification it sends for this (see _start_server).
|
||||
self.analysis_complete = threading.Event()
|
||||
|
||||
def _create_initialize_params_builder(self) -> InitializeParamsBuilder:
|
||||
# The Dart analysis server treats rootUri as an additional analysis root on top of
|
||||
# workspaceFolders, with no de-duplication, so on a monorepo root that is not a Dart
|
||||
# package the whole tree is analysed and the server burns CPU at idle (oraios/serena#2045).
|
||||
# Omit rootUri/rootPath and rely on workspaceFolders alone.
|
||||
return DefaultInitializeParamsBuilder(self, set_root_uri=False)
|
||||
|
||||
@override
|
||||
def _document_symbols_cache_fingerprint(self) -> Hashable:
|
||||
normalize_symbol_name_version = 1
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from solidlsp.language_servers.dart_language_server import DartLanguageServer
|
||||
|
||||
|
||||
def _make_dart_ls() -> DartLanguageServer:
|
||||
ls = object.__new__(DartLanguageServer)
|
||||
ls._custom_settings = {}
|
||||
# Windows: as_uri() rejects drive-less paths like "/tmp/..."
|
||||
project_dir = Path(tempfile.mkdtemp(prefix="fake-dart-project-")) / "project"
|
||||
project_dir.mkdir(parents=True, exist_ok=True)
|
||||
ls.repository_root_path = str(project_dir)
|
||||
|
||||
class _Cfg:
|
||||
@staticmethod
|
||||
def get_absolute_workspace_folders(root):
|
||||
return [root]
|
||||
|
||||
@staticmethod
|
||||
def get_absolute_additional_workspace_folders(root):
|
||||
return []
|
||||
|
||||
ls.config = _Cfg()
|
||||
# custom_settings property reads from _custom_settings on SolidLanguageServer
|
||||
return ls
|
||||
|
||||
|
||||
def test_dart_omits_root_uri():
|
||||
builder = _make_dart_ls()._create_initialize_params_builder()
|
||||
params = builder.build()
|
||||
# rootUri must be present (some servers reject an undefined key) but null, so that only
|
||||
# workspaceFolders determine the analysis roots (oraios/serena#2045).
|
||||
assert params["rootUri"] is None
|
||||
assert params["rootPath"] is None
|
||||
assert params["workspaceFolders"]
|
||||
@@ -0,0 +1,39 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from solidlsp.initialize_params import DefaultInitializeParamsBuilder
|
||||
|
||||
|
||||
class _FakeLS:
|
||||
# Windows: as_uri() rejects drive-less paths like "/tmp/..."
|
||||
repository_root_path = str(Path(tempfile.mkdtemp(prefix="fake-project-")) / "root")
|
||||
|
||||
class config:
|
||||
@staticmethod
|
||||
def get_absolute_workspace_folders(root):
|
||||
return [root]
|
||||
|
||||
@staticmethod
|
||||
def get_absolute_additional_workspace_folders(root):
|
||||
return []
|
||||
|
||||
custom_settings: dict = {}
|
||||
|
||||
|
||||
def test_default_builder_sets_root_uri():
|
||||
builder = DefaultInitializeParamsBuilder(_FakeLS())
|
||||
params = builder.build()
|
||||
assert "rootUri" in params
|
||||
assert "rootPath" in params
|
||||
|
||||
|
||||
def test_builder_sends_null_root_uri_when_disabled():
|
||||
builder = DefaultInitializeParamsBuilder(_FakeLS(), set_root_uri=False)
|
||||
params = builder.build()
|
||||
# keys must be present (some servers reject an undefined rootUri); values are null
|
||||
assert params["rootUri"] is None
|
||||
assert params["rootPath"] is None
|
||||
assert params["processId"] is not None
|
||||
assert params["clientInfo"] == {"name": "Serena"}
|
||||
Reference in new issue
Block a user