Merge pull request #1617 from Lauritz-Timm/fix/windows-csharp-startup-install

Fix Windows C# startup and Roslyn install
This commit is contained in:
Michael Panchenko authored and GitHub committed 2026-06-28 12:48:07 +02:00
commit 42294fdf32
4 files changed
+82 -6

No files matched your search

@@ -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),
+7 -2
View File
@@ -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))
@@ -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)
@@ -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()