mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-15 02:17:26 +00:00
Formatting, minor ruff fixes
This commit is contained in:
1 parent
6e2ed3969d
commit
3d4dba77bd
3 files changed
+131
-140
No files matched your search
@@ -6,7 +6,6 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import platform
|
import platform
|
||||||
import shlex
|
|
||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -56,17 +55,17 @@ def find_solution_or_project_file(root_dir) -> str | None:
|
|||||||
"""
|
"""
|
||||||
sln_file = None
|
sln_file = None
|
||||||
csproj_file = None
|
csproj_file = None
|
||||||
|
|
||||||
for filename in breadth_first_file_scan(root_dir):
|
for filename in breadth_first_file_scan(root_dir):
|
||||||
if filename.endswith(".sln") and sln_file is None:
|
if filename.endswith(".sln") and sln_file is None:
|
||||||
sln_file = filename
|
sln_file = filename
|
||||||
elif filename.endswith(".csproj") and csproj_file is None:
|
elif filename.endswith(".csproj") and csproj_file is None:
|
||||||
csproj_file = filename
|
csproj_file = filename
|
||||||
|
|
||||||
# If we found a .sln file, return it immediately
|
# If we found a .sln file, return it immediately
|
||||||
if sln_file:
|
if sln_file:
|
||||||
return sln_file
|
return sln_file
|
||||||
|
|
||||||
# If no .sln file was found, return the first .csproj file
|
# If no .sln file was found, return the first .csproj file
|
||||||
return csproj_file
|
return csproj_file
|
||||||
|
|
||||||
@@ -83,39 +82,39 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
Use LanguageServer.create() instead.
|
Use LanguageServer.create() instead.
|
||||||
"""
|
"""
|
||||||
dotnet_path, language_server_path, cache_dir = self.setup_runtime_dependencies(logger, config)
|
dotnet_path, language_server_path, cache_dir = self.setup_runtime_dependencies(logger, config)
|
||||||
|
|
||||||
# Find solution or project file
|
# Find solution or project file
|
||||||
solution_or_project = find_solution_or_project_file(repository_root_path)
|
solution_or_project = find_solution_or_project_file(repository_root_path)
|
||||||
|
|
||||||
# Build command - Microsoft.CodeAnalysis.LanguageServer requires specific parameters
|
# Build command - Microsoft.CodeAnalysis.LanguageServer requires specific parameters
|
||||||
cmd_parts = [dotnet_path, language_server_path]
|
cmd_parts = [dotnet_path, language_server_path]
|
||||||
|
|
||||||
# Required parameters
|
# Required parameters
|
||||||
cmd_parts.extend(["--stdio"]) # Use stdio for communication
|
cmd_parts.extend(["--stdio"]) # Use stdio for communication
|
||||||
|
|
||||||
# Set log level based on logger settings
|
# Set log level based on logger settings
|
||||||
if logger.logger.level <= logging.DEBUG:
|
if logger.logger.level <= logging.DEBUG:
|
||||||
cmd_parts.extend(["--logLevel", "Information"])
|
cmd_parts.extend(["--logLevel", "Information"])
|
||||||
else:
|
else:
|
||||||
cmd_parts.extend(["--logLevel", "Warning"])
|
cmd_parts.extend(["--logLevel", "Warning"])
|
||||||
|
|
||||||
# Create log directory
|
# Create log directory
|
||||||
log_dir = cache_dir / "logs"
|
log_dir = cache_dir / "logs"
|
||||||
log_dir.mkdir(parents=True, exist_ok=True)
|
log_dir.mkdir(parents=True, exist_ok=True)
|
||||||
cmd_parts.extend(["--extensionLogDirectory", str(log_dir)])
|
cmd_parts.extend(["--extensionLogDirectory", str(log_dir)])
|
||||||
|
|
||||||
# The language server will discover the solution/project from the workspace root
|
# The language server will discover the solution/project from the workspace root
|
||||||
if solution_or_project:
|
if solution_or_project:
|
||||||
logger.log(f"Found solution/project file: {solution_or_project}", logging.INFO)
|
logger.log(f"Found solution/project file: {solution_or_project}", logging.INFO)
|
||||||
else:
|
else:
|
||||||
logger.log("No .sln or .csproj file found, language server will attempt auto-discovery", logging.WARNING)
|
logger.log("No .sln or .csproj file found, language server will attempt auto-discovery", logging.WARNING)
|
||||||
|
|
||||||
# Join command parts with spaces
|
# Join command parts with spaces
|
||||||
# ProcessLaunchInfo expects a simple string command, not shell-quoted
|
# ProcessLaunchInfo expects a simple string command, not shell-quoted
|
||||||
cmd = " ".join(cmd_parts)
|
cmd = " ".join(cmd_parts)
|
||||||
|
|
||||||
logger.log(f"Language server command: {cmd}", logging.DEBUG)
|
logger.log(f"Language server command: {cmd}", logging.DEBUG)
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
config,
|
config,
|
||||||
logger,
|
logger,
|
||||||
@@ -123,7 +122,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path),
|
ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path),
|
||||||
"csharp",
|
"csharp",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.initialization_complete = threading.Event()
|
self.initialization_complete = threading.Event()
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -136,15 +135,15 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
Returns a tuple of (dotnet_path, language_server_dll_path, cache_dir).
|
Returns a tuple of (dotnet_path, language_server_dll_path, cache_dir).
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
|
||||||
# Load runtime dependencies configuration
|
# Load runtime dependencies configuration
|
||||||
with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), encoding="utf-8") as f:
|
with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), encoding="utf-8") as f:
|
||||||
runtime_deps = json.load(f)
|
runtime_deps = json.load(f)
|
||||||
|
|
||||||
# Determine the runtime ID based on the platform
|
# Determine the runtime ID based on the platform
|
||||||
system = platform.system().lower()
|
system = platform.system().lower()
|
||||||
machine = platform.machine().lower()
|
machine = platform.machine().lower()
|
||||||
|
|
||||||
# Map platform info to runtime ID
|
# Map platform info to runtime ID
|
||||||
if system == "windows":
|
if system == "windows":
|
||||||
runtime_id = "win-x64" if machine in ["amd64", "x86_64"] else "win-arm64"
|
runtime_id = "win-x64" if machine in ["amd64", "x86_64"] else "win-arm64"
|
||||||
@@ -154,80 +153,78 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
runtime_id = "linux-x64" if machine in ["x86_64", "amd64"] else "linux-arm64"
|
runtime_id = "linux-x64" if machine in ["x86_64", "amd64"] else "linux-arm64"
|
||||||
else:
|
else:
|
||||||
raise LanguageServerException(f"Unsupported platform: {system} {machine}")
|
raise LanguageServerException(f"Unsupported platform: {system} {machine}")
|
||||||
|
|
||||||
# Set up cache directory
|
# Set up cache directory
|
||||||
cache_dir = Path.home() / ".cache" / "serena" / "language-servers" / "csharp"
|
cache_dir = Path.home() / ".cache" / "serena" / "language-servers" / "csharp"
|
||||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Find the appropriate language server dependency
|
# Find the appropriate language server dependency
|
||||||
lang_server_dep = None
|
lang_server_dep = None
|
||||||
dotnet_runtime_dep = None
|
dotnet_runtime_dep = None
|
||||||
|
|
||||||
for dep in runtime_deps["runtimeDependencies"]:
|
for dep in runtime_deps["runtimeDependencies"]:
|
||||||
if dep["id"] == "CSharpLanguageServer" and dep["platformId"] == runtime_id:
|
if dep["id"] == "CSharpLanguageServer" and dep["platformId"] == runtime_id:
|
||||||
lang_server_dep = dep
|
lang_server_dep = dep
|
||||||
elif dep["id"] == "DotNetRuntime" and dep["platformId"] == runtime_id:
|
elif dep["id"] == "DotNetRuntime" and dep["platformId"] == runtime_id:
|
||||||
dotnet_runtime_dep = dep
|
dotnet_runtime_dep = dep
|
||||||
|
|
||||||
if not lang_server_dep:
|
if not lang_server_dep:
|
||||||
raise LanguageServerException(f"No C# language server dependency found for platform {runtime_id}")
|
raise LanguageServerException(f"No C# language server dependency found for platform {runtime_id}")
|
||||||
if not dotnet_runtime_dep:
|
if not dotnet_runtime_dep:
|
||||||
raise LanguageServerException(f"No .NET runtime dependency found for platform {runtime_id}")
|
raise LanguageServerException(f"No .NET runtime dependency found for platform {runtime_id}")
|
||||||
|
|
||||||
# First, ensure we have .NET 9 runtime
|
# First, ensure we have .NET 9 runtime
|
||||||
dotnet_path = self._ensure_dotnet_runtime_from_config(logger, cache_dir, dotnet_runtime_dep)
|
dotnet_path = self._ensure_dotnet_runtime_from_config(logger, cache_dir, dotnet_runtime_dep)
|
||||||
|
|
||||||
# Then set up the language server
|
# Then set up the language server
|
||||||
package_name = lang_server_dep["packageName"]
|
package_name = lang_server_dep["packageName"]
|
||||||
package_version = lang_server_dep["packageVersion"]
|
package_version = lang_server_dep["packageVersion"]
|
||||||
|
|
||||||
server_dir = cache_dir / f"{package_name}.{package_version}"
|
server_dir = cache_dir / f"{package_name}.{package_version}"
|
||||||
server_dll = server_dir / lang_server_dep["binaryName"]
|
server_dll = server_dir / lang_server_dep["binaryName"]
|
||||||
|
|
||||||
if server_dll.exists():
|
if server_dll.exists():
|
||||||
logger.log(f"Using cached Microsoft.CodeAnalysis.LanguageServer from {server_dll}", logging.INFO)
|
logger.log(f"Using cached Microsoft.CodeAnalysis.LanguageServer from {server_dll}", logging.INFO)
|
||||||
return dotnet_path, str(server_dll), cache_dir
|
return dotnet_path, str(server_dll), cache_dir
|
||||||
|
|
||||||
# Download the language server package
|
# Download the language server package
|
||||||
logger.log(f"Downloading {package_name} version {package_version}...", logging.INFO)
|
logger.log(f"Downloading {package_name} version {package_version}...", logging.INFO)
|
||||||
|
|
||||||
# Check if nuget or dotnet is available
|
# Check if nuget or dotnet is available
|
||||||
nuget_cmd = shutil.which("nuget")
|
nuget_cmd = shutil.which("nuget")
|
||||||
dotnet_cmd = shutil.which("dotnet")
|
dotnet_cmd = shutil.which("dotnet")
|
||||||
|
|
||||||
if not nuget_cmd and not dotnet_cmd:
|
if not nuget_cmd and not dotnet_cmd:
|
||||||
raise LanguageServerException(
|
raise LanguageServerException(
|
||||||
"Neither nuget nor dotnet CLI is available. "
|
"Neither nuget nor dotnet CLI is available. " "Please install .NET SDK from https://dotnet.microsoft.com/download"
|
||||||
"Please install .NET SDK from https://dotnet.microsoft.com/download"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as temp_dir:
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
temp_path = Path(temp_dir)
|
temp_path = Path(temp_dir)
|
||||||
|
|
||||||
# Try to download directly from the NuGet API first
|
# Try to download directly from the NuGet API first
|
||||||
direct_download_url = f"https://api.nuget.org/v3-flatcontainer/{package_name.lower()}/{package_version}/{package_name.lower()}.{package_version}.nupkg"
|
direct_download_url = f"https://api.nuget.org/v3-flatcontainer/{package_name.lower()}/{package_version}/{package_name.lower()}.{package_version}.nupkg"
|
||||||
|
|
||||||
package_path = None
|
package_path = None
|
||||||
try:
|
try:
|
||||||
nupkg_path = temp_path / f"{package_name}.{package_version}.nupkg"
|
nupkg_path = temp_path / f"{package_name}.{package_version}.nupkg"
|
||||||
logger.log(f"Attempting direct download from {direct_download_url}", logging.INFO)
|
logger.log(f"Attempting direct download from {direct_download_url}", logging.INFO)
|
||||||
|
|
||||||
urllib.request.urlretrieve(direct_download_url, nupkg_path)
|
urllib.request.urlretrieve(direct_download_url, nupkg_path)
|
||||||
|
|
||||||
# Extract the nupkg (it's a zip file)
|
# Extract the nupkg (it's a zip file)
|
||||||
package_path = temp_path / f"{package_name}.{package_version}"
|
package_path = temp_path / f"{package_name}.{package_version}"
|
||||||
with zipfile.ZipFile(nupkg_path, 'r') as zip_ref:
|
with zipfile.ZipFile(nupkg_path, "r") as zip_ref:
|
||||||
zip_ref.extractall(package_path)
|
zip_ref.extractall(package_path)
|
||||||
|
|
||||||
logger.log("Successfully downloaded and extracted package", logging.INFO)
|
logger.log("Successfully downloaded and extracted package", logging.INFO)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.log(f"Direct download failed: {e}, falling back to package manager", logging.WARNING)
|
logger.log(f"Direct download failed: {e}, falling back to package manager", logging.WARNING)
|
||||||
package_path = None
|
package_path = None
|
||||||
|
|
||||||
if package_path is None and dotnet_cmd:
|
if package_path is None and dotnet_cmd:
|
||||||
# Use dotnet restore to download the package
|
# Use dotnet restore to download the package
|
||||||
nuget_sources = " ".join([f"--source {src}" for src in runtime_deps["nugetSources"]])
|
|
||||||
project_content = f"""<Project Sdk="Microsoft.NET.Sdk">
|
project_content = f"""<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
@@ -241,84 +238,90 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
</RestoreAdditionalProjectSources>
|
</RestoreAdditionalProjectSources>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>"""
|
</Project>"""
|
||||||
|
|
||||||
project_file = temp_path / "temp.csproj"
|
project_file = temp_path / "temp.csproj"
|
||||||
project_file.write_text(project_content)
|
project_file.write_text(project_content)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Use dotnet restore with no dependencies
|
# Use dotnet restore with no dependencies
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[dotnet_cmd, "restore", str(project_file), "--packages", str(temp_path),
|
[
|
||||||
"--no-dependencies", "--ignore-failed-sources"],
|
dotnet_cmd,
|
||||||
|
"restore",
|
||||||
|
str(project_file),
|
||||||
|
"--packages",
|
||||||
|
str(temp_path),
|
||||||
|
"--no-dependencies",
|
||||||
|
"--ignore-failed-sources",
|
||||||
|
],
|
||||||
check=True,
|
check=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True
|
text=True,
|
||||||
)
|
)
|
||||||
package_path = temp_path / package_name.lower() / package_version
|
package_path = temp_path / package_name.lower() / package_version
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
logger.log(f"Dotnet restore stdout: {e.stdout}", logging.ERROR)
|
logger.log(f"Dotnet restore stdout: {e.stdout}", logging.ERROR)
|
||||||
logger.log(f"Dotnet restore stderr: {e.stderr}", logging.ERROR)
|
logger.log(f"Dotnet restore stderr: {e.stderr}", logging.ERROR)
|
||||||
raise LanguageServerException(f"Failed to download package: stdout={e.stdout}, stderr={e.stderr}")
|
raise LanguageServerException(f"Failed to download package: stdout={e.stdout}, stderr={e.stderr}")
|
||||||
|
|
||||||
elif package_path is None and nuget_cmd:
|
elif package_path is None and nuget_cmd:
|
||||||
# Use nuget to download the package
|
# Use nuget to download the package
|
||||||
try:
|
try:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
nuget_cmd, "install", package_name,
|
nuget_cmd,
|
||||||
"-Version", package_version,
|
"install",
|
||||||
"-OutputDirectory", str(temp_path),
|
package_name,
|
||||||
"-NonInteractive"
|
"-Version",
|
||||||
|
package_version,
|
||||||
|
"-OutputDirectory",
|
||||||
|
str(temp_path),
|
||||||
|
"-NonInteractive",
|
||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True
|
text=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Find the downloaded package
|
# Find the downloaded package
|
||||||
package_path = temp_path / f"{package_name}.{package_version}"
|
package_path = temp_path / f"{package_name}.{package_version}"
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
raise LanguageServerException(f"Failed to download package: {e.stderr}")
|
raise LanguageServerException(f"Failed to download package: {e.stderr}")
|
||||||
|
|
||||||
if package_path is None or not package_path.exists():
|
if package_path is None or not package_path.exists():
|
||||||
raise LanguageServerException("Failed to download Microsoft.CodeAnalysis.LanguageServer package")
|
raise LanguageServerException("Failed to download Microsoft.CodeAnalysis.LanguageServer package")
|
||||||
|
|
||||||
# Extract the language server files
|
# Extract the language server files
|
||||||
extract_path = lang_server_dep.get("extractPath", "lib/net9.0")
|
extract_path = lang_server_dep.get("extractPath", "lib/net9.0")
|
||||||
source_dir = package_path / extract_path
|
source_dir = package_path / extract_path
|
||||||
|
|
||||||
if not source_dir.exists():
|
if not source_dir.exists():
|
||||||
# Try alternative locations
|
# Try alternative locations
|
||||||
for possible_dir in [
|
for possible_dir in [
|
||||||
package_path / "tools" / "net9.0" / "any",
|
package_path / "tools" / "net9.0" / "any",
|
||||||
package_path / "lib" / "net9.0",
|
package_path / "lib" / "net9.0",
|
||||||
package_path / "contentFiles" / "any" / "net9.0"
|
package_path / "contentFiles" / "any" / "net9.0",
|
||||||
]:
|
]:
|
||||||
if possible_dir.exists():
|
if possible_dir.exists():
|
||||||
source_dir = possible_dir
|
source_dir = possible_dir
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
raise LanguageServerException(
|
raise LanguageServerException(f"Could not find language server files in package. " f"Searched in {package_path}")
|
||||||
f"Could not find language server files in package. "
|
|
||||||
f"Searched in {package_path}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Copy files to cache directory
|
# Copy files to cache directory
|
||||||
server_dir.mkdir(parents=True, exist_ok=True)
|
server_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
shutil.copytree(source_dir, server_dir, dirs_exist_ok=True)
|
shutil.copytree(source_dir, server_dir, dirs_exist_ok=True)
|
||||||
|
|
||||||
if not server_dll.exists():
|
if not server_dll.exists():
|
||||||
raise LanguageServerException(
|
raise LanguageServerException("Microsoft.CodeAnalysis.LanguageServer.dll not found after extraction")
|
||||||
"Microsoft.CodeAnalysis.LanguageServer.dll not found after extraction"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Make the DLL executable on Unix-like systems
|
# Make the DLL executable on Unix-like systems
|
||||||
if system != "windows":
|
if system != "windows":
|
||||||
server_dll.chmod(server_dll.stat().st_mode | stat.S_IEXEC)
|
server_dll.chmod(server_dll.stat().st_mode | stat.S_IEXEC)
|
||||||
|
|
||||||
logger.log(f"Successfully installed Microsoft.CodeAnalysis.LanguageServer to {server_dll}", logging.INFO)
|
logger.log(f"Successfully installed Microsoft.CodeAnalysis.LanguageServer to {server_dll}", logging.INFO)
|
||||||
return dotnet_path, str(server_dll), cache_dir
|
return dotnet_path, str(server_dll), cache_dir
|
||||||
|
|
||||||
@@ -332,22 +335,17 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
if system_dotnet:
|
if system_dotnet:
|
||||||
# Check if it's .NET 9
|
# Check if it's .NET 9
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run([system_dotnet, "--list-runtimes"], capture_output=True, text=True, check=True)
|
||||||
[system_dotnet, "--list-runtimes"],
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
check=True
|
|
||||||
)
|
|
||||||
if "Microsoft.NETCore.App 9." in result.stdout:
|
if "Microsoft.NETCore.App 9." in result.stdout:
|
||||||
logger.log("Found system .NET 9 runtime", logging.INFO)
|
logger.log("Found system .NET 9 runtime", logging.INFO)
|
||||||
return system_dotnet
|
return system_dotnet
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Download .NET 9 runtime using config
|
# Download .NET 9 runtime using config
|
||||||
dotnet_dir = cache_dir / "dotnet-runtime-9.0"
|
dotnet_dir = cache_dir / "dotnet-runtime-9.0"
|
||||||
dotnet_exe = dotnet_dir / runtime_dep["binaryName"]
|
dotnet_exe = dotnet_dir / runtime_dep["binaryName"]
|
||||||
|
|
||||||
if dotnet_exe.exists():
|
if dotnet_exe.exists():
|
||||||
# Verify it still works
|
# Verify it still works
|
||||||
try:
|
try:
|
||||||
@@ -357,75 +355,73 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
logger.log("Cached .NET runtime is corrupted, re-downloading", logging.WARNING)
|
logger.log("Cached .NET runtime is corrupted, re-downloading", logging.WARNING)
|
||||||
shutil.rmtree(dotnet_dir, ignore_errors=True)
|
shutil.rmtree(dotnet_dir, ignore_errors=True)
|
||||||
|
|
||||||
# Download .NET runtime
|
# Download .NET runtime
|
||||||
logger.log("Downloading .NET 9 runtime...", logging.INFO)
|
logger.log("Downloading .NET 9 runtime...", logging.INFO)
|
||||||
dotnet_dir.mkdir(parents=True, exist_ok=True)
|
dotnet_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
url = runtime_dep["url"]
|
url = runtime_dep["url"]
|
||||||
archive_type = runtime_dep["archiveType"]
|
archive_type = runtime_dep["archiveType"]
|
||||||
|
|
||||||
# Download the runtime
|
# Download the runtime
|
||||||
download_path = dotnet_dir / f"dotnet-runtime.{archive_type}"
|
download_path = dotnet_dir / f"dotnet-runtime.{archive_type}"
|
||||||
try:
|
try:
|
||||||
logger.log(f"Downloading from {url}", logging.DEBUG)
|
logger.log(f"Downloading from {url}", logging.DEBUG)
|
||||||
urllib.request.urlretrieve(url, download_path)
|
urllib.request.urlretrieve(url, download_path)
|
||||||
|
|
||||||
# Extract the archive
|
# Extract the archive
|
||||||
if archive_type == "zip":
|
if archive_type == "zip":
|
||||||
with zipfile.ZipFile(download_path, 'r') as zip_ref:
|
with zipfile.ZipFile(download_path, "r") as zip_ref:
|
||||||
zip_ref.extractall(dotnet_dir)
|
zip_ref.extractall(dotnet_dir)
|
||||||
else:
|
else:
|
||||||
# tar.gz
|
# tar.gz
|
||||||
with tarfile.open(download_path, 'r:gz') as tar_ref:
|
with tarfile.open(download_path, "r:gz") as tar_ref:
|
||||||
tar_ref.extractall(dotnet_dir)
|
tar_ref.extractall(dotnet_dir)
|
||||||
|
|
||||||
# Remove the archive
|
# Remove the archive
|
||||||
download_path.unlink()
|
download_path.unlink()
|
||||||
|
|
||||||
# Make dotnet executable on Unix
|
# Make dotnet executable on Unix
|
||||||
if platform.system().lower() != "windows":
|
if platform.system().lower() != "windows":
|
||||||
dotnet_exe.chmod(dotnet_exe.stat().st_mode | stat.S_IEXEC)
|
dotnet_exe.chmod(dotnet_exe.stat().st_mode | stat.S_IEXEC)
|
||||||
|
|
||||||
logger.log(f"Successfully installed .NET 9 runtime to {dotnet_exe}", logging.INFO)
|
logger.log(f"Successfully installed .NET 9 runtime to {dotnet_exe}", logging.INFO)
|
||||||
return str(dotnet_exe)
|
return str(dotnet_exe)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise LanguageServerException(f"Failed to download .NET 9 runtime from {url}: {e}") from e
|
raise LanguageServerException(f"Failed to download .NET 9 runtime from {url}: {e}") from e
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
|
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
|
||||||
"""
|
"""
|
||||||
Returns the initialize params for the Microsoft.CodeAnalysis.LanguageServer.
|
Returns the initialize params for the Microsoft.CodeAnalysis.LanguageServer.
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
|
||||||
# Load initialization parameters from JSON file
|
# Load initialization parameters from JSON file
|
||||||
with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), encoding="utf-8") as f:
|
with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), encoding="utf-8") as f:
|
||||||
initialize_params_template = json.load(f)
|
initialize_params_template = json.load(f)
|
||||||
|
|
||||||
# Convert the template string to actual values
|
# Convert the template string to actual values
|
||||||
initialize_params_str = json.dumps(initialize_params_template)
|
initialize_params_str = json.dumps(initialize_params_template)
|
||||||
|
|
||||||
# Replace template variables
|
# Replace template variables
|
||||||
root_uri = pathlib.Path(repository_absolute_path).as_uri()
|
root_uri = pathlib.Path(repository_absolute_path).as_uri()
|
||||||
root_name = os.path.basename(repository_absolute_path)
|
root_name = os.path.basename(repository_absolute_path)
|
||||||
|
|
||||||
initialize_params_str = initialize_params_str.replace("$rootPath", repository_absolute_path)
|
initialize_params_str = initialize_params_str.replace("$rootPath", repository_absolute_path)
|
||||||
initialize_params_str = initialize_params_str.replace("$rootUri", root_uri)
|
initialize_params_str = initialize_params_str.replace("$rootUri", root_uri)
|
||||||
initialize_params_str = initialize_params_str.replace("$rootName", root_name)
|
initialize_params_str = initialize_params_str.replace("$rootName", root_name)
|
||||||
initialize_params_str = initialize_params_str.replace('"os.getpid()"', str(os.getpid()))
|
initialize_params_str = initialize_params_str.replace('"os.getpid()"', str(os.getpid()))
|
||||||
|
|
||||||
# Parse back to dictionary
|
# Parse back to dictionary
|
||||||
initialize_params = json.loads(initialize_params_str)
|
initialize_params = json.loads(initialize_params_str)
|
||||||
|
|
||||||
return initialize_params
|
return initialize_params
|
||||||
|
|
||||||
def _start_server(self):
|
def _start_server(self):
|
||||||
"""
|
"""
|
||||||
Starts the Microsoft.CodeAnalysis.LanguageServer.
|
Starts the Microsoft.CodeAnalysis.LanguageServer.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
```
|
```
|
||||||
async with lsp.start_server():
|
async with lsp.start_server():
|
||||||
@@ -436,7 +432,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
# LanguageServer has been shutdown cleanly
|
# LanguageServer has been shutdown cleanly
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def do_nothing(params):
|
def do_nothing(params):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -444,17 +440,12 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
"""Log messages from the language server."""
|
"""Log messages from the language server."""
|
||||||
message_text = msg.get("message", "")
|
message_text = msg.get("message", "")
|
||||||
level = msg.get("type", 4) # Default to Log level
|
level = msg.get("type", 4) # Default to Log level
|
||||||
|
|
||||||
# Map LSP message types to Python logging levels
|
# Map LSP message types to Python logging levels
|
||||||
level_map = {
|
level_map = {1: logging.ERROR, 2: logging.WARNING, 3: logging.INFO, 4: logging.DEBUG} # Error # Warning # Info # Log
|
||||||
1: logging.ERROR, # Error
|
|
||||||
2: logging.WARNING, # Warning
|
|
||||||
3: logging.INFO, # Info
|
|
||||||
4: logging.DEBUG # Log
|
|
||||||
}
|
|
||||||
|
|
||||||
self.logger.log(f"LSP: {message_text}", level_map.get(level, logging.DEBUG))
|
self.logger.log(f"LSP: {message_text}", level_map.get(level, logging.DEBUG))
|
||||||
|
|
||||||
# Also print important messages to console
|
# Also print important messages to console
|
||||||
if level <= 3: # Error, Warning, or Info
|
if level <= 3: # Error, Warning, or Info
|
||||||
print(f"C# Language Server: {message_text}")
|
print(f"C# Language Server: {message_text}")
|
||||||
@@ -463,36 +454,36 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
"""Handle progress notifications from the language server."""
|
"""Handle progress notifications from the language server."""
|
||||||
token = params.get("token", "")
|
token = params.get("token", "")
|
||||||
value = params.get("value", {})
|
value = params.get("value", {})
|
||||||
|
|
||||||
# Log raw progress for debugging
|
# Log raw progress for debugging
|
||||||
self.logger.log(f"Progress notification received: {params}", logging.DEBUG)
|
self.logger.log(f"Progress notification received: {params}", logging.DEBUG)
|
||||||
|
|
||||||
# Handle different progress notification types
|
# Handle different progress notification types
|
||||||
kind = value.get("kind")
|
kind = value.get("kind")
|
||||||
|
|
||||||
if kind == "begin":
|
if kind == "begin":
|
||||||
title = value.get("title", "Operation in progress")
|
title = value.get("title", "Operation in progress")
|
||||||
message = value.get("message", "")
|
message = value.get("message", "")
|
||||||
percentage = value.get("percentage")
|
percentage = value.get("percentage")
|
||||||
|
|
||||||
if percentage is not None:
|
if percentage is not None:
|
||||||
self.logger.log(f"Progress [{token}]: {title} - {message} ({percentage}%)", logging.INFO)
|
self.logger.log(f"Progress [{token}]: {title} - {message} ({percentage}%)", logging.INFO)
|
||||||
print(f"C# Language Server: {title} - {message} ({percentage}%)")
|
print(f"C# Language Server: {title} - {message} ({percentage}%)")
|
||||||
else:
|
else:
|
||||||
self.logger.log(f"Progress [{token}]: {title} - {message}", logging.INFO)
|
self.logger.log(f"Progress [{token}]: {title} - {message}", logging.INFO)
|
||||||
print(f"C# Language Server: {title} - {message}")
|
print(f"C# Language Server: {title} - {message}")
|
||||||
|
|
||||||
elif kind == "report":
|
elif kind == "report":
|
||||||
message = value.get("message", "")
|
message = value.get("message", "")
|
||||||
percentage = value.get("percentage")
|
percentage = value.get("percentage")
|
||||||
|
|
||||||
if percentage is not None:
|
if percentage is not None:
|
||||||
self.logger.log(f"Progress [{token}]: {message} ({percentage}%)", logging.INFO)
|
self.logger.log(f"Progress [{token}]: {message} ({percentage}%)", logging.INFO)
|
||||||
if percentage % 10 == 0: # Print every 10%
|
if percentage % 10 == 0: # Print every 10%
|
||||||
print(f"C# Language Server: {message} ({percentage}%)")
|
print(f"C# Language Server: {message} ({percentage}%)")
|
||||||
elif message:
|
elif message:
|
||||||
self.logger.log(f"Progress [{token}]: {message}", logging.INFO)
|
self.logger.log(f"Progress [{token}]: {message}", logging.INFO)
|
||||||
|
|
||||||
elif kind == "end":
|
elif kind == "end":
|
||||||
message = value.get("message", "Operation completed")
|
message = value.get("message", "Operation completed")
|
||||||
self.logger.log(f"Progress [{token}]: {message}", logging.INFO)
|
self.logger.log(f"Progress [{token}]: {message}", logging.INFO)
|
||||||
@@ -502,12 +493,12 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
"""Handle workspace/configuration requests from the server."""
|
"""Handle workspace/configuration requests from the server."""
|
||||||
items = params.get("items", [])
|
items = params.get("items", [])
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
section = item.get("section", "")
|
section = item.get("section", "")
|
||||||
|
|
||||||
# Provide default values based on the configuration section
|
# Provide default values based on the configuration section
|
||||||
if section.startswith("dotnet") or section.startswith("csharp"):
|
if section.startswith(("dotnet", "csharp")):
|
||||||
# Default configuration for C# settings
|
# Default configuration for C# settings
|
||||||
if "enable" in section or "show" in section or "suppress" in section or "navigate" in section:
|
if "enable" in section or "show" in section or "suppress" in section or "navigate" in section:
|
||||||
# Boolean settings
|
# Boolean settings
|
||||||
@@ -524,7 +515,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
# ImplementTypeInsertionBehavior enum
|
# ImplementTypeInsertionBehavior enum
|
||||||
result.append("with_other_members_of_the_same_kind")
|
result.append("with_other_members_of_the_same_kind")
|
||||||
elif section == "dotnet_property_generation_behavior":
|
elif section == "dotnet_property_generation_behavior":
|
||||||
# ImplementTypePropertyGenerationBehavior enum
|
# ImplementTypePropertyGenerationBehavior enum
|
||||||
result.append("prefer_throwing_properties")
|
result.append("prefer_throwing_properties")
|
||||||
elif "location" in section or "behavior" in section:
|
elif "location" in section or "behavior" in section:
|
||||||
# Other enum settings - return null to avoid parsing errors
|
# Other enum settings - return null to avoid parsing errors
|
||||||
@@ -541,18 +532,18 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
else:
|
else:
|
||||||
# Unknown configuration - return null
|
# Unknown configuration - return null
|
||||||
result.append(None)
|
result.append(None)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def handle_work_done_progress_create(params):
|
def handle_work_done_progress_create(params):
|
||||||
"""Handle work done progress create requests."""
|
"""Handle work done progress create requests."""
|
||||||
# Just acknowledge the request
|
# Just acknowledge the request
|
||||||
return None
|
return
|
||||||
|
|
||||||
def handle_register_capability(params):
|
def handle_register_capability(params):
|
||||||
"""Handle client/registerCapability requests."""
|
"""Handle client/registerCapability requests."""
|
||||||
# Just acknowledge the request - we don't need to track these for now
|
# Just acknowledge the request - we don't need to track these for now
|
||||||
return None
|
return
|
||||||
|
|
||||||
# Set up notification handlers
|
# Set up notification handlers
|
||||||
self.server.on_notification("window/logMessage", window_log_message)
|
self.server.on_notification("window/logMessage", window_log_message)
|
||||||
@@ -561,18 +552,18 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
self.server.on_request("workspace/configuration", handle_workspace_configuration)
|
self.server.on_request("workspace/configuration", handle_workspace_configuration)
|
||||||
self.server.on_request("window/workDoneProgress/create", handle_work_done_progress_create)
|
self.server.on_request("window/workDoneProgress/create", handle_work_done_progress_create)
|
||||||
self.server.on_request("client/registerCapability", handle_register_capability)
|
self.server.on_request("client/registerCapability", handle_register_capability)
|
||||||
|
|
||||||
self.logger.log("Starting Microsoft.CodeAnalysis.LanguageServer process", logging.INFO)
|
self.logger.log("Starting Microsoft.CodeAnalysis.LanguageServer process", logging.INFO)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.server.start()
|
self.server.start()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.log(f"Failed to start language server process: {e}", logging.ERROR)
|
self.logger.log(f"Failed to start language server process: {e}", logging.ERROR)
|
||||||
raise LanguageServerException(f"Failed to start C# language server: {e}")
|
raise LanguageServerException(f"Failed to start C# language server: {e}")
|
||||||
|
|
||||||
# Send initialization
|
# Send initialization
|
||||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||||
|
|
||||||
self.logger.log("Sending initialize request to language server", logging.INFO)
|
self.logger.log("Sending initialize request to language server", logging.INFO)
|
||||||
try:
|
try:
|
||||||
init_response = self.server.send.initialize(initialize_params)
|
init_response = self.server.send.initialize(initialize_params)
|
||||||
@@ -584,19 +575,19 @@ class CSharpLanguageServer(SolidLanguageServer):
|
|||||||
self.logger.log("2. Microsoft.CodeAnalysis.LanguageServer not found", logging.ERROR)
|
self.logger.log("2. Microsoft.CodeAnalysis.LanguageServer not found", logging.ERROR)
|
||||||
self.logger.log("3. Project too large or complex for default timeout", logging.ERROR)
|
self.logger.log("3. Project too large or complex for default timeout", logging.ERROR)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
# Verify required capabilities
|
# Verify required capabilities
|
||||||
capabilities = init_response.get("capabilities", {})
|
capabilities = init_response.get("capabilities", {})
|
||||||
assert "textDocumentSync" in capabilities
|
assert "textDocumentSync" in capabilities
|
||||||
assert "definitionProvider" in capabilities
|
assert "definitionProvider" in capabilities
|
||||||
assert "referencesProvider" in capabilities
|
assert "referencesProvider" in capabilities
|
||||||
assert "documentSymbolProvider" in capabilities
|
assert "documentSymbolProvider" in capabilities
|
||||||
|
|
||||||
# Complete initialization
|
# Complete initialization
|
||||||
self.server.notify.initialized({})
|
self.server.notify.initialized({})
|
||||||
self.initialization_complete.set()
|
self.initialization_complete.set()
|
||||||
self.completions_available.set()
|
self.completions_available.set()
|
||||||
|
|
||||||
self.logger.log("Microsoft.CodeAnalysis.LanguageServer initialized and ready", logging.INFO)
|
self.logger.log("Microsoft.CodeAnalysis.LanguageServer initialized and ready", logging.INFO)
|
||||||
self.logger.log("Waiting for language server to index project files...", logging.INFO)
|
self.logger.log("Waiting for language server to index project files...", logging.INFO)
|
||||||
self.logger.log("This may take a while for large projects", logging.INFO)
|
self.logger.log("This may take a while for large projects", logging.INFO)
|
||||||
+1
-1
@@ -139,7 +139,7 @@ class SolidLanguageServer(ABC):
|
|||||||
# But OmniSharp seems to crash and has some issues with newer C# versions
|
# But OmniSharp seems to crash and has some issues with newer C# versions
|
||||||
# from solidlsp.language_servers.omnisharp.omnisharp import OmniSharp
|
# from solidlsp.language_servers.omnisharp.omnisharp import OmniSharp
|
||||||
# ls = OmniSharp(config, logger, repository_root_path)
|
# ls = OmniSharp(config, logger, repository_root_path)
|
||||||
|
|
||||||
from solidlsp.language_servers.csharp_language_server.csharp_language_server import CSharpLanguageServer
|
from solidlsp.language_servers.csharp_language_server.csharp_language_server import CSharpLanguageServer
|
||||||
|
|
||||||
ls = CSharpLanguageServer(config, logger, repository_root_path)
|
ls = CSharpLanguageServer(config, logger, repository_root_path)
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ class TestCSharpBasic:
|
|||||||
"""Test getting document symbols from a C# file."""
|
"""Test getting document symbols from a C# file."""
|
||||||
file_path = os.path.join("Program.cs")
|
file_path = os.path.join("Program.cs")
|
||||||
symbols = csharp_ls.request_document_symbols(file_path)
|
symbols = csharp_ls.request_document_symbols(file_path)
|
||||||
|
|
||||||
# Check that we have symbols
|
# Check that we have symbols
|
||||||
assert len(symbols) > 0
|
assert len(symbols) > 0
|
||||||
|
|
||||||
# Flatten the symbols if they're nested
|
# Flatten the symbols if they're nested
|
||||||
if isinstance(symbols[0], list):
|
if isinstance(symbols[0], list):
|
||||||
symbols = symbols[0]
|
symbols = symbols[0]
|
||||||
|
|
||||||
# Look for expected classes
|
# Look for expected classes
|
||||||
class_names = [s.get("name") for s in symbols if s.get("kind") == 5] # 5 is class
|
class_names = [s.get("name") for s in symbols if s.get("kind") == 5] # 5 is class
|
||||||
assert "Program" in class_names
|
assert "Program" in class_names
|
||||||
@@ -52,12 +52,12 @@ class TestCSharpBasic:
|
|||||||
def test_find_definition(self, csharp_ls: SolidLanguageServer):
|
def test_find_definition(self, csharp_ls: SolidLanguageServer):
|
||||||
"""Test finding definition of a symbol."""
|
"""Test finding definition of a symbol."""
|
||||||
file_path = os.path.join("Program.cs")
|
file_path = os.path.join("Program.cs")
|
||||||
|
|
||||||
# Open the file first
|
# Open the file first
|
||||||
with csharp_ls.open_file(file_path):
|
with csharp_ls.open_file(file_path):
|
||||||
# Find usage of Calculator class (line 11, column 28)
|
# Find usage of Calculator class (line 11, column 28)
|
||||||
definitions = csharp_ls.request_definition(file_path, 11, 28)
|
definitions = csharp_ls.request_definition(file_path, 11, 28)
|
||||||
|
|
||||||
# Should find the Calculator class definition
|
# Should find the Calculator class definition
|
||||||
assert len(definitions) > 0
|
assert len(definitions) > 0
|
||||||
assert any("Calculator" in str(d) for d in definitions)
|
assert any("Calculator" in str(d) for d in definitions)
|
||||||
@@ -65,12 +65,12 @@ class TestCSharpBasic:
|
|||||||
def test_find_references(self, csharp_ls: SolidLanguageServer):
|
def test_find_references(self, csharp_ls: SolidLanguageServer):
|
||||||
"""Test finding references to a symbol."""
|
"""Test finding references to a symbol."""
|
||||||
file_path = os.path.join("Program.cs")
|
file_path = os.path.join("Program.cs")
|
||||||
|
|
||||||
# Open the file first
|
# Open the file first
|
||||||
with csharp_ls.open_file(file_path):
|
with csharp_ls.open_file(file_path):
|
||||||
# Find references to the Add method (line 19)
|
# Find references to the Add method (line 19)
|
||||||
references = csharp_ls.request_references(file_path, 19, 20, include_declaration=True)
|
references = csharp_ls.request_references(file_path, 19, 20, include_declaration=True)
|
||||||
|
|
||||||
# Should find at least the definition and one usage
|
# Should find at least the definition and one usage
|
||||||
assert len(references) >= 2
|
assert len(references) >= 2
|
||||||
|
|
||||||
@@ -78,17 +78,17 @@ class TestCSharpBasic:
|
|||||||
"""Test getting symbols from nested namespace."""
|
"""Test getting symbols from nested namespace."""
|
||||||
file_path = os.path.join("Models", "Person.cs")
|
file_path = os.path.join("Models", "Person.cs")
|
||||||
symbols = csharp_ls.request_document_symbols(file_path)
|
symbols = csharp_ls.request_document_symbols(file_path)
|
||||||
|
|
||||||
# Check that we have symbols
|
# Check that we have symbols
|
||||||
assert len(symbols) > 0
|
assert len(symbols) > 0
|
||||||
|
|
||||||
# Flatten the symbols if they're nested
|
# Flatten the symbols if they're nested
|
||||||
if isinstance(symbols[0], list):
|
if isinstance(symbols[0], list):
|
||||||
symbols = symbols[0]
|
symbols = symbols[0]
|
||||||
|
|
||||||
# Check that we have the Person class
|
# Check that we have the Person class
|
||||||
assert any(s.get("name") == "Person" and s.get("kind") == 5 for s in symbols)
|
assert any(s.get("name") == "Person" and s.get("kind") == 5 for s in symbols)
|
||||||
|
|
||||||
# Check for properties and methods
|
# Check for properties and methods
|
||||||
symbol_names = [s.get("name") for s in symbols]
|
symbol_names = [s.get("name") for s in symbols]
|
||||||
assert "Name" in symbol_names
|
assert "Name" in symbol_names
|
||||||
|
|||||||
Reference in new issue
Block a user