mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 10:16:00 +00:00
Merge pull request #781 from oraios/remove-ls-logger
Remove LanguageServerLogger
This commit is contained in:
50 files changed
+729
-1158
No files matched your search
@@ -202,20 +202,12 @@ pylint.html
|
||||
/agent-ui
|
||||
/test/**/.serena
|
||||
|
||||
# dynamic LS installations
|
||||
/src/multilspy/language_servers/*/static
|
||||
/src/solidlsp/language_servers/*/static
|
||||
/src/solidlsp/language_servers/static
|
||||
|
||||
# clojure-lsp temporary files
|
||||
.calva/
|
||||
.clj-kondo/
|
||||
.cpcache/
|
||||
.lsp/
|
||||
|
||||
# Cache is in .multilspy
|
||||
**/.multilspy/**
|
||||
|
||||
# temporary and backup files
|
||||
*.bak
|
||||
*.tmp
|
||||
|
||||
@@ -21,39 +21,35 @@ Have a look at `intelephense.py` for a reference implementation of a language se
|
||||
dependencies, and on `pyright_server.py` that does not need any additional dependencies
|
||||
because the language server can be installed directly as python package.
|
||||
|
||||
|
||||
```python
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
|
||||
class NewLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
Language server implementation for NewLanguage.
|
||||
"""
|
||||
|
||||
def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str):
|
||||
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str):
|
||||
# Determine language server command
|
||||
cmd = self._get_language_server_command()
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
|
||||
super().__init__(config,
|
||||
ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path),
|
||||
"new_language", # Language ID for LSP
|
||||
)
|
||||
|
||||
"new_language",,
|
||||
|
||||
def _get_language_server_command(self) -> list[str]:
|
||||
"""Get the command to start the language server."""
|
||||
# Example: return ["new-language-server", "--stdio"]
|
||||
pass
|
||||
|
||||
|
||||
@override
|
||||
def is_ignored_dirname(self, dirname: str) -> bool:
|
||||
"""Define language-specific directories to ignore."""
|
||||
return super().is_ignored_dirname(dirname) or dirname in ["build", "dist", "target"]
|
||||
return super().is_ignored_dirname(dirname) or dirname in ["build",
|
||||
"dist", "target"]
|
||||
```
|
||||
|
||||
### 1.2 Language Server Discovery and Installation
|
||||
@@ -62,7 +58,7 @@ For languages requiring automatic installation, implement download logic similar
|
||||
|
||||
```python
|
||||
@classmethod
|
||||
def _ensure_server_installed(cls, logger: LanguageServerLogger) -> str:
|
||||
def _ensure_server_installed(cls) -> str:
|
||||
"""Ensure language server is installed and return path."""
|
||||
# Check system installation first
|
||||
system_server = shutil.which("new-language-server")
|
||||
@@ -70,10 +66,10 @@ def _ensure_server_installed(cls, logger: LanguageServerLogger) -> str:
|
||||
return system_server
|
||||
|
||||
# Download and install if needed
|
||||
server_path = cls._download_and_install_server(logger)
|
||||
server_path = cls._download_and_install_server()
|
||||
return server_path
|
||||
|
||||
def _download_and_install_server(cls, logger: LanguageServerLogger) -> str:
|
||||
def _download_and_install_server(cls) -> str:
|
||||
"""Download and install the language server."""
|
||||
# Implementation specific to your language server
|
||||
pass
|
||||
@@ -129,12 +125,12 @@ In `src/solidlsp/ls.py`, add your language to the `create` method:
|
||||
|
||||
```python
|
||||
@classmethod
|
||||
def create(cls, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str) -> "SolidLanguageServer":
|
||||
def create(cls, config: LanguageServerConfig, repository_root_path: str) -> "SolidLanguageServer":
|
||||
match config.code_language:
|
||||
# Existing cases...
|
||||
case Language.NEW_LANGUAGE:
|
||||
from solidlsp.language_servers.new_language_server import NewLanguageServer
|
||||
return NewLanguageServer(config, logger, repository_root_path)
|
||||
return NewLanguageServer(config, repository_root_path)
|
||||
```
|
||||
|
||||
## Step 3: Test Repository
|
||||
|
||||
@@ -7,7 +7,6 @@ from sensai.util.logging import LogTime
|
||||
from serena.constants import SERENA_MANAGED_DIR_IN_HOME, SERENA_MANAGED_DIR_NAME
|
||||
from solidlsp import SolidLanguageServer
|
||||
from solidlsp.ls_config import Language, LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -21,7 +20,6 @@ class LanguageServerFactory:
|
||||
ignored_patterns: list[str],
|
||||
ls_timeout: float | None = None,
|
||||
ls_specific_settings: dict | None = None,
|
||||
log_level: int = logging.INFO,
|
||||
trace_lsp_communication: bool = False,
|
||||
):
|
||||
self.project_root = project_root
|
||||
@@ -29,7 +27,6 @@ class LanguageServerFactory:
|
||||
self.ignored_patterns = ignored_patterns
|
||||
self.ls_timeout = ls_timeout
|
||||
self.ls_specific_settings = ls_specific_settings
|
||||
self.log_level = log_level
|
||||
self.trace_lsp_communication = trace_lsp_communication
|
||||
|
||||
def create_language_server(self, language: Language) -> SolidLanguageServer:
|
||||
@@ -39,12 +36,10 @@ class LanguageServerFactory:
|
||||
trace_lsp_communication=self.trace_lsp_communication,
|
||||
encoding=self.encoding,
|
||||
)
|
||||
ls_logger = LanguageServerLogger(log_level=self.log_level)
|
||||
|
||||
log.info(f"Creating language server instance for {self.project_root}, language={language}.")
|
||||
return SolidLanguageServer.create(
|
||||
ls_config,
|
||||
ls_logger,
|
||||
self.project_root,
|
||||
timeout=self.ls_timeout,
|
||||
solidlsp_settings=SolidLSPSettings(
|
||||
|
||||
@@ -388,7 +388,6 @@ class Project(ToStringMixin):
|
||||
ignored_patterns=self._ignored_patterns,
|
||||
ls_timeout=ls_timeout,
|
||||
ls_specific_settings=ls_specific_settings,
|
||||
log_level=log_level,
|
||||
trace_lsp_communication=trace_lsp_communication,
|
||||
)
|
||||
self.language_server_manager = LanguageServerManager.from_languages(self.project_config.languages, factory)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import queue
|
||||
import threading
|
||||
from collections.abc import Callable
|
||||
from typing import Optional
|
||||
|
||||
from sensai.util import logging
|
||||
|
||||
from serena.constants import SERENA_LOG_FORMAT
|
||||
|
||||
lg = logging
|
||||
|
||||
|
||||
class MemoryLogHandler(logging.Handler):
|
||||
def __init__(self, level: int = logging.NOTSET) -> None:
|
||||
@@ -65,3 +68,42 @@ class LogBuffer:
|
||||
def get_log_messages(self) -> list[str]:
|
||||
with self._lock:
|
||||
return self._log_messages.copy()
|
||||
|
||||
|
||||
class SuspendedLoggersContext:
|
||||
"""A context manager that provides an isolated logging environment.
|
||||
|
||||
Temporarily removes all root log handlers upon entry, providing a clean slate
|
||||
for defining new log handlers within the context. Upon exit, restores the original
|
||||
logging configuration. This is useful when you need to temporarily configure
|
||||
an isolated logging setup with well-defined log handlers.
|
||||
|
||||
The context manager:
|
||||
- Removes all existing (root) log handlers on entry
|
||||
- Allows defining new temporary handlers within the context
|
||||
- Restores the original configuration (handlers and root log level) on exit
|
||||
|
||||
Example:
|
||||
>>> with SuspendedLoggersContext():
|
||||
... # No handlers are active here (configure your own and set desired log level)
|
||||
... pass
|
||||
>>> # Original log handlers are restored here
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.saved_root_handlers: list = []
|
||||
self.saved_root_level: Optional[int] = None
|
||||
|
||||
def __enter__(self) -> "SuspendedLoggersContext":
|
||||
root_logger = lg.getLogger()
|
||||
self.saved_root_handlers = root_logger.handlers.copy()
|
||||
self.saved_root_level = root_logger.level
|
||||
root_logger.handlers.clear()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb) -> None: # type: ignore
|
||||
root_logger = lg.getLogger()
|
||||
root_logger.handlers = self.saved_root_handlers
|
||||
if self.saved_root_level is not None:
|
||||
root_logger.setLevel(self.saved_root_level)
|
||||
@@ -15,12 +15,13 @@ from overrides import override
|
||||
from solidlsp.language_servers.common import quote_windows_path
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_types import SymbolKind, UnifiedSymbolInformation
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import Definition, DefinitionParams, LocationLink
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ALLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -37,9 +38,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
- File opening requirement before symbol retrieval
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Initialize the AL Language Server.
|
||||
|
||||
@@ -58,7 +57,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
# Setup runtime dependencies and get the language server command
|
||||
# This will download the AL extension if needed
|
||||
cmd = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
cmd = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
|
||||
self._project_load_check_supported: bool = True
|
||||
"""Whether the AL server supports the project load status check request.
|
||||
@@ -68,17 +67,10 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
request fails, preventing repeated unsuccessful attempts.
|
||||
"""
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path),
|
||||
"al", # Language ID for LSP
|
||||
solidlsp_settings,
|
||||
)
|
||||
super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path), "al", solidlsp_settings)
|
||||
|
||||
@classmethod
|
||||
def _download_al_extension(cls, logger: LanguageServerLogger, url: str, target_dir: str) -> bool:
|
||||
def _download_al_extension(cls, url: str, target_dir: str) -> bool:
|
||||
"""
|
||||
Download and extract the AL extension from VS Code marketplace.
|
||||
|
||||
@@ -99,7 +91,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
|
||||
"""
|
||||
try:
|
||||
logger.log(f"Downloading AL extension from {url}", logging.INFO)
|
||||
log.info(f"Downloading AL extension from {url}")
|
||||
|
||||
# Create target directory for the extension
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
@@ -118,7 +110,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
temp_file = os.path.join(target_dir, "al_extension_temp.vsix")
|
||||
total_size = int(response.headers.get("content-length", 0))
|
||||
|
||||
logger.log(f"Downloading {total_size / 1024 / 1024:.1f} MB...", logging.INFO)
|
||||
log.info(f"Downloading {total_size / 1024 / 1024:.1f} MB...")
|
||||
|
||||
with open(temp_file, "wb") as f:
|
||||
downloaded = 0
|
||||
@@ -128,9 +120,9 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
downloaded += len(chunk)
|
||||
if total_size > 0 and downloaded % (10 * 1024 * 1024) == 0: # Log progress every 10MB
|
||||
progress = (downloaded / total_size) * 100
|
||||
logger.log(f"Download progress: {progress:.1f}%", logging.INFO)
|
||||
log.info(f"Download progress: {progress:.1f}%")
|
||||
|
||||
logger.log("Download complete, extracting...", logging.INFO)
|
||||
log.info("Download complete, extracting...")
|
||||
|
||||
# Extract VSIX file (VSIX files are just ZIP archives with a different extension)
|
||||
# This will extract the extension folder containing the language server binaries
|
||||
@@ -140,17 +132,15 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
# Clean up temp file
|
||||
os.remove(temp_file)
|
||||
|
||||
logger.log("AL extension extracted successfully", logging.INFO)
|
||||
log.info("AL extension extracted successfully")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.log(f"Error downloading/extracting AL extension: {e}", logging.ERROR)
|
||||
log.error(f"Error downloading/extracting AL extension: {e}")
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> str:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""
|
||||
Setup runtime dependencies for AL Language Server and return the command to start the server.
|
||||
|
||||
@@ -168,10 +158,10 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
system = platform.system()
|
||||
|
||||
# Find existing extension or download if needed
|
||||
extension_path = cls._find_al_extension(logger, solidlsp_settings)
|
||||
extension_path = cls._find_al_extension(solidlsp_settings)
|
||||
if extension_path is None:
|
||||
logger.log("AL extension not found on disk, attempting to download...", logging.INFO)
|
||||
extension_path = cls._download_and_install_al_extension(logger, solidlsp_settings)
|
||||
log.info("AL extension not found on disk, attempting to download...")
|
||||
extension_path = cls._download_and_install_al_extension(solidlsp_settings)
|
||||
|
||||
if extension_path is None:
|
||||
raise RuntimeError(
|
||||
@@ -188,10 +178,10 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
raise RuntimeError(f"AL Language Server executable not found at: {executable_path}")
|
||||
|
||||
# Prepare and return the executable command
|
||||
return cls._prepare_executable(executable_path, system, logger)
|
||||
return cls._prepare_executable(executable_path, system)
|
||||
|
||||
@classmethod
|
||||
def _find_al_extension(cls, logger: LanguageServerLogger, solidlsp_settings: SolidLSPSettings) -> str | None:
|
||||
def _find_al_extension(cls, solidlsp_settings: SolidLSPSettings) -> str | None:
|
||||
"""
|
||||
Find AL extension in various locations.
|
||||
|
||||
@@ -207,28 +197,28 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
# Check environment variable
|
||||
env_path = os.environ.get("AL_EXTENSION_PATH")
|
||||
if env_path and os.path.exists(env_path):
|
||||
logger.log(f"Found AL extension via AL_EXTENSION_PATH: {env_path}", logging.DEBUG)
|
||||
log.debug(f"Found AL extension via AL_EXTENSION_PATH: {env_path}")
|
||||
return env_path
|
||||
elif env_path:
|
||||
logger.log(f"AL_EXTENSION_PATH set but directory not found: {env_path}", logging.WARNING)
|
||||
log.warning(f"AL_EXTENSION_PATH set but directory not found: {env_path}")
|
||||
|
||||
# Check default download location
|
||||
default_path = os.path.join(cls.ls_resources_dir(solidlsp_settings), "al-extension", "extension")
|
||||
if os.path.exists(default_path):
|
||||
logger.log(f"Found AL extension in default location: {default_path}", logging.DEBUG)
|
||||
log.debug(f"Found AL extension in default location: {default_path}")
|
||||
return default_path
|
||||
|
||||
# Search VS Code extensions
|
||||
vscode_path = cls._find_al_extension_in_vscode(logger)
|
||||
vscode_path = cls._find_al_extension_in_vscode()
|
||||
if vscode_path:
|
||||
logger.log(f"Found AL extension in VS Code: {vscode_path}", logging.DEBUG)
|
||||
log.debug(f"Found AL extension in VS Code: {vscode_path}")
|
||||
return vscode_path
|
||||
|
||||
logger.log("AL extension not found in any known location", logging.DEBUG)
|
||||
log.debug("AL extension not found in any known location")
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def _download_and_install_al_extension(cls, logger: LanguageServerLogger, solidlsp_settings: SolidLSPSettings) -> str | None:
|
||||
def _download_and_install_al_extension(cls, solidlsp_settings: SolidLSPSettings) -> str | None:
|
||||
"""
|
||||
Download and install AL extension from VS Code marketplace.
|
||||
|
||||
@@ -242,17 +232,17 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
AL_VERSION = "latest"
|
||||
url = f"https://marketplace.visualstudio.com/_apis/public/gallery/publishers/ms-dynamics-smb/vsextensions/al/{AL_VERSION}/vspackage"
|
||||
|
||||
logger.log(f"Downloading AL extension from: {url}", logging.INFO)
|
||||
log.info(f"Downloading AL extension from: {url}")
|
||||
|
||||
if cls._download_al_extension(logger, url, al_extension_dir):
|
||||
if cls._download_al_extension(url, al_extension_dir):
|
||||
extension_path = os.path.join(al_extension_dir, "extension")
|
||||
if os.path.exists(extension_path):
|
||||
logger.log("AL extension downloaded and installed successfully", logging.INFO)
|
||||
log.info("AL extension downloaded and installed successfully")
|
||||
return extension_path
|
||||
else:
|
||||
logger.log(f"Download completed but extension not found at: {extension_path}", logging.ERROR)
|
||||
log.error(f"Download completed but extension not found at: {extension_path}")
|
||||
else:
|
||||
logger.log("Failed to download AL extension from marketplace", logging.ERROR)
|
||||
log.error("Failed to download AL extension from marketplace")
|
||||
|
||||
return None
|
||||
|
||||
@@ -279,7 +269,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
raise RuntimeError(f"Unsupported platform: {system}")
|
||||
|
||||
@classmethod
|
||||
def _prepare_executable(cls, executable_path: str, system: str, logger: LanguageServerLogger) -> str:
|
||||
def _prepare_executable(cls, executable_path: str, system: str) -> str:
|
||||
"""
|
||||
Prepare the executable by setting permissions and handling path quoting.
|
||||
|
||||
@@ -296,16 +286,16 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
if system in ["Linux", "Darwin"]:
|
||||
st = os.stat(executable_path)
|
||||
os.chmod(executable_path, st.st_mode | stat.S_IEXEC)
|
||||
logger.log(f"Set execute permission on: {executable_path}", logging.DEBUG)
|
||||
log.debug(f"Set execute permission on: {executable_path}")
|
||||
|
||||
logger.log(f"Using AL Language Server executable: {executable_path}", logging.INFO)
|
||||
log.info(f"Using AL Language Server executable: {executable_path}")
|
||||
|
||||
# The AL Language Server uses stdio for LSP communication by default
|
||||
# Use the utility function to handle Windows path quoting
|
||||
return quote_windows_path(executable_path)
|
||||
|
||||
@classmethod
|
||||
def _get_language_server_command_fallback(cls, logger: LanguageServerLogger) -> str:
|
||||
def _get_language_server_command_fallback(cls) -> str:
|
||||
"""
|
||||
Get the command to start the AL language server.
|
||||
|
||||
@@ -333,10 +323,10 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
|
||||
if potential_extension:
|
||||
al_extension_path = str(potential_extension)
|
||||
logger.log(f"Found AL extension in current directory: {al_extension_path}", logging.DEBUG)
|
||||
log.debug(f"Found AL extension in current directory: {al_extension_path}")
|
||||
else:
|
||||
# Try to find in common VS Code extension locations
|
||||
al_extension_path = cls._find_al_extension_in_vscode(logger)
|
||||
al_extension_path = cls._find_al_extension_in_vscode()
|
||||
|
||||
if not al_extension_path:
|
||||
raise RuntimeError(
|
||||
@@ -368,14 +358,14 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
st = os.stat(executable)
|
||||
os.chmod(executable, st.st_mode | stat.S_IEXEC)
|
||||
|
||||
logger.log(f"Using AL Language Server executable: {executable}", logging.INFO)
|
||||
log.info(f"Using AL Language Server executable: {executable}")
|
||||
|
||||
# The AL Language Server uses stdio for LSP communication (no --stdio flag needed)
|
||||
# Use the utility function to handle Windows path quoting
|
||||
return quote_windows_path(executable)
|
||||
|
||||
@classmethod
|
||||
def _find_al_extension_in_vscode(cls, logger: LanguageServerLogger) -> str | None:
|
||||
def _find_al_extension_in_vscode(cls) -> str | None:
|
||||
"""
|
||||
Try to find AL extension in common VS Code extension locations.
|
||||
|
||||
@@ -407,11 +397,11 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
|
||||
for base_path in possible_paths:
|
||||
if base_path.exists():
|
||||
logger.log(f"Searching for AL extension in: {base_path}", logging.DEBUG)
|
||||
log.debug(f"Searching for AL extension in: {base_path}")
|
||||
# Look for AL extension directories
|
||||
for item in base_path.iterdir():
|
||||
if item.is_dir() and item.name.startswith("ms-dynamics-smb.al-"):
|
||||
logger.log(f"Found AL extension at: {item}", logging.DEBUG)
|
||||
log.debug(f"Found AL extension at: {item}")
|
||||
return str(item)
|
||||
|
||||
return None
|
||||
@@ -499,18 +489,18 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
def do_nothing(params: str) -> None:
|
||||
return
|
||||
|
||||
def window_log_message(msg: str) -> None:
|
||||
self.logger.log(f"AL LSP: window/logMessage: {msg}", logging.INFO)
|
||||
def window_log_message(msg: dict) -> None:
|
||||
log.info(f"AL LSP: window/logMessage: {msg}")
|
||||
|
||||
def publish_diagnostics(params: dict) -> None:
|
||||
# AL server publishes diagnostics during initialization
|
||||
uri = params.get("uri", "")
|
||||
diagnostics = params.get("diagnostics", [])
|
||||
self.logger.log(f"AL LSP: Diagnostics for {uri}: {len(diagnostics)} issues", logging.DEBUG)
|
||||
log.debug(f"AL LSP: Diagnostics for {uri}: {len(diagnostics)} issues")
|
||||
|
||||
def handle_al_notifications(params: dict) -> None:
|
||||
# AL server sends custom notifications during project loading
|
||||
self.logger.log("AL LSP: Notification received", logging.DEBUG)
|
||||
log.debug("AL LSP: Notification received")
|
||||
|
||||
# Register handlers for AL-specific notifications
|
||||
# These notifications are sent by the AL server during initialization and operation
|
||||
@@ -520,27 +510,24 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("al/refreshExplorerObjects", handle_al_notifications) # AL-specific object updates
|
||||
|
||||
# Start the server process
|
||||
self.logger.log("Starting AL Language Server process", logging.INFO)
|
||||
log.info("Starting AL Language Server process")
|
||||
self.server.start()
|
||||
|
||||
# Send initialize request
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to AL LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to AL LSP server and awaiting response")
|
||||
|
||||
# Send initialize and wait for response
|
||||
resp = self.server.send_request("initialize", initialize_params)
|
||||
if resp is None:
|
||||
raise RuntimeError("AL Language Server initialization failed - no response")
|
||||
|
||||
self.logger.log("AL Language Server initialized successfully", logging.INFO)
|
||||
log.info("AL Language Server initialized successfully")
|
||||
|
||||
# Send initialized notification
|
||||
self.server.send_notification("initialized", {})
|
||||
self.logger.log("Sent initialized notification", logging.INFO)
|
||||
log.info("Sent initialized notification")
|
||||
|
||||
@override
|
||||
def start(self) -> "ALLanguageServer":
|
||||
@@ -598,9 +585,9 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
}
|
||||
},
|
||||
)
|
||||
self.logger.log("Sent workspace configuration", logging.DEBUG)
|
||||
log.debug("Sent workspace configuration")
|
||||
except Exception as e:
|
||||
self.logger.log(f"Failed to send workspace config: {e}", logging.WARNING)
|
||||
log.warning(f"Failed to send workspace config: {e}")
|
||||
|
||||
# Check if app.json exists and open it
|
||||
# app.json is the AL project manifest file (similar to package.json for Node.js)
|
||||
@@ -620,9 +607,9 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
{"textDocument": {"uri": app_json_uri, "languageId": "json", "version": 1, "text": app_json_content}},
|
||||
)
|
||||
|
||||
self.logger.log(f"Opened app.json: {app_json_uri}", logging.DEBUG)
|
||||
log.debug(f"Opened app.json: {app_json_uri}")
|
||||
except Exception as e:
|
||||
self.logger.log(f"Failed to open app.json: {e}", logging.WARNING)
|
||||
log.warning(f"Failed to open app.json: {e}")
|
||||
|
||||
# Try to set active workspace (AL-specific custom LSP request)
|
||||
# This is optional and may not be supported by all AL server versions
|
||||
@@ -639,10 +626,10 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
"timeout": 2, # Quick timeout since this is optional
|
||||
},
|
||||
)
|
||||
self.logger.log(f"Set active workspace result: {result}", logging.DEBUG)
|
||||
log.debug(f"Set active workspace result: {result}")
|
||||
except Exception as e:
|
||||
# This is a custom AL request, not critical if it fails
|
||||
self.logger.log(f"Failed to set active workspace (non-critical): {e}", logging.DEBUG)
|
||||
log.debug(f"Failed to set active workspace (non-critical): {e}")
|
||||
|
||||
# Check if project supports load status check (optional)
|
||||
# Many AL server versions don't support this, so we use a short timeout
|
||||
@@ -704,7 +691,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
Full symbol tree with all AL symbols from opened files organized by directory
|
||||
|
||||
"""
|
||||
self.logger.log("AL: Starting request_full_symbol_tree with file opening", logging.DEBUG)
|
||||
log.debug("AL: Starting request_full_symbol_tree with file opening")
|
||||
|
||||
# Determine the root path for scanning
|
||||
if within_relative_path is not None:
|
||||
@@ -743,10 +730,10 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
# File is outside repository root, skip it
|
||||
continue
|
||||
|
||||
self.logger.log(f"AL: Found {len(al_files)} AL files", logging.DEBUG)
|
||||
log.debug(f"AL: Found {len(al_files)} AL files")
|
||||
|
||||
if not al_files:
|
||||
self.logger.log("AL: No AL files found in repository", logging.WARNING)
|
||||
log.warning("AL: No AL files found in repository")
|
||||
return []
|
||||
|
||||
# Collect all symbols from all files
|
||||
@@ -756,7 +743,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
for file_path, relative_path in al_files:
|
||||
try:
|
||||
# Use our overridden request_document_symbols which handles opening
|
||||
self.logger.log(f"AL: Getting symbols for {relative_path}", logging.DEBUG)
|
||||
log.debug(f"AL: Getting symbols for {relative_path}")
|
||||
all_syms, root_syms = self.request_document_symbols(relative_path).get_all_symbols_and_roots()
|
||||
|
||||
if root_syms:
|
||||
@@ -773,7 +760,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
},
|
||||
}
|
||||
all_file_symbols.append(file_symbol)
|
||||
self.logger.log(f"AL: Added {len(root_syms)} symbols from {relative_path}", logging.DEBUG)
|
||||
log.debug(f"AL: Added {len(root_syms)} symbols from {relative_path}")
|
||||
elif all_syms:
|
||||
# If we only got all_syms but not root, use all_syms
|
||||
file_symbol = {
|
||||
@@ -788,13 +775,13 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
},
|
||||
}
|
||||
all_file_symbols.append(file_symbol)
|
||||
self.logger.log(f"AL: Added {len(all_syms)} symbols from {relative_path}", logging.DEBUG)
|
||||
log.debug(f"AL: Added {len(all_syms)} symbols from {relative_path}")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.log(f"AL: Failed to get symbols for {relative_path}: {e}", logging.WARNING)
|
||||
log.warning(f"AL: Failed to get symbols for {relative_path}: {e}")
|
||||
|
||||
if all_file_symbols:
|
||||
self.logger.log(f"AL: Returning symbols from {len(all_file_symbols)} files", logging.DEBUG)
|
||||
log.debug(f"AL: Returning symbols from {len(all_file_symbols)} files")
|
||||
|
||||
# Group files by directory
|
||||
directory_structure: dict[str, list] = {}
|
||||
@@ -839,7 +826,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
|
||||
return result
|
||||
else:
|
||||
self.logger.log("AL: No symbols found in any files", logging.WARNING)
|
||||
log.warning("AL: No symbols found in any files")
|
||||
return []
|
||||
|
||||
# ===== Phase 1: Custom AL Command Implementations =====
|
||||
@@ -862,10 +849,10 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
try:
|
||||
# Use custom AL command instead of standard LSP
|
||||
response = self.server.send_request("al/gotodefinition", al_params)
|
||||
self.logger.log(f"AL gotodefinition response: {response}", logging.DEBUG)
|
||||
log.debug(f"AL gotodefinition response: {response}")
|
||||
return response # type: ignore[return-value]
|
||||
except Exception as e:
|
||||
self.logger.log(f"Failed to use al/gotodefinition, falling back to standard: {e}", logging.WARNING)
|
||||
log.warning(f"Failed to use al/gotodefinition, falling back to standard: {e}")
|
||||
# Fallback to standard LSP method if custom command fails
|
||||
return super()._send_definition_request(definition_params)
|
||||
|
||||
@@ -883,7 +870,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
|
||||
"""
|
||||
if not hasattr(self, "server") or not self.server_started:
|
||||
self.logger.log("Cannot check project load - server not started", logging.DEBUG)
|
||||
log.debug("Cannot check project load - server not started")
|
||||
return False
|
||||
|
||||
# Check if we've already determined this request isn't supported
|
||||
@@ -900,15 +887,15 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
return response.get("loaded", False)
|
||||
elif response is None:
|
||||
# None typically means the project is still loading
|
||||
self.logger.log("Project load check returned None", logging.DEBUG)
|
||||
log.debug("Project load check returned None")
|
||||
return False
|
||||
else:
|
||||
self.logger.log(f"Unexpected response type for project load check: {type(response)}", logging.DEBUG)
|
||||
log.debug(f"Unexpected response type for project load check: {type(response)}")
|
||||
return False
|
||||
except Exception as e:
|
||||
# Mark as unsupported to avoid repeated failed attempts
|
||||
self._project_load_check_supported = False
|
||||
self.logger.log(f"Project load check not supported by this AL server version: {e}", logging.DEBUG)
|
||||
log.debug(f"Project load check not supported by this AL server version: {e}")
|
||||
# Assume loaded if we can't check
|
||||
return True
|
||||
|
||||
@@ -928,16 +915,16 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
|
||||
"""
|
||||
start_time = time.time()
|
||||
self.logger.log(f"Checking AL project load status (timeout: {timeout}s)...", logging.DEBUG)
|
||||
log.debug(f"Checking AL project load status (timeout: {timeout}s)...")
|
||||
|
||||
while time.time() - start_time < timeout:
|
||||
if self.check_project_loaded():
|
||||
elapsed = time.time() - start_time
|
||||
self.logger.log(f"AL project fully loaded after {elapsed:.1f}s", logging.INFO)
|
||||
log.info(f"AL project fully loaded after {elapsed:.1f}s")
|
||||
return True
|
||||
time.sleep(0.5)
|
||||
|
||||
self.logger.log(f"Project load check timed out after {timeout}s (non-critical)", logging.DEBUG)
|
||||
log.debug(f"Project load check timed out after {timeout}s (non-critical)")
|
||||
return False
|
||||
|
||||
def set_active_workspace(self, workspace_uri: str | None = None) -> None:
|
||||
@@ -956,7 +943,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
|
||||
"""
|
||||
if not hasattr(self, "server") or not self.server_started:
|
||||
self.logger.log("Cannot set active workspace - server not started", logging.DEBUG)
|
||||
log.debug("Cannot set active workspace - server not started")
|
||||
return
|
||||
|
||||
if workspace_uri is None:
|
||||
@@ -966,7 +953,7 @@ class ALLanguageServer(SolidLanguageServer):
|
||||
|
||||
try:
|
||||
self.server.send_request("al/setActiveWorkspace", params)
|
||||
self.logger.log(f"Set active workspace to: {workspace_uri}", logging.INFO)
|
||||
log.info(f"Set active workspace to: {workspace_uri}")
|
||||
except Exception as e:
|
||||
self.logger.log(f"Failed to set active workspace: {e}", logging.WARNING)
|
||||
log.warning(f"Failed to set active workspace: {e}")
|
||||
# Non-critical error, continue operation
|
||||
@@ -12,11 +12,12 @@ import threading
|
||||
from solidlsp.language_servers.common import RuntimeDependency, RuntimeDependencyCollection
|
||||
from solidlsp.ls import DocumentSymbols, LSPFileBuffer, SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BashLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -24,17 +25,14 @@ class BashLanguageServer(SolidLanguageServer):
|
||||
Contains various configurations and settings specific to Bash scripting.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a BashLanguageServer instance. This class is not meant to be instantiated directly.
|
||||
Use LanguageServer.create() instead.
|
||||
"""
|
||||
bash_lsp_executable_path = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
bash_lsp_executable_path = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=bash_lsp_executable_path, cwd=repository_root_path),
|
||||
"bash",
|
||||
@@ -44,9 +42,7 @@ class BashLanguageServer(SolidLanguageServer):
|
||||
self.initialize_searcher_command_available = threading.Event()
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> str:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""
|
||||
Setup runtime dependencies for Bash Language Server and return the command to start the server.
|
||||
"""
|
||||
@@ -76,9 +72,9 @@ class BashLanguageServer(SolidLanguageServer):
|
||||
bash_executable_path += ".cmd"
|
||||
|
||||
if not os.path.exists(bash_executable_path):
|
||||
logger.log(f"Bash Language Server executable not found at {bash_executable_path}. Installing...", logging.INFO)
|
||||
deps.install(logger, bash_ls_dir)
|
||||
logger.log("Bash language server dependencies installed successfully", logging.INFO)
|
||||
log.info(f"Bash Language Server executable not found at {bash_executable_path}. Installing...")
|
||||
deps.install(bash_ls_dir)
|
||||
log.info("Bash language server dependencies installed successfully")
|
||||
|
||||
if not os.path.exists(bash_executable_path):
|
||||
raise FileNotFoundError(
|
||||
@@ -146,11 +142,11 @@ class BashLanguageServer(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
# Check for bash-language-server ready signals
|
||||
message_text = msg.get("message", "")
|
||||
if "Analyzing" in message_text or "analysis complete" in message_text.lower():
|
||||
self.logger.log("Bash language server analysis signals detected", logging.INFO)
|
||||
log.info("Bash language server analysis signals detected")
|
||||
self.server_ready.set()
|
||||
self.completions_available.set()
|
||||
|
||||
@@ -160,16 +156,13 @@ class BashLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting Bash server process", logging.INFO)
|
||||
log.info("Starting Bash server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
self.logger.log(f"Received initialize response from bash server: {init_response}", logging.DEBUG)
|
||||
log.debug(f"Received initialize response from bash server: {init_response}")
|
||||
|
||||
# Enhanced capability checks for bash-language-server 5.6.0
|
||||
assert init_response["capabilities"]["textDocumentSync"] in [1, 2] # Full or Incremental
|
||||
@@ -177,22 +170,22 @@ class BashLanguageServer(SolidLanguageServer):
|
||||
|
||||
# Verify document symbol support is available
|
||||
if "documentSymbolProvider" in init_response["capabilities"]:
|
||||
self.logger.log("Bash server supports document symbols", logging.INFO)
|
||||
log.info("Bash server supports document symbols")
|
||||
else:
|
||||
self.logger.log("Warning: Bash server does not report document symbol support", logging.WARNING)
|
||||
log.warning("Warning: Bash server does not report document symbol support")
|
||||
|
||||
self.server.notify.initialized({})
|
||||
|
||||
# Wait for server readiness with timeout
|
||||
self.logger.log("Waiting for Bash language server to be ready...", logging.INFO)
|
||||
log.info("Waiting for Bash language server to be ready...")
|
||||
if not self.server_ready.wait(timeout=3.0):
|
||||
# Fallback: assume server is ready after timeout
|
||||
# This is common. bash-language-server doesn't always send explicit ready signals. Log as info
|
||||
self.logger.log("Timeout waiting for bash server ready signal, proceeding anyway", logging.INFO)
|
||||
log.info("Timeout waiting for bash server ready signal, proceeding anyway")
|
||||
self.server_ready.set()
|
||||
self.completions_available.set()
|
||||
else:
|
||||
self.logger.log("Bash server initialization complete", logging.INFO)
|
||||
log.info("Bash server initialization complete")
|
||||
|
||||
def request_document_symbols(self, relative_file_path: str, file_buffer: LSPFileBuffer | None = None) -> DocumentSymbols:
|
||||
# Uses the standard LSP documentSymbol request which provides reliable function detection
|
||||
@@ -202,16 +195,13 @@ class BashLanguageServer(SolidLanguageServer):
|
||||
# - Functions with various indentation levels
|
||||
# - Functions with comments before/after/inside
|
||||
|
||||
self.logger.log(f"Requesting document symbols via LSP for {relative_file_path}", logging.DEBUG)
|
||||
log.debug(f"Requesting document symbols via LSP for {relative_file_path}")
|
||||
|
||||
# Use the standard LSP approach - bash-language-server handles all function syntaxes correctly
|
||||
document_symbols = super().request_document_symbols(relative_file_path, file_buffer=file_buffer)
|
||||
|
||||
# Log detection results for debugging
|
||||
functions = [s for s in document_symbols.iter_symbols() if s.get("kind") == 12]
|
||||
self.logger.log(
|
||||
f"LSP function detection for {relative_file_path}: Found {len(functions)} functions",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info(f"LSP function detection for {relative_file_path}: Found {len(functions)} functions")
|
||||
|
||||
return document_symbols
|
||||
@@ -10,13 +10,14 @@ from typing import Any, cast
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
from .common import RuntimeDependency, RuntimeDependencyCollection
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ClangdLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -25,20 +26,13 @@ class ClangdLanguageServer(SolidLanguageServer):
|
||||
Also make sure compile_commands.json is created at root of the source directory. Check clangd test case for example.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a ClangdLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
clangd_executable_path = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
clangd_executable_path = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=clangd_executable_path, cwd=repository_root_path),
|
||||
"cpp",
|
||||
solidlsp_settings,
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd=clangd_executable_path, cwd=repository_root_path), "cpp", solidlsp_settings
|
||||
)
|
||||
self.server_ready = threading.Event()
|
||||
self.service_ready_event = threading.Event()
|
||||
@@ -46,9 +40,7 @@ class ClangdLanguageServer(SolidLanguageServer):
|
||||
self.resolve_main_method_available = threading.Event()
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> str:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""
|
||||
Setup runtime dependencies for ClangdLanguageServer and return the command to start the server.
|
||||
"""
|
||||
@@ -110,16 +102,13 @@ class ClangdLanguageServer(SolidLanguageServer):
|
||||
+ " Arch Linux: sudo pacman -S clang\n"
|
||||
+ "See https://clangd.llvm.org/installation for more details."
|
||||
)
|
||||
logger.log(f"Using system-installed clangd at {clangd_executable_path}", logging.INFO)
|
||||
log.info(f"Using system-installed clangd at {clangd_executable_path}")
|
||||
else:
|
||||
# Standard download and install for platforms with prebuilt binaries
|
||||
clangd_executable_path = deps.binary_path(clangd_ls_dir)
|
||||
if not os.path.exists(clangd_executable_path):
|
||||
logger.log(
|
||||
f"Clangd executable not found at {clangd_executable_path}. Downloading from {dep.url}",
|
||||
logging.INFO,
|
||||
)
|
||||
_ = deps.install(logger, clangd_ls_dir)
|
||||
log.info(f"Clangd executable not found at {clangd_executable_path}. Downloading from {dep.url}")
|
||||
_ = deps.install(clangd_ls_dir)
|
||||
if not os.path.exists(clangd_executable_path):
|
||||
raise FileNotFoundError(
|
||||
f"Clangd executable not found at {clangd_executable_path}.\n"
|
||||
@@ -197,7 +186,7 @@ class ClangdLanguageServer(SolidLanguageServer):
|
||||
self.server_ready.set()
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
self.server.on_request("client/registerCapability", register_capability_handler)
|
||||
self.server.on_notification("language/status", lang_status_handler)
|
||||
@@ -208,14 +197,11 @@ class ClangdLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
self.server.on_notification("experimental/serverStatus", check_experimental_status)
|
||||
|
||||
self.logger.log("Starting Clangd server process", logging.INFO)
|
||||
log.info("Starting Clangd server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 # type: ignore
|
||||
assert "completionProvider" in init_response["capabilities"]
|
||||
|
||||
@@ -12,13 +12,14 @@ from typing import cast
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
from .common import RuntimeDependency, RuntimeDependencyCollection
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def run_command(cmd: list, capture_output: bool = True) -> subprocess.CompletedProcess:
|
||||
return subprocess.run(
|
||||
@@ -86,16 +87,13 @@ class ClojureLSP(SolidLanguageServer):
|
||||
]
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a ClojureLSP instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
clojure_lsp_executable_path = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
clojure_lsp_executable_path = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=clojure_lsp_executable_path, cwd=repository_root_path),
|
||||
"clojure",
|
||||
@@ -107,9 +105,7 @@ class ClojureLSP(SolidLanguageServer):
|
||||
self.service_ready_event = threading.Event()
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> str:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""Setup runtime dependencies for clojure-lsp and return the command to start the server."""
|
||||
verify_clojure_cli()
|
||||
deps = ClojureLSP.runtime_dependencies
|
||||
@@ -118,11 +114,10 @@ class ClojureLSP(SolidLanguageServer):
|
||||
clojurelsp_ls_dir = cls.ls_resources_dir(solidlsp_settings)
|
||||
clojurelsp_executable_path = deps.binary_path(clojurelsp_ls_dir)
|
||||
if not os.path.exists(clojurelsp_executable_path):
|
||||
logger.log(
|
||||
log.info(
|
||||
f"Downloading and extracting clojure-lsp from {dependency.url} to {clojurelsp_ls_dir}",
|
||||
logging.INFO,
|
||||
)
|
||||
deps.install(logger, clojurelsp_ls_dir)
|
||||
deps.install(clojurelsp_ls_dir)
|
||||
if not os.path.exists(clojurelsp_executable_path):
|
||||
raise FileNotFoundError(f"Download failed? Could not find clojure-lsp executable at {clojurelsp_executable_path}")
|
||||
os.chmod(clojurelsp_executable_path, 0o755)
|
||||
@@ -189,7 +184,7 @@ class ClojureLSP(SolidLanguageServer):
|
||||
self.server_ready.set()
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
self.server.on_request("client/registerCapability", register_capability_handler)
|
||||
self.server.on_notification("language/status", lang_status_handler)
|
||||
@@ -200,15 +195,12 @@ class ClojureLSP(SolidLanguageServer):
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
self.server.on_notification("experimental/serverStatus", check_experimental_status)
|
||||
|
||||
self.logger.log("Starting clojure-lsp server process", logging.INFO)
|
||||
log.info("Starting clojure-lsp server process")
|
||||
self.server.start()
|
||||
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 # type: ignore
|
||||
assert "completionProvider" in init_response["capabilities"]
|
||||
|
||||
@@ -8,7 +8,6 @@ from collections.abc import Iterable, Mapping, Sequence
|
||||
from dataclasses import dataclass, replace
|
||||
from typing import Any, cast
|
||||
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import FileUtils, PlatformUtils
|
||||
from solidlsp.util.subprocess_util import subprocess_kwargs
|
||||
|
||||
@@ -79,7 +78,7 @@ class RuntimeDependencyCollection:
|
||||
return target_dir
|
||||
return os.path.join(target_dir, dep.binary_name)
|
||||
|
||||
def install(self, logger: LanguageServerLogger, target_dir: str) -> dict[str, str]:
|
||||
def install(self, target_dir: str) -> dict[str, str]:
|
||||
"""Install all dependencies for the current platform into *target_dir*.
|
||||
|
||||
Returns a mapping from dependency id to the resolved binary path.
|
||||
@@ -88,7 +87,7 @@ class RuntimeDependencyCollection:
|
||||
results: dict[str, str] = {}
|
||||
for dep in self.get_dependencies_for_current_platform():
|
||||
if dep.url:
|
||||
self._install_from_url(dep, logger, target_dir)
|
||||
self._install_from_url(dep, target_dir)
|
||||
if dep.command:
|
||||
self._run_command(dep.command, target_dir)
|
||||
if dep.binary_name:
|
||||
@@ -131,15 +130,15 @@ class RuntimeDependencyCollection:
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _install_from_url(dep: RuntimeDependency, logger: LanguageServerLogger, target_dir: str) -> None:
|
||||
def _install_from_url(dep: RuntimeDependency, target_dir: str) -> None:
|
||||
if not dep.url:
|
||||
raise ValueError(f"Dependency {dep.id} has no URL")
|
||||
|
||||
if dep.archive_type in ("gz", "binary") and dep.binary_name:
|
||||
dest = os.path.join(target_dir, dep.binary_name)
|
||||
FileUtils.download_and_extract_archive(logger, dep.url, dest, dep.archive_type)
|
||||
FileUtils.download_and_extract_archive(dep.url, dest, dep.archive_type)
|
||||
else:
|
||||
FileUtils.download_and_extract_archive(logger, dep.url, target_dir, dep.archive_type or "zip")
|
||||
FileUtils.download_and_extract_archive(dep.url, target_dir, dep.archive_type or "zip")
|
||||
|
||||
|
||||
def quote_windows_path(path: str) -> str:
|
||||
|
||||
@@ -21,7 +21,6 @@ from overrides import override
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_exceptions import SolidLSPException
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import PathUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams, InitializeResult
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
@@ -30,6 +29,8 @@ from solidlsp.util.zip import SafeZipExtractor
|
||||
|
||||
from .common import RuntimeDependency, RuntimeDependencyCollection
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_RUNTIME_DEPENDENCIES = [
|
||||
RuntimeDependency(
|
||||
id="CSharpLanguageServer",
|
||||
@@ -198,14 +199,12 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
See the `_RUNTIME_DEPENDENCIES` variable above for the available dependency ids and platform_ids.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a CSharpLanguageServer instance. This class is not meant to be instantiated directly.
|
||||
Use LanguageServer.create() instead.
|
||||
"""
|
||||
dotnet_path, language_server_path = self._ensure_server_installed(logger, config, solidlsp_settings)
|
||||
dotnet_path, language_server_path = self._ensure_server_installed(config, solidlsp_settings)
|
||||
|
||||
# Find solution or project file
|
||||
solution_or_project = find_solution_or_project_file(repository_root_path)
|
||||
@@ -219,20 +218,13 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
|
||||
# The language server will discover the solution/project from the workspace root
|
||||
if solution_or_project:
|
||||
logger.log(f"Found solution/project file: {solution_or_project}", logging.INFO)
|
||||
log.info(f"Found solution/project file: {solution_or_project}")
|
||||
else:
|
||||
logger.log("No .sln or .csproj file found, language server will attempt auto-discovery", logging.WARNING)
|
||||
log.warning("No .sln or .csproj file found, language server will attempt auto-discovery")
|
||||
|
||||
logger.log(f"Language server command: {' '.join(cmd)}", logging.DEBUG)
|
||||
log.debug(f"Language server command: {' '.join(cmd)}")
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path),
|
||||
"csharp",
|
||||
solidlsp_settings,
|
||||
)
|
||||
super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path), "csharp", solidlsp_settings)
|
||||
|
||||
self.initialization_complete = threading.Event()
|
||||
|
||||
@@ -241,9 +233,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
return super().is_ignored_dirname(dirname) or dirname in ["bin", "obj", "packages", ".vs"]
|
||||
|
||||
@classmethod
|
||||
def _ensure_server_installed(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> tuple[str, str]:
|
||||
def _ensure_server_installed(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> tuple[str, str]:
|
||||
"""
|
||||
Ensure .NET runtime and Microsoft.CodeAnalysis.LanguageServer are available.
|
||||
Returns a tuple of (dotnet_path, language_server_dll_path).
|
||||
@@ -251,30 +241,27 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
language_specific_config = solidlsp_settings.get_ls_specific_settings(cls.get_language_enum_instance())
|
||||
runtime_dependency_overrides = cast(list[dict[str, Any]], language_specific_config.get("runtime_dependencies", []))
|
||||
|
||||
logger.log("Resolving runtime dependencies", logging.DEBUG)
|
||||
log.debug("Resolving runtime dependencies")
|
||||
|
||||
runtime_dependencies = RuntimeDependencyCollection(
|
||||
_RUNTIME_DEPENDENCIES,
|
||||
overrides=runtime_dependency_overrides,
|
||||
)
|
||||
|
||||
logger.log(
|
||||
log.debug(
|
||||
f"Available runtime dependencies: {runtime_dependencies.get_dependencies_for_current_platform}",
|
||||
logging.DEBUG,
|
||||
)
|
||||
|
||||
# Find the dependencies for our platform
|
||||
lang_server_dep = runtime_dependencies.get_single_dep_for_current_platform("CSharpLanguageServer")
|
||||
dotnet_runtime_dep = runtime_dependencies.get_single_dep_for_current_platform("DotNetRuntime")
|
||||
dotnet_path = CSharpLanguageServer._ensure_dotnet_runtime(logger, dotnet_runtime_dep, solidlsp_settings)
|
||||
server_dll_path = CSharpLanguageServer._ensure_language_server(logger, lang_server_dep, solidlsp_settings)
|
||||
dotnet_path = CSharpLanguageServer._ensure_dotnet_runtime(dotnet_runtime_dep, solidlsp_settings)
|
||||
server_dll_path = CSharpLanguageServer._ensure_language_server(lang_server_dep, solidlsp_settings)
|
||||
|
||||
return dotnet_path, server_dll_path
|
||||
|
||||
@classmethod
|
||||
def _ensure_dotnet_runtime(
|
||||
cls, logger: LanguageServerLogger, dotnet_runtime_dep: RuntimeDependency, solidlsp_settings: SolidLSPSettings
|
||||
) -> str:
|
||||
def _ensure_dotnet_runtime(cls, dotnet_runtime_dep: RuntimeDependency, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""Ensure .NET runtime is available and return the dotnet executable path."""
|
||||
# TODO: use RuntimeDependency util methods instead of custom validation/download logic
|
||||
|
||||
@@ -285,18 +272,16 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
try:
|
||||
result = subprocess.run([system_dotnet, "--list-runtimes"], capture_output=True, text=True, check=True)
|
||||
if "Microsoft.NETCore.App 9." in result.stdout:
|
||||
logger.log("Found system .NET 9 runtime", logging.INFO)
|
||||
log.info("Found system .NET 9 runtime")
|
||||
return system_dotnet
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
|
||||
# Download .NET 9 runtime using config
|
||||
return cls._ensure_dotnet_runtime_from_config(logger, dotnet_runtime_dep, solidlsp_settings)
|
||||
return cls._ensure_dotnet_runtime_from_config(dotnet_runtime_dep, solidlsp_settings)
|
||||
|
||||
@classmethod
|
||||
def _ensure_language_server(
|
||||
cls, logger: LanguageServerLogger, lang_server_dep: RuntimeDependency, solidlsp_settings: SolidLSPSettings
|
||||
) -> str:
|
||||
def _ensure_language_server(cls, lang_server_dep: RuntimeDependency, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""Ensure language server is available and return the DLL path."""
|
||||
package_name = lang_server_dep.package_name
|
||||
package_version = lang_server_dep.package_version
|
||||
@@ -306,14 +291,14 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
server_dll = server_dir / lang_server_dep.binary_name
|
||||
|
||||
if server_dll.exists():
|
||||
logger.log(f"Using cached Microsoft.CodeAnalysis.LanguageServer from {server_dll}", logging.INFO)
|
||||
log.info(f"Using cached Microsoft.CodeAnalysis.LanguageServer from {server_dll}")
|
||||
return str(server_dll)
|
||||
|
||||
# Download and install the language server
|
||||
logger.log(f"Downloading {package_name} version {package_version}...", logging.INFO)
|
||||
log.info(f"Downloading {package_name} version {package_version}...")
|
||||
assert package_version is not None
|
||||
assert package_name is not None
|
||||
package_path = cls._download_nuget_package_direct(logger, package_name, package_version, solidlsp_settings)
|
||||
package_path = cls._download_nuget_package_direct(package_name, package_version, solidlsp_settings)
|
||||
|
||||
# Extract and install
|
||||
cls._extract_language_server(lang_server_dep, package_path, server_dir)
|
||||
@@ -325,7 +310,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
if platform.system().lower() != "windows":
|
||||
server_dll.chmod(0o755)
|
||||
|
||||
logger.log(f"Successfully installed Microsoft.CodeAnalysis.LanguageServer to {server_dll}", logging.INFO)
|
||||
log.info(f"Successfully installed Microsoft.CodeAnalysis.LanguageServer to {server_dll}")
|
||||
return str(server_dll)
|
||||
|
||||
@staticmethod
|
||||
@@ -352,9 +337,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
shutil.copytree(source_dir, server_dir, dirs_exist_ok=True)
|
||||
|
||||
@classmethod
|
||||
def _download_nuget_package_direct(
|
||||
cls, logger: LanguageServerLogger, package_name: str, package_version: str, solidlsp_settings: SolidLSPSettings
|
||||
) -> Path:
|
||||
def _download_nuget_package_direct(cls, package_name: str, package_version: str, solidlsp_settings: SolidLSPSettings) -> Path:
|
||||
"""
|
||||
Download a NuGet package directly from the Azure NuGet feed.
|
||||
Returns the path to the extracted package directory.
|
||||
@@ -367,7 +350,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
|
||||
try:
|
||||
# First, get the service index from the Azure feed
|
||||
logger.log("Fetching NuGet service index from Azure feed...", logging.DEBUG)
|
||||
log.debug("Fetching NuGet service index from Azure feed...")
|
||||
with urllib.request.urlopen(azure_feed_url) as response:
|
||||
service_index = json.loads(response.read().decode())
|
||||
|
||||
@@ -386,7 +369,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
package_version_lower = package_version.lower()
|
||||
package_url = f"{package_base_address.rstrip('/')}/{package_id_lower}/{package_version_lower}/{package_id_lower}.{package_version_lower}.nupkg"
|
||||
|
||||
logger.log(f"Downloading package from: {package_url}", logging.DEBUG)
|
||||
log.debug(f"Downloading package from: {package_url}")
|
||||
|
||||
# Download the .nupkg file
|
||||
nupkg_file = temp_dir / f"{package_name}.{package_version}.nupkg"
|
||||
@@ -403,7 +386,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
# Clean up the nupkg file
|
||||
nupkg_file.unlink()
|
||||
|
||||
logger.log(f"Successfully downloaded and extracted {package_name} version {package_version}", logging.INFO)
|
||||
log.info(f"Successfully downloaded and extracted {package_name} version {package_version}")
|
||||
return package_extract_dir
|
||||
|
||||
except Exception as e:
|
||||
@@ -412,9 +395,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
) from e
|
||||
|
||||
@classmethod
|
||||
def _ensure_dotnet_runtime_from_config(
|
||||
cls, logger: LanguageServerLogger, dotnet_runtime_dep: RuntimeDependency, solidlsp_settings: SolidLSPSettings
|
||||
) -> str:
|
||||
def _ensure_dotnet_runtime_from_config(cls, dotnet_runtime_dep: RuntimeDependency, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""
|
||||
Ensure .NET 9 runtime is available using runtime dependency configuration.
|
||||
Returns the path to the dotnet executable.
|
||||
@@ -428,7 +409,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
try:
|
||||
result = subprocess.run([system_dotnet, "--list-runtimes"], capture_output=True, text=True, check=True)
|
||||
if "Microsoft.NETCore.App 9." in result.stdout:
|
||||
logger.log("Found system .NET 9 runtime", logging.INFO)
|
||||
log.info("Found system .NET 9 runtime")
|
||||
return system_dotnet
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
@@ -439,17 +420,17 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
dotnet_exe = dotnet_dir / dotnet_runtime_dep.binary_name
|
||||
|
||||
if dotnet_exe.exists():
|
||||
logger.log(f"Using cached .NET runtime from {dotnet_exe}", logging.INFO)
|
||||
log.info(f"Using cached .NET runtime from {dotnet_exe}")
|
||||
return str(dotnet_exe)
|
||||
|
||||
# Download .NET runtime
|
||||
logger.log("Downloading .NET 9 runtime...", logging.INFO)
|
||||
log.info("Downloading .NET 9 runtime...")
|
||||
dotnet_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
custom_settings = solidlsp_settings.get_ls_specific_settings(cls.get_language_enum_instance())
|
||||
custom_dotnet_runtime_url = custom_settings.get("dotnet_runtime_url")
|
||||
if custom_dotnet_runtime_url is not None:
|
||||
logger.log(f"Using custom .NET runtime url: {custom_dotnet_runtime_url}", logging.INFO)
|
||||
log.info(f"Using custom .NET runtime url: {custom_dotnet_runtime_url}")
|
||||
url = custom_dotnet_runtime_url
|
||||
else:
|
||||
url = dotnet_runtime_dep.url
|
||||
@@ -459,7 +440,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
# Download the runtime
|
||||
download_path = dotnet_dir / f"dotnet-runtime.{archive_type}"
|
||||
try:
|
||||
logger.log(f"Downloading from {url}", logging.DEBUG)
|
||||
log.debug(f"Downloading from {url}")
|
||||
urllib.request.urlretrieve(url, download_path)
|
||||
|
||||
# Extract the archive
|
||||
@@ -478,7 +459,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
if platform.system().lower() != "windows":
|
||||
dotnet_exe.chmod(0o755)
|
||||
|
||||
logger.log(f"Successfully installed .NET 9 runtime to {dotnet_exe}", logging.INFO)
|
||||
log.info(f"Successfully installed .NET 9 runtime to {dotnet_exe}")
|
||||
return str(dotnet_exe)
|
||||
|
||||
except Exception as e:
|
||||
@@ -551,7 +532,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
# Map LSP message types to Python logging levels
|
||||
level_map = {1: logging.ERROR, 2: logging.WARNING, 3: logging.INFO, 4: logging.DEBUG} # Error # Warning # Info # Log
|
||||
|
||||
self.logger.log(f"LSP: {message_text}", level_map.get(level, logging.DEBUG))
|
||||
log.info(f"LSP: {message_text}", level_map.get(level, logging.DEBUG))
|
||||
|
||||
def handle_progress(params: dict) -> None:
|
||||
"""Handle progress notifications from the language server."""
|
||||
@@ -559,7 +540,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
value = params.get("value", {})
|
||||
|
||||
# Log raw progress for debugging
|
||||
self.logger.log(f"Progress notification received: {params}", logging.DEBUG)
|
||||
log.debug(f"Progress notification received: {params}")
|
||||
|
||||
# Handle different progress notification types
|
||||
kind = value.get("kind")
|
||||
@@ -570,22 +551,22 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
percentage = value.get("percentage")
|
||||
|
||||
if percentage is not None:
|
||||
self.logger.log(f"Progress [{token}]: {title} - {message} ({percentage}%)", logging.INFO)
|
||||
log.debug(f"Progress [{token}]: {title} - {message} ({percentage}%)")
|
||||
else:
|
||||
self.logger.log(f"Progress [{token}]: {title} - {message}", logging.INFO)
|
||||
log.info(f"Progress [{token}]: {title} - {message}")
|
||||
|
||||
elif kind == "report":
|
||||
message = value.get("message", "")
|
||||
percentage = value.get("percentage")
|
||||
|
||||
if percentage is not None:
|
||||
self.logger.log(f"Progress [{token}]: {message} ({percentage}%)", logging.INFO)
|
||||
log.info(f"Progress [{token}]: {message} ({percentage}%)")
|
||||
elif message:
|
||||
self.logger.log(f"Progress [{token}]: {message}", logging.INFO)
|
||||
log.info(f"Progress [{token}]: {message}")
|
||||
|
||||
elif kind == "end":
|
||||
message = value.get("message", "Operation completed")
|
||||
self.logger.log(f"Progress [{token}]: {message}", logging.INFO)
|
||||
log.info(f"Progress [{token}]: {message}")
|
||||
|
||||
def handle_workspace_configuration(params: dict) -> list:
|
||||
"""Handle workspace/configuration requests from the server."""
|
||||
@@ -655,21 +636,21 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
self.server.on_request("client/registerCapability", handle_register_capability)
|
||||
self.server.on_request("workspace/_roslyn_projectNeedsRestore", handle_project_needs_restore)
|
||||
|
||||
self.logger.log("Starting Microsoft.CodeAnalysis.LanguageServer process", logging.INFO)
|
||||
log.info("Starting Microsoft.CodeAnalysis.LanguageServer process")
|
||||
|
||||
try:
|
||||
self.server.start()
|
||||
except Exception as e:
|
||||
self.logger.log(f"Failed to start language server process: {e}", logging.ERROR)
|
||||
log.info(f"Failed to start language server process: {e}", logging.ERROR)
|
||||
raise SolidLSPException(f"Failed to start C# language server: {e}")
|
||||
|
||||
# Send initialization
|
||||
initialize_params = self._get_initialize_params()
|
||||
|
||||
self.logger.log("Sending initialize request to language server", logging.INFO)
|
||||
log.info("Sending initialize request to language server")
|
||||
try:
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
self.logger.log(f"Received initialize response: {init_response}", logging.DEBUG)
|
||||
log.info(f"Received initialize response: {init_response}")
|
||||
except Exception as e:
|
||||
raise SolidLSPException(f"Failed to initialize C# language server for {self.repository_root_path}: {e}") from e
|
||||
|
||||
@@ -700,11 +681,10 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
self.initialization_complete.set()
|
||||
self.completions_available.set()
|
||||
|
||||
self.logger.log(
|
||||
log.info(
|
||||
"Microsoft.CodeAnalysis.LanguageServer initialized and ready\n"
|
||||
"Waiting for language server to index project files...\n"
|
||||
"This may take a while for large projects",
|
||||
logging.INFO,
|
||||
"This may take a while for large projects"
|
||||
)
|
||||
|
||||
def _force_pull_diagnostics(self, init_response: dict | InitializeResult) -> None:
|
||||
@@ -724,7 +704,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
"workspaceDiagnostics": True,
|
||||
}
|
||||
)
|
||||
self.logger.log("Applied diagnostic capabilities hack for better C# diagnostics", logging.DEBUG)
|
||||
log.debug("Applied diagnostic capabilities hack for better C# diagnostics")
|
||||
|
||||
def _open_solution_and_projects(self) -> None:
|
||||
"""
|
||||
@@ -741,7 +721,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
if solution_file:
|
||||
solution_uri = PathUtils.path_to_uri(solution_file)
|
||||
self.server.notify.send_notification("solution/open", {"solution": solution_uri})
|
||||
self.logger.log(f"Opened solution file: {solution_file}", logging.INFO)
|
||||
log.debug(f"Opened solution file: {solution_file}")
|
||||
|
||||
# Find and open project files
|
||||
project_files = []
|
||||
@@ -753,7 +733,7 @@ class CSharpLanguageServer(SolidLanguageServer):
|
||||
if project_files:
|
||||
project_uris = [PathUtils.path_to_uri(project_file) for project_file in project_files]
|
||||
self.server.notify.send_notification("project/open", {"projects": project_uris})
|
||||
self.logger.log(f"Opened project files: {project_files}", logging.DEBUG)
|
||||
log.debug(f"Opened project files: {project_files}")
|
||||
|
||||
@override
|
||||
def _get_wait_time_for_cross_file_referencing(self) -> float:
|
||||
|
||||
@@ -4,7 +4,6 @@ import pathlib
|
||||
from typing import cast
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
@@ -12,30 +11,25 @@ from ..ls_config import LanguageServerConfig
|
||||
from ..lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from .common import RuntimeDependency, RuntimeDependencyCollection
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DartLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
Provides Dart specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Dart.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
) -> None:
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings) -> None:
|
||||
"""
|
||||
Creates a DartServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
executable_path = self._setup_runtime_dependencies(logger, solidlsp_settings)
|
||||
executable_path = self._setup_runtime_dependencies(solidlsp_settings)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=executable_path, cwd=repository_root_path),
|
||||
"dart",
|
||||
solidlsp_settings,
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd=executable_path, cwd=repository_root_path), "dart", solidlsp_settings
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(cls, logger: "LanguageServerLogger", solidlsp_settings: SolidLSPSettings) -> str:
|
||||
def _setup_runtime_dependencies(cls, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
deps = RuntimeDependencyCollection(
|
||||
[
|
||||
RuntimeDependency(
|
||||
@@ -85,7 +79,7 @@ class DartLanguageServer(SolidLanguageServer):
|
||||
dart_executable_path = deps.binary_path(dart_ls_dir)
|
||||
|
||||
if not os.path.exists(dart_executable_path):
|
||||
deps.install(logger, dart_ls_dir)
|
||||
deps.install(dart_ls_dir)
|
||||
|
||||
assert os.path.exists(dart_executable_path)
|
||||
os.chmod(dart_executable_path, 0o755)
|
||||
@@ -136,7 +130,7 @@ class DartLanguageServer(SolidLanguageServer):
|
||||
pass
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
self.server.on_request("client/registerCapability", do_nothing)
|
||||
self.server.on_notification("language/status", do_nothing)
|
||||
@@ -147,17 +141,11 @@ class DartLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
self.server.on_notification("experimental/serverStatus", check_experimental_status)
|
||||
|
||||
self.logger.log("Starting dart-language-server server process", logging.INFO)
|
||||
log.info("Starting dart-language-server server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
self.logger.log(
|
||||
"Sending initialize request to dart-language-server",
|
||||
logging.DEBUG,
|
||||
)
|
||||
log.debug("Sending initialize request to dart-language-server")
|
||||
init_response = self.server.send_request("initialize", initialize_params) # type: ignore
|
||||
self.logger.log(
|
||||
f"Received initialize response from dart-language-server: {init_response}",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info(f"Received initialize response from dart-language-server: {init_response}")
|
||||
|
||||
self.server.notify.initialized({})
|
||||
@@ -16,13 +16,14 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import LSPFileBuffer, SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_types import UnifiedSymbolInformation
|
||||
from solidlsp.ls_utils import FileUtils, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import DocumentSymbol, InitializeParams, SymbolInformation
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class RuntimeDependencyPaths:
|
||||
@@ -61,14 +62,12 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a new EclipseJDTLS instance initializing the language server settings appropriately.
|
||||
This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
runtime_dependency_paths = self._setupRuntimeDependencies(logger, config, solidlsp_settings)
|
||||
runtime_dependency_paths = self._setupRuntimeDependencies(config, solidlsp_settings)
|
||||
self.runtime_dependency_paths = runtime_dependency_paths
|
||||
|
||||
# ws_dir is the workspace directory for the EclipseJDTLS server
|
||||
@@ -150,7 +149,7 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
self.initialize_searcher_command_available = threading.Event()
|
||||
|
||||
super().__init__(
|
||||
config, logger, repository_root_path, ProcessLaunchInfo(cmd, proc_env, proc_cwd), "java", solidlsp_settings=solidlsp_settings
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd, proc_env, proc_cwd), "java", solidlsp_settings=solidlsp_settings
|
||||
)
|
||||
|
||||
@override
|
||||
@@ -172,9 +171,7 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _setupRuntimeDependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> RuntimeDependencyPaths:
|
||||
def _setupRuntimeDependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> RuntimeDependencyPaths:
|
||||
"""
|
||||
Setup runtime dependencies for EclipseJDTLS and return the paths.
|
||||
"""
|
||||
@@ -266,7 +263,6 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
|
||||
if not os.path.exists(gradle_path):
|
||||
FileUtils.download_and_extract_archive(
|
||||
logger,
|
||||
runtime_dependencies["gradle"]["platform-agnostic"]["url"],
|
||||
str(PurePath(gradle_path).parent),
|
||||
runtime_dependencies["gradle"]["platform-agnostic"]["archiveType"],
|
||||
@@ -292,7 +288,7 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
os.path.exists(jdtls_readonly_config_path),
|
||||
]
|
||||
):
|
||||
FileUtils.download_and_extract_archive(logger, dependency["url"], vscode_java_path, dependency["archiveType"])
|
||||
FileUtils.download_and_extract_archive(dependency["url"], vscode_java_path, dependency["archiveType"])
|
||||
|
||||
os.chmod(jre_path, 0o755)
|
||||
|
||||
@@ -315,7 +311,7 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
os.path.exists(intellisense_members_path),
|
||||
]
|
||||
):
|
||||
FileUtils.download_and_extract_archive(logger, dependency["url"], intellicode_directory_path, dependency["archiveType"])
|
||||
FileUtils.download_and_extract_archive(dependency["url"], intellicode_directory_path, dependency["archiveType"])
|
||||
|
||||
assert os.path.exists(intellicode_directory_path)
|
||||
assert os.path.exists(intellicode_jar_path)
|
||||
@@ -355,18 +351,16 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
f"Fix: create the file, update path in ~/.serena/serena_config.yml (ls_specific_settings -> java -> maven_user_settings), "
|
||||
f"or remove the setting to use default ({default_maven_settings_path})"
|
||||
)
|
||||
self.logger.log(error_msg, logging.ERROR)
|
||||
log.error(error_msg)
|
||||
raise FileNotFoundError(error_msg)
|
||||
maven_settings_path = custom_maven_settings_path
|
||||
self.logger.log(f"Using Maven settings from custom location: {maven_settings_path}", logging.INFO)
|
||||
log.info(f"Using Maven settings from custom location: {maven_settings_path}")
|
||||
elif os.path.exists(default_maven_settings_path):
|
||||
maven_settings_path = default_maven_settings_path
|
||||
self.logger.log(f"Using Maven settings from default location: {maven_settings_path}", logging.INFO)
|
||||
log.info(f"Using Maven settings from default location: {maven_settings_path}")
|
||||
else:
|
||||
maven_settings_path = None
|
||||
self.logger.log(
|
||||
f"Maven settings not found at default location ({default_maven_settings_path}), will use JDTLS defaults", logging.INFO
|
||||
)
|
||||
log.info(f"Maven settings not found at default location ({default_maven_settings_path}), will use JDTLS defaults")
|
||||
|
||||
# Gradle user home: default to ~/.gradle
|
||||
default_gradle_home = os.path.join(os.path.expanduser("~"), ".gradle")
|
||||
@@ -379,18 +373,16 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
f"Fix: create the directory, update path in ~/.serena/serena_config.yml (ls_specific_settings -> java -> gradle_user_home), "
|
||||
f"or remove the setting to use default (~/.gradle)"
|
||||
)
|
||||
self.logger.log(error_msg, logging.ERROR)
|
||||
log.error(error_msg)
|
||||
raise FileNotFoundError(error_msg)
|
||||
gradle_user_home = custom_gradle_home
|
||||
self.logger.log(f"Using Gradle user home from custom location: {gradle_user_home}", logging.INFO)
|
||||
log.info(f"Using Gradle user home from custom location: {gradle_user_home}")
|
||||
elif os.path.exists(default_gradle_home):
|
||||
gradle_user_home = default_gradle_home
|
||||
self.logger.log(f"Using Gradle user home from default location: {gradle_user_home}", logging.INFO)
|
||||
log.info(f"Using Gradle user home from default location: {gradle_user_home}")
|
||||
else:
|
||||
gradle_user_home = None
|
||||
self.logger.log(
|
||||
f"Gradle user home not found at default location ({default_gradle_home}), will use JDTLS defaults", logging.INFO
|
||||
)
|
||||
log.info(f"Gradle user home not found at default location ({default_gradle_home}), will use JDTLS defaults")
|
||||
|
||||
initialize_params = {
|
||||
"locale": "en",
|
||||
@@ -745,7 +737,7 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
return []
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(params: dict) -> None:
|
||||
return
|
||||
@@ -758,14 +750,11 @@ class EclipseJDTLS(SolidLanguageServer):
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
|
||||
self.logger.log("Starting EclipseJDTLS server process", logging.INFO)
|
||||
log.info("Starting EclipseJDTLS server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 # type: ignore
|
||||
assert "completionProvider" not in init_response["capabilities"]
|
||||
|
||||
@@ -10,7 +10,6 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import FileUtils, PlatformId, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler import lsp_types
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
@@ -19,6 +18,8 @@ from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
from ..common import RuntimeDependency
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ElixirTools(SolidLanguageServer):
|
||||
"""
|
||||
@@ -78,7 +79,7 @@ class ElixirTools(SolidLanguageServer):
|
||||
if isinstance(item, dict) and "uri" in item:
|
||||
abs_path = PathUtils.uri_to_path(item["uri"])
|
||||
if self._is_next_ls_internal_file(abs_path):
|
||||
self.logger.log(f"Filtering out Next LS internal file: {abs_path}", logging.DEBUG)
|
||||
log.debug(f"Filtering out Next LS internal file: {abs_path}")
|
||||
continue
|
||||
filtered_response.append(item)
|
||||
|
||||
@@ -96,9 +97,7 @@ class ElixirTools(SolidLanguageServer):
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> str:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""
|
||||
Setup runtime dependencies for Next LS.
|
||||
Downloads the Next LS binary for the current platform and returns the path to the executable.
|
||||
@@ -110,7 +109,7 @@ class ElixirTools(SolidLanguageServer):
|
||||
"Elixir is not installed. Please install Elixir from https://elixir-lang.org/install.html and make sure it is added to your PATH."
|
||||
)
|
||||
|
||||
logger.log(f"Found Elixir: {elixir_version}", logging.INFO)
|
||||
log.info(f"Found Elixir: {elixir_version}")
|
||||
|
||||
platform_id = PlatformUtils.get_platform_id()
|
||||
|
||||
@@ -175,9 +174,9 @@ class ElixirTools(SolidLanguageServer):
|
||||
binary_path = os.path.join(next_ls_dir, dependency.binary_name)
|
||||
|
||||
if not os.path.exists(executable_path):
|
||||
logger.log(f"Downloading Next LS binary from {dependency.url}", logging.INFO)
|
||||
log.info(f"Downloading Next LS binary from {dependency.url}")
|
||||
assert dependency.url is not None
|
||||
FileUtils.download_file(logger, dependency.url, binary_path)
|
||||
FileUtils.download_file(dependency.url, binary_path)
|
||||
|
||||
# Make the binary executable on Unix-like systems
|
||||
os.chmod(binary_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
|
||||
@@ -190,17 +189,14 @@ class ElixirTools(SolidLanguageServer):
|
||||
|
||||
assert os.path.exists(executable_path), f"Next LS executable not found at {executable_path}"
|
||||
|
||||
logger.log(f"Next LS binary ready at: {executable_path}", logging.INFO)
|
||||
log.info(f"Next LS binary ready at: {executable_path}")
|
||||
return executable_path
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
nextls_executable_path = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
nextls_executable_path = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=f'"{nextls_executable_path}" --stdio', cwd=repository_root_path),
|
||||
"elixir",
|
||||
@@ -287,12 +283,12 @@ class ElixirTools(SolidLanguageServer):
|
||||
def window_log_message(msg: Any) -> None:
|
||||
"""Handle window/logMessage notifications from Next LS"""
|
||||
message_text = msg.get("message", "")
|
||||
self.logger.log(f"LSP: window/logMessage: {message_text}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {message_text}")
|
||||
|
||||
# Check for the specific Next LS readiness signal
|
||||
# Based on Next LS source: "Runtime for folder #{name} is ready..."
|
||||
if "Runtime for folder" in message_text and "is ready..." in message_text:
|
||||
self.logger.log("Next LS runtime is ready based on official log message", logging.INFO)
|
||||
log.info("Next LS runtime is ready based on official log message")
|
||||
self.server_ready.set()
|
||||
|
||||
def do_nothing(params: Any) -> None:
|
||||
@@ -310,7 +306,7 @@ class ElixirTools(SolidLanguageServer):
|
||||
if value.get("kind") == "end":
|
||||
message = value.get("message", "")
|
||||
if "has initialized!" in message:
|
||||
self.logger.log("Next LS initialization progress completed", logging.INFO)
|
||||
log.info("Next LS initialization progress completed")
|
||||
# Note: We don't set server_ready here - we wait for the log message
|
||||
|
||||
def work_done_progress(params: Any) -> None:
|
||||
@@ -320,7 +316,7 @@ class ElixirTools(SolidLanguageServer):
|
||||
"""
|
||||
value = params.get("value", {})
|
||||
if value.get("kind") == "end":
|
||||
self.logger.log("Next LS work done progress completed", logging.INFO)
|
||||
log.info("Next LS work done progress completed")
|
||||
# Note: We don't set server_ready here - we wait for the log message
|
||||
|
||||
self.server.on_request("client/registerCapability", register_capability_handler)
|
||||
@@ -330,28 +326,25 @@ class ElixirTools(SolidLanguageServer):
|
||||
self.server.on_notification("$/workDoneProgress", work_done_progress)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting Next LS server process", logging.INFO)
|
||||
log.info("Starting Next LS server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
# Verify server capabilities - be more lenient with Next LS
|
||||
self.logger.log(f"Next LS capabilities: {list(init_response['capabilities'].keys())}", logging.INFO)
|
||||
log.info(f"Next LS capabilities: {list(init_response['capabilities'].keys())}")
|
||||
|
||||
# Next LS may not provide all capabilities immediately, so we check for basic ones
|
||||
assert "textDocumentSync" in init_response["capabilities"], f"Missing textDocumentSync in {init_response['capabilities']}"
|
||||
|
||||
# Some capabilities might be optional or provided later. This is expected, so we log as info
|
||||
if "completionProvider" not in init_response["capabilities"]:
|
||||
self.logger.log("completionProvider not available in initial capabilities", logging.INFO)
|
||||
log.info("completionProvider not available in initial capabilities")
|
||||
|
||||
if "definitionProvider" not in init_response["capabilities"]:
|
||||
self.logger.log("definitionProvider not available in initial capabilities", logging.INFO)
|
||||
log.info("definitionProvider not available in initial capabilities")
|
||||
|
||||
self.server.notify.initialized({})
|
||||
self.completions_available.set()
|
||||
@@ -359,12 +352,12 @@ class ElixirTools(SolidLanguageServer):
|
||||
# Wait for Next LS to send the specific "Runtime for folder X is ready..." log message
|
||||
# This is the authoritative signal that Next LS is truly ready for requests
|
||||
ready_timeout = 180.0
|
||||
self.logger.log(f"Waiting up to {ready_timeout} seconds for Next LS runtime readiness...", logging.INFO)
|
||||
log.info(f"Waiting up to {ready_timeout} seconds for Next LS runtime readiness...")
|
||||
|
||||
if self.server_ready.wait(timeout=ready_timeout):
|
||||
self.logger.log("Next LS is ready and available for requests", logging.INFO)
|
||||
log.info("Next LS is ready and available for requests")
|
||||
|
||||
else:
|
||||
error_msg = f"Next LS failed to initialize within {ready_timeout} seconds. This may indicate a problem with the Elixir installation, project compilation, or Next LS itself."
|
||||
self.logger.log(error_msg, logging.ERROR)
|
||||
log.error(error_msg)
|
||||
raise RuntimeError(error_msg)
|
||||
@@ -13,33 +13,27 @@ from sensai.util.logging import LogTime
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
from .common import RuntimeDependency, RuntimeDependencyCollection
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ElmLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
Provides Elm specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Elm.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates an ElmLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
elm_lsp_executable_path = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
elm_lsp_executable_path = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=elm_lsp_executable_path, cwd=repository_root_path),
|
||||
"elm",
|
||||
solidlsp_settings,
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd=elm_lsp_executable_path, cwd=repository_root_path), "elm", solidlsp_settings
|
||||
)
|
||||
self.server_ready = threading.Event()
|
||||
|
||||
@@ -53,16 +47,14 @@ class ElmLanguageServer(SolidLanguageServer):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> list[str]:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> list[str]:
|
||||
"""
|
||||
Setup runtime dependencies for Elm Language Server and return the command to start the server.
|
||||
"""
|
||||
# Check if elm-language-server is already installed globally
|
||||
system_elm_ls = shutil.which("elm-language-server")
|
||||
if system_elm_ls:
|
||||
logger.log(f"Found system-installed elm-language-server at {system_elm_ls}", logging.INFO)
|
||||
log.info(f"Found system-installed elm-language-server at {system_elm_ls}")
|
||||
return [system_elm_ls, "--stdio"]
|
||||
|
||||
# Verify node and npm are installed
|
||||
@@ -86,9 +78,9 @@ class ElmLanguageServer(SolidLanguageServer):
|
||||
elm_ls_dir = os.path.join(cls.ls_resources_dir(solidlsp_settings), "elm-lsp")
|
||||
elm_ls_executable_path = os.path.join(elm_ls_dir, "node_modules", ".bin", "elm-language-server")
|
||||
if not os.path.exists(elm_ls_executable_path):
|
||||
logger.log(f"Elm Language Server executable not found at {elm_ls_executable_path}. Installing...", logging.INFO)
|
||||
with LogTime("Installation of Elm language server dependencies", logger=logger.logger):
|
||||
deps.install(logger, elm_ls_dir)
|
||||
log.info(f"Elm Language Server executable not found at {elm_ls_executable_path}. Installing...")
|
||||
with LogTime("Installation of Elm language server dependencies", logger=log):
|
||||
deps.install(elm_ls_dir)
|
||||
|
||||
if not os.path.exists(elm_ls_executable_path):
|
||||
raise FileNotFoundError(
|
||||
@@ -154,20 +146,17 @@ class ElmLanguageServer(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
self.server.on_notification("window/logMessage", window_log_message)
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting Elm server process", logging.INFO)
|
||||
log.info("Starting Elm server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
# Elm-specific capability checks
|
||||
@@ -178,11 +167,11 @@ class ElmLanguageServer(SolidLanguageServer):
|
||||
assert "documentSymbolProvider" in init_response["capabilities"]
|
||||
|
||||
self.server.notify.initialized({})
|
||||
self.logger.log("Elm server initialized successfully, waiting for workspace scan...", logging.INFO)
|
||||
log.info("Elm server initialized successfully, waiting for workspace scan...")
|
||||
|
||||
self.server_ready.set()
|
||||
self.completions_available.set()
|
||||
self.logger.log("Elm server ready", logging.INFO)
|
||||
log.info("Elm server ready")
|
||||
|
||||
@override
|
||||
def _get_wait_time_for_cross_file_referencing(self) -> float:
|
||||
|
||||
@@ -11,21 +11,16 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ErlangLanguageServer(SolidLanguageServer):
|
||||
"""Language server for Erlang using Erlang LS."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: LanguageServerConfig,
|
||||
logger: LanguageServerLogger,
|
||||
repository_root_path: str,
|
||||
solidlsp_settings: SolidLSPSettings,
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates an ErlangLanguageServer instance. This class is not meant to be instantiated directly.
|
||||
Use LanguageServer.create() instead.
|
||||
@@ -39,7 +34,6 @@ class ErlangLanguageServer(SolidLanguageServer):
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=[self.erlang_ls_path, "--transport", "stdio"], cwd=repository_root_path),
|
||||
"erlang",
|
||||
@@ -89,7 +83,7 @@ class ErlangLanguageServer(SolidLanguageServer):
|
||||
def window_log_message(msg: dict) -> None:
|
||||
"""Handle window/logMessage notifications from Erlang LS"""
|
||||
message_text = msg.get("message", "")
|
||||
self.logger.log(f"LSP: window/logMessage: {message_text}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {message_text}")
|
||||
|
||||
# Look for Erlang LS readiness signals
|
||||
# Common patterns: "Started Erlang LS", "initialized", "ready"
|
||||
@@ -105,7 +99,7 @@ class ErlangLanguageServer(SolidLanguageServer):
|
||||
message_lower = message_text.lower()
|
||||
for signal in readiness_signals:
|
||||
if signal.lower() in message_lower:
|
||||
self.logger.log(f"Erlang LS readiness signal detected: {message_text}", logging.INFO)
|
||||
log.info(f"Erlang LS readiness signal detected: {message_text}")
|
||||
self.server_ready.set()
|
||||
break
|
||||
|
||||
@@ -120,7 +114,7 @@ class ErlangLanguageServer(SolidLanguageServer):
|
||||
if value.get("kind") == "end":
|
||||
message = value.get("message", "")
|
||||
if any(word in message.lower() for word in ["initialized", "ready", "complete"]):
|
||||
self.logger.log("Erlang LS initialization progress completed", logging.INFO)
|
||||
log.info("Erlang LS initialization progress completed")
|
||||
# Set as fallback if no window/logMessage was received
|
||||
if not self.server_ready.is_set():
|
||||
self.server_ready.set()
|
||||
@@ -133,7 +127,7 @@ class ErlangLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("$/workDoneProgress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting Erlang LS server process", logging.INFO)
|
||||
log.info("Starting Erlang LS server process")
|
||||
self.server.start()
|
||||
|
||||
# Send initialize request
|
||||
@@ -153,12 +147,12 @@ class ErlangLanguageServer(SolidLanguageServer):
|
||||
},
|
||||
}
|
||||
|
||||
self.logger.log("Sending initialize request to Erlang LS", logging.INFO)
|
||||
log.info("Sending initialize request to Erlang LS")
|
||||
init_response = self.server.send.initialize(initialize_params) # type: ignore[arg-type]
|
||||
|
||||
# Verify server capabilities
|
||||
if "capabilities" in init_response:
|
||||
self.logger.log(f"Erlang LS capabilities: {list(init_response['capabilities'].keys())}", logging.INFO)
|
||||
log.info(f"Erlang LS capabilities: {list(init_response['capabilities'].keys())}")
|
||||
|
||||
self.server.notify.initialized({})
|
||||
self.completions_available.set()
|
||||
@@ -178,28 +172,26 @@ class ErlangLanguageServer(SolidLanguageServer):
|
||||
ready_timeout = 60.0 # 1 minute for local
|
||||
env_desc = "local"
|
||||
|
||||
self.logger.log(f"Waiting up to {ready_timeout} seconds for Erlang LS readiness ({env_desc} environment)...", logging.INFO)
|
||||
log.info(f"Waiting up to {ready_timeout} seconds for Erlang LS readiness ({env_desc} environment)...")
|
||||
|
||||
if self.server_ready.wait(timeout=ready_timeout):
|
||||
self.logger.log("Erlang LS is ready and available for requests", logging.INFO)
|
||||
log.info("Erlang LS is ready and available for requests")
|
||||
|
||||
# Add settling period for indexing - adjust based on environment
|
||||
settling_time = 15.0 if is_ci else 5.0
|
||||
self.logger.log(f"Allowing {settling_time} seconds for Erlang LS indexing to complete...", logging.INFO)
|
||||
log.info(f"Allowing {settling_time} seconds for Erlang LS indexing to complete...")
|
||||
time.sleep(settling_time)
|
||||
self.logger.log("Erlang LS settling period complete", logging.INFO)
|
||||
log.info("Erlang LS settling period complete")
|
||||
else:
|
||||
# Set ready anyway and continue - Erlang LS might not send explicit ready messages
|
||||
self.logger.log(
|
||||
f"Erlang LS readiness timeout reached after {ready_timeout}s, proceeding anyway (common in CI)", logging.WARNING
|
||||
)
|
||||
log.warning(f"Erlang LS readiness timeout reached after {ready_timeout}s, proceeding anyway (common in CI)")
|
||||
self.server_ready.set()
|
||||
|
||||
# Still give some time for basic initialization even without explicit readiness signal
|
||||
basic_settling_time = 20.0 if is_ci else 10.0
|
||||
self.logger.log(f"Allowing {basic_settling_time} seconds for basic Erlang LS initialization...", logging.INFO)
|
||||
log.info(f"Allowing {basic_settling_time} seconds for basic Erlang LS initialization...")
|
||||
time.sleep(basic_settling_time)
|
||||
self.logger.log("Basic Erlang LS initialization period complete", logging.INFO)
|
||||
log.info("Basic Erlang LS initialization period complete")
|
||||
|
||||
@override
|
||||
def is_ignored_dirname(self, dirname: str) -> bool:
|
||||
|
||||
@@ -14,11 +14,12 @@ from overrides import override
|
||||
from solidlsp import ls_types
|
||||
from solidlsp.ls import DocumentSymbols, LSPFileBuffer, SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FortranLanguageServer(SolidLanguageServer):
|
||||
"""Fortran Language Server implementation using fortls."""
|
||||
@@ -127,10 +128,7 @@ class FortranLanguageServer(SolidLanguageServer):
|
||||
corrected_symbol = symbol.copy()
|
||||
corrected_symbol["selectionRange"] = new_sel_range # type: ignore[typeddict-item]
|
||||
|
||||
self.logger.log(
|
||||
f"Fixed fortls selectionRange for {identifier_name}: char {start_char} -> {identifier_start}",
|
||||
logging.DEBUG,
|
||||
)
|
||||
log.debug(f"Fixed fortls selectionRange for {identifier_name}: char {start_char} -> {identifier_start}")
|
||||
|
||||
return corrected_symbol
|
||||
|
||||
@@ -181,13 +179,7 @@ class FortranLanguageServer(SolidLanguageServer):
|
||||
raise RuntimeError("fortls is not installed or not in PATH.\nInstall it with: pip install fortls")
|
||||
return fortls_path
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: LanguageServerConfig,
|
||||
logger: LanguageServerLogger,
|
||||
repository_root_path: str,
|
||||
solidlsp_settings: SolidLSPSettings,
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
# Check fortls installation
|
||||
fortls_path = self._check_fortls_installation()
|
||||
|
||||
@@ -196,12 +188,7 @@ class FortranLanguageServer(SolidLanguageServer):
|
||||
fortls_cmd = f"{fortls_path}"
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=fortls_cmd, cwd=repository_root_path),
|
||||
"fortran", # Language ID for LSP
|
||||
solidlsp_settings,
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd=fortls_cmd, cwd=repository_root_path), "fortran", solidlsp_settings
|
||||
)
|
||||
self.server_ready = threading.Event()
|
||||
|
||||
@@ -261,7 +248,7 @@ class FortranLanguageServer(SolidLanguageServer):
|
||||
"""Start Fortran Language Server process."""
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"Fortran LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"Fortran LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(params: dict) -> None:
|
||||
return
|
||||
@@ -275,14 +262,11 @@ class FortranLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting Fortran Language Server (fortls) process", logging.INFO)
|
||||
log.info("Starting Fortran Language Server (fortls) process")
|
||||
self.server.start()
|
||||
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
self.logger.log(
|
||||
"Sending initialize request to Fortran Language Server",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request to Fortran Language Server")
|
||||
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
@@ -290,17 +274,17 @@ class FortranLanguageServer(SolidLanguageServer):
|
||||
capabilities = init_response.get("capabilities", {})
|
||||
assert "textDocumentSync" in capabilities
|
||||
if "completionProvider" in capabilities:
|
||||
self.logger.log("Fortran LSP completion provider available", logging.INFO)
|
||||
log.info("Fortran LSP completion provider available")
|
||||
if "definitionProvider" in capabilities:
|
||||
self.logger.log("Fortran LSP definition provider available", logging.INFO)
|
||||
log.info("Fortran LSP definition provider available")
|
||||
if "referencesProvider" in capabilities:
|
||||
self.logger.log("Fortran LSP references provider available", logging.INFO)
|
||||
log.info("Fortran LSP references provider available")
|
||||
if "documentSymbolProvider" in capabilities:
|
||||
self.logger.log("Fortran LSP document symbol provider available", logging.INFO)
|
||||
log.info("Fortran LSP document symbol provider available")
|
||||
|
||||
self.server.notify.initialized({})
|
||||
self.completions_available.set()
|
||||
|
||||
# Fortran Language Server is ready after initialization
|
||||
self.server_ready.set()
|
||||
self.logger.log("Fortran Language Server initialization complete", logging.INFO)
|
||||
log.info("Fortran Language Server initialization complete")
|
||||
@@ -9,11 +9,12 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Gopls(SolidLanguageServer):
|
||||
"""
|
||||
@@ -90,19 +91,10 @@ class Gopls(SolidLanguageServer):
|
||||
|
||||
return True
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
self._setup_runtime_dependency()
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd="gopls", cwd=repository_root_path),
|
||||
"go",
|
||||
solidlsp_settings,
|
||||
)
|
||||
super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd="gopls", cwd=repository_root_path), "go", solidlsp_settings)
|
||||
self.server_ready = threading.Event()
|
||||
self.request_id = 0
|
||||
|
||||
@@ -145,7 +137,7 @@ class Gopls(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(params: dict) -> None:
|
||||
return
|
||||
@@ -155,14 +147,11 @@ class Gopls(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting gopls server process", logging.INFO)
|
||||
log.info("Starting gopls server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
# Verify server capabilities
|
||||
|
||||
@@ -13,11 +13,12 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HaskellLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -71,14 +72,12 @@ class HaskellLanguageServer(SolidLanguageServer):
|
||||
" - Homebrew (macOS): brew install haskell-language-server"
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a HaskellLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
hls_executable_path = self._ensure_hls_installed()
|
||||
logger.log(f"Using haskell-language-server at: {hls_executable_path}", logging.INFO)
|
||||
log.info(f"Using haskell-language-server at: {hls_executable_path}")
|
||||
|
||||
# Check if there's a haskell subdirectory with Stack/Cabal project
|
||||
haskell_subdir = os.path.join(repository_root_path, "haskell")
|
||||
@@ -86,7 +85,7 @@ class HaskellLanguageServer(SolidLanguageServer):
|
||||
os.path.exists(os.path.join(haskell_subdir, "stack.yaml")) or os.path.exists(os.path.join(haskell_subdir, "cabal.project"))
|
||||
):
|
||||
working_dir = haskell_subdir
|
||||
logger.log(f"Using Haskell project directory: {working_dir}", logging.INFO)
|
||||
log.info(f"Using Haskell project directory: {working_dir}")
|
||||
else:
|
||||
working_dir = repository_root_path
|
||||
|
||||
@@ -98,7 +97,6 @@ class HaskellLanguageServer(SolidLanguageServer):
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=[hls_executable_path, "--lsp", "--cwd", working_dir], cwd=working_dir, env=env),
|
||||
"haskell",
|
||||
@@ -322,19 +320,19 @@ class HaskellLanguageServer(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def register_capability_handler(params: dict) -> None:
|
||||
"""Handle dynamic capability registration from HLS"""
|
||||
if "registrations" in params:
|
||||
for registration in params.get("registrations", []):
|
||||
method = registration.get("method", "")
|
||||
self.logger.log(f"HLS registered capability: {method}", logging.INFO)
|
||||
log.info(f"HLS registered capability: {method}")
|
||||
return
|
||||
|
||||
def workspace_configuration_handler(params: dict) -> Any:
|
||||
"""Handle workspace/configuration requests from HLS"""
|
||||
self.logger.log(f"HLS requesting configuration: {params}", logging.INFO)
|
||||
log.info(f"HLS requesting configuration: {params}")
|
||||
|
||||
# Configuration matching VS Code settings and initialization options
|
||||
haskell_config = {
|
||||
@@ -352,7 +350,7 @@ class HaskellLanguageServer(SolidLanguageServer):
|
||||
result.append(haskell_config)
|
||||
else:
|
||||
result.append({})
|
||||
self.logger.log(f"Returning configuration: {result}", logging.INFO)
|
||||
log.info(f"Returning configuration: {result}")
|
||||
return result
|
||||
|
||||
# Fallback: return single config
|
||||
@@ -364,26 +362,23 @@ class HaskellLanguageServer(SolidLanguageServer):
|
||||
self.server.on_request("client/registerCapability", register_capability_handler)
|
||||
self.server.on_request("workspace/configuration", workspace_configuration_handler)
|
||||
|
||||
self.logger.log("Starting Haskell Language Server process", logging.INFO)
|
||||
log.info("Starting Haskell Language Server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
# Log capabilities returned by HLS
|
||||
capabilities = init_response.get("capabilities", {})
|
||||
self.logger.log(f"HLS capabilities: {list(capabilities.keys())}", logging.INFO)
|
||||
log.info(f"HLS capabilities: {list(capabilities.keys())}")
|
||||
|
||||
self.server.notify.initialized({})
|
||||
self.completions_available.set()
|
||||
|
||||
# Give HLS time to index the project
|
||||
# HLS can be slow to index, especially on first run
|
||||
self.logger.log("Waiting for HLS to index project...", logging.INFO)
|
||||
log.info("Waiting for HLS to index project...")
|
||||
time.sleep(5)
|
||||
|
||||
self.logger.log("Haskell Language Server initialized successfully", logging.INFO)
|
||||
log.info("Haskell Language Server initialized successfully")
|
||||
@@ -12,7 +12,6 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import PlatformId, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import Definition, DefinitionParams, InitializeParams, LocationLink
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
@@ -21,6 +20,8 @@ from solidlsp.settings import SolidLSPSettings
|
||||
from ..lsp_protocol_handler import lsp_types
|
||||
from .common import RuntimeDependency, RuntimeDependencyCollection
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Intelephense(SolidLanguageServer):
|
||||
"""
|
||||
@@ -37,7 +38,7 @@ class Intelephense(SolidLanguageServer):
|
||||
return super().is_ignored_dirname(dirname) or dirname in self._ignored_dirnames
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(cls, logger: LanguageServerLogger, solidlsp_settings: SolidLSPSettings) -> list[str]:
|
||||
def _setup_runtime_dependencies(cls, solidlsp_settings: SolidLSPSettings) -> list[str]:
|
||||
"""
|
||||
Setup runtime dependencies for Intelephense and return the command to start the server.
|
||||
"""
|
||||
@@ -52,7 +53,7 @@ class Intelephense(SolidLanguageServer):
|
||||
PlatformId.WIN_x64,
|
||||
PlatformId.WIN_arm64,
|
||||
]
|
||||
assert platform_id in valid_platforms, f"Platform {platform_id} is not supported for multilspy PHP at the moment"
|
||||
assert platform_id in valid_platforms, f"Platform {platform_id} is not supported by {cls.__name__} at the moment"
|
||||
|
||||
# Verify both node and npm are installed
|
||||
is_node_installed = shutil.which("node") is not None
|
||||
@@ -74,7 +75,7 @@ class Intelephense(SolidLanguageServer):
|
||||
)
|
||||
]
|
||||
)
|
||||
deps.install(logger, intelephense_ls_dir)
|
||||
deps.install(intelephense_ls_dir)
|
||||
|
||||
assert os.path.exists(
|
||||
intelephense_executable_path
|
||||
@@ -82,19 +83,12 @@ class Intelephense(SolidLanguageServer):
|
||||
|
||||
return [intelephense_executable_path, "--stdio"]
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
# Setup runtime dependencies before initializing
|
||||
intelephense_cmd = self._setup_runtime_dependencies(logger, solidlsp_settings)
|
||||
intelephense_cmd = self._setup_runtime_dependencies(solidlsp_settings)
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=intelephense_cmd, cwd=repository_root_path),
|
||||
"php",
|
||||
solidlsp_settings,
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd=intelephense_cmd, cwd=repository_root_path), "php", solidlsp_settings
|
||||
)
|
||||
self.request_id = 0
|
||||
|
||||
@@ -105,10 +99,7 @@ class Intelephense(SolidLanguageServer):
|
||||
self._ignored_dirnames = {"node_modules", "cache"}
|
||||
if self._custom_settings.get("ignore_vendor", True):
|
||||
self._ignored_dirnames.add("vendor")
|
||||
self.logger.log(
|
||||
f"Ignoring the following directories for PHP projects: {', '.join(sorted(self._ignored_dirnames))}",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info(f"Ignoring the following directories for PHP projects: {', '.join(sorted(self._ignored_dirnames))}")
|
||||
|
||||
def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams:
|
||||
"""
|
||||
@@ -157,7 +148,7 @@ class Intelephense(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(params: dict) -> None:
|
||||
return
|
||||
@@ -167,19 +158,13 @@ class Intelephense(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting Intelephense server process", logging.INFO)
|
||||
log.info("Starting Intelephense server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
self.logger.log(
|
||||
"After sent initialize params",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("After sent initialize params")
|
||||
|
||||
# Verify server capabilities
|
||||
assert "textDocumentSync" in init_response["capabilities"]
|
||||
|
||||
@@ -11,26 +11,24 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class JediServer(SolidLanguageServer):
|
||||
"""
|
||||
Provides Python specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Python.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a JediServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd="jedi-language-server", cwd=repository_root_path),
|
||||
"python",
|
||||
@@ -169,7 +167,7 @@ class JediServer(SolidLanguageServer):
|
||||
self.completions_available.set()
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
self.server.on_request("client/registerCapability", do_nothing)
|
||||
self.server.on_notification("language/status", do_nothing)
|
||||
@@ -180,14 +178,11 @@ class JediServer(SolidLanguageServer):
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
self.server.on_notification("experimental/serverStatus", check_experimental_status)
|
||||
|
||||
self.logger.log("Starting jedi-language-server server process", logging.INFO)
|
||||
log.info("Starting jedi-language-server server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 # type: ignore
|
||||
assert "completionProvider" in init_response["capabilities"]
|
||||
|
||||
@@ -10,25 +10,20 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class JuliaLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
Language server implementation for Julia using LanguageServer.jl.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: LanguageServerConfig,
|
||||
logger: LanguageServerLogger,
|
||||
repository_root_path: str,
|
||||
solidlsp_settings: SolidLSPSettings,
|
||||
):
|
||||
julia_executable = self._setup_runtime_dependency(logger) # PASS LOGGER
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
julia_executable = self._setup_runtime_dependency() # PASS LOGGER
|
||||
julia_code = "using LanguageServer; runserver()"
|
||||
|
||||
julia_ls_cmd: str | list[str]
|
||||
@@ -47,19 +42,14 @@ class JuliaLanguageServer(SolidLanguageServer):
|
||||
f"{shlex.quote(repository_root_path)}"
|
||||
)
|
||||
|
||||
logger.log(f"[JULIA DEBUG] Command: {julia_ls_cmd}", logging.INFO)
|
||||
log.info(f"[JULIA DEBUG] Command: {julia_ls_cmd}")
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=julia_ls_cmd, cwd=repository_root_path),
|
||||
"julia",
|
||||
solidlsp_settings,
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd=julia_ls_cmd, cwd=repository_root_path), "julia", solidlsp_settings
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _setup_runtime_dependency(logger: LanguageServerLogger) -> str:
|
||||
def _setup_runtime_dependency() -> str:
|
||||
"""
|
||||
Check if the Julia runtime is available and return its full path.
|
||||
Raises RuntimeError with a helpful message if the dependency is missing.
|
||||
@@ -94,17 +84,17 @@ class JuliaLanguageServer(SolidLanguageServer):
|
||||
result = subprocess.run(check_cmd, check=False, capture_output=True, text=True, timeout=10)
|
||||
if result.returncode != 0:
|
||||
# LanguageServer.jl not found, install it
|
||||
JuliaLanguageServer._install_language_server(julia_path, logger)
|
||||
JuliaLanguageServer._install_language_server(julia_path)
|
||||
except subprocess.TimeoutExpired:
|
||||
# Assume it needs installation
|
||||
JuliaLanguageServer._install_language_server(julia_path, logger)
|
||||
JuliaLanguageServer._install_language_server(julia_path)
|
||||
|
||||
return julia_path
|
||||
|
||||
@staticmethod
|
||||
def _install_language_server(julia_path: str, logger: LanguageServerLogger) -> None:
|
||||
def _install_language_server(julia_path: str) -> None:
|
||||
"""Install LanguageServer.jl package."""
|
||||
logger.log("LanguageServer.jl not found. Installing... (this may take a minute)", logging.INFO)
|
||||
log.info("LanguageServer.jl not found. Installing... (this may take a minute)")
|
||||
|
||||
install_cmd = [julia_path, "-e", 'using Pkg; Pkg.add("LanguageServer")']
|
||||
|
||||
@@ -112,7 +102,7 @@ class JuliaLanguageServer(SolidLanguageServer):
|
||||
result = subprocess.run(install_cmd, check=False, capture_output=True, text=True, timeout=300) # 5 minutes for installation
|
||||
|
||||
if result.returncode == 0:
|
||||
logger.log("LanguageServer.jl installed successfully!", logging.INFO)
|
||||
log.info("LanguageServer.jl installed successfully!")
|
||||
else:
|
||||
raise RuntimeError(f"Failed to install LanguageServer.jl: {result.stderr}")
|
||||
except subprocess.TimeoutExpired:
|
||||
@@ -158,20 +148,17 @@ class JuliaLanguageServer(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
self.server.on_notification("window/logMessage", window_log_message)
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting LanguageServer.jl server process", logging.INFO)
|
||||
log.info("Starting LanguageServer.jl server process")
|
||||
self.server.start()
|
||||
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
self.logger.log(
|
||||
"Sending initialize request to Julia Language Server",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request to Julia Language Server")
|
||||
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
assert "definitionProvider" in init_response["capabilities"]
|
||||
@@ -180,4 +167,4 @@ class JuliaLanguageServer(SolidLanguageServer):
|
||||
|
||||
self.server.notify.initialized({})
|
||||
self.completions_available.set()
|
||||
self.logger.log("Julia Language Server is initialized and ready.", logging.INFO)
|
||||
log.info("Julia Language Server is initialized and ready.")
|
||||
@@ -11,12 +11,13 @@ from typing import cast
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import FileUtils, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class KotlinRuntimeDependencyPaths:
|
||||
@@ -34,13 +35,11 @@ class KotlinLanguageServer(SolidLanguageServer):
|
||||
Provides Kotlin specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Kotlin.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a Kotlin Language Server instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
runtime_dependency_paths = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
runtime_dependency_paths = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
self.runtime_dependency_paths = runtime_dependency_paths
|
||||
|
||||
# Create command to execute the Kotlin Language Server script
|
||||
@@ -50,18 +49,11 @@ class KotlinLanguageServer(SolidLanguageServer):
|
||||
proc_env = {"JAVA_HOME": self.runtime_dependency_paths.java_home_path}
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=cmd, env=proc_env, cwd=repository_root_path),
|
||||
"kotlin",
|
||||
solidlsp_settings,
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd=cmd, env=proc_env, cwd=repository_root_path), "kotlin", solidlsp_settings
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> KotlinRuntimeDependencyPaths:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> KotlinRuntimeDependencyPaths:
|
||||
"""
|
||||
Setup runtime dependencies for Kotlin Language Server and return the paths.
|
||||
"""
|
||||
@@ -130,8 +122,8 @@ class KotlinLanguageServer(SolidLanguageServer):
|
||||
|
||||
# Download and extract Java if not exists
|
||||
if not os.path.exists(java_path):
|
||||
logger.log(f"Downloading Java for {platform_id.value}...", logging.INFO)
|
||||
FileUtils.download_and_extract_archive(logger, java_dependency["url"], java_dir, java_dependency["archiveType"])
|
||||
log.info(f"Downloading Java for {platform_id.value}...")
|
||||
FileUtils.download_and_extract_archive(java_dependency["url"], java_dir, java_dependency["archiveType"])
|
||||
# Make Java executable
|
||||
if not platform_id.value.startswith("win-"):
|
||||
os.chmod(java_path, 0o755)
|
||||
@@ -149,8 +141,8 @@ class KotlinLanguageServer(SolidLanguageServer):
|
||||
|
||||
# Download and extract Kotlin Language Server if script doesn't exist
|
||||
if not os.path.exists(kotlin_script):
|
||||
logger.log("Downloading Kotlin Language Server...", logging.INFO)
|
||||
FileUtils.download_and_extract_archive(logger, kotlin_dependency["url"], static_dir, kotlin_dependency["archiveType"]) # type: ignore
|
||||
log.info("Downloading Kotlin Language Server...")
|
||||
FileUtils.download_and_extract_archive(kotlin_dependency["url"], static_dir, kotlin_dependency["archiveType"]) # type: ignore
|
||||
|
||||
# Make script executable on Unix platforms
|
||||
if os.path.exists(kotlin_script) and not platform_id.value.startswith("win-"):
|
||||
@@ -161,7 +153,7 @@ class KotlinLanguageServer(SolidLanguageServer):
|
||||
# Use script file
|
||||
if os.path.exists(kotlin_script):
|
||||
kotlin_executable_path = kotlin_script
|
||||
logger.log(f"Using Kotlin Language Server script at {kotlin_script}", logging.INFO)
|
||||
log.info(f"Using Kotlin Language Server script at {kotlin_script}")
|
||||
else:
|
||||
raise FileNotFoundError(f"Kotlin Language Server script not found at {kotlin_script}")
|
||||
|
||||
@@ -435,7 +427,7 @@ class KotlinLanguageServer(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
self.server.on_request("client/registerCapability", do_nothing)
|
||||
self.server.on_notification("language/status", do_nothing)
|
||||
@@ -445,14 +437,11 @@ class KotlinLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
|
||||
self.logger.log("Starting Kotlin server process", logging.INFO)
|
||||
log.info("Starting Kotlin server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
capabilities = init_response["capabilities"]
|
||||
|
||||
@@ -17,11 +17,12 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LuaLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -154,18 +155,11 @@ class LuaLanguageServer(SolidLanguageServer):
|
||||
|
||||
return lua_ls_path
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
lua_ls_path = self._setup_runtime_dependency()
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=lua_ls_path, cwd=repository_root_path),
|
||||
"lua",
|
||||
solidlsp_settings,
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd=lua_ls_path, cwd=repository_root_path), "lua", solidlsp_settings
|
||||
)
|
||||
self.server_ready = threading.Event()
|
||||
self.request_id = 0
|
||||
@@ -263,7 +257,7 @@ class LuaLanguageServer(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(params: dict) -> None:
|
||||
return
|
||||
@@ -273,14 +267,11 @@ class LuaLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting Lua Language Server process", logging.INFO)
|
||||
log.info("Starting Lua Language Server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
# Verify server capabilities
|
||||
|
||||
@@ -12,13 +12,14 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
from .common import RuntimeDependency, RuntimeDependencyCollection
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Marksman(SolidLanguageServer):
|
||||
"""
|
||||
@@ -67,9 +68,7 @@ class Marksman(SolidLanguageServer):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> str:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""Setup runtime dependencies for marksman and return the command to start the server."""
|
||||
deps = cls.runtime_dependencies
|
||||
dependency = deps.get_single_dep_for_current_platform()
|
||||
@@ -77,28 +76,24 @@ class Marksman(SolidLanguageServer):
|
||||
marksman_ls_dir = cls.ls_resources_dir(solidlsp_settings)
|
||||
marksman_executable_path = deps.binary_path(marksman_ls_dir)
|
||||
if not os.path.exists(marksman_executable_path):
|
||||
logger.log(
|
||||
log.info(
|
||||
f"Downloading marksman from {dependency.url} to {marksman_ls_dir}",
|
||||
logging.INFO,
|
||||
)
|
||||
deps.install(logger, marksman_ls_dir)
|
||||
deps.install(marksman_ls_dir)
|
||||
if not os.path.exists(marksman_executable_path):
|
||||
raise FileNotFoundError(f"Download failed? Could not find marksman executable at {marksman_executable_path}")
|
||||
os.chmod(marksman_executable_path, 0o755)
|
||||
return marksman_executable_path
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a Marksman instance. This class is not meant to be instantiated directly.
|
||||
Use LanguageServer.create() instead.
|
||||
"""
|
||||
marksman_executable_path = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
marksman_executable_path = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=f"{marksman_executable_path} server", cwd=repository_root_path),
|
||||
"markdown",
|
||||
@@ -159,7 +154,7 @@ class Marksman(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(_params: dict) -> None:
|
||||
return
|
||||
@@ -169,16 +164,13 @@ class Marksman(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting marksman server process", logging.INFO)
|
||||
log.info("Starting marksman server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to marksman server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to marksman server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
self.logger.log(f"Received initialize response from marksman server: {init_response}", logging.DEBUG)
|
||||
log.debug(f"Received initialize response from marksman server: {init_response}")
|
||||
|
||||
# Verify server capabilities
|
||||
assert "textDocumentSync" in init_response["capabilities"]
|
||||
@@ -188,6 +180,6 @@ class Marksman(SolidLanguageServer):
|
||||
self.server.notify.initialized({})
|
||||
|
||||
# marksman is typically ready immediately after initialization
|
||||
self.logger.log("Marksman server initialization complete", logging.INFO)
|
||||
log.info("Marksman server initialization complete")
|
||||
self.server_ready.set()
|
||||
self.completions_available.set()
|
||||
@@ -19,11 +19,12 @@ from overrides import override
|
||||
from solidlsp import ls_types
|
||||
from solidlsp.ls import DocumentSymbols, LSPFileBuffer, SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NixLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -244,19 +245,10 @@ class NixLanguageServer(SolidLanguageServer):
|
||||
|
||||
return nixd_path
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
nixd_path = self._setup_runtime_dependency()
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=nixd_path, cwd=repository_root_path),
|
||||
"nix",
|
||||
solidlsp_settings,
|
||||
)
|
||||
super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd=nixd_path, cwd=repository_root_path), "nix", solidlsp_settings)
|
||||
self.server_ready = threading.Event()
|
||||
self.request_id = 0
|
||||
|
||||
@@ -358,7 +350,7 @@ class NixLanguageServer(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg):
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(params):
|
||||
return
|
||||
@@ -368,14 +360,11 @@ class NixLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting nixd server process", logging.INFO)
|
||||
log.info("Starting nixd server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
# Verify server capabilities
|
||||
|
||||
@@ -14,12 +14,13 @@ from overrides import override
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_exceptions import SolidLSPException
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import DotnetVersion, FileUtils, PlatformId, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def breadth_first_file_scan(root: str) -> Iterable[str]:
|
||||
"""
|
||||
@@ -59,17 +60,15 @@ class OmniSharp(SolidLanguageServer):
|
||||
Provides C# specific instantiation of the LanguageServer class. Contains various configurations and settings specific to C#.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates an OmniSharp instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
omnisharp_executable_path, dll_path = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
omnisharp_executable_path, dll_path = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
|
||||
slnfilename = find_least_depth_sln_file(repository_root_path)
|
||||
if slnfilename is None:
|
||||
logger.log("No *.sln file found in repository", logging.ERROR)
|
||||
log.error("No *.sln file found in repository")
|
||||
raise SolidLSPException("No SLN file found in repository")
|
||||
|
||||
cmd = " ".join(
|
||||
@@ -104,9 +103,7 @@ class OmniSharp(SolidLanguageServer):
|
||||
"formattingOptions:indentationSize=4",
|
||||
]
|
||||
)
|
||||
super().__init__(
|
||||
config, logger, repository_root_path, ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path), "csharp", solidlsp_settings
|
||||
)
|
||||
super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path), "csharp", solidlsp_settings)
|
||||
|
||||
self.server_ready = threading.Event()
|
||||
self.definition_available = threading.Event()
|
||||
@@ -142,9 +139,7 @@ class OmniSharp(SolidLanguageServer):
|
||||
return d
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> tuple[str, str]:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> tuple[str, str]:
|
||||
"""
|
||||
Setup runtime dependencies for OmniSharp.
|
||||
"""
|
||||
@@ -190,7 +185,7 @@ class OmniSharp(SolidLanguageServer):
|
||||
omnisharp_ls_dir = os.path.join(cls.ls_resources_dir(solidlsp_settings), "OmniSharp")
|
||||
if not os.path.exists(omnisharp_ls_dir):
|
||||
os.makedirs(omnisharp_ls_dir)
|
||||
FileUtils.download_and_extract_archive(logger, runtime_dependencies["OmniSharp"]["url"], omnisharp_ls_dir, "zip")
|
||||
FileUtils.download_and_extract_archive(runtime_dependencies["OmniSharp"]["url"], omnisharp_ls_dir, "zip")
|
||||
omnisharp_executable_path = os.path.join(omnisharp_ls_dir, runtime_dependencies["OmniSharp"]["binaryName"])
|
||||
assert os.path.exists(omnisharp_executable_path)
|
||||
os.chmod(omnisharp_executable_path, 0o755)
|
||||
@@ -198,7 +193,7 @@ class OmniSharp(SolidLanguageServer):
|
||||
razor_omnisharp_ls_dir = os.path.join(cls.ls_resources_dir(solidlsp_settings), "RazorOmnisharp")
|
||||
if not os.path.exists(razor_omnisharp_ls_dir):
|
||||
os.makedirs(razor_omnisharp_ls_dir)
|
||||
FileUtils.download_and_extract_archive(logger, runtime_dependencies["RazorOmnisharp"]["url"], razor_omnisharp_ls_dir, "zip")
|
||||
FileUtils.download_and_extract_archive(runtime_dependencies["RazorOmnisharp"]["url"], razor_omnisharp_ls_dir, "zip")
|
||||
razor_omnisharp_dll_path = os.path.join(razor_omnisharp_ls_dir, runtime_dependencies["RazorOmnisharp"]["dll_path"])
|
||||
assert os.path.exists(razor_omnisharp_dll_path)
|
||||
|
||||
@@ -358,14 +353,11 @@ class OmniSharp(SolidLanguageServer):
|
||||
self.server.on_notification("experimental/serverStatus", check_experimental_status)
|
||||
self.server.on_request("workspace/configuration", workspace_configuration_handler)
|
||||
|
||||
self.logger.log("Starting OmniSharp server process", logging.INFO)
|
||||
log.info("Starting OmniSharp server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
self.server.notify.initialized({})
|
||||
with open(os.path.join(os.path.dirname(__file__), "omnisharp", "workspace_did_change_configuration.json"), encoding="utf-8") as f:
|
||||
|
||||
@@ -15,12 +15,13 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import PlatformId, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import DidChangeConfigurationParams, InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PerlLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -98,19 +99,12 @@ class PerlLanguageServer(SolidLanguageServer):
|
||||
|
||||
return "perl -MPerl::LanguageServer -e 'Perl::LanguageServer::run'"
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
# Setup runtime dependencies before initializing
|
||||
perl_ls_cmd = self._setup_runtime_dependencies()
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=perl_ls_cmd, cwd=repository_root_path),
|
||||
"perl",
|
||||
solidlsp_settings,
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd=perl_ls_cmd, cwd=repository_root_path), "perl", solidlsp_settings
|
||||
)
|
||||
self.request_id = 0
|
||||
|
||||
@@ -156,15 +150,15 @@ class PerlLanguageServer(SolidLanguageServer):
|
||||
def register_capability_handler(params: Any) -> None:
|
||||
return
|
||||
|
||||
def window_log_message(msg: str) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
def window_log_message(msg: dict) -> None:
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(params: Any) -> None:
|
||||
return
|
||||
|
||||
def workspace_configuration_handler(params: Any) -> Any:
|
||||
"""Handle workspace/configuration request from Perl::LanguageServer."""
|
||||
self.logger.log(f"Received workspace/configuration request: {params}", logging.INFO)
|
||||
log.info(f"Received workspace/configuration request: {params}")
|
||||
|
||||
perl_config = {
|
||||
"perlInc": [self.repository_root_path, "."],
|
||||
@@ -180,18 +174,14 @@ class PerlLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting Perl::LanguageServer process", logging.INFO)
|
||||
log.info("Starting Perl::LanguageServer process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
self.logger.log(
|
||||
log.info(
|
||||
"After sent initialize params",
|
||||
logging.INFO,
|
||||
)
|
||||
|
||||
# Verify server capabilities
|
||||
@@ -213,7 +203,7 @@ class PerlLanguageServer(SolidLanguageServer):
|
||||
}
|
||||
}
|
||||
}
|
||||
self.logger.log(f"Sending workspace/didChangeConfiguration notification with config: {perl_config}", logging.INFO)
|
||||
log.info(f"Sending workspace/didChangeConfiguration notification with config: {perl_config}")
|
||||
self.server.notify.workspace_did_change_configuration(perl_config)
|
||||
|
||||
self.completions_available.set()
|
||||
@@ -221,6 +211,6 @@ class PerlLanguageServer(SolidLanguageServer):
|
||||
# Perl::LanguageServer needs time to index files and resolve cross-file references
|
||||
# Without this delay, requests for definitions/references may return empty results
|
||||
settling_time = 0.5
|
||||
self.logger.log(f"Allowing {settling_time} seconds for Perl::LanguageServer to index files...", logging.INFO)
|
||||
log.info(f"Allowing {settling_time} seconds for Perl::LanguageServer to index files...")
|
||||
time.sleep(settling_time)
|
||||
self.logger.log("Perl::LanguageServer settling period complete", logging.INFO)
|
||||
log.info("Perl::LanguageServer settling period complete")
|
||||
@@ -13,11 +13,12 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PyrightServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -25,19 +26,14 @@ class PyrightServer(SolidLanguageServer):
|
||||
Contains various configurations and settings specific to Python.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a PyrightServer instance. This class is not meant to be instantiated directly.
|
||||
Use LanguageServer.create() instead.
|
||||
"""
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
# Note 1: we can also use `pyright-langserver --stdio` but it requires pyright to be installed with npm
|
||||
# Note 2: we can also use `bpyright-langserver --stdio` if we ever are unhappy with pyright
|
||||
ProcessLaunchInfo(cmd="python -m pyright.langserver --stdio", cwd=repository_root_path),
|
||||
"python",
|
||||
solidlsp_settings,
|
||||
@@ -140,12 +136,12 @@ class PyrightServer(SolidLanguageServer):
|
||||
Pyright logs "Found X source files" when it finishes scanning the workspace.
|
||||
"""
|
||||
message_text = msg.get("message", "")
|
||||
self.logger.log(f"LSP: window/logMessage: {message_text}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {message_text}")
|
||||
|
||||
# Look for "Found X source files" which indicates workspace scanning is complete
|
||||
# Unfortunately, pyright is unreliable and there seems to be no better way
|
||||
if re.search(r"Found \d+ source files?", message_text):
|
||||
self.logger.log("Pyright workspace scanning complete", logging.INFO)
|
||||
log.info("Pyright workspace scanning complete")
|
||||
self.found_source_files = True
|
||||
self.analysis_complete.set()
|
||||
self.completions_available.set()
|
||||
@@ -155,7 +151,7 @@ class PyrightServer(SolidLanguageServer):
|
||||
Also listen for experimental/serverStatus as a backup signal
|
||||
"""
|
||||
if params.get("quiescent") == True:
|
||||
self.logger.log("Received experimental/serverStatus with quiescent=true", logging.INFO)
|
||||
log.info("Received experimental/serverStatus with quiescent=true")
|
||||
if not self.found_source_files:
|
||||
self.analysis_complete.set()
|
||||
self.completions_available.set()
|
||||
@@ -170,18 +166,15 @@ class PyrightServer(SolidLanguageServer):
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
self.server.on_notification("experimental/serverStatus", check_experimental_status)
|
||||
|
||||
self.logger.log("Starting pyright-langserver server process", logging.INFO)
|
||||
log.info("Starting pyright-langserver server process")
|
||||
self.server.start()
|
||||
|
||||
# Send proper initialization parameters
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to pyright server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to pyright server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
self.logger.log(f"Received initialize response from pyright server: {init_response}", logging.INFO)
|
||||
log.info(f"Received initialize response from pyright server: {init_response}")
|
||||
|
||||
# Verify that the server supports our required features
|
||||
assert "textDocumentSync" in init_response["capabilities"]
|
||||
@@ -193,11 +186,11 @@ class PyrightServer(SolidLanguageServer):
|
||||
|
||||
# Wait for Pyright to complete its initial workspace analysis
|
||||
# This prevents zombie processes by ensuring background tasks finish
|
||||
self.logger.log("Waiting for Pyright to complete initial workspace analysis...", logging.INFO)
|
||||
log.info("Waiting for Pyright to complete initial workspace analysis...")
|
||||
if self.analysis_complete.wait(timeout=5.0):
|
||||
self.logger.log("Pyright initial analysis complete, server ready", logging.INFO)
|
||||
log.info("Pyright initial analysis complete, server ready")
|
||||
else:
|
||||
self.logger.log("Timeout waiting for Pyright analysis completion, proceeding anyway", logging.WARNING)
|
||||
log.warning("Timeout waiting for Pyright analysis completion, proceeding anyway")
|
||||
# Fallback: assume analysis is complete after timeout
|
||||
self.analysis_complete.set()
|
||||
self.completions_available.set()
|
||||
@@ -9,11 +9,12 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RLanguageServer(SolidLanguageServer):
|
||||
"""R Language Server implementation using the languageserver R package."""
|
||||
@@ -57,9 +58,7 @@ class RLanguageServer(SolidLanguageServer):
|
||||
except FileNotFoundError:
|
||||
raise RuntimeError("R is not installed. Please install R from https://www.r-project.org/")
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
# Check R installation
|
||||
self._check_r_installation()
|
||||
|
||||
@@ -68,14 +67,7 @@ class RLanguageServer(SolidLanguageServer):
|
||||
# Set specific options to improve parsing stability
|
||||
r_cmd = 'R --vanilla --quiet --slave -e "options(languageserver.debug_mode = FALSE); languageserver::run()"'
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=r_cmd, cwd=repository_root_path),
|
||||
"r",
|
||||
solidlsp_settings,
|
||||
)
|
||||
super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd=r_cmd, cwd=repository_root_path), "r", solidlsp_settings)
|
||||
self.server_ready = threading.Event()
|
||||
|
||||
@staticmethod
|
||||
@@ -132,8 +124,8 @@ class RLanguageServer(SolidLanguageServer):
|
||||
def _start_server(self) -> None:
|
||||
"""Start R Language Server process."""
|
||||
|
||||
def window_log_message(msg: str) -> None:
|
||||
self.logger.log(f"R LSP: window/logMessage: {msg}", logging.INFO)
|
||||
def window_log_message(msg: dict) -> None:
|
||||
log.info(f"R LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(params: Any) -> None:
|
||||
return
|
||||
@@ -147,13 +139,12 @@ class RLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting R Language Server process", logging.INFO)
|
||||
log.info("Starting R Language Server process")
|
||||
self.server.start()
|
||||
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
self.logger.log(
|
||||
log.info(
|
||||
"Sending initialize request to R Language Server",
|
||||
logging.INFO,
|
||||
)
|
||||
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
@@ -162,9 +153,9 @@ class RLanguageServer(SolidLanguageServer):
|
||||
capabilities = init_response.get("capabilities", {})
|
||||
assert "textDocumentSync" in capabilities
|
||||
if "completionProvider" in capabilities:
|
||||
self.logger.log("R LSP completion provider available", logging.INFO)
|
||||
log.info("R LSP completion provider available")
|
||||
if "definitionProvider" in capabilities:
|
||||
self.logger.log("R LSP definition provider available", logging.INFO)
|
||||
log.info("R LSP definition provider available")
|
||||
|
||||
self.server.notify.initialized({})
|
||||
self.completions_available.set()
|
||||
|
||||
@@ -9,12 +9,13 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import PathUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RegalLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -28,16 +29,13 @@ class RegalLanguageServer(SolidLanguageServer):
|
||||
def is_ignored_dirname(self, dirname: str) -> bool:
|
||||
return super().is_ignored_dirname(dirname) or dirname in [".regal", ".opa"]
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a RegalLanguageServer instance.
|
||||
|
||||
This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
|
||||
:param config: Language server configuration
|
||||
:param logger: Logger instance
|
||||
:param repository_root_path: Path to the repository root
|
||||
:param solidlsp_settings: Settings for solidlsp
|
||||
"""
|
||||
@@ -50,7 +48,6 @@ class RegalLanguageServer(SolidLanguageServer):
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=f"{regal_executable_path} language-server", cwd=repository_root_path),
|
||||
"rego",
|
||||
@@ -108,7 +105,7 @@ class RegalLanguageServer(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg) -> None: # type: ignore[no-untyped-def]
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(params) -> None: # type: ignore[no-untyped-def]
|
||||
return
|
||||
@@ -118,13 +115,12 @@ class RegalLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting Regal language server process", logging.INFO)
|
||||
log.info("Starting Regal language server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
log.info(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
|
||||
@@ -15,11 +15,12 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams, InitializeResult
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RubyLsp(SolidLanguageServer):
|
||||
"""
|
||||
@@ -27,21 +28,14 @@ class RubyLsp(SolidLanguageServer):
|
||||
Contains various configurations and settings specific to Ruby with modern LSP features.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a RubyLsp instance. This class is not meant to be instantiated directly.
|
||||
Use LanguageServer.create() instead.
|
||||
"""
|
||||
ruby_lsp_executable = self._setup_runtime_dependencies(logger, config, repository_root_path)
|
||||
ruby_lsp_executable = self._setup_runtime_dependencies(config, repository_root_path)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=ruby_lsp_executable, cwd=repository_root_path),
|
||||
"ruby",
|
||||
solidlsp_settings,
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd=ruby_lsp_executable, cwd=repository_root_path), "ruby", solidlsp_settings
|
||||
)
|
||||
self.analysis_complete = threading.Event()
|
||||
self.service_ready_event = threading.Event()
|
||||
@@ -98,7 +92,7 @@ class RubyLsp(SolidLanguageServer):
|
||||
return shutil.which(executable_name)
|
||||
|
||||
@staticmethod
|
||||
def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig, repository_root_path: str) -> list[str]:
|
||||
def _setup_runtime_dependencies(config: LanguageServerConfig, repository_root_path: str) -> list[str]:
|
||||
"""
|
||||
Setup runtime dependencies for ruby-lsp and return the command list to start the server.
|
||||
Installation strategy: Bundler project > global ruby-lsp > gem install ruby-lsp
|
||||
@@ -125,24 +119,23 @@ class RubyLsp(SolidLanguageServer):
|
||||
if use_rbenv:
|
||||
ruby_cmd = ["rbenv", "exec", "ruby"]
|
||||
bundle_cmd = ["rbenv", "exec", "bundle"]
|
||||
logger.log(f"Using rbenv-managed Ruby (found {ruby_version_file})", logging.INFO)
|
||||
log.info(f"Using rbenv-managed Ruby (found {ruby_version_file})")
|
||||
else:
|
||||
ruby_cmd = ["ruby"]
|
||||
bundle_cmd = ["bundle"]
|
||||
if os.path.exists(ruby_version_file):
|
||||
logger.log(
|
||||
log.warning(
|
||||
f"Found {ruby_version_file} but rbenv is not installed. "
|
||||
"Using system Ruby. Consider installing rbenv for better version management: https://github.com/rbenv/rbenv",
|
||||
logging.WARNING,
|
||||
)
|
||||
else:
|
||||
logger.log("No .ruby-version file found, using system Ruby", logging.INFO)
|
||||
log.info("No .ruby-version file found, using system Ruby")
|
||||
|
||||
# Check if Ruby is installed
|
||||
try:
|
||||
result = subprocess.run(ruby_cmd + ["--version"], check=True, capture_output=True, cwd=repository_root_path, text=True)
|
||||
ruby_version = result.stdout.strip()
|
||||
logger.log(f"Ruby version: {ruby_version}", logging.INFO)
|
||||
log.info(f"Ruby version: {ruby_version}")
|
||||
|
||||
# Extract version number for compatibility checks
|
||||
import re
|
||||
@@ -151,7 +144,7 @@ class RubyLsp(SolidLanguageServer):
|
||||
if version_match:
|
||||
major, minor, patch = map(int, version_match.groups())
|
||||
if major < 2 or (major == 2 and minor < 6):
|
||||
logger.log(f"Warning: Ruby {major}.{minor}.{patch} detected. ruby-lsp works best with Ruby 2.6+", logging.WARNING)
|
||||
log.warning(f"Warning: Ruby {major}.{minor}.{patch} detected. ruby-lsp works best with Ruby 2.6+")
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
error_msg = e.stderr if isinstance(e.stderr, str) else e.stderr.decode() if e.stderr else "Unknown error"
|
||||
@@ -173,7 +166,7 @@ class RubyLsp(SolidLanguageServer):
|
||||
is_bundler_project = os.path.exists(gemfile_path)
|
||||
|
||||
if is_bundler_project:
|
||||
logger.log("Detected Bundler project (Gemfile found)", logging.INFO)
|
||||
log.info("Detected Bundler project (Gemfile found)")
|
||||
|
||||
# Check if bundle command is available using Windows-compatible search
|
||||
bundle_path = RubyLsp._find_executable_with_extensions(bundle_cmd[0] if len(bundle_cmd) == 1 else "bundle")
|
||||
@@ -190,9 +183,8 @@ class RubyLsp(SolidLanguageServer):
|
||||
break
|
||||
|
||||
if not bundle_path:
|
||||
logger.log(
|
||||
log.warning(
|
||||
"Bundler project detected but 'bundle' command not found. Falling back to global ruby-lsp installation.",
|
||||
logging.WARNING,
|
||||
)
|
||||
else:
|
||||
# Check if ruby-lsp is in Gemfile.lock
|
||||
@@ -203,29 +195,28 @@ class RubyLsp(SolidLanguageServer):
|
||||
content = f.read()
|
||||
ruby_lsp_in_bundle = "ruby-lsp" in content.lower()
|
||||
except Exception as e:
|
||||
logger.log(f"Warning: Could not read Gemfile.lock: {e}", logging.WARNING)
|
||||
log.warning(f"Warning: Could not read Gemfile.lock: {e}")
|
||||
|
||||
if ruby_lsp_in_bundle:
|
||||
logger.log("Found ruby-lsp in Gemfile.lock", logging.INFO)
|
||||
log.info("Found ruby-lsp in Gemfile.lock")
|
||||
return bundle_cmd + ["exec", "ruby-lsp"]
|
||||
else:
|
||||
logger.log(
|
||||
log.info(
|
||||
"ruby-lsp not found in Gemfile.lock. Consider adding 'gem \"ruby-lsp\"' to your Gemfile for better compatibility.",
|
||||
logging.INFO,
|
||||
)
|
||||
# Fall through to global installation check
|
||||
|
||||
# Check if ruby-lsp is available globally using Windows-compatible search
|
||||
ruby_lsp_path = RubyLsp._find_executable_with_extensions("ruby-lsp")
|
||||
if ruby_lsp_path:
|
||||
logger.log(f"Found ruby-lsp at: {ruby_lsp_path}", logging.INFO)
|
||||
log.info(f"Found ruby-lsp at: {ruby_lsp_path}")
|
||||
return [ruby_lsp_path]
|
||||
|
||||
# Try to install ruby-lsp globally
|
||||
logger.log("ruby-lsp not found, attempting to install globally...", logging.INFO)
|
||||
log.info("ruby-lsp not found, attempting to install globally...")
|
||||
try:
|
||||
subprocess.run(["gem", "install", "ruby-lsp"], check=True, capture_output=True, cwd=repository_root_path)
|
||||
logger.log("Successfully installed ruby-lsp globally", logging.INFO)
|
||||
log.info("Successfully installed ruby-lsp globally")
|
||||
# Find the newly installed ruby-lsp executable
|
||||
ruby_lsp_path = RubyLsp._find_executable_with_extensions("ruby-lsp")
|
||||
return [ruby_lsp_path] if ruby_lsp_path else ["ruby-lsp"]
|
||||
@@ -351,13 +342,13 @@ class RubyLsp(SolidLanguageServer):
|
||||
def register_capability_handler(params: dict) -> None:
|
||||
assert "registrations" in params
|
||||
for registration in params["registrations"]:
|
||||
self.logger.log(f"Registered capability: {registration['method']}", logging.INFO)
|
||||
log.info(f"Registered capability: {registration['method']}")
|
||||
return
|
||||
|
||||
def lang_status_handler(params: dict) -> None:
|
||||
self.logger.log(f"LSP: language/status: {params}", logging.INFO)
|
||||
log.info(f"LSP: language/status: {params}")
|
||||
if params.get("type") == "ready":
|
||||
self.logger.log("ruby-lsp service is ready.", logging.INFO)
|
||||
log.info("ruby-lsp service is ready.")
|
||||
self.analysis_complete.set()
|
||||
self.completions_available.set()
|
||||
|
||||
@@ -368,36 +359,36 @@ class RubyLsp(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def progress_handler(params: dict) -> None:
|
||||
# ruby-lsp sends progress notifications during indexing
|
||||
self.logger.log(f"LSP: $/progress: {params}", logging.DEBUG)
|
||||
log.debug(f"LSP: $/progress: {params}")
|
||||
if "value" in params:
|
||||
value = params["value"]
|
||||
# Check for completion indicators
|
||||
if value.get("kind") == "end":
|
||||
self.logger.log("ruby-lsp indexing complete ($/progress end)", logging.INFO)
|
||||
log.info("ruby-lsp indexing complete ($/progress end)")
|
||||
self.analysis_complete.set()
|
||||
self.completions_available.set()
|
||||
elif value.get("kind") == "begin":
|
||||
self.logger.log("ruby-lsp indexing started ($/progress begin)", logging.INFO)
|
||||
log.info("ruby-lsp indexing started ($/progress begin)")
|
||||
elif "percentage" in value:
|
||||
percentage = value.get("percentage", 0)
|
||||
self.logger.log(f"ruby-lsp indexing progress: {percentage}%", logging.DEBUG)
|
||||
log.debug(f"ruby-lsp indexing progress: {percentage}%")
|
||||
# Handle direct progress format (fallback)
|
||||
elif "token" in params and "value" in params:
|
||||
token = params.get("token")
|
||||
if isinstance(token, str) and "indexing" in token.lower():
|
||||
value = params.get("value", {})
|
||||
if value.get("kind") == "end" or value.get("percentage") == 100:
|
||||
self.logger.log("ruby-lsp indexing complete (token progress)", logging.INFO)
|
||||
log.info("ruby-lsp indexing complete (token progress)")
|
||||
self.analysis_complete.set()
|
||||
self.completions_available.set()
|
||||
|
||||
def window_work_done_progress_create(params: dict) -> dict:
|
||||
"""Handle workDoneProgress/create requests from ruby-lsp"""
|
||||
self.logger.log(f"LSP: window/workDoneProgress/create: {params}", logging.DEBUG)
|
||||
log.debug(f"LSP: window/workDoneProgress/create: {params}")
|
||||
return {}
|
||||
|
||||
self.server.on_request("client/registerCapability", register_capability_handler)
|
||||
@@ -408,17 +399,14 @@ class RubyLsp(SolidLanguageServer):
|
||||
self.server.on_request("window/workDoneProgress/create", window_work_done_progress_create)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting ruby-lsp server process", logging.INFO)
|
||||
log.info("Starting ruby-lsp server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params()
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
self.logger.log(f"Sending init params: {json.dumps(initialize_params, indent=4)}", logging.INFO)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
log.info(f"Sending init params: {json.dumps(initialize_params, indent=4)}")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
self.logger.log(f"Received init response: {init_response}", logging.INFO)
|
||||
log.info(f"Received init response: {init_response}")
|
||||
|
||||
# Verify expected capabilities
|
||||
# Note: ruby-lsp may return textDocumentSync in different formats (number or object)
|
||||
@@ -434,11 +422,11 @@ class RubyLsp(SolidLanguageServer):
|
||||
self.server.notify.initialized({})
|
||||
# Wait for ruby-lsp to complete its initial indexing
|
||||
# ruby-lsp has fast indexing
|
||||
self.logger.log("Waiting for ruby-lsp to complete initial indexing...", logging.INFO)
|
||||
log.info("Waiting for ruby-lsp to complete initial indexing...")
|
||||
if self.analysis_complete.wait(timeout=30.0):
|
||||
self.logger.log("ruby-lsp initial indexing complete, server ready", logging.INFO)
|
||||
log.info("ruby-lsp initial indexing complete, server ready")
|
||||
else:
|
||||
self.logger.log("Timeout waiting for ruby-lsp indexing completion, proceeding anyway", logging.WARNING)
|
||||
log.warning("Timeout waiting for ruby-lsp indexing completion, proceeding anyway")
|
||||
# Fallback: assume indexing is complete after timeout
|
||||
self.analysis_complete.set()
|
||||
self.completions_available.set()
|
||||
@@ -472,7 +460,7 @@ class RubyLsp(SolidLanguageServer):
|
||||
|
||||
for cap in important_capabilities:
|
||||
if cap in capabilities:
|
||||
self.logger.log(f"ruby-lsp {cap}: available", logging.DEBUG)
|
||||
log.debug(f"ruby-lsp {cap}: available")
|
||||
|
||||
# Signal that the service is ready
|
||||
self.service_ready_event.set()
|
||||
@@ -13,11 +13,12 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RustAnalyzer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -95,18 +96,15 @@ class RustAnalyzer(SolidLanguageServer):
|
||||
|
||||
return path
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a RustAnalyzer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
rustanalyzer_executable_path = self._ensure_rust_analyzer_installed()
|
||||
logger.log(f"Using rust-analyzer at: {rustanalyzer_executable_path}", logging.INFO)
|
||||
log.info(f"Using rust-analyzer at: {rustanalyzer_executable_path}")
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=rustanalyzer_executable_path, cwd=repository_root_path),
|
||||
"rust",
|
||||
@@ -626,7 +624,7 @@ class RustAnalyzer(SolidLanguageServer):
|
||||
self.server_ready.set()
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
self.server.on_request("client/registerCapability", register_capability_handler)
|
||||
self.server.on_notification("language/status", lang_status_handler)
|
||||
@@ -637,14 +635,11 @@ class RustAnalyzer(SolidLanguageServer):
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
self.server.on_notification("experimental/serverStatus", check_experimental_status)
|
||||
|
||||
self.logger.log("Starting RustAnalyzer server process", logging.INFO)
|
||||
log.info("Starting RustAnalyzer server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 # type: ignore
|
||||
assert "completionProvider" in init_response["capabilities"]
|
||||
|
||||
@@ -12,7 +12,6 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
@@ -22,21 +21,21 @@ if not PlatformUtils.get_platform_id().value.startswith("win"):
|
||||
pass
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ScalaLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
Provides Scala specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Scala.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a ScalaLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
scala_lsp_executable_path = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
scala_lsp_executable_path = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=scala_lsp_executable_path, cwd=repository_root_path),
|
||||
config.code_language.value,
|
||||
@@ -52,9 +51,7 @@ class ScalaLanguageServer(SolidLanguageServer):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> list[str]:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> list[str]:
|
||||
"""
|
||||
Setup runtime dependencies for Scala Language Server and return the command to start the server.
|
||||
"""
|
||||
@@ -69,9 +66,9 @@ class ScalaLanguageServer(SolidLanguageServer):
|
||||
|
||||
if not os.path.exists(metals_executable):
|
||||
if not cs_command_path:
|
||||
logger.log("'cs' command not found. Trying to install it using 'coursier'.", logging.INFO)
|
||||
log.info("'cs' command not found. Trying to install it using 'coursier'.")
|
||||
try:
|
||||
logger.log("Running 'coursier setup --yes' to install 'cs'...", logging.INFO)
|
||||
log.info("Running 'coursier setup --yes' to install 'cs'...")
|
||||
subprocess.run([coursier_command_path, "setup", "--yes"], check=True, capture_output=True, text=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise RuntimeError(f"Failed to set up 'cs' command with 'coursier setup'. Stderr: {e.stderr}")
|
||||
@@ -81,9 +78,9 @@ class ScalaLanguageServer(SolidLanguageServer):
|
||||
raise RuntimeError(
|
||||
"'cs' command not found after running 'coursier setup'. Please check your PATH or install it manually."
|
||||
)
|
||||
logger.log("'cs' command installed successfully.", logging.INFO)
|
||||
log.info("'cs' command installed successfully.")
|
||||
|
||||
logger.log(f"metals executable not found at {metals_executable}, bootstrapping...", logging.INFO)
|
||||
log.info(f"metals executable not found at {metals_executable}, bootstrapping...")
|
||||
artifact = "org.scalameta:metals_2.13:1.6.2"
|
||||
cmd = [
|
||||
cs_command_path,
|
||||
@@ -103,9 +100,9 @@ class ScalaLanguageServer(SolidLanguageServer):
|
||||
metals_executable,
|
||||
"-f",
|
||||
]
|
||||
logger.log("Bootstrapping metals...", logging.INFO)
|
||||
log.info("Bootstrapping metals...")
|
||||
subprocess.run(cmd, cwd=metals_home, check=True)
|
||||
logger.log("Bootstrapping metals finished.", logging.INFO)
|
||||
log.info("Bootstrapping metals finished.")
|
||||
return [metals_executable]
|
||||
|
||||
@staticmethod
|
||||
@@ -159,13 +156,10 @@ class ScalaLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
Starts the Scala Language Server
|
||||
"""
|
||||
self.logger.log("Starting Scala server process", logging.INFO)
|
||||
log.info("Starting Scala server process")
|
||||
self.server.start()
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
self.server.send.initialize(initialize_params)
|
||||
|
||||
@@ -16,11 +16,12 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Solargraph(SolidLanguageServer):
|
||||
"""
|
||||
@@ -28,17 +29,14 @@ class Solargraph(SolidLanguageServer):
|
||||
Contains various configurations and settings specific to Ruby.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a Solargraph instance. This class is not meant to be instantiated directly.
|
||||
Use LanguageServer.create() instead.
|
||||
"""
|
||||
solargraph_executable_path = self._setup_runtime_dependencies(logger, config, repository_root_path)
|
||||
solargraph_executable_path = self._setup_runtime_dependencies(config, repository_root_path)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=f"{solargraph_executable_path} stdio", cwd=repository_root_path),
|
||||
"ruby",
|
||||
@@ -73,7 +71,7 @@ class Solargraph(SolidLanguageServer):
|
||||
return super().is_ignored_dirname(dirname) or dirname in ruby_ignored_dirs
|
||||
|
||||
@staticmethod
|
||||
def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig, repository_root_path: str) -> str:
|
||||
def _setup_runtime_dependencies(config: LanguageServerConfig, repository_root_path: str) -> str:
|
||||
"""
|
||||
Setup runtime dependencies for Solargraph and return the command to start the server.
|
||||
"""
|
||||
@@ -81,14 +79,14 @@ class Solargraph(SolidLanguageServer):
|
||||
try:
|
||||
result = subprocess.run(["ruby", "--version"], check=True, capture_output=True, cwd=repository_root_path, text=True)
|
||||
ruby_version = result.stdout.strip()
|
||||
logger.log(f"Ruby version: {ruby_version}", logging.INFO)
|
||||
log.info(f"Ruby version: {ruby_version}")
|
||||
|
||||
# Extract version number for compatibility checks
|
||||
version_match = re.search(r"ruby (\d+)\.(\d+)\.(\d+)", ruby_version)
|
||||
if version_match:
|
||||
major, minor, patch = map(int, version_match.groups())
|
||||
if major < 2 or (major == 2 and minor < 6):
|
||||
logger.log(f"Warning: Ruby {major}.{minor}.{patch} detected. Solargraph works best with Ruby 2.6+", logging.WARNING)
|
||||
log.warning(f"Warning: Ruby {major}.{minor}.{patch} detected. Solargraph works best with Ruby 2.6+")
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
error_msg = e.stderr.decode() if e.stderr else "Unknown error"
|
||||
@@ -124,7 +122,7 @@ class Solargraph(SolidLanguageServer):
|
||||
is_bundler_project = os.path.exists(gemfile_path)
|
||||
|
||||
if is_bundler_project:
|
||||
logger.log("Detected Bundler project (Gemfile found)", logging.INFO)
|
||||
log.info("Detected Bundler project (Gemfile found)")
|
||||
|
||||
# Check if bundle command is available
|
||||
bundle_path = find_executable_with_extensions("bundle")
|
||||
@@ -155,15 +153,14 @@ class Solargraph(SolidLanguageServer):
|
||||
content = f.read()
|
||||
solargraph_in_bundle = "solargraph" in content.lower()
|
||||
except Exception as e:
|
||||
logger.log(f"Warning: Could not read Gemfile.lock: {e}", logging.WARNING)
|
||||
log.warning(f"Warning: Could not read Gemfile.lock: {e}")
|
||||
|
||||
if solargraph_in_bundle:
|
||||
logger.log("Found solargraph in Gemfile.lock", logging.INFO)
|
||||
log.info("Found solargraph in Gemfile.lock")
|
||||
return f"{bundle_path} exec solargraph"
|
||||
else:
|
||||
logger.log(
|
||||
log.warning(
|
||||
"solargraph not found in Gemfile.lock. Please add 'gem \"solargraph\"' to your Gemfile and run 'bundle install'",
|
||||
logging.WARNING,
|
||||
)
|
||||
# Fall through to global installation check
|
||||
|
||||
@@ -171,7 +168,7 @@ class Solargraph(SolidLanguageServer):
|
||||
# First, try to find solargraph in PATH (includes asdf shims) with Windows support
|
||||
solargraph_path = find_executable_with_extensions("solargraph")
|
||||
if solargraph_path:
|
||||
logger.log(f"Found solargraph at: {solargraph_path}", logging.INFO)
|
||||
log.info(f"Found solargraph at: {solargraph_path}")
|
||||
return solargraph_path
|
||||
|
||||
# Fallback to gem exec (for non-Bundler projects or when global solargraph not found)
|
||||
@@ -191,7 +188,7 @@ class Solargraph(SolidLanguageServer):
|
||||
["gem", "list", "^solargraph$", "-i"], check=False, capture_output=True, text=True, cwd=repository_root_path
|
||||
)
|
||||
if result.stdout.strip() == "false":
|
||||
logger.log("Installing Solargraph...", logging.INFO)
|
||||
log.info("Installing Solargraph...")
|
||||
subprocess.run(dependency["installCommand"].split(), check=True, capture_output=True, cwd=repository_root_path)
|
||||
|
||||
return "gem exec solargraph"
|
||||
@@ -319,9 +316,9 @@ class Solargraph(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def lang_status_handler(params: dict) -> None:
|
||||
self.logger.log(f"LSP: language/status: {params}", logging.INFO)
|
||||
log.info(f"LSP: language/status: {params}")
|
||||
if params.get("type") == "ServiceReady" and params.get("message") == "Service is ready.":
|
||||
self.logger.log("Solargraph service is ready.", logging.INFO)
|
||||
log.info("Solargraph service is ready.")
|
||||
self.analysis_complete.set()
|
||||
self.completions_available.set()
|
||||
|
||||
@@ -332,7 +329,7 @@ class Solargraph(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
self.server.on_request("client/registerCapability", register_capability_handler)
|
||||
self.server.on_notification("language/status", lang_status_handler)
|
||||
@@ -342,17 +339,14 @@ class Solargraph(SolidLanguageServer):
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
self.server.on_notification("language/actionableNotification", do_nothing)
|
||||
|
||||
self.logger.log("Starting solargraph server process", logging.INFO)
|
||||
log.info("Starting solargraph server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
self.logger.log(f"Sending init params: {json.dumps(initialize_params, indent=4)}", logging.INFO)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
log.info(f"Sending init params: {json.dumps(initialize_params, indent=4)}")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
self.logger.log(f"Received init response: {init_response}", logging.INFO)
|
||||
log.info(f"Received init response: {init_response}")
|
||||
assert init_response["capabilities"]["textDocumentSync"] == 2
|
||||
assert "completionProvider" in init_response["capabilities"]
|
||||
assert init_response["capabilities"]["completionProvider"] == {
|
||||
@@ -363,11 +357,11 @@ class Solargraph(SolidLanguageServer):
|
||||
|
||||
# Wait for Solargraph to complete its initial workspace analysis
|
||||
# This prevents issues by ensuring background tasks finish
|
||||
self.logger.log("Waiting for Solargraph to complete initial workspace analysis...", logging.INFO)
|
||||
log.info("Waiting for Solargraph to complete initial workspace analysis...")
|
||||
if self.analysis_complete.wait(timeout=60.0):
|
||||
self.logger.log("Solargraph initial analysis complete, server ready", logging.INFO)
|
||||
log.info("Solargraph initial analysis complete, server ready")
|
||||
else:
|
||||
self.logger.log("Timeout waiting for Solargraph analysis completion, proceeding anyway", logging.WARNING)
|
||||
log.warning("Timeout waiting for Solargraph analysis completion, proceeding anyway")
|
||||
# Fallback: assume analysis is complete after timeout
|
||||
self.analysis_complete.set()
|
||||
self.completions_available.set()
|
||||
@@ -10,11 +10,12 @@ from overrides import override
|
||||
from solidlsp import ls_types
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SourceKitLSP(SolidLanguageServer):
|
||||
"""
|
||||
@@ -45,19 +46,12 @@ class SourceKitLSP(SolidLanguageServer):
|
||||
"And make sure it is available on your PATH."
|
||||
) from e
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
sourcekit_version = self._get_sourcekit_lsp_version()
|
||||
logger.log(f"Starting sourcekit lsp with version: {sourcekit_version}", logging.INFO)
|
||||
log.info(f"Starting sourcekit lsp with version: {sourcekit_version}")
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd="sourcekit-lsp", cwd=repository_root_path),
|
||||
"swift",
|
||||
solidlsp_settings,
|
||||
config, repository_root_path, ProcessLaunchInfo(cmd="sourcekit-lsp", cwd=repository_root_path), "swift", solidlsp_settings
|
||||
)
|
||||
self.server_ready = threading.Event()
|
||||
self.request_id = 0
|
||||
@@ -314,7 +308,7 @@ class SourceKitLSP(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(_params: dict) -> None:
|
||||
return
|
||||
@@ -324,18 +318,15 @@ class SourceKitLSP(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting sourcekit-lsp server process", logging.INFO)
|
||||
log.info("Starting sourcekit-lsp server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
capabilities = init_response["capabilities"]
|
||||
self.logger.log(f"SourceKit LSP capabilities: {list(capabilities.keys())}", logging.INFO)
|
||||
log.info(f"SourceKit LSP capabilities: {list(capabilities.keys())}")
|
||||
|
||||
assert "textDocumentSync" in capabilities, "textDocumentSync capability missing"
|
||||
assert "definitionProvider" in capabilities, "definitionProvider capability missing"
|
||||
@@ -366,10 +357,7 @@ class SourceKitLSP(SolidLanguageServer):
|
||||
# Fallback if initialization timestamp is missing
|
||||
remaining_delay = 15 if os.getenv("CI") else 5
|
||||
|
||||
self.logger.log(
|
||||
f"Sleeping {remaining_delay:.1f}s before requesting references for the first time (CI needs extra indexing time)",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info(f"Sleeping {remaining_delay:.1f}s before requesting references for the first time (CI needs extra indexing time)")
|
||||
time.sleep(remaining_delay)
|
||||
self._did_sleep_before_requesting_references = True
|
||||
|
||||
@@ -378,7 +366,7 @@ class SourceKitLSP(SolidLanguageServer):
|
||||
|
||||
# In CI, if no references found, retry once after additional delay
|
||||
if os.getenv("CI") and not references:
|
||||
self.logger.log("No references found in CI - retrying after additional 5s delay", logging.INFO)
|
||||
log.info("No references found in CI - retrying after additional 5s delay")
|
||||
time.sleep(5)
|
||||
references = super().request_references(relative_file_path, line, column)
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import PathUtils, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
@@ -16,6 +15,8 @@ from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
from .common import RuntimeDependency, RuntimeDependencyCollection
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TerraformLS(SolidLanguageServer):
|
||||
"""
|
||||
@@ -55,13 +56,13 @@ class TerraformLS(SolidLanguageServer):
|
||||
return SolidLanguageServer._determine_log_level(line)
|
||||
|
||||
@staticmethod
|
||||
def _ensure_tf_command_available(logger: LanguageServerLogger) -> None:
|
||||
logger.log("Starting terraform version detection...", logging.DEBUG)
|
||||
def _ensure_tf_command_available() -> None:
|
||||
log.debug("Starting terraform version detection...")
|
||||
|
||||
# 1. Try to find terraform using shutil.which
|
||||
terraform_cmd = shutil.which("terraform")
|
||||
if terraform_cmd is not None:
|
||||
logger.log(f"Found terraform via shutil.which: {terraform_cmd}", logging.DEBUG)
|
||||
log.debug(f"Found terraform via shutil.which: {terraform_cmd}")
|
||||
return
|
||||
|
||||
# TODO: is this needed?
|
||||
@@ -69,7 +70,7 @@ class TerraformLS(SolidLanguageServer):
|
||||
if not terraform_cmd:
|
||||
terraform_cli_path = os.environ.get("TERRAFORM_CLI_PATH")
|
||||
if terraform_cli_path:
|
||||
logger.log(f"Trying TERRAFORM_CLI_PATH: {terraform_cli_path}", logging.DEBUG)
|
||||
log.debug(f"Trying TERRAFORM_CLI_PATH: {terraform_cli_path}")
|
||||
# TODO: use binary name from runtime dependencies if we keep this code
|
||||
if os.name == "nt":
|
||||
terraform_binary = os.path.join(terraform_cli_path, "terraform.exe")
|
||||
@@ -77,7 +78,7 @@ class TerraformLS(SolidLanguageServer):
|
||||
terraform_binary = os.path.join(terraform_cli_path, "terraform")
|
||||
if os.path.exists(terraform_binary):
|
||||
terraform_cmd = terraform_binary
|
||||
logger.log(f"Found terraform via TERRAFORM_CLI_PATH: {terraform_cmd}", logging.DEBUG)
|
||||
log.debug(f"Found terraform via TERRAFORM_CLI_PATH: {terraform_cmd}")
|
||||
return
|
||||
|
||||
raise RuntimeError(
|
||||
@@ -86,12 +87,12 @@ class TerraformLS(SolidLanguageServer):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(cls, logger: LanguageServerLogger, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
def _setup_runtime_dependencies(cls, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""
|
||||
Setup runtime dependencies for terraform-ls.
|
||||
Downloads and installs terraform-ls if not already present.
|
||||
"""
|
||||
cls._ensure_tf_command_available(logger)
|
||||
cls._ensure_tf_command_available()
|
||||
platform_id = PlatformUtils.get_platform_id()
|
||||
deps = RuntimeDependencyCollection(
|
||||
[
|
||||
@@ -141,8 +142,8 @@ class TerraformLS(SolidLanguageServer):
|
||||
|
||||
terraform_ls_executable_path = deps.binary_path(cls.ls_resources_dir(solidlsp_settings))
|
||||
if not os.path.exists(terraform_ls_executable_path):
|
||||
logger.log(f"Downloading terraform-ls from {dependency.url}", logging.INFO)
|
||||
deps.install(logger, cls.ls_resources_dir(solidlsp_settings))
|
||||
log.info(f"Downloading terraform-ls from {dependency.url}")
|
||||
deps.install(cls.ls_resources_dir(solidlsp_settings))
|
||||
|
||||
assert os.path.exists(terraform_ls_executable_path), f"terraform-ls executable not found at {terraform_ls_executable_path}"
|
||||
|
||||
@@ -152,17 +153,14 @@ class TerraformLS(SolidLanguageServer):
|
||||
|
||||
return terraform_ls_executable_path
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a TerraformLS instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
terraform_ls_executable_path = self._setup_runtime_dependencies(logger, solidlsp_settings)
|
||||
terraform_ls_executable_path = self._setup_runtime_dependencies(solidlsp_settings)
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=f"{terraform_ls_executable_path} serve", cwd=repository_root_path),
|
||||
"terraform",
|
||||
@@ -211,7 +209,7 @@ class TerraformLS(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(params: dict) -> None:
|
||||
return
|
||||
@@ -221,14 +219,11 @@ class TerraformLS(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting terraform-ls server process", logging.INFO)
|
||||
log.info("Starting terraform-ls server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
# Verify server capabilities
|
||||
|
||||
@@ -14,7 +14,6 @@ from sensai.util.logging import LogTime
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import PlatformId, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
@@ -22,6 +21,8 @@ from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
from .common import RuntimeDependency, RuntimeDependencyCollection
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Platform-specific imports
|
||||
if os.name != "nt": # Unix-like systems
|
||||
import pwd
|
||||
@@ -43,16 +44,13 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
Provides TypeScript specific instantiation of the LanguageServer class. Contains various configurations and settings specific to TypeScript.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a TypeScriptLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
ts_lsp_executable_path = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
ts_lsp_executable_path = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=ts_lsp_executable_path, cwd=repository_root_path),
|
||||
"typescript",
|
||||
@@ -76,9 +74,7 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
return SolidLanguageServer._determine_log_level(line)
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> list[str]:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> list[str]:
|
||||
"""
|
||||
Setup runtime dependencies for TypeScript Language Server and return the command to start the server.
|
||||
"""
|
||||
@@ -128,9 +124,9 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
tsserver_ls_dir = os.path.join(cls.ls_resources_dir(solidlsp_settings), "ts-lsp")
|
||||
tsserver_executable_path = os.path.join(tsserver_ls_dir, "node_modules", ".bin", "typescript-language-server")
|
||||
if not os.path.exists(tsserver_executable_path):
|
||||
logger.log(f"Typescript Language Server executable not found at {tsserver_executable_path}. Installing...", logging.INFO)
|
||||
with LogTime("Installation of TypeScript language server dependencies", logger=logger.logger):
|
||||
deps.install(logger, tsserver_ls_dir)
|
||||
log.info(f"Typescript Language Server executable not found at {tsserver_executable_path}. Installing...")
|
||||
with LogTime("Installation of TypeScript language server dependencies", logger=log):
|
||||
deps.install(tsserver_ls_dir)
|
||||
|
||||
if not os.path.exists(tsserver_executable_path):
|
||||
raise FileNotFoundError(
|
||||
@@ -210,7 +206,7 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def check_experimental_status(params: dict) -> None:
|
||||
"""
|
||||
@@ -227,13 +223,12 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
self.server.on_notification("experimental/serverStatus", check_experimental_status)
|
||||
|
||||
self.logger.log("Starting TypeScript server process", logging.INFO)
|
||||
log.info("Starting TypeScript server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
log.info(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
@@ -247,9 +242,9 @@ class TypeScriptLanguageServer(SolidLanguageServer):
|
||||
|
||||
self.server.notify.initialized({})
|
||||
if self.server_ready.wait(timeout=1.0):
|
||||
self.logger.log("TypeScript server is ready", logging.INFO)
|
||||
log.info("TypeScript server is ready")
|
||||
else:
|
||||
self.logger.log("Timeout waiting for TypeScript server to become ready, proceeding anyway", logging.INFO)
|
||||
log.info("Timeout waiting for TypeScript server to become ready, proceeding anyway")
|
||||
# Fallback: assume server is ready after timeout
|
||||
self.server_ready.set()
|
||||
self.completions_available.set()
|
||||
|
||||
@@ -15,7 +15,6 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import PlatformId, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
@@ -23,6 +22,8 @@ from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
from .common import RuntimeDependency, RuntimeDependencyCollection
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VtsLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -30,16 +31,13 @@ class VtsLanguageServer(SolidLanguageServer):
|
||||
Contains various configurations and settings specific to TypeScript via vtsls wrapper.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a VtsLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead.
|
||||
"""
|
||||
vts_lsp_executable_path = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
vts_lsp_executable_path = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=vts_lsp_executable_path, cwd=repository_root_path),
|
||||
"typescript",
|
||||
@@ -58,9 +56,7 @@ class VtsLanguageServer(SolidLanguageServer):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> str:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""
|
||||
Setup runtime dependencies for VTS Language Server and return the command to start the server.
|
||||
"""
|
||||
@@ -99,7 +95,7 @@ class VtsLanguageServer(SolidLanguageServer):
|
||||
# Install vtsls if not already installed
|
||||
if not os.path.exists(vts_ls_dir):
|
||||
os.makedirs(vts_ls_dir, exist_ok=True)
|
||||
deps.install(logger, vts_ls_dir)
|
||||
deps.install(vts_ls_dir)
|
||||
|
||||
vts_executable_path = os.path.join(vts_ls_dir, "node_modules", ".bin", "vtsls")
|
||||
|
||||
@@ -182,7 +178,7 @@ class VtsLanguageServer(SolidLanguageServer):
|
||||
return
|
||||
|
||||
def window_log_message(msg: dict) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def check_experimental_status(params: dict) -> None:
|
||||
"""
|
||||
@@ -200,33 +196,30 @@ class VtsLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
self.server.on_notification("experimental/serverStatus", check_experimental_status)
|
||||
|
||||
self.logger.log("Starting VTS server process", logging.INFO)
|
||||
log.info("Starting VTS server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
# VTS-specific capability checks
|
||||
# Be more flexible with capabilities since vtsls might have different structure
|
||||
self.logger.log(f"VTS init response capabilities: {init_response['capabilities']}", logging.DEBUG)
|
||||
log.debug(f"VTS init response capabilities: {init_response['capabilities']}")
|
||||
|
||||
# Basic checks to ensure essential capabilities are present
|
||||
assert "textDocumentSync" in init_response["capabilities"]
|
||||
assert "completionProvider" in init_response["capabilities"]
|
||||
|
||||
# Log the actual values for debugging
|
||||
self.logger.log(f"textDocumentSync: {init_response['capabilities']['textDocumentSync']}", logging.DEBUG)
|
||||
self.logger.log(f"completionProvider: {init_response['capabilities']['completionProvider']}", logging.DEBUG)
|
||||
log.debug(f"textDocumentSync: {init_response['capabilities']['textDocumentSync']}")
|
||||
log.debug(f"completionProvider: {init_response['capabilities']['completionProvider']}")
|
||||
|
||||
self.server.notify.initialized({})
|
||||
if self.server_ready.wait(timeout=1.0):
|
||||
self.logger.log("VTS server is ready", logging.INFO)
|
||||
log.info("VTS server is ready")
|
||||
else:
|
||||
self.logger.log("Timeout waiting for VTS server to become ready, proceeding anyway", logging.INFO)
|
||||
log.info("Timeout waiting for VTS server to become ready, proceeding anyway")
|
||||
# Fallback: assume server is ready after timeout
|
||||
self.server_ready.set()
|
||||
self.completions_available.set()
|
||||
|
||||
@@ -13,11 +13,12 @@ from typing import Any
|
||||
from solidlsp.language_servers.common import RuntimeDependency, RuntimeDependencyCollection
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class YamlLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -41,17 +42,14 @@ class YamlLanguageServer(SolidLanguageServer):
|
||||
|
||||
return SolidLanguageServer._determine_log_level(line)
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
"""
|
||||
Creates a YamlLanguageServer instance. This class is not meant to be instantiated directly.
|
||||
Use LanguageServer.create() instead.
|
||||
"""
|
||||
yaml_lsp_executable_path = self._setup_runtime_dependencies(logger, config, solidlsp_settings)
|
||||
yaml_lsp_executable_path = self._setup_runtime_dependencies(config, solidlsp_settings)
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd=yaml_lsp_executable_path, cwd=repository_root_path),
|
||||
"yaml",
|
||||
@@ -60,9 +58,7 @@ class YamlLanguageServer(SolidLanguageServer):
|
||||
self.server_ready = threading.Event()
|
||||
|
||||
@classmethod
|
||||
def _setup_runtime_dependencies(
|
||||
cls, logger: LanguageServerLogger, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings
|
||||
) -> str:
|
||||
def _setup_runtime_dependencies(cls, config: LanguageServerConfig, solidlsp_settings: SolidLSPSettings) -> str:
|
||||
"""
|
||||
Setup runtime dependencies for YAML Language Server and return the command to start the server.
|
||||
"""
|
||||
@@ -92,9 +88,9 @@ class YamlLanguageServer(SolidLanguageServer):
|
||||
yaml_executable_path += ".cmd"
|
||||
|
||||
if not os.path.exists(yaml_executable_path):
|
||||
logger.log(f"YAML Language Server executable not found at {yaml_executable_path}. Installing...", logging.INFO)
|
||||
deps.install(logger, yaml_ls_dir)
|
||||
logger.log("YAML language server dependencies installed successfully", logging.INFO)
|
||||
log.info(f"YAML Language Server executable not found at {yaml_executable_path}. Installing...")
|
||||
deps.install(yaml_ls_dir)
|
||||
log.info("YAML language server dependencies installed successfully")
|
||||
|
||||
if not os.path.exists(yaml_executable_path):
|
||||
raise FileNotFoundError(
|
||||
@@ -162,34 +158,31 @@ class YamlLanguageServer(SolidLanguageServer):
|
||||
def do_nothing(params: Any) -> None:
|
||||
return
|
||||
|
||||
def window_log_message(msg: str) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
def window_log_message(msg: dict) -> None:
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
self.server.on_request("client/registerCapability", register_capability_handler)
|
||||
self.server.on_notification("window/logMessage", window_log_message)
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting YAML server process", logging.INFO)
|
||||
log.info("Starting YAML server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
self.logger.log(f"Received initialize response from YAML server: {init_response}", logging.DEBUG)
|
||||
log.debug(f"Received initialize response from YAML server: {init_response}")
|
||||
|
||||
# Verify document symbol support is available
|
||||
if "documentSymbolProvider" in init_response["capabilities"]:
|
||||
self.logger.log("YAML server supports document symbols", logging.INFO)
|
||||
log.info("YAML server supports document symbols")
|
||||
else:
|
||||
self.logger.log("Warning: YAML server does not report document symbol support", logging.WARNING)
|
||||
log.warning("Warning: YAML server does not report document symbol support")
|
||||
|
||||
self.server.notify.initialized({})
|
||||
|
||||
# YAML language server is ready immediately after initialization
|
||||
self.logger.log("YAML server initialization complete", logging.INFO)
|
||||
log.info("YAML server initialization complete")
|
||||
self.server_ready.set()
|
||||
self.completions_available.set()
|
||||
@@ -14,11 +14,12 @@ from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ZigLanguageServer(SolidLanguageServer):
|
||||
"""
|
||||
@@ -95,19 +96,10 @@ class ZigLanguageServer(SolidLanguageServer):
|
||||
|
||||
return True
|
||||
|
||||
def __init__(
|
||||
self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, solidlsp_settings: SolidLSPSettings
|
||||
):
|
||||
def __init__(self, config: LanguageServerConfig, repository_root_path: str, solidlsp_settings: SolidLSPSettings):
|
||||
self._setup_runtime_dependency()
|
||||
|
||||
super().__init__(
|
||||
config,
|
||||
logger,
|
||||
repository_root_path,
|
||||
ProcessLaunchInfo(cmd="zls", cwd=repository_root_path),
|
||||
"zig",
|
||||
solidlsp_settings,
|
||||
)
|
||||
super().__init__(config, repository_root_path, ProcessLaunchInfo(cmd="zls", cwd=repository_root_path), "zig", solidlsp_settings)
|
||||
self.server_ready = threading.Event()
|
||||
self.request_id = 0
|
||||
|
||||
@@ -196,8 +188,8 @@ class ZigLanguageServer(SolidLanguageServer):
|
||||
def register_capability_handler(params: dict) -> None:
|
||||
return
|
||||
|
||||
def window_log_message(msg: str) -> None:
|
||||
self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO)
|
||||
def window_log_message(msg: dict) -> None:
|
||||
log.info(f"LSP: window/logMessage: {msg}")
|
||||
|
||||
def do_nothing(params: dict) -> None:
|
||||
return
|
||||
@@ -207,14 +199,11 @@ class ZigLanguageServer(SolidLanguageServer):
|
||||
self.server.on_notification("$/progress", do_nothing)
|
||||
self.server.on_notification("textDocument/publishDiagnostics", do_nothing)
|
||||
|
||||
self.logger.log("Starting ZLS server process", logging.INFO)
|
||||
log.info("Starting ZLS server process")
|
||||
self.server.start()
|
||||
initialize_params = self._get_initialize_params(self.repository_root_path)
|
||||
|
||||
self.logger.log(
|
||||
"Sending initialize request from LSP client to LSP server and awaiting response",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info("Sending initialize request from LSP client to LSP server and awaiting response")
|
||||
init_response = self.server.send.initialize(initialize_params)
|
||||
|
||||
# Verify server capabilities
|
||||
@@ -247,6 +236,6 @@ class ZigLanguageServer(SolidLanguageServer):
|
||||
}
|
||||
}
|
||||
)
|
||||
self.logger.log("Opened build.zig to provide project context to ZLS", logging.INFO)
|
||||
log.info("Opened build.zig to provide project context to ZLS")
|
||||
except Exception as e:
|
||||
self.logger.log(f"Failed to open build.zig: {e}", logging.WARNING)
|
||||
log.warning(f"Failed to open build.zig: {e}")
|
||||
+55
-106
@@ -25,7 +25,6 @@ from solidlsp import ls_types
|
||||
from solidlsp.ls_config import Language, LanguageServerConfig
|
||||
from solidlsp.ls_exceptions import SolidLSPException
|
||||
from solidlsp.ls_handler import SolidLanguageServerHandler
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_types import UnifiedSymbolInformation
|
||||
from solidlsp.ls_utils import FileUtils, PathUtils, TextUtils
|
||||
from solidlsp.lsp_protocol_handler import lsp_types
|
||||
@@ -207,7 +206,6 @@ class SolidLanguageServer(ABC):
|
||||
def create(
|
||||
cls,
|
||||
config: LanguageServerConfig,
|
||||
logger: LanguageServerLogger,
|
||||
repository_root_path: str,
|
||||
timeout: float | None = None,
|
||||
solidlsp_settings: SolidLSPSettings | None = None,
|
||||
@@ -233,14 +231,13 @@ class SolidLanguageServer(ABC):
|
||||
# For now, we assume that all language server implementations have the same signature of the constructor
|
||||
# (which, unfortunately, differs from the signature of the base class).
|
||||
# If this assumption is ever violated, we need branching logic here.
|
||||
ls = ls_class(config, logger, repository_root_path, solidlsp_settings) # type: ignore
|
||||
ls = ls_class(config, repository_root_path, solidlsp_settings) # type: ignore
|
||||
ls.set_request_timeout(timeout)
|
||||
return ls
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: LanguageServerConfig,
|
||||
logger: LanguageServerLogger,
|
||||
repository_root_path: str,
|
||||
process_launch_info: ProcessLaunchInfo,
|
||||
language_id: str,
|
||||
@@ -252,13 +249,11 @@ class SolidLanguageServer(ABC):
|
||||
|
||||
Do not instantiate this class directly. Use `LanguageServer.create` method instead.
|
||||
|
||||
:param config: The Multilspy configuration.
|
||||
:param logger: The logger to use.
|
||||
:param repository_root_path: The root path of the repository.
|
||||
:param process_launch_info: Each language server has a specific command used to start the server.
|
||||
This parameter is the command to launch the language server process.
|
||||
The command must pass appropriate flags to the binary, so that it runs in the stdio mode,
|
||||
as opposed to HTTP, TCP modes supported by some language servers.
|
||||
:param config: the global SolidLSP configuration.
|
||||
:param repository_root_path: the root path of the repository.
|
||||
:param process_launch_info: the command used to start the actual language server.
|
||||
The command must pass appropriate flags to the binary, so that it runs in the stdio mode,
|
||||
as opposed to HTTP, TCP modes supported by some language servers.
|
||||
:param cache_version_raw_document_symbols: the version, for caching, of the raw document symbols coming
|
||||
from this specific language server. This should be incremented by subclasses calling this constructor
|
||||
whenever the format of the raw document symbols changes (typically because the language server
|
||||
@@ -267,16 +262,11 @@ class SolidLanguageServer(ABC):
|
||||
self._solidlsp_settings = solidlsp_settings
|
||||
lang = self.get_language_enum_instance()
|
||||
self._custom_settings = solidlsp_settings.get_ls_specific_settings(lang)
|
||||
logger.log(
|
||||
f"Custom config (LS-specific settings) for {lang}: {self._custom_settings}",
|
||||
logging.DEBUG,
|
||||
)
|
||||
log.debug(f"Custom config (LS-specific settings) for {lang}: {self._custom_settings}")
|
||||
self._encoding = config.encoding
|
||||
self.logger = logger
|
||||
self.repository_root_path: str = repository_root_path
|
||||
self.logger.log(
|
||||
f"Creating language server instance for {repository_root_path=} with {language_id=} and process launch info: {process_launch_info}",
|
||||
logging.DEBUG,
|
||||
log.debug(
|
||||
f"Creating language server instance for {repository_root_path=} with {language_id=} and process launch info: {process_launch_info}"
|
||||
)
|
||||
|
||||
self.language_id = language_id
|
||||
@@ -305,16 +295,14 @@ class SolidLanguageServer(ABC):
|
||||
if config.trace_lsp_communication:
|
||||
|
||||
def logging_fn(source: str, target: str, msg: StringDict | str) -> None:
|
||||
self.logger.log(f"LSP: {source} -> {target}: {msg!s}", self.logger.logger.level)
|
||||
log.debug(f"LSP: {source} -> {target}: {msg!s}")
|
||||
|
||||
else:
|
||||
logging_fn = None # type: ignore
|
||||
|
||||
# cmd is obtained from the child classes, which provide the language specific command to start the language server
|
||||
# LanguageServerHandler provides the functionality to start the language server and communicate with it
|
||||
self.logger.log(
|
||||
f"Creating language server instance with {language_id=} and process launch info: {process_launch_info}", logging.DEBUG
|
||||
)
|
||||
log.debug(f"Creating language server instance with {language_id=} and process launch info: {process_launch_info}")
|
||||
self.server = SolidLanguageServerHandler(
|
||||
process_launch_info,
|
||||
language=self.language,
|
||||
@@ -330,7 +318,7 @@ class SolidLanguageServer(ABC):
|
||||
# Normalize separators (pathspec expects forward slashes)
|
||||
pattern = pattern.replace(os.path.sep, "/")
|
||||
processed_patterns.append(pattern)
|
||||
self.logger.log(f"Processing {len(processed_patterns)} ignored paths from the config", logging.DEBUG)
|
||||
log.debug(f"Processing {len(processed_patterns)} ignored paths from the config", logging.DEBUG)
|
||||
|
||||
# Create a pathspec matcher from the processed patterns
|
||||
self._ignore_spec = pathspec.PathSpec.from_lines(pathspec.patterns.GitWildMatchPattern, processed_patterns)
|
||||
@@ -354,10 +342,11 @@ class SolidLanguageServer(ABC):
|
||||
self.server.set_request_timeout(timeout)
|
||||
|
||||
def get_ignore_spec(self) -> pathspec.PathSpec:
|
||||
"""Returns the pathspec matcher for the paths that were configured to be ignored through
|
||||
the multilspy config.
|
||||
"""
|
||||
Returns the pathspec matcher for the paths that were configured to be ignored through
|
||||
the language server configuration.
|
||||
|
||||
This is is a subset of the full language-specific ignore spec that determines
|
||||
This is a subset of the full language-specific ignore spec that determines
|
||||
which files are relevant for the language server.
|
||||
|
||||
This matcher is useful for operations outside of the language server,
|
||||
@@ -407,20 +396,20 @@ class SolidLanguageServer(ABC):
|
||||
by explicitly closing all I/O pipes.
|
||||
"""
|
||||
if not self.server.is_running():
|
||||
self.logger.log("Server process not running, skipping shutdown.", logging.DEBUG)
|
||||
log.debug("Server process not running, skipping shutdown.")
|
||||
return
|
||||
|
||||
self.logger.log(f"Initiating final robust shutdown with a {timeout}s timeout...", logging.INFO)
|
||||
log.info(f"Initiating final robust shutdown with a {timeout}s timeout...")
|
||||
process = self.server.process
|
||||
if process is None:
|
||||
self.logger.log("Server process is None, cannot shutdown.", logging.DEBUG)
|
||||
log.debug("Server process is None, cannot shutdown.")
|
||||
return
|
||||
|
||||
# --- Main Shutdown Logic ---
|
||||
# Stage 1: Graceful Termination Request
|
||||
# Send LSP shutdown and close stdin to signal no more input.
|
||||
try:
|
||||
self.logger.log("Sending LSP shutdown request...", logging.DEBUG)
|
||||
log.debug("Sending LSP shutdown request...")
|
||||
# Use a thread to timeout the LSP shutdown call since it can hang
|
||||
shutdown_thread = threading.Thread(target=self.server.shutdown)
|
||||
shutdown_thread.daemon = True
|
||||
@@ -428,37 +417,37 @@ class SolidLanguageServer(ABC):
|
||||
shutdown_thread.join(timeout=2.0) # 2 second timeout for LSP shutdown
|
||||
|
||||
if shutdown_thread.is_alive():
|
||||
self.logger.log("LSP shutdown request timed out, proceeding to terminate...", logging.DEBUG)
|
||||
log.debug("LSP shutdown request timed out, proceeding to terminate...")
|
||||
else:
|
||||
self.logger.log("LSP shutdown request completed.", logging.DEBUG)
|
||||
log.debug("LSP shutdown request completed.")
|
||||
|
||||
if process.stdin and not process.stdin.closed:
|
||||
process.stdin.close()
|
||||
self.logger.log("Stage 1 shutdown complete.", logging.DEBUG)
|
||||
log.debug("Stage 1 shutdown complete.")
|
||||
except Exception as e:
|
||||
self.logger.log(f"Exception during graceful shutdown: {e}", logging.DEBUG)
|
||||
log.debug(f"Exception during graceful shutdown: {e}")
|
||||
# Ignore errors here, we are proceeding to terminate anyway.
|
||||
|
||||
# Stage 2: Terminate and Wait for Process to Exit
|
||||
self.logger.log(f"Terminating process {process.pid}, current status: {process.poll()}", logging.DEBUG)
|
||||
log.debug(f"Terminating process {process.pid}, current status: {process.poll()}")
|
||||
process.terminate()
|
||||
|
||||
# Stage 3: Wait for process termination with timeout
|
||||
try:
|
||||
self.logger.log(f"Waiting for process {process.pid} to terminate...", logging.DEBUG)
|
||||
log.debug(f"Waiting for process {process.pid} to terminate...")
|
||||
exit_code = process.wait(timeout=timeout)
|
||||
self.logger.log(f"Language server process terminated successfully with exit code {exit_code}.", logging.INFO)
|
||||
log.info(f"Language server process terminated successfully with exit code {exit_code}.")
|
||||
except subprocess.TimeoutExpired:
|
||||
# If termination failed, forcefully kill the process
|
||||
self.logger.log(f"Process {process.pid} termination timed out, killing process forcefully...", logging.WARNING)
|
||||
log.warning(f"Process {process.pid} termination timed out, killing process forcefully...")
|
||||
process.kill()
|
||||
try:
|
||||
exit_code = process.wait(timeout=2.0)
|
||||
self.logger.log(f"Language server process killed successfully with exit code {exit_code}.", logging.INFO)
|
||||
log.info(f"Language server process killed successfully with exit code {exit_code}.")
|
||||
except subprocess.TimeoutExpired:
|
||||
self.logger.log(f"Process {process.pid} could not be killed within timeout.", logging.ERROR)
|
||||
log.error(f"Process {process.pid} could not be killed within timeout.")
|
||||
except Exception as e:
|
||||
self.logger.log(f"Error during process shutdown: {e}", logging.ERROR)
|
||||
log.error(f"Error during process shutdown: {e}")
|
||||
|
||||
@contextmanager
|
||||
def start_server(self) -> Iterator["SolidLanguageServer"]:
|
||||
@@ -482,10 +471,7 @@ class SolidLanguageServer(ABC):
|
||||
:param relative_file_path: The relative path of the file to open.
|
||||
"""
|
||||
if not self.server_started:
|
||||
self.logger.log(
|
||||
"open_file called before Language Server started",
|
||||
logging.ERROR,
|
||||
)
|
||||
log.error("open_file called before Language Server started")
|
||||
raise SolidLSPException("Language Server not started")
|
||||
|
||||
absolute_file_path = str(PurePath(self.repository_root_path, relative_file_path))
|
||||
@@ -552,10 +538,7 @@ class SolidLanguageServer(ABC):
|
||||
:param text_to_be_inserted: The text to insert.
|
||||
"""
|
||||
if not self.server_started:
|
||||
self.logger.log(
|
||||
"insert_text_at_position called before Language Server started",
|
||||
logging.ERROR,
|
||||
)
|
||||
log.error("insert_text_at_position called before Language Server started")
|
||||
raise SolidLSPException("Language Server not started")
|
||||
|
||||
absolute_file_path = str(PurePath(self.repository_root_path, relative_file_path))
|
||||
@@ -598,10 +581,7 @@ class SolidLanguageServer(ABC):
|
||||
Delete text between the given start and end positions in the given file and return the deleted text.
|
||||
"""
|
||||
if not self.server_started:
|
||||
self.logger.log(
|
||||
"insert_text_at_position called before Language Server started",
|
||||
logging.ERROR,
|
||||
)
|
||||
log.error("insert_text_at_position called before Language Server started")
|
||||
raise SolidLSPException("Language Server not started")
|
||||
|
||||
absolute_file_path = str(PurePath(self.repository_root_path, relative_file_path))
|
||||
@@ -639,13 +619,10 @@ class SolidLanguageServer(ABC):
|
||||
:param line: The line number of the symbol
|
||||
:param column: The column number of the symbol
|
||||
|
||||
:return List[multilspy_types.Location]: A list of locations where the symbol is defined
|
||||
:return: the list of locations where the symbol is defined
|
||||
"""
|
||||
if not self.server_started:
|
||||
self.logger.log(
|
||||
"request_definition called before Language Server started",
|
||||
logging.ERROR,
|
||||
)
|
||||
log.error("request_definition called before language server started")
|
||||
raise SolidLSPException("Language Server not started")
|
||||
|
||||
if not self._has_waited_for_cross_file_references:
|
||||
@@ -703,10 +680,7 @@ class SolidLanguageServer(ABC):
|
||||
elif response is None:
|
||||
# Some language servers return None when they cannot find a definition
|
||||
# This is expected for certain symbol types like generics or types with incomplete information
|
||||
self.logger.log(
|
||||
f"Language server returned None for definition request at {relative_file_path}:{line}:{column}",
|
||||
logging.WARNING,
|
||||
)
|
||||
log.warning(f"Language server returned None for definition request at {relative_file_path}:{line}:{column}")
|
||||
else:
|
||||
assert False, f"Unexpected response from Language Server: {response}"
|
||||
|
||||
@@ -735,10 +709,7 @@ class SolidLanguageServer(ABC):
|
||||
:return: A list of locations where the symbol is referenced (excluding ignored directories)
|
||||
"""
|
||||
if not self.server_started:
|
||||
self.logger.log(
|
||||
"request_references called before Language Server started",
|
||||
logging.ERROR,
|
||||
)
|
||||
log.error("request_references called before Language Server started")
|
||||
raise SolidLSPException("Language Server not started")
|
||||
|
||||
if not self._has_waited_for_cross_file_references:
|
||||
@@ -770,16 +741,15 @@ class SolidLanguageServer(ABC):
|
||||
|
||||
abs_path = PathUtils.uri_to_path(item[LSPConstants.URI]) # type: ignore
|
||||
if not Path(abs_path).is_relative_to(self.repository_root_path):
|
||||
self.logger.log(
|
||||
log.warning(
|
||||
"Found a reference in a path outside the repository, probably the LS is parsing things in installed packages or in the standardlib! "
|
||||
f"Path: {abs_path}. This is a bug but we currently simply skip these references.",
|
||||
logging.WARNING,
|
||||
f"Path: {abs_path}. This is a bug but we currently simply skip these references."
|
||||
)
|
||||
continue
|
||||
|
||||
rel_path = Path(abs_path).relative_to(self.repository_root_path)
|
||||
if self.is_ignored_path(str(rel_path)):
|
||||
self.logger.log(f"Ignoring reference in {rel_path} since it should be ignored", logging.DEBUG)
|
||||
log.debug("Ignoring reference in %s since it should be ignored", rel_path)
|
||||
continue
|
||||
|
||||
new_item: dict = {}
|
||||
@@ -800,10 +770,7 @@ class SolidLanguageServer(ABC):
|
||||
:return: A list of diagnostics for the file
|
||||
"""
|
||||
if not self.server_started:
|
||||
self.logger.log(
|
||||
"request_text_document_diagnostics called before Language Server started",
|
||||
logging.ERROR,
|
||||
)
|
||||
log.error("request_text_document_diagnostics called before Language Server started")
|
||||
raise SolidLSPException("Language Server not started")
|
||||
|
||||
with self.open_file(relative_file_path):
|
||||
@@ -875,7 +842,7 @@ class SolidLanguageServer(ABC):
|
||||
:param line: The line number of the symbol
|
||||
:param column: The column number of the symbol
|
||||
|
||||
:return List[multilspy_types.CompletionItem]: A list of completions
|
||||
:return: A list of completions
|
||||
"""
|
||||
with self.open_file(relative_file_path):
|
||||
open_file_buffer = self.open_file_buffers[pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri()]
|
||||
@@ -983,7 +950,7 @@ class SolidLanguageServer(ABC):
|
||||
return response
|
||||
|
||||
# no cached result, query language server
|
||||
self.logger.log(f"Requesting document symbols for {relative_file_path} from the Language Server", logging.DEBUG)
|
||||
log.debug(f"Requesting document symbols for {relative_file_path} from the Language Server")
|
||||
response = self.server.send.document_symbol(
|
||||
{"textDocument": {"uri": pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri()}}
|
||||
)
|
||||
@@ -1420,10 +1387,7 @@ class SolidLanguageServer(ABC):
|
||||
:return: List of objects containing the symbol and the location of the reference.
|
||||
"""
|
||||
if not self.server_started:
|
||||
self.logger.log(
|
||||
"request_referencing_symbols called before Language Server started",
|
||||
logging.ERROR,
|
||||
)
|
||||
log.error("request_referencing_symbols called before Language Server started")
|
||||
raise SolidLSPException("Language Server not started")
|
||||
|
||||
# First, get all references to the symbol
|
||||
@@ -1470,10 +1434,7 @@ class SolidLanguageServer(ABC):
|
||||
|
||||
# We failed retrieving the symbol, falling back to creating a file symbol
|
||||
if containing_symbol is None and include_file_symbols:
|
||||
self.logger.log(
|
||||
f"Could not find containing symbol for {ref_path}:{ref_line}:{ref_col}. Returning file symbol instead",
|
||||
logging.WARNING,
|
||||
)
|
||||
log.warning(f"Could not find containing symbol for {ref_path}:{ref_line}:{ref_col}. Returning file symbol instead")
|
||||
fileRange = self._get_range_from_file_content(file_data.contents)
|
||||
location = ls_types.Location(
|
||||
uri=str(pathlib.Path(os.path.join(self.repository_root_path, ref_path)).as_uri()),
|
||||
@@ -1513,7 +1474,7 @@ class SolidLanguageServer(ABC):
|
||||
if include_self:
|
||||
result.append(ReferenceInSymbol(symbol=containing_symbol, line=ref_line, character=ref_col))
|
||||
continue
|
||||
self.logger.log(f"Found self-reference for {incoming_symbol['name']}, skipping it since {include_self=}", logging.DEBUG)
|
||||
log.debug(f"Found self-reference for {incoming_symbol['name']}, skipping it since {include_self=}")
|
||||
continue
|
||||
|
||||
# checking whether reference is an import
|
||||
@@ -1526,10 +1487,9 @@ class SolidLanguageServer(ABC):
|
||||
and containing_symbol["name"] == incoming_symbol["name"]
|
||||
and containing_symbol["kind"] == incoming_symbol["kind"]
|
||||
):
|
||||
self.logger.log(
|
||||
log.debug(
|
||||
f"Found import of referenced symbol {incoming_symbol['name']}"
|
||||
f"in {containing_symbol['location']['relativePath']}, skipping",
|
||||
logging.DEBUG,
|
||||
f"in {containing_symbol['location']['relativePath']}, skipping"
|
||||
)
|
||||
continue
|
||||
|
||||
@@ -1576,10 +1536,7 @@ class SolidLanguageServer(ABC):
|
||||
absolute_file_path = str(PurePath(self.repository_root_path, relative_file_path))
|
||||
content = FileUtils.read_file(absolute_file_path, self._encoding)
|
||||
if content.split("\n")[line].strip() == "":
|
||||
self.logger.log(
|
||||
f"Passing empty lines to request_container_symbol is currently not supported, {relative_file_path=}, {line=}",
|
||||
logging.ERROR,
|
||||
)
|
||||
log.error(f"Passing empty lines to request_container_symbol is currently not supported, {relative_file_path=}, {line=}")
|
||||
return None
|
||||
|
||||
document_symbols = self.request_document_symbols(relative_file_path)
|
||||
@@ -1693,10 +1650,7 @@ class SolidLanguageServer(ABC):
|
||||
:return: The symbol information for the definition, or None if not found.
|
||||
"""
|
||||
if not self.server_started:
|
||||
self.logger.log(
|
||||
"request_defining_symbol called before Language Server started",
|
||||
logging.ERROR,
|
||||
)
|
||||
log.error("request_defining_symbol called before language server started")
|
||||
raise SolidLSPException("Language Server not started")
|
||||
|
||||
# Get the definition location(s)
|
||||
@@ -1773,9 +1727,7 @@ class SolidLanguageServer(ABC):
|
||||
saved_cache = load_cache(str(cache_file), self._raw_document_symbols_cache_version())
|
||||
if saved_cache is not None:
|
||||
self._raw_document_symbols_cache = saved_cache
|
||||
self.logger.log(
|
||||
f"Loaded {len(self._raw_document_symbols_cache)} entries from raw document symbols cache.", logging.INFO
|
||||
)
|
||||
log.info(f"Loaded {len(self._raw_document_symbols_cache)} entries from raw document symbols cache.")
|
||||
except Exception as e:
|
||||
# cache can become corrupt, so just skip loading it
|
||||
log.warning(
|
||||
@@ -1810,7 +1762,7 @@ class SolidLanguageServer(ABC):
|
||||
saved_cache = load_cache(str(cache_file), self.DOCUMENT_SYMBOL_CACHE_VERSION)
|
||||
if saved_cache is not None:
|
||||
self._document_symbols_cache = saved_cache
|
||||
self.logger.log(f"Loaded {len(self._document_symbols_cache)} entries from document symbols cache.", logging.INFO)
|
||||
log.info(f"Loaded {len(self._document_symbols_cache)} entries from document symbols cache.")
|
||||
except Exception as e:
|
||||
# cache can become corrupt, so just skip loading it
|
||||
log.warning(
|
||||
@@ -1902,10 +1854,7 @@ class SolidLanguageServer(ABC):
|
||||
|
||||
:return: self for method chaining
|
||||
"""
|
||||
self.logger.log(
|
||||
f"Starting language server with language {self.language_server.language} for {self.language_server.repository_root_path}",
|
||||
logging.INFO,
|
||||
)
|
||||
log.info(f"Starting language server with language {self.language_server.language} for {self.language_server.repository_root_path}")
|
||||
self._start_server_process()
|
||||
return self
|
||||
|
||||
@@ -1919,7 +1868,7 @@ class SolidLanguageServer(ABC):
|
||||
try:
|
||||
self._shutdown(timeout=shutdown_timeout)
|
||||
except Exception as e:
|
||||
self.logger.log(f"Exception while shutting down language server: {e}", logging.WARNING)
|
||||
log.warning(f"Exception while shutting down language server: {e}")
|
||||
|
||||
@property
|
||||
def language_server(self) -> Self:
|
||||
|
||||
@@ -28,7 +28,7 @@ class FilenameMatcher:
|
||||
|
||||
class Language(str, Enum):
|
||||
"""
|
||||
Possible languages with Multilspy.
|
||||
Enumeration of language servers supported by SolidLSP.
|
||||
"""
|
||||
|
||||
CSHARP = "csharp"
|
||||
@@ -351,9 +351,6 @@ class LanguageServerConfig:
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, env: dict) -> Self:
|
||||
"""
|
||||
Create a MultilspyConfig instance from a dictionary
|
||||
"""
|
||||
import inspect
|
||||
|
||||
return cls(**{k: v for k, v in env.items() if k in inspect.signature(cls).parameters})
|
||||
@@ -1,66 +0,0 @@
|
||||
"""
|
||||
Multilspy logger module.
|
||||
"""
|
||||
|
||||
import inspect
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class LogLine(BaseModel):
|
||||
"""
|
||||
Represents a line in the Multilspy log
|
||||
"""
|
||||
|
||||
time: str
|
||||
level: str
|
||||
caller_file: str
|
||||
caller_name: str
|
||||
caller_line: int
|
||||
message: str
|
||||
|
||||
|
||||
class LanguageServerLogger:
|
||||
"""
|
||||
Logger class
|
||||
"""
|
||||
|
||||
def __init__(self, json_format: bool = False, log_level: int = logging.INFO) -> None:
|
||||
self.logger = logging.getLogger("solidlsp")
|
||||
self.logger.setLevel(log_level)
|
||||
self.json_format = json_format
|
||||
|
||||
def log(self, debug_message: str, level: int, sanitized_error_message: str = "", stacklevel: int = 2) -> None:
|
||||
"""
|
||||
Log the debug and sanitized messages using the logger
|
||||
"""
|
||||
debug_message = debug_message.replace("'", '"').replace("\n", " ")
|
||||
sanitized_error_message = sanitized_error_message.replace("'", '"').replace("\n", " ")
|
||||
|
||||
# Collect details about the callee
|
||||
curframe = inspect.currentframe()
|
||||
calframe = inspect.getouterframes(curframe, 2)
|
||||
caller_file = calframe[1][1].split("/")[-1]
|
||||
caller_line = calframe[1][2]
|
||||
caller_name = calframe[1][3]
|
||||
|
||||
if self.json_format:
|
||||
# Construct the debug log line
|
||||
debug_log_line = LogLine(
|
||||
time=str(datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
|
||||
level=logging.getLevelName(level),
|
||||
caller_file=caller_file,
|
||||
caller_name=caller_name,
|
||||
caller_line=caller_line,
|
||||
message=debug_message,
|
||||
)
|
||||
|
||||
self.logger.log(
|
||||
level=level,
|
||||
msg=debug_log_line.json(),
|
||||
stacklevel=stacklevel,
|
||||
)
|
||||
else:
|
||||
self.logger.log(level, debug_message, stacklevel=stacklevel)
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Defines wrapper objects around the types returned by LSP to ensure decoupling between LSP versions and multilspy
|
||||
Defines wrapper objects around the types returned by LSP to ensure decoupling between LSP versions and SolidLSP
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -17,7 +17,6 @@ import charset_normalizer
|
||||
import requests
|
||||
|
||||
from solidlsp.ls_exceptions import SolidLSPException
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_types import UnifiedSymbolInformation
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -201,7 +200,7 @@ class FileUtils:
|
||||
raise exc
|
||||
|
||||
@staticmethod
|
||||
def download_file(logger: LanguageServerLogger, url: str, target_path: str) -> None:
|
||||
def download_file(url: str, target_path: str) -> None:
|
||||
"""
|
||||
Downloads the file from the given URL to the given {target_path}
|
||||
"""
|
||||
@@ -209,25 +208,25 @@ class FileUtils:
|
||||
try:
|
||||
response = requests.get(url, stream=True, timeout=60)
|
||||
if response.status_code != 200:
|
||||
logger.log(f"Error downloading file '{url}': {response.status_code} {response.text}", logging.ERROR)
|
||||
log.error(f"Error downloading file '{url}': {response.status_code} {response.text}")
|
||||
raise SolidLSPException("Error downloading file.")
|
||||
with open(target_path, "wb") as f:
|
||||
shutil.copyfileobj(response.raw, f)
|
||||
except Exception as exc:
|
||||
logger.log(f"Error downloading file '{url}': {exc}", logging.ERROR)
|
||||
log.error(f"Error downloading file '{url}': {exc}")
|
||||
raise SolidLSPException("Error downloading file.") from None
|
||||
|
||||
@staticmethod
|
||||
def download_and_extract_archive(logger: LanguageServerLogger, url: str, target_path: str, archive_type: str) -> None:
|
||||
def download_and_extract_archive(url: str, target_path: str, archive_type: str) -> None:
|
||||
"""
|
||||
Downloads the archive from the given URL having format {archive_type} and extracts it to the given {target_path}
|
||||
"""
|
||||
try:
|
||||
tmp_files = []
|
||||
tmp_file_name = str(PurePath(os.path.expanduser("~"), "multilspy_tmp", uuid.uuid4().hex))
|
||||
tmp_file_name = str(PurePath(os.path.expanduser("~"), "solidlsp_tmp", uuid.uuid4().hex))
|
||||
tmp_files.append(tmp_file_name)
|
||||
os.makedirs(os.path.dirname(tmp_file_name), exist_ok=True)
|
||||
FileUtils.download_file(logger, url, tmp_file_name)
|
||||
FileUtils.download_file(url, tmp_file_name)
|
||||
if archive_type in ["tar", "gztar", "bztar", "xztar"]:
|
||||
os.makedirs(target_path, exist_ok=True)
|
||||
shutil.unpack_archive(tmp_file_name, target_path, archive_type)
|
||||
@@ -258,10 +257,10 @@ class FileUtils:
|
||||
# For single binary files, just move to target without extraction
|
||||
shutil.move(tmp_file_name, target_path)
|
||||
else:
|
||||
logger.log(f"Unknown archive type '{archive_type}' for extraction", logging.ERROR)
|
||||
log.error(f"Unknown archive type '{archive_type}' for extraction")
|
||||
raise SolidLSPException(f"Unknown archive type '{archive_type}'")
|
||||
except Exception as exc:
|
||||
logger.log(f"Error extracting archive '{tmp_file_name}' obtained from '{url}': {exc}", logging.ERROR)
|
||||
log.error(f"Error extracting archive '{tmp_file_name}' obtained from '{url}': {exc}")
|
||||
raise SolidLSPException("Error extracting archive.") from exc
|
||||
finally:
|
||||
for tmp_file_name in tmp_files:
|
||||
@@ -270,10 +269,6 @@ class FileUtils:
|
||||
|
||||
|
||||
class PlatformId(str, Enum):
|
||||
"""
|
||||
multilspy supported platforms
|
||||
"""
|
||||
|
||||
WIN_x86 = "win-x86"
|
||||
WIN_x64 = "win-x64"
|
||||
WIN_arm64 = "win-arm64"
|
||||
@@ -291,10 +286,6 @@ class PlatformId(str, Enum):
|
||||
|
||||
|
||||
class DotnetVersion(str, Enum):
|
||||
"""
|
||||
multilspy supported dotnet versions
|
||||
"""
|
||||
|
||||
V4 = "4"
|
||||
V6 = "6"
|
||||
V7 = "7"
|
||||
|
||||
@@ -46,17 +46,25 @@ log = logging.getLogger(__name__)
|
||||
@dataclasses.dataclass
|
||||
class ProcessLaunchInfo:
|
||||
"""
|
||||
This class is used to store the information required to launch a process.
|
||||
This class is used to store the information required to launch a (language server) process.
|
||||
"""
|
||||
|
||||
# The command to launch the process
|
||||
cmd: str | list[str]
|
||||
"""
|
||||
the command used to launch the process.
|
||||
Specification as a list is preferred (as it is more robust and avoids incorrect quoting of arguments);
|
||||
the string variant is supported for backward compatibility only
|
||||
"""
|
||||
|
||||
# The environment variables to set for the process
|
||||
env: dict[str, str] = dataclasses.field(default_factory=dict)
|
||||
"""
|
||||
the environment variables to set for the process
|
||||
"""
|
||||
|
||||
# The working directory for the process
|
||||
cwd: str = os.getcwd()
|
||||
"""
|
||||
the working directory for the process
|
||||
"""
|
||||
|
||||
|
||||
class LSPError(Exception):
|
||||
|
||||
@@ -9,7 +9,6 @@ from serena.project import Project
|
||||
from serena.util.file_system import GitignoreParser
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.ls_config import Language, LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
configure(level=logging.ERROR)
|
||||
@@ -44,10 +43,8 @@ def create_ls(
|
||||
for spec in gitignore_parser.get_ignore_specs():
|
||||
ignored_paths.extend(spec.patterns)
|
||||
config = LanguageServerConfig(code_language=language, ignored_paths=ignored_paths, trace_lsp_communication=trace_lsp_communication)
|
||||
logger = LanguageServerLogger(log_level=log_level)
|
||||
return SolidLanguageServer.create(
|
||||
config,
|
||||
logger,
|
||||
repo_path,
|
||||
solidlsp_settings=SolidLSPSettings(solidlsp_dir=SERENA_MANAGED_DIR_IN_HOME, project_data_relative_path=SERENA_MANAGED_DIR_NAME),
|
||||
)
|
||||
|
||||
@@ -5,7 +5,9 @@ from typing import cast
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
from sensai.util import logging
|
||||
|
||||
from serena.util.logging import SuspendedLoggersContext
|
||||
from solidlsp import SolidLanguageServer
|
||||
from solidlsp.language_servers.csharp_language_server import (
|
||||
CSharpLanguageServer,
|
||||
@@ -247,8 +249,6 @@ class TestCSharpSolutionProjectOpening:
|
||||
solution_file = temp_path / "TestSolution.sln"
|
||||
solution_file.touch()
|
||||
|
||||
# Mock logger to capture log messages
|
||||
mock_logger = Mock()
|
||||
mock_config = Mock(spec=LanguageServerConfig)
|
||||
mock_config.ignored_paths = []
|
||||
|
||||
@@ -256,10 +256,15 @@ class TestCSharpSolutionProjectOpening:
|
||||
mock_settings = Mock(spec=SolidLSPSettings)
|
||||
mock_settings.ls_resources_dir = "/tmp/test_ls_resources"
|
||||
mock_settings.project_data_relative_path = "project_data"
|
||||
CSharpLanguageServer(mock_config, mock_logger, str(temp_path), mock_settings)
|
||||
|
||||
# Verify that logger was called with solution file discovery
|
||||
mock_logger.log.assert_any_call(f"Found solution/project file: {solution_file}", 20) # logging.INFO
|
||||
with SuspendedLoggersContext():
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
with logging.MemoryLoggerContext() as mem_log:
|
||||
CSharpLanguageServer(mock_config, str(temp_path), mock_settings)
|
||||
|
||||
# Verify that logger was called with solution file discovery
|
||||
expected_log_msg = f"Found solution/project file: {solution_file}"
|
||||
assert expected_log_msg in mem_log.get_log()
|
||||
|
||||
@patch("solidlsp.language_servers.csharp_language_server.CSharpLanguageServer._ensure_server_installed")
|
||||
@patch("solidlsp.language_servers.csharp_language_server.CSharpLanguageServer._start_server")
|
||||
@@ -273,20 +278,22 @@ class TestCSharpSolutionProjectOpening:
|
||||
temp_path = Path(temp_dir)
|
||||
|
||||
# Mock logger to capture log messages
|
||||
mock_logger = Mock()
|
||||
mock_config = Mock(spec=LanguageServerConfig)
|
||||
mock_config.ignored_paths = []
|
||||
|
||||
# Create CSharpLanguageServer instance
|
||||
mock_settings = Mock(spec=SolidLSPSettings)
|
||||
mock_settings.ls_resources_dir = "/tmp/test_ls_resources"
|
||||
mock_settings.project_data_relative_path = "project_data"
|
||||
CSharpLanguageServer(mock_config, mock_logger, str(temp_path), mock_settings)
|
||||
|
||||
# Verify that logger was called with warning about no solution/project files
|
||||
mock_logger.log.assert_any_call(
|
||||
"No .sln or .csproj file found, language server will attempt auto-discovery", 30 # logging.WARNING
|
||||
)
|
||||
# Create CSharpLanguageServer instance
|
||||
with SuspendedLoggersContext():
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
with logging.MemoryLoggerContext() as mem_log:
|
||||
CSharpLanguageServer(mock_config, str(temp_path), mock_settings)
|
||||
|
||||
# Verify that logger was called with warning about no solution/project files
|
||||
expected_log_msg = "No .sln or .csproj file found, language server will attempt auto-discovery"
|
||||
assert expected_log_msg in mem_log.get_log()
|
||||
|
||||
def test_solution_and_project_opening_with_real_test_repo(self):
|
||||
"""Test solution and project opening with the actual C# test repository."""
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from solidlsp.language_servers.scala_language_server import ScalaLanguageServer
|
||||
from solidlsp.ls_config import Language, LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
pytest.skip("Scala must be compiled for these tests to run through, which is a huge hassle", allow_module_level=True)
|
||||
@@ -18,10 +16,9 @@ pytestmark = pytest.mark.scala
|
||||
@pytest.fixture(scope="module")
|
||||
def scala_ls():
|
||||
repo_root = os.path.abspath("test/resources/repos/scala")
|
||||
logger = LanguageServerLogger(json_format=False, log_level=logging.INFO)
|
||||
config = LanguageServerConfig(code_language=Language.SCALA)
|
||||
solidlsp_settings = SolidLSPSettings()
|
||||
ls = ScalaLanguageServer(config, logger, repo_root, solidlsp_settings)
|
||||
ls = ScalaLanguageServer(config, repo_root, solidlsp_settings)
|
||||
|
||||
with ls.start_server():
|
||||
yield ls
|
||||
|
||||
Reference in new issue
Block a user