From 82d495b2a67163ced8e1b25dea6ff1a710919761 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Wed, 19 Nov 2025 21:30:10 +0100 Subject: [PATCH] Remove LanguageServerLogger Resolves #745 --- .../adding_new_language_support_guide.md | 34 ++-- src/serena/ls_manager.py | 5 - src/serena/project.py | 1 - .../language_servers/al_language_server.py | 163 ++++++++---------- .../language_servers/bash_language_server.py | 50 +++--- .../clangd_language_server.py | 38 ++-- src/solidlsp/language_servers/clojure_lsp.py | 28 ++- src/solidlsp/language_servers/common.py | 11 +- .../csharp_language_server.py | 116 ++++++------- .../language_servers/dart_language_server.py | 34 ++-- .../language_servers/eclipse_jdtls.py | 49 ++---- .../elixir_tools/elixir_tools.py | 51 +++--- .../language_servers/elm_language_server.py | 41 ++--- .../erlang_language_server.py | 40 ++--- .../fortran_language_server.py | 42 ++--- src/solidlsp/language_servers/gopls.py | 25 +-- .../haskell_language_server.py | 33 ++-- src/solidlsp/language_servers/intelephense.py | 39 ++--- src/solidlsp/language_servers/jedi_server.py | 17 +- src/solidlsp/language_servers/julia_server.py | 45 ++--- .../kotlin_language_server.py | 39 ++--- src/solidlsp/language_servers/lua_ls.py | 23 +-- src/solidlsp/language_servers/marksman.py | 32 ++-- src/solidlsp/language_servers/nixd_ls.py | 25 +-- src/solidlsp/language_servers/omnisharp.py | 30 ++-- .../language_servers/perl_language_server.py | 36 ++-- .../language_servers/pyright_server.py | 31 ++-- .../language_servers/r_language_server.py | 29 ++-- src/solidlsp/language_servers/regal_server.py | 16 +- src/solidlsp/language_servers/ruby_lsp.py | 86 ++++----- .../language_servers/rust_analyzer.py | 19 +- .../language_servers/scala_language_server.py | 34 ++-- src/solidlsp/language_servers/solargraph.py | 52 +++--- .../language_servers/sourcekit_lsp.py | 34 ++-- src/solidlsp/language_servers/terraform_ls.py | 37 ++-- .../typescript_language_server.py | 31 ++-- .../language_servers/vts_language_server.py | 35 ++-- .../language_servers/yaml_language_server.py | 39 ++--- src/solidlsp/language_servers/zls.py | 31 ++-- src/solidlsp/ls.py | 139 +++++---------- src/solidlsp/ls_logger.py | 66 ------- src/solidlsp/ls_utils.py | 15 +- test/conftest.py | 3 - test/solidlsp/csharp/test_csharp_basic.py | 30 ++-- .../scala/test_scala_language_server.py | 5 +- 45 files changed, 659 insertions(+), 1120 deletions(-) delete mode 100644 src/solidlsp/ls_logger.py diff --git a/.serena/memories/adding_new_language_support_guide.md b/.serena/memories/adding_new_language_support_guide.md index 21980531..18d12436 100644 --- a/.serena/memories/adding_new_language_support_guide.md +++ b/.serena/memories/adding_new_language_support_guide.md @@ -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 diff --git a/src/serena/ls_manager.py b/src/serena/ls_manager.py index a3072286..106cb3ed 100644 --- a/src/serena/ls_manager.py +++ b/src/serena/ls_manager.py @@ -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( diff --git a/src/serena/project.py b/src/serena/project.py index 3cd5eb08..0a23a4a6 100644 --- a/src/serena/project.py +++ b/src/serena/project.py @@ -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) diff --git a/src/solidlsp/language_servers/al_language_server.py b/src/solidlsp/language_servers/al_language_server.py index b98551bd..d6052a2a 100644 --- a/src/solidlsp/language_servers/al_language_server.py +++ b/src/solidlsp/language_servers/al_language_server.py @@ -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 diff --git a/src/solidlsp/language_servers/bash_language_server.py b/src/solidlsp/language_servers/bash_language_server.py index 6759b5a6..870e08b6 100644 --- a/src/solidlsp/language_servers/bash_language_server.py +++ b/src/solidlsp/language_servers/bash_language_server.py @@ -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 diff --git a/src/solidlsp/language_servers/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server.py index d4f67e9b..eccd6017 100644 --- a/src/solidlsp/language_servers/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server.py @@ -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"] diff --git a/src/solidlsp/language_servers/clojure_lsp.py b/src/solidlsp/language_servers/clojure_lsp.py index 241e57a0..f32a4d5a 100644 --- a/src/solidlsp/language_servers/clojure_lsp.py +++ b/src/solidlsp/language_servers/clojure_lsp.py @@ -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"] diff --git a/src/solidlsp/language_servers/common.py b/src/solidlsp/language_servers/common.py index c1cea673..504b0637 100644 --- a/src/solidlsp/language_servers/common.py +++ b/src/solidlsp/language_servers/common.py @@ -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: diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index 2382b761..b2aec7db 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -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: diff --git a/src/solidlsp/language_servers/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server.py index 0135ae7a..12dc3143 100644 --- a/src/solidlsp/language_servers/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server.py @@ -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({}) diff --git a/src/solidlsp/language_servers/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls.py index 7c718c07..d058521e 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls.py @@ -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"] diff --git a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py index fa14d3e0..926ee792 100644 --- a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py +++ b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py @@ -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) diff --git a/src/solidlsp/language_servers/elm_language_server.py b/src/solidlsp/language_servers/elm_language_server.py index f75af40c..0a4c25bd 100644 --- a/src/solidlsp/language_servers/elm_language_server.py +++ b/src/solidlsp/language_servers/elm_language_server.py @@ -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: diff --git a/src/solidlsp/language_servers/erlang_language_server.py b/src/solidlsp/language_servers/erlang_language_server.py index 36651688..065f96bf 100644 --- a/src/solidlsp/language_servers/erlang_language_server.py +++ b/src/solidlsp/language_servers/erlang_language_server.py @@ -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: diff --git a/src/solidlsp/language_servers/fortran_language_server.py b/src/solidlsp/language_servers/fortran_language_server.py index 993c21fa..406ab182 100644 --- a/src/solidlsp/language_servers/fortran_language_server.py +++ b/src/solidlsp/language_servers/fortran_language_server.py @@ -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") diff --git a/src/solidlsp/language_servers/gopls.py b/src/solidlsp/language_servers/gopls.py index 9f9aec7a..afb628b9 100644 --- a/src/solidlsp/language_servers/gopls.py +++ b/src/solidlsp/language_servers/gopls.py @@ -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 diff --git a/src/solidlsp/language_servers/haskell_language_server.py b/src/solidlsp/language_servers/haskell_language_server.py index f1353a34..e4e13bba 100644 --- a/src/solidlsp/language_servers/haskell_language_server.py +++ b/src/solidlsp/language_servers/haskell_language_server.py @@ -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") diff --git a/src/solidlsp/language_servers/intelephense.py b/src/solidlsp/language_servers/intelephense.py index dc62a70e..487c7fca 100644 --- a/src/solidlsp/language_servers/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -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. """ @@ -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"] diff --git a/src/solidlsp/language_servers/jedi_server.py b/src/solidlsp/language_servers/jedi_server.py index f08cd09a..1352cb37 100644 --- a/src/solidlsp/language_servers/jedi_server.py +++ b/src/solidlsp/language_servers/jedi_server.py @@ -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"] diff --git a/src/solidlsp/language_servers/julia_server.py b/src/solidlsp/language_servers/julia_server.py index 178ac91b..f255a539 100644 --- a/src/solidlsp/language_servers/julia_server.py +++ b/src/solidlsp/language_servers/julia_server.py @@ -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.") diff --git a/src/solidlsp/language_servers/kotlin_language_server.py b/src/solidlsp/language_servers/kotlin_language_server.py index c1c6b975..cd3624fd 100644 --- a/src/solidlsp/language_servers/kotlin_language_server.py +++ b/src/solidlsp/language_servers/kotlin_language_server.py @@ -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"] diff --git a/src/solidlsp/language_servers/lua_ls.py b/src/solidlsp/language_servers/lua_ls.py index 0ff6b7c4..8802ee87 100644 --- a/src/solidlsp/language_servers/lua_ls.py +++ b/src/solidlsp/language_servers/lua_ls.py @@ -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 diff --git a/src/solidlsp/language_servers/marksman.py b/src/solidlsp/language_servers/marksman.py index 85919113..d799abb2 100644 --- a/src/solidlsp/language_servers/marksman.py +++ b/src/solidlsp/language_servers/marksman.py @@ -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() diff --git a/src/solidlsp/language_servers/nixd_ls.py b/src/solidlsp/language_servers/nixd_ls.py index edf092e3..d3e3e96a 100644 --- a/src/solidlsp/language_servers/nixd_ls.py +++ b/src/solidlsp/language_servers/nixd_ls.py @@ -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 diff --git a/src/solidlsp/language_servers/omnisharp.py b/src/solidlsp/language_servers/omnisharp.py index 5c5be862..83bfae10 100644 --- a/src/solidlsp/language_servers/omnisharp.py +++ b/src/solidlsp/language_servers/omnisharp.py @@ -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: diff --git a/src/solidlsp/language_servers/perl_language_server.py b/src/solidlsp/language_servers/perl_language_server.py index 5937b6a4..b7ab42a0 100644 --- a/src/solidlsp/language_servers/perl_language_server.py +++ b/src/solidlsp/language_servers/perl_language_server.py @@ -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") diff --git a/src/solidlsp/language_servers/pyright_server.py b/src/solidlsp/language_servers/pyright_server.py index 10cc7886..649dad24 100644 --- a/src/solidlsp/language_servers/pyright_server.py +++ b/src/solidlsp/language_servers/pyright_server.py @@ -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() diff --git a/src/solidlsp/language_servers/r_language_server.py b/src/solidlsp/language_servers/r_language_server.py index 467cc144..9802381d 100644 --- a/src/solidlsp/language_servers/r_language_server.py +++ b/src/solidlsp/language_servers/r_language_server.py @@ -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() diff --git a/src/solidlsp/language_servers/regal_server.py b/src/solidlsp/language_servers/regal_server.py index eb6abc1d..35c7bee1 100644 --- a/src/solidlsp/language_servers/regal_server.py +++ b/src/solidlsp/language_servers/regal_server.py @@ -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) diff --git a/src/solidlsp/language_servers/ruby_lsp.py b/src/solidlsp/language_servers/ruby_lsp.py index 95c9f7b6..744213fe 100644 --- a/src/solidlsp/language_servers/ruby_lsp.py +++ b/src/solidlsp/language_servers/ruby_lsp.py @@ -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() diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index aa5617c5..7791b3f1 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -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"] diff --git a/src/solidlsp/language_servers/scala_language_server.py b/src/solidlsp/language_servers/scala_language_server.py index 5911c453..79a0e205 100644 --- a/src/solidlsp/language_servers/scala_language_server.py +++ b/src/solidlsp/language_servers/scala_language_server.py @@ -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) diff --git a/src/solidlsp/language_servers/solargraph.py b/src/solidlsp/language_servers/solargraph.py index 61249cde..4915f730 100644 --- a/src/solidlsp/language_servers/solargraph.py +++ b/src/solidlsp/language_servers/solargraph.py @@ -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() diff --git a/src/solidlsp/language_servers/sourcekit_lsp.py b/src/solidlsp/language_servers/sourcekit_lsp.py index d21eac4d..ad004d5d 100644 --- a/src/solidlsp/language_servers/sourcekit_lsp.py +++ b/src/solidlsp/language_servers/sourcekit_lsp.py @@ -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) diff --git a/src/solidlsp/language_servers/terraform_ls.py b/src/solidlsp/language_servers/terraform_ls.py index 66fd01a1..2b54c0fa 100644 --- a/src/solidlsp/language_servers/terraform_ls.py +++ b/src/solidlsp/language_servers/terraform_ls.py @@ -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 diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index 2ebcb869..10c5cc69 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -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() diff --git a/src/solidlsp/language_servers/vts_language_server.py b/src/solidlsp/language_servers/vts_language_server.py index 51922e70..21320374 100644 --- a/src/solidlsp/language_servers/vts_language_server.py +++ b/src/solidlsp/language_servers/vts_language_server.py @@ -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() diff --git a/src/solidlsp/language_servers/yaml_language_server.py b/src/solidlsp/language_servers/yaml_language_server.py index 6e2dc959..6ec52501 100644 --- a/src/solidlsp/language_servers/yaml_language_server.py +++ b/src/solidlsp/language_servers/yaml_language_server.py @@ -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() diff --git a/src/solidlsp/language_servers/zls.py b/src/solidlsp/language_servers/zls.py index 9ab8daac..f8019a4e 100644 --- a/src/solidlsp/language_servers/zls.py +++ b/src/solidlsp/language_servers/zls.py @@ -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}") diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index fcb0734f..5ca9912b 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -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, @@ -253,7 +250,6 @@ 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. @@ -267,16 +263,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 +296,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 +319,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) @@ -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)) @@ -642,10 +622,7 @@ class SolidLanguageServer(ABC): :return List[multilspy_types.Location]: A 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): @@ -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: diff --git a/src/solidlsp/ls_logger.py b/src/solidlsp/ls_logger.py deleted file mode 100644 index 7aa0e0a6..00000000 --- a/src/solidlsp/ls_logger.py +++ /dev/null @@ -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) diff --git a/src/solidlsp/ls_utils.py b/src/solidlsp/ls_utils.py index 08e6a279..8c60a13a 100644 --- a/src/solidlsp/ls_utils.py +++ b/src/solidlsp/ls_utils.py @@ -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,16 +208,16 @@ 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} """ @@ -227,7 +226,7 @@ class FileUtils: tmp_file_name = str(PurePath(os.path.expanduser("~"), "multilspy_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: diff --git a/test/conftest.py b/test/conftest.py index 1ef2501c..8c8fb33a 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -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), ) diff --git a/test/solidlsp/csharp/test_csharp_basic.py b/test/solidlsp/csharp/test_csharp_basic.py index 21ec4146..66772ef4 100644 --- a/test/solidlsp/csharp/test_csharp_basic.py +++ b/test/solidlsp/csharp/test_csharp_basic.py @@ -5,6 +5,7 @@ from typing import cast from unittest.mock import Mock, patch import pytest +from sensai.util import logging from solidlsp import SolidLanguageServer from solidlsp.language_servers.csharp_language_server import ( @@ -247,8 +248,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 +255,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 logging.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 +277,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 logging.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.""" diff --git a/test/solidlsp/scala/test_scala_language_server.py b/test/solidlsp/scala/test_scala_language_server.py index f22e2e96..9a25009a 100644 --- a/test/solidlsp/scala/test_scala_language_server.py +++ b/test/solidlsp/scala/test_scala_language_server.py @@ -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