From 8e7c8feb842d0e6d798d6fdb3b8068ee755e881e Mon Sep 17 00:00:00 2001 From: Lauritz-Configo Date: Sat, 27 Jun 2026 19:29:43 +0200 Subject: [PATCH] Fix Windows C# startup and Roslyn install --- .../csharp_language_server.py | 12 +++++-- src/solidlsp/language_servers/omnisharp.py | 9 +++-- .../csharp/test_csharp_nuget_download.py | 32 ++++++++++++++++- .../solidlsp/csharp/test_omnisharp_startup.py | 35 +++++++++++++++++++ 4 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 test/solidlsp/csharp/test_omnisharp_startup.py diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index 51bed54e..66049a7e 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -6,6 +6,7 @@ import logging import os import platform import shutil +import tempfile import threading from collections.abc import Hashable, Iterable from dataclasses import replace @@ -34,6 +35,8 @@ log = logging.getLogger(__name__) NUGET_ALLOWED_HOSTS = ("www.nuget.org", "nuget.org", "globalcdn.nuget.org") DEFAULT_CSHARP_LANGUAGE_SERVER_VERSION = "5.5.0-2.26078.4" +WINDOWS_MAX_PATH = 260 +ROSLYN_LONGEST_KNOWN_PACKAGE_MEMBER = "Microsoft.CodeAnalysis.ExternalAccess.CompilerDeveloperSDK.dll" _RUNTIME_DEPENDENCIES = [ RuntimeDependency( @@ -460,12 +463,15 @@ class CSharpLanguageServer(SolidLanguageServer): if url is None: raise SolidLSPException(f"No URL specified for package {package_name} version {package_version}") - temp_dir = Path(self._ls_resources_dir) / "temp_downloads" - temp_dir.mkdir(parents=True, exist_ok=True) + package_extract_dir = Path(self._ls_resources_dir) / "temp_downloads" / f"{package_name}.{package_version}" + projected_path = package_extract_dir / (dependency.extract_path or "") / ROSLYN_LONGEST_KNOWN_PACKAGE_MEMBER + if platform.system() == "Windows" and len(str(projected_path)) >= WINDOWS_MAX_PATH: + log.warning("Using a short temporary directory for Roslyn package extraction because the configured cache path is too deep") + package_extract_dir = Path(tempfile.mkdtemp(prefix="serena-roslyn-")) / package_extract_dir.name + package_extract_dir.parent.mkdir(parents=True, exist_ok=True) try: log.debug(f"Downloading package from: {url}") - package_extract_dir = temp_dir / f"{package_name}.{package_version}" FileUtils.download_and_extract_archive_verified( url, str(package_extract_dir), diff --git a/src/solidlsp/language_servers/omnisharp.py b/src/solidlsp/language_servers/omnisharp.py index f4ece965..58dda4ee 100644 --- a/src/solidlsp/language_servers/omnisharp.py +++ b/src/solidlsp/language_servers/omnisharp.py @@ -415,5 +415,10 @@ class OmniSharp(SolidLanguageServer): if "referencesProvider" in init_response["capabilities"] and init_response["capabilities"]["referencesProvider"]: self.references_available.set() - self.definition_available.wait() - self.references_available.wait() + missing_capabilities = [ + name + for event, name in ((self.definition_available, "definition"), (self.references_available, "references")) + if not event.is_set() + ] + if missing_capabilities: + log.warning("OmniSharp did not advertise capabilities during initialization: %s", ", ".join(missing_capabilities)) diff --git a/test/solidlsp/csharp/test_csharp_nuget_download.py b/test/solidlsp/csharp/test_csharp_nuget_download.py index 2319766d..548dfeb7 100644 --- a/test/solidlsp/csharp/test_csharp_nuget_download.py +++ b/test/solidlsp/csharp/test_csharp_nuget_download.py @@ -7,7 +7,7 @@ from unittest.mock import patch import pytest from solidlsp.language_servers.common import RuntimeDependency -from solidlsp.language_servers.csharp_language_server import CSharpLanguageServer +from solidlsp.language_servers.csharp_language_server import ROSLYN_LONGEST_KNOWN_PACKAGE_MEMBER, CSharpLanguageServer from solidlsp.settings import SolidLSPSettings @@ -128,3 +128,33 @@ class TestNuGetOrgDownload: "solidlsp.language_servers.csharp_language_server.FileUtils.download_and_extract_archive_verified", ): dependency_provider._download_nuget_package(test_dependency) + + def test_download_nuget_package_uses_short_extraction_path_for_deep_windows_cache(self, tmp_path: Path): + provider = object.__new__(CSharpLanguageServer.DependencyProvider) + provider._ls_resources_dir = str(tmp_path.joinpath(*(["nested-cache-root"] * 8))) + + test_dependency = RuntimeDependency( + id="TestPackage", + package_name="roslyn-language-server.win-x64", + package_version="5.5.0-2.26078.4", + url="https://www.nuget.org/api/v2/package/roslyn-language-server.win-x64/5.5.0-2.26078.4", + extract_path="tools/net10.0/win-x64", + ) + + def fake_download_and_extract( + _url: str, + target_path: str, + *_args: object, + **_kwargs: object, + ) -> None: + deepest_package_member = Path(target_path) / f"tools/net10.0/win-x64/{ROSLYN_LONGEST_KNOWN_PACKAGE_MEMBER}" + assert len(str(deepest_package_member)) < 260 + + with ( + patch("solidlsp.language_servers.csharp_language_server.platform.system", return_value="Windows"), + patch( + "solidlsp.language_servers.csharp_language_server.FileUtils.download_and_extract_archive_verified", + side_effect=fake_download_and_extract, + ), + ): + provider._download_nuget_package(test_dependency) diff --git a/test/solidlsp/csharp/test_omnisharp_startup.py b/test/solidlsp/csharp/test_omnisharp_startup.py new file mode 100644 index 00000000..74b9df34 --- /dev/null +++ b/test/solidlsp/csharp/test_omnisharp_startup.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from pathlib import Path +from types import SimpleNamespace + +import pytest + +from solidlsp.language_servers.omnisharp import OmniSharp + + +class _NoWaitEvent: + def is_set(self) -> bool: + return False + + def wait(self, timeout: float | None = None) -> bool: + raise AssertionError("OmniSharp startup must not wait for dynamic capability registration") + + +@pytest.mark.csharp +def test_omnisharp_startup_does_not_wait_for_capability_registration(tmp_path: Path) -> None: + omnisharp = OmniSharp.__new__(OmniSharp) + omnisharp.repository_root_path = str(tmp_path) + omnisharp.server = SimpleNamespace( + send=SimpleNamespace(initialize=lambda _params: {"capabilities": {}}), + notify=SimpleNamespace(initialized=lambda _params: None, workspace_did_change_configuration=lambda _params: None), + on_request=lambda *_args: None, + on_notification=lambda *_args: None, + start=lambda: None, + ) + omnisharp.server_ready = _NoWaitEvent() + omnisharp.definition_available = _NoWaitEvent() + omnisharp.references_available = _NoWaitEvent() + omnisharp.completions_available = _NoWaitEvent() + + omnisharp._start_server()