From b972f2959de1f8c3279c25705826818b2b048b55 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Mon, 23 Jun 2025 02:07:07 -0400 Subject: [PATCH 01/11] Just minor formatting fixes - no space after ** for bold - consistent trailing periods in a list --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1a5b7a51..d67000aa 100644 --- a/README.md +++ b/README.md @@ -414,7 +414,7 @@ For more information on MCP servers with Claude Desktop, see [the official quick Being an MCP Server, Serena can be included in any MCP Client. The same configuration as above, perhaps with small client-specific modifications, should work. Most of the popular existing coding assistants (IDE extensions or VSCode-like IDEs) support connections -to MCP Servers. It is ** recommended to use the `ide-assistant` context** for these integrations by adding `"--context", "ide-assistant"` to the `args` in your MCP client's configuration. Including Serena generally boosts their performance +to MCP Servers. It is **recommended to use the `ide-assistant` context** for these integrations by adding `"--context", "ide-assistant"` to the `args` in your MCP client's configuration. Including Serena generally boosts their performance by providing them tools for symbolic operations. In this case, the billing for the usage continues to be controlled by the client of your choice @@ -422,8 +422,8 @@ In this case, the billing for the usage continues to be controlled by the client e.g., for one of the following reasons: 1. You are already using a coding assistant (say Cline or Cursor) and just want to make it more powerful. -2. You are on Linux and don't want to use the [community-created Claude Desktop](https://github.com/aaddrick/claude-desktop-debian) -3. You want tighter integration of Serena into your IDE and don't mind paying for that +2. You are on Linux and don't want to use the [community-created Claude Desktop](https://github.com/aaddrick/claude-desktop-debian). +3. You want tighter integration of Serena into your IDE and don't mind paying for that. ### Agno Agent From f04bd7d65be2f8e0f1459efdc660ce9c40597ce7 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Mon, 23 Jun 2025 15:29:48 +0200 Subject: [PATCH 02/11] Apply black & ruff to solidlsp --- pyproject.toml | 5 +- .../clangd_language_server.py | 23 +- .../dart_language_server.py | 37 +- .../eclipse_jdtls/eclipse_jdtls.py | 56 +-- src/solidlsp/language_servers/gopls/gopls.py | 19 +- .../intelephense/intelephense.py | 38 +- .../jedi_language_server/jedi_server.py | 4 +- .../kotlin_language_server.py | 61 ++- .../language_servers/omnisharp/omnisharp.py | 52 +-- .../pyright_language_server/pyright_server.py | 3 +- .../rust_analyzer/rust_analyzer.py | 19 +- .../language_servers/solargraph/solargraph.py | 23 +- .../typescript_language_server.py | 25 +- src/solidlsp/ls.py | 437 ++++++++---------- src/solidlsp/ls_handler.py | 58 ++- src/solidlsp/lsp_request.py | 316 ++++++------- 16 files changed, 522 insertions(+), 654 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a34dd303..0b0f9f95 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,7 +83,6 @@ target-version = [ exclude = ''' /( src/multilspy - | src/solidlsp )/ ''' @@ -140,7 +139,6 @@ target-version = "py311" line-length = 140 exclude = [ "src/multilspy", - "src/solidlsp" ] [tool.ruff.format] @@ -240,6 +238,9 @@ ignore = [ "SIM110", # requires use of any(...) instead of for-loop "G001", # forbids str.format in log statements "E722", # forbids unspecific except clause + "SIM105", # forbids empty/general except clause + "SIM113", # wants to enforce use of enumerate + "E712", # forbids equality comparison with True/False ] unfixable = [ "F841", diff --git a/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py index 0ee5f471..54d364e1 100644 --- a/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py @@ -13,8 +13,7 @@ from multilspy.lsp_protocol_handler.lsp_types import InitializeParams from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo from multilspy.multilspy_config import MultilspyConfig from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import FileUtils -from multilspy.multilspy_utils import PlatformUtils +from multilspy.multilspy_utils import FileUtils, PlatformUtils from solidlsp.ls import SolidLanguageServer @@ -48,7 +47,7 @@ class ClangdLanguageServer(SolidLanguageServer): """ platform_id = PlatformUtils.get_platform_id() - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r") as f: + with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json")) as f: d = json.load(f) del d["_description"] @@ -56,12 +55,12 @@ class ClangdLanguageServer(SolidLanguageServer): "linux-x64", "win-x64", "osx-arm64", - ], "Unsupported platform: " + platform_id.value + ], ( + "Unsupported platform: " + platform_id.value + ) runtime_dependencies = d["runtimeDependencies"] - runtime_dependencies = [ - dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value - ] + runtime_dependencies = [dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value] assert len(runtime_dependencies) == 1 # Select dependency matching the current platform dependency = next((dep for dep in runtime_dependencies if dep["platformId"] == platform_id.value), None) @@ -75,9 +74,7 @@ class ClangdLanguageServer(SolidLanguageServer): logger.log(f"Clangd executable not found at {clangd_executable_path}. Downloading from {clangd_url}", logging.INFO) os.makedirs(clangd_ls_dir, exist_ok=True) if dependency["archiveType"] == "zip": - FileUtils.download_and_extract_archive( - logger, clangd_url, clangd_ls_dir, dependency["archiveType"] - ) + FileUtils.download_and_extract_archive(logger, clangd_url, clangd_ls_dir, dependency["archiveType"]) else: raise RuntimeError(f"Unsupported archive type: {dependency['archiveType']}") if not os.path.exists(clangd_executable_path): @@ -93,7 +90,7 @@ class ClangdLanguageServer(SolidLanguageServer): """ Returns the initialize params for the clangd Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r") as f: + with open(os.path.join(os.path.dirname(__file__), "initialize_params.json")) as f: d = json.load(f) del d["_description"] @@ -126,6 +123,7 @@ class ClangdLanguageServer(SolidLanguageServer): # Shutdown the LanguageServer on exit from scope # LanguageServer has been shutdown """ + def register_capability_handler(params): assert "registrations" in params for registration in params["registrations"]: @@ -175,7 +173,7 @@ class ClangdLanguageServer(SolidLanguageServer): assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 assert "completionProvider" in init_response["capabilities"] assert init_response["capabilities"]["completionProvider"] == { - "triggerCharacters": ['.', '<', '>', ':', '"', '/', '*'], + "triggerCharacters": [".", "<", ">", ":", '"', "/", "*"], "resolveProvider": False, } @@ -185,4 +183,3 @@ class ClangdLanguageServer(SolidLanguageServer): # set ready flag self.server_ready.set() self.server_ready.wait() - diff --git a/src/solidlsp/language_servers/dart_language_server/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server/dart_language_server.py index 2a0c4e66..ec81c8c7 100644 --- a/src/solidlsp/language_servers/dart_language_server/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server/dart_language_server.py @@ -19,7 +19,6 @@ class DartLanguageServer(SolidLanguageServer): """ Creates a DartServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ - executable_path = self.setup_runtime_dependencies(logger) super().__init__( config, @@ -32,14 +31,12 @@ class DartLanguageServer(SolidLanguageServer): def setup_runtime_dependencies(self, logger: "MultilspyLogger") -> str: platform_id = PlatformUtils.get_platform_id() - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r") as f: + with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json")) as f: d = json.load(f) del d["_description"] runtime_dependencies = d["runtimeDependencies"] - runtime_dependencies = [ - dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value - ] + runtime_dependencies = [dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value] assert len(runtime_dependencies) == 1 dependency = runtime_dependencies[0] @@ -49,24 +46,18 @@ class DartLanguageServer(SolidLanguageServer): if not os.path.exists(dart_ls_dir): os.makedirs(dart_ls_dir) - FileUtils.download_and_extract_archive( - logger, dependency["url"], dart_ls_dir, dependency["archiveType"] - ) - + FileUtils.download_and_extract_archive(logger, dependency["url"], dart_ls_dir, dependency["archiveType"]) assert os.path.exists(dart_executable_path) os.chmod(dart_executable_path, stat.S_IEXEC) return f"{dart_executable_path} language-server --client-id multilspy.dart --client-version 1.2" - def _get_initialize_params(self, repository_absolute_path: str): """ Returns the initialize params for the Dart Language Server. """ - with open( - os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r" - ) as f: + with open(os.path.join(os.path.dirname(__file__), "initialize_params.json")) as f: d = json.load(f) del d["_description"] @@ -79,9 +70,7 @@ class DartLanguageServer(SolidLanguageServer): d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() assert d["workspaceFolders"][0]["uri"] == "$uri" - d["workspaceFolders"][0]["uri"] = pathlib.Path( - repository_absolute_path - ).as_uri() + d["workspaceFolders"][0]["uri"] = pathlib.Path(repository_absolute_path).as_uri() assert d["workspaceFolders"][0]["name"] == "$name" d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) @@ -108,28 +97,20 @@ class DartLanguageServer(SolidLanguageServer): self.server.on_request("client/registerCapability", do_nothing) self.server.on_notification("language/status", do_nothing) self.server.on_notification("window/logMessage", window_log_message) - self.server.on_request( - "workspace/executeClientCommand", execute_client_command_handler - ) + self.server.on_request("workspace/executeClientCommand", execute_client_command_handler) self.server.on_notification("$/progress", do_nothing) self.server.on_notification("textDocument/publishDiagnostics", do_nothing) self.server.on_notification("language/actionableNotification", do_nothing) - self.server.on_notification( - "experimental/serverStatus", check_experimental_status - ) + self.server.on_notification("experimental/serverStatus", check_experimental_status) - self.logger.log( - "Starting dart-language-server server process", logging.INFO - ) + self.logger.log("Starting dart-language-server server process", logging.INFO) 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, ) - init_response = self.server.send_request( - "initialize", initialize_params - ) + init_response = self.server.send_request("initialize", initialize_params) self.logger.log( f"Received initialize response from dart-language-server: {init_response}", logging.INFO, diff --git a/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py index 1b92fc4d..8bce13be 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py @@ -20,8 +20,7 @@ from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo from multilspy.multilspy_config import MultilspyConfig from multilspy.multilspy_logger import MultilspyLogger from multilspy.multilspy_settings import MultilspySettings -from multilspy.multilspy_utils import FileUtils -from multilspy.multilspy_utils import PlatformUtils +from multilspy.multilspy_utils import FileUtils, PlatformUtils from solidlsp.ls import SolidLanguageServer @@ -51,7 +50,6 @@ class EclipseJDTLS(SolidLanguageServer): 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) self.runtime_dependency_paths = runtime_dependency_paths @@ -66,9 +64,7 @@ class EclipseJDTLS(SolidLanguageServer): ) # shared_cache_location is the global cache used by Eclipse JDTLS across all workspaces - shared_cache_location = str( - PurePath(MultilspySettings.get_global_cache_directory(), "lsp", "EclipseJDTLS", "sharedIndex") - ) + shared_cache_location = str(PurePath(MultilspySettings.get_global_cache_directory(), "lsp", "EclipseJDTLS", "sharedIndex")) jre_path = self.runtime_dependency_paths.jre_path lombok_jar_path = self.runtime_dependency_paths.lombok_jar_path @@ -138,7 +134,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") - + @override def is_ignored_dirname(self, dirname: str) -> bool: # Ignore common Java build directories from different build tools: @@ -148,13 +144,13 @@ class EclipseJDTLS(SolidLanguageServer): # - IntelliJ IDEA: out, .idea # - General: classes, dist, lib return super().is_ignored_dirname(dirname) or dirname in [ - "target", # Maven - "build", # Gradle - "bin", # Eclipse - "out", # IntelliJ IDEA - "classes", # General - "dist", # General - "lib" # General + "target", # Maven + "build", # Gradle + "bin", # Eclipse + "out", # IntelliJ IDEA + "classes", # General + "dist", # General + "lib", # General ] def setupRuntimeDependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> RuntimeDependencyPaths: @@ -163,7 +159,7 @@ class EclipseJDTLS(SolidLanguageServer): """ platformId = PlatformUtils.get_platform_id() - with open(str(PurePath(os.path.dirname(__file__), "runtime_dependencies.json")), "r", encoding="utf-8") as f: + with open(str(PurePath(os.path.dirname(__file__), "runtime_dependencies.json")), encoding="utf-8") as f: runtimeDependencies = json.load(f) del runtimeDependencies["_description"] @@ -192,9 +188,7 @@ class EclipseJDTLS(SolidLanguageServer): assert os.path.exists(gradle_path) dependency = runtimeDependencies["vscode-java"][platformId.value] - vscode_java_path = str( - PurePath(os.path.abspath(os.path.dirname(__file__)), "static", dependency["relative_extraction_path"]) - ) + vscode_java_path = str(PurePath(os.path.abspath(os.path.dirname(__file__)), "static", dependency["relative_extraction_path"])) os.makedirs(vscode_java_path, exist_ok=True) jre_home_path = str(PurePath(vscode_java_path, dependency["jre_home_path"])) jre_path = str(PurePath(vscode_java_path, dependency["jre_path"])) @@ -211,9 +205,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(logger, dependency["url"], vscode_java_path, dependency["archiveType"]) os.chmod(jre_path, stat.S_IEXEC) @@ -238,9 +230,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(logger, dependency["url"], intellicode_directory_path, dependency["archiveType"]) assert os.path.exists(intellicode_directory_path) assert os.path.exists(intellicode_jar_path) @@ -262,7 +252,7 @@ class EclipseJDTLS(SolidLanguageServer): Returns the initialize parameters for the EclipseJDTLS server. """ # Look into https://github.com/eclipse/eclipse.jdt.ls/blob/master/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java to understand all the options available - with open(str(PurePath(os.path.dirname(__file__), "initialize_params.json")), "r", encoding="utf-8") as f: + with open(str(PurePath(os.path.dirname(__file__), "initialize_params.json")), encoding="utf-8") as f: d: InitializeParams = json.load(f) del d["_description"] @@ -307,18 +297,12 @@ class EclipseJDTLS(SolidLanguageServer): for runtime in d["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"]: assert "name" in runtime assert "path" in runtime - assert os.path.exists( - runtime["path"] - ), f"Runtime required for eclipse_jdtls at path {runtime['path']} does not exist" + assert os.path.exists(runtime["path"]), f"Runtime required for eclipse_jdtls at path {runtime['path']} does not exist" assert d["initializationOptions"]["settings"]["java"]["import"]["gradle"]["home"] == "abs(static/gradle-7.3.3)" - d["initializationOptions"]["settings"]["java"]["import"]["gradle"][ - "home" - ] = self.runtime_dependency_paths.gradle_path + d["initializationOptions"]["settings"]["java"]["import"]["gradle"]["home"] = self.runtime_dependency_paths.gradle_path - d["initializationOptions"]["settings"]["java"]["import"]["gradle"]["java"][ - "home" - ] = self.runtime_dependency_paths.jre_path + d["initializationOptions"]["settings"]["java"]["import"]["gradle"]["java"]["home"] = self.runtime_dependency_paths.jre_path return d @@ -386,9 +370,7 @@ class EclipseJDTLS(SolidLanguageServer): self.server.notify.initialized({}) - self.server.notify.workspace_did_change_configuration( - {"settings": initialize_params["initializationOptions"]["settings"]} - ) + self.server.notify.workspace_did_change_configuration({"settings": initialize_params["initializationOptions"]["settings"]}) self.intellicode_enable_command_available.wait() diff --git a/src/solidlsp/language_servers/gopls/gopls.py b/src/solidlsp/language_servers/gopls/gopls.py index a2ec2722..78f15caf 100644 --- a/src/solidlsp/language_servers/gopls/gopls.py +++ b/src/solidlsp/language_servers/gopls/gopls.py @@ -18,7 +18,7 @@ class Gopls(SolidLanguageServer): """ Provides Go specific instantiation of the LanguageServer class using gopls. """ - + @override def is_ignored_dirname(self, dirname: str) -> bool: # For Go projects, we should ignore: @@ -31,7 +31,7 @@ class Gopls(SolidLanguageServer): def _get_go_version(): """Get the installed Go version or None if not found.""" try: - result = subprocess.run(['go', 'version'], capture_output=True, text=True) + result = subprocess.run(["go", "version"], capture_output=True, text=True, check=False) if result.returncode == 0: return result.stdout.strip() except FileNotFoundError: @@ -42,7 +42,7 @@ class Gopls(SolidLanguageServer): def _get_gopls_version(): """Get the installed gopls version or None if not found.""" try: - result = subprocess.run(['gopls', 'version'], capture_output=True, text=True) + result = subprocess.run(["gopls", "version"], capture_output=True, text=True, check=False) if result.returncode == 0: return result.stdout.strip() except FileNotFoundError: @@ -57,8 +57,10 @@ class Gopls(SolidLanguageServer): """ go_version = cls._get_go_version() if not go_version: - raise RuntimeError("Go is not installed. Please install Go from https://golang.org/doc/install and make sure it is added to your PATH.") - + raise RuntimeError( + "Go is not installed. Please install Go from https://golang.org/doc/install and make sure it is added to your PATH." + ) + gopls_version = cls._get_gopls_version() if not gopls_version: raise RuntimeError( @@ -66,12 +68,12 @@ class Gopls(SolidLanguageServer): "Please install gopls as described in https://pkg.go.dev/golang.org/x/tools/gopls#section-readme\n\n" "After installation, make sure it is added to your PATH (it might be installed in a different location than Go)." ) - + return True def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): self.setup_runtime_dependency() - + super().__init__( config, logger, @@ -86,7 +88,7 @@ class Gopls(SolidLanguageServer): """ Returns the initialize params for the TypeScript Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] @@ -108,6 +110,7 @@ class Gopls(SolidLanguageServer): def _start_server(self): """Start gopls server process""" + def register_capability_handler(params): return diff --git a/src/solidlsp/language_servers/intelephense/intelephense.py b/src/solidlsp/language_servers/intelephense/intelephense.py index 9b6eb955..fa415734 100644 --- a/src/solidlsp/language_servers/intelephense/intelephense.py +++ b/src/solidlsp/language_servers/intelephense/intelephense.py @@ -16,7 +16,7 @@ from multilspy.lsp_protocol_handler.lsp_types import DefinitionParams, Initializ from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo from multilspy.multilspy_config import MultilspyConfig from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import PlatformUtils, PlatformId +from multilspy.multilspy_utils import PlatformId, PlatformUtils from solidlsp.ls import SolidLanguageServer @@ -24,14 +24,14 @@ class Intelephense(SolidLanguageServer): """ Provides PHP specific instantiation of the LanguageServer class using Intelephense. """ - + @override def is_ignored_dirname(self, dirname: str) -> bool: # For PHP projects, we should ignore: # - vendor: third-party dependencies managed by Composer # - node_modules: if the project has JavaScript components # - cache: commonly used for caching - return super().is_ignored_dirname(dirname) or dirname in ["node_modules", "vendor", "cache"] + return super().is_ignored_dirname(dirname) or dirname in ["node_modules", "vendor", "cache"] def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str: """ @@ -50,17 +50,17 @@ class Intelephense(SolidLanguageServer): ] assert platform_id in valid_platforms, f"Platform {platform_id} is not supported for multilspy PHP at the moment" - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] runtime_dependencies = d.get("runtimeDependencies", []) intelephense_ls_dir = os.path.join(os.path.dirname(__file__), "static", "php-lsp") - + # Verify both node and npm are installed - is_node_installed = shutil.which('node') is not None + is_node_installed = shutil.which("node") is not None assert is_node_installed, "node is not installed or isn't in PATH. Please install NodeJS and try again." - is_npm_installed = shutil.which('npm') is not None + is_npm_installed = shutil.which("npm") is not None assert is_npm_installed, "npm is not installed or isn't in PATH. Please install npm and try again." # Install intelephense if not already installed @@ -75,11 +75,12 @@ class Intelephense(SolidLanguageServer): check=True, cwd=intelephense_ls_dir, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL + stderr=subprocess.DEVNULL, ) else: # On Unix-like systems, run as non-root user import pwd + user = pwd.getpwuid(os.getuid()).pw_name subprocess.run( dependency["command"], @@ -88,32 +89,26 @@ class Intelephense(SolidLanguageServer): user=user, cwd=intelephense_ls_dir, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL + stderr=subprocess.DEVNULL, ) - + intelephense_executable_path = os.path.join(intelephense_ls_dir, "node_modules", ".bin", "intelephense") assert os.path.exists(intelephense_executable_path), "intelephense executable not found. Please install intelephense and try again." - + return f"{intelephense_executable_path} --stdio" def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): # Setup runtime dependencies before initializing intelephense_cmd = self.setup_runtime_dependencies(logger, config) - - super().__init__( - config, - logger, - repository_root_path, - ProcessLaunchInfo(cmd=intelephense_cmd, cwd=repository_root_path), - "php" - ) + + super().__init__(config, logger, repository_root_path, ProcessLaunchInfo(cmd=intelephense_cmd, cwd=repository_root_path), "php") self.request_id = 0 def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the TypeScript Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] @@ -135,6 +130,7 @@ class Intelephense(SolidLanguageServer): def _start_server(self): """Start Intelephense server process""" + def register_capability_handler(params): return @@ -184,7 +180,7 @@ class Intelephense(SolidLanguageServer): # The sleeping doesn't seem to be needed on all systems sleep(1) return super()._send_references_request(relative_file_path, line, column) - + @override def _send_definition_request(self, definition_params: DefinitionParams): # TODO: same as above, also only a problem if the definition is in another file diff --git a/src/solidlsp/language_servers/jedi_language_server/jedi_server.py b/src/solidlsp/language_servers/jedi_language_server/jedi_server.py index 9a4dbc8a..65fbe623 100644 --- a/src/solidlsp/language_servers/jedi_language_server/jedi_server.py +++ b/src/solidlsp/language_servers/jedi_language_server/jedi_server.py @@ -32,7 +32,7 @@ class JediServer(SolidLanguageServer): ProcessLaunchInfo(cmd="jedi-language-server", cwd=repository_root_path), "python", ) - + @override def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["venv", "__pycache__"] @@ -41,7 +41,7 @@ class JediServer(SolidLanguageServer): """ Returns the initialize params for the Jedi Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] diff --git a/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py b/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py index 88e71518..bdb13330 100644 --- a/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py +++ b/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py @@ -13,8 +13,7 @@ from multilspy.lsp_protocol_handler.lsp_types import InitializeParams from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo from multilspy.multilspy_config import MultilspyConfig from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import FileUtils -from multilspy.multilspy_utils import PlatformUtils +from multilspy.multilspy_utils import FileUtils, PlatformUtils from solidlsp.ls import SolidLanguageServer @@ -23,6 +22,7 @@ class KotlinRuntimeDependencyPaths: """ Stores the paths to the runtime dependencies of Kotlin Language Server """ + java_path: str java_home_path: str kotlin_executable_path: str @@ -39,13 +39,13 @@ class KotlinLanguageServer(SolidLanguageServer): """ runtime_dependency_paths = self.setup_runtime_dependencies(logger, config) self.runtime_dependency_paths = runtime_dependency_paths - + # Create command to execute the Kotlin Language Server script cmd = f'"{self.runtime_dependency_paths.kotlin_executable_path}"' - + # Set environment variables including JAVA_HOME proc_env = {"JAVA_HOME": self.runtime_dependency_paths.java_home_path} - + super().__init__( config, logger, @@ -61,77 +61,75 @@ class KotlinLanguageServer(SolidLanguageServer): platform_id = PlatformUtils.get_platform_id() # Verify platform support - assert platform_id.value.startswith("win-") or platform_id.value.startswith("linux-") or platform_id.value.startswith("osx-"), "Only Windows, Linux and macOS platforms are supported for Kotlin in multilspy at the moment" + assert ( + platform_id.value.startswith("win-") or platform_id.value.startswith("linux-") or platform_id.value.startswith("osx-") + ), "Only Windows, Linux and macOS platforms are supported for Kotlin in multilspy at the moment" # Load dependency information - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] - + kotlin_dependency = d["runtimeDependency"] java_dependency = d["java"][platform_id.value] # Setup paths for dependencies static_dir = os.path.join(os.path.dirname(__file__), "static") os.makedirs(static_dir, exist_ok=True) - + # Setup Java paths java_dir = os.path.join(static_dir, "java") os.makedirs(java_dir, exist_ok=True) - + java_home_path = os.path.join(java_dir, java_dependency["java_home_path"]) java_path = os.path.join(java_dir, java_dependency["java_path"]) - + # 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"] - ) + FileUtils.download_and_extract_archive(logger, java_dependency["url"], java_dir, java_dependency["archiveType"]) # Make Java executable if not platform_id.value.startswith("win-"): os.chmod(java_path, 0o755) - + assert os.path.exists(java_path), f"Java executable not found at {java_path}" - + # Setup Kotlin Language Server paths kotlin_ls_dir = os.path.join(static_dir, "server") - + # Get platform-specific executable script path if platform_id.value.startswith("win-"): kotlin_script = os.path.join(kotlin_ls_dir, "bin", "kotlin-language-server.bat") else: kotlin_script = os.path.join(kotlin_ls_dir, "bin", "kotlin-language-server") - + # 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"] - ) - + FileUtils.download_and_extract_archive(logger, kotlin_dependency["url"], static_dir, kotlin_dependency["archiveType"]) + # Make script executable on Unix platforms if os.path.exists(kotlin_script) and not platform_id.value.startswith("win-"): - os.chmod(kotlin_script, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) - + os.chmod( + kotlin_script, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH + ) + # 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) else: raise FileNotFoundError(f"Kotlin Language Server script not found at {kotlin_script}") - + return KotlinRuntimeDependencyPaths( - java_path=java_path, - java_home_path=java_home_path, - kotlin_executable_path=kotlin_executable_path + java_path=java_path, java_home_path=java_home_path, kotlin_executable_path=kotlin_executable_path ) def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the Kotlin Language Server. """ - with open(str(pathlib.PurePath(os.path.dirname(__file__), "initialize_params.json")), "r", encoding="utf-8") as f: + with open(str(pathlib.PurePath(os.path.dirname(__file__), "initialize_params.json")), encoding="utf-8") as f: d: InitializeParams = json.load(f) del d["_description"] @@ -152,8 +150,8 @@ class KotlinLanguageServer(SolidLanguageServer): d["initializationOptions"]["workspaceFolders"] = [pathlib.Path(repository_absolute_path).as_uri()] assert ( - d["workspaceFolders"] - == '[\n {\n "uri": pathlib.Path(repository_absolute_path).as_uri(),\n "name": os.path.basename(repository_absolute_path),\n }\n ]' + d["workspaceFolders"] + == '[\n {\n "uri": pathlib.Path(repository_absolute_path).as_uri(),\n "name": os.path.basename(repository_absolute_path),\n }\n ]' ) d["workspaceFolders"] = [ { @@ -168,6 +166,7 @@ class KotlinLanguageServer(SolidLanguageServer): """ Starts the Kotlin Language Server """ + def execute_client_command_handler(params): return [] diff --git a/src/solidlsp/language_servers/omnisharp/omnisharp.py b/src/solidlsp/language_servers/omnisharp/omnisharp.py index a449a985..cc10f58a 100644 --- a/src/solidlsp/language_servers/omnisharp/omnisharp.py +++ b/src/solidlsp/language_servers/omnisharp/omnisharp.py @@ -8,7 +8,7 @@ import os import pathlib import stat import threading -from typing import Iterable +from collections.abc import Iterable from overrides import override @@ -17,7 +17,7 @@ from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo from multilspy.multilspy_config import MultilspyConfig from multilspy.multilspy_exceptions import MultilspyException from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import FileUtils, PlatformUtils, PlatformId, DotnetVersion +from multilspy.multilspy_utils import DotnetVersion, FileUtils, PlatformId, PlatformUtils from solidlsp.ls import SolidLanguageServer @@ -28,7 +28,7 @@ def breadth_first_file_scan(root) -> Iterable[str]: """ dirs = [root] # while we has dirs to scan - while len(dirs): + while dirs: next_dirs = [] for parent in dirs: # scan each dir @@ -102,14 +102,12 @@ class OmniSharp(SolidLanguageServer): "formattingOptions:indentationSize=4", ] ) - super().__init__( - config, logger, repository_root_path, ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path), "csharp" - ) + super().__init__(config, logger, repository_root_path, ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path), "csharp") self.server_ready = threading.Event() self.definition_available = threading.Event() self.references_available = threading.Event() - + @override def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["bin", "obj"] @@ -118,7 +116,7 @@ class OmniSharp(SolidLanguageServer): """ Returns the initialize params for the Omnisharp Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] @@ -145,7 +143,7 @@ class OmniSharp(SolidLanguageServer): platform_id = PlatformUtils.get_platform_id() dotnet_version = PlatformUtils.get_dotnet_version() - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] @@ -156,7 +154,7 @@ class OmniSharp(SolidLanguageServer): assert dotnet_version in [ DotnetVersion.V6, DotnetVersion.V7, - DotnetVersion.V8 + DotnetVersion.V8, ], "Only dotnet version 6 and 7 are supported in multilspy at the moment" # TODO: Do away with this assumption @@ -165,13 +163,11 @@ class OmniSharp(SolidLanguageServer): dotnet_version = DotnetVersion.V6 runtime_dependencies = d["runtimeDependencies"] - runtime_dependencies = [ - dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value - ] + runtime_dependencies = [dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value] runtime_dependencies = [ dependency for dependency in runtime_dependencies - if not ("dotnet_version" in dependency) or dependency["dotnet_version"] == dotnet_version.value + if "dotnet_version" not in dependency or dependency["dotnet_version"] == dotnet_version.value ] assert len(runtime_dependencies) == 2 runtime_dependencies = { @@ -185,9 +181,7 @@ class OmniSharp(SolidLanguageServer): omnisharp_ls_dir = os.path.join(os.path.dirname(__file__), "static", "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(logger, 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, stat.S_IEXEC) @@ -195,12 +189,8 @@ class OmniSharp(SolidLanguageServer): razor_omnisharp_ls_dir = os.path.join(os.path.dirname(__file__), "static", "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" - ) - razor_omnisharp_dll_path = os.path.join( - razor_omnisharp_ls_dir, runtime_dependencies["RazorOmnisharp"]["dll_path"] - ) + FileUtils.download_and_extract_archive(logger, 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) return omnisharp_executable_path, razor_omnisharp_dll_path @@ -373,20 +363,12 @@ class OmniSharp(SolidLanguageServer): ) init_response = self.server.send.initialize(initialize_params) self.server.notify.initialized({}) - with open(os.path.join(os.path.dirname(__file__), "workspace_did_change_configuration.json"), "r", encoding="utf-8") as f: - self.server.notify.workspace_did_change_configuration({ - "settings": json.load(f) - }) + with open(os.path.join(os.path.dirname(__file__), "workspace_did_change_configuration.json"), encoding="utf-8") as f: + self.server.notify.workspace_did_change_configuration({"settings": json.load(f)}) assert "capabilities" in init_response - if ( - "definitionProvider" in init_response["capabilities"] - and init_response["capabilities"]["definitionProvider"] - ): + if "definitionProvider" in init_response["capabilities"] and init_response["capabilities"]["definitionProvider"]: self.definition_available.set() - if ( - "referencesProvider" in init_response["capabilities"] - and init_response["capabilities"]["referencesProvider"] - ): + if "referencesProvider" in init_response["capabilities"] and init_response["capabilities"]["referencesProvider"]: self.references_available.set() self.definition_available.wait() diff --git a/src/solidlsp/language_servers/pyright_language_server/pyright_server.py b/src/solidlsp/language_servers/pyright_language_server/pyright_server.py index b3490502..1307e0e1 100644 --- a/src/solidlsp/language_servers/pyright_language_server/pyright_server.py +++ b/src/solidlsp/language_servers/pyright_language_server/pyright_server.py @@ -22,6 +22,7 @@ class PyrightServer(SolidLanguageServer): Provides Python specific instantiation of the LanguageServer class using Pyright. Contains various configurations and settings specific to Python. """ + def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): """ Creates a PyrightServer instance. This class is not meant to be instantiated directly. @@ -50,7 +51,7 @@ class PyrightServer(SolidLanguageServer): Returns the initialize params for the Pyright Language Server. """ # Create basic initialization parameters - initialize_params: InitializeParams = { # type: ignore + initialize_params: InitializeParams = { # type: ignore "processId": os.getpid(), "rootPath": repository_absolute_path, "rootUri": pathlib.Path(repository_absolute_path).as_uri(), diff --git a/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py index eb072c49..e0b1a8ca 100644 --- a/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py @@ -15,8 +15,7 @@ from multilspy.lsp_protocol_handler.lsp_types import InitializeParams from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo from multilspy.multilspy_config import MultilspyConfig from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import FileUtils -from multilspy.multilspy_utils import PlatformUtils +from multilspy.multilspy_utils import FileUtils, PlatformUtils from solidlsp.ls import SolidLanguageServer @@ -52,7 +51,7 @@ class RustAnalyzer(SolidLanguageServer): """ platform_id = PlatformUtils.get_platform_id() - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] @@ -62,9 +61,7 @@ class RustAnalyzer(SolidLanguageServer): # ], "Only linux-x64 and win-x64 platform is supported for in multilspy at the moment" runtime_dependencies = d["runtimeDependencies"] - runtime_dependencies = [ - dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value - ] + runtime_dependencies = [dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value] assert len(runtime_dependencies) == 1 dependency = runtime_dependencies[0] @@ -73,13 +70,9 @@ class RustAnalyzer(SolidLanguageServer): if not os.path.exists(rustanalyzer_ls_dir): os.makedirs(rustanalyzer_ls_dir) if dependency["archiveType"] == "gz": - FileUtils.download_and_extract_archive( - logger, dependency["url"], rustanalyzer_executable_path, dependency["archiveType"] - ) + FileUtils.download_and_extract_archive(logger, dependency["url"], rustanalyzer_executable_path, dependency["archiveType"]) else: - FileUtils.download_and_extract_archive( - logger, dependency["url"], rustanalyzer_ls_dir, dependency["archiveType"] - ) + FileUtils.download_and_extract_archive(logger, dependency["url"], rustanalyzer_ls_dir, dependency["archiveType"]) assert os.path.exists(rustanalyzer_executable_path) os.chmod(rustanalyzer_executable_path, stat.S_IEXEC) @@ -89,7 +82,7 @@ class RustAnalyzer(SolidLanguageServer): """ Returns the initialize params for the Rust Analyzer Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] diff --git a/src/solidlsp/language_servers/solargraph/solargraph.py b/src/solidlsp/language_servers/solargraph/solargraph.py index d036ed1e..96fc7045 100644 --- a/src/solidlsp/language_servers/solargraph/solargraph.py +++ b/src/solidlsp/language_servers/solargraph/solargraph.py @@ -51,8 +51,7 @@ class Solargraph(SolidLanguageServer): """ Setup runtime dependencies for Solargraph. """ - - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] @@ -64,38 +63,40 @@ class Solargraph(SolidLanguageServer): ruby_version = result.stdout.strip() logger.log(f"Ruby version: {ruby_version}", logging.INFO) except subprocess.CalledProcessError as e: - raise RuntimeError(f"Error checking for Ruby installation: {e.stderr}") - except FileNotFoundError: - raise RuntimeError("Ruby is not installed. Please install Ruby before continuing.") + raise RuntimeError(f"Error checking for Ruby installation: {e.stderr}") from e + except FileNotFoundError as e: + raise RuntimeError("Ruby is not installed. Please install Ruby before continuing.") from e # Check if solargraph is installed try: - result = subprocess.run(["gem", "list", "^solargraph$", "-i"], check=False, capture_output=True, text=True, cwd=repository_root_path) + result = subprocess.run( + ["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) subprocess.run(dependency["installCommand"].split(), check=True, capture_output=True, cwd=repository_root_path) - + # Get the gem executable path directly result = subprocess.run(["gem", "which", "solargraph"], check=True, capture_output=True, text=True, cwd=repository_root_path) gem_path = result.stdout.strip() bin_dir = os.path.join(os.path.dirname(os.path.dirname(gem_path)), "bin") executable_path = os.path.join(bin_dir, "solargraph") - + if not os.path.exists(executable_path): raise RuntimeError(f"Solargraph executable not found at {executable_path}") - + # Ensure the executable has the right permissions os.chmod(executable_path, os.stat(executable_path).st_mode | stat.S_IEXEC) return executable_path except subprocess.CalledProcessError as e: - raise RuntimeError(f"Failed to check or install Solargraph. {e.stderr}") + raise RuntimeError(f"Failed to check or install Solargraph. {e.stderr}") from e def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the Solargraph Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] diff --git a/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py index e17a0987..7d5fae62 100644 --- a/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py @@ -21,14 +21,14 @@ from multilspy.multilspy_utils import PlatformId, PlatformUtils from solidlsp.ls import SolidLanguageServer # Platform-specific imports -if os.name != 'nt': # Unix-like systems +if os.name != "nt": # Unix-like systems import pwd else: # Dummy pwd module for Windows class pwd: @staticmethod def getpwuid(uid): - return type('obj', (), {'pw_name': os.environ.get('USERNAME', 'unknown')})() + return type("obj", (), {"pw_name": os.environ.get("USERNAME", "unknown")})() # Conditionally import pwd module (Unix-only) @@ -91,9 +91,9 @@ class TypeScriptLanguageServer(SolidLanguageServer): tsserver_executable_path = os.path.join(tsserver_ls_dir, "typescript-language-server") # Verify both node and npm are installed - is_node_installed = shutil.which('node') is not None + is_node_installed = shutil.which("node") is not None assert is_node_installed, "node is not installed or isn't in PATH. Please install NodeJS and try again." - is_npm_installed = shutil.which('npm') is not None + is_npm_installed = shutil.which("npm") is not None assert is_npm_installed, "npm is not installed or isn't in PATH. Please install npm and try again." # Install typescript and typescript-language-server if not already installed @@ -108,7 +108,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): check=True, cwd=tsserver_ls_dir, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL + stderr=subprocess.DEVNULL, ) else: # On Unix-like systems, run as non-root user @@ -120,12 +120,14 @@ class TypeScriptLanguageServer(SolidLanguageServer): user=user, cwd=tsserver_ls_dir, stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL + stderr=subprocess.DEVNULL, ) - + tsserver_executable_path = os.path.join(tsserver_ls_dir, "node_modules", ".bin", "typescript-language-server") - assert os.path.exists(tsserver_executable_path), "typescript-language-server executable not found. Please install typescript-language-server and try again." + assert os.path.exists( + tsserver_executable_path + ), "typescript-language-server executable not found. Please install typescript-language-server and try again." return f"{tsserver_executable_path} --stdio" def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: @@ -184,8 +186,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): def window_log_message(msg): self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - + def check_experimental_status(params): """ Also listen for experimental/serverStatus as a backup signal @@ -215,8 +216,8 @@ class TypeScriptLanguageServer(SolidLanguageServer): assert init_response["capabilities"]["textDocumentSync"] == 2 assert "completionProvider" in init_response["capabilities"] assert init_response["capabilities"]["completionProvider"] == { - "triggerCharacters": ['.', '"', "'", '/', '@', '<'], - "resolveProvider": True + "triggerCharacters": [".", '"', "'", "/", "@", "<"], + "resolveProvider": True, } self.server.notify.initialized({}) diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index 9981ed1e..11c9febb 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -5,18 +5,19 @@ import pathlib import pickle import re import threading -from abc import abstractmethod, ABC +from abc import ABC, abstractmethod from collections import defaultdict +from collections.abc import Iterator from contextlib import contextmanager from copy import copy from pathlib import Path, PurePath -from typing import Dict, Iterator, List, Optional, Tuple, Union, cast, Self +from typing import Self, cast import pathspec import tqdm from multilspy import multilspy_types -from multilspy.language_server import LSPFileBuffer, GenericDocumentSymbol, ReferenceInSymbol +from multilspy.language_server import GenericDocumentSymbol, LSPFileBuffer, ReferenceInSymbol from multilspy.lsp_protocol_handler import lsp_types from multilspy.lsp_protocol_handler import lsp_types as LSPTypes from multilspy.lsp_protocol_handler.lsp_constants import LSPConstants @@ -46,10 +47,12 @@ class SolidLanguageServer(ABC): A language-specific condition for directories that should always be ignored. For example, venv in Python and node_modules in JS/TS should be ignored always. """ - return dirname.startswith('.') + return dirname.startswith(".") @classmethod - def create(cls, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str, timeout: Optional[float] = None) -> "SolidLanguageServer": + def create( + cls, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str, timeout: float | None = None + ) -> "SolidLanguageServer": """ Creates a language specific LanguageServer instance based on the given configuration, and appropriate settings for the programming language. @@ -66,6 +69,7 @@ class SolidLanguageServer(ABC): from solidlsp.language_servers.pyright_language_server.pyright_server import ( PyrightServer, ) + return PyrightServer(config, logger, repository_root_path) # It used to be jedi, but pyright is a bit faster, and also more actively maintained # Keeping the previous code for reference @@ -79,48 +83,58 @@ class SolidLanguageServer(ABC): from solidlsp.language_servers.eclipse_jdtls.eclipse_jdtls import ( EclipseJDTLS, ) + return EclipseJDTLS(config, logger, repository_root_path) elif config.code_language == Language.KOTLIN: from solidlsp.language_servers.kotlin_language_server.kotlin_language_server import ( KotlinLanguageServer, ) + return KotlinLanguageServer(config, logger, repository_root_path) elif config.code_language == Language.RUST: from solidlsp.language_servers.rust_analyzer.rust_analyzer import ( RustAnalyzer, ) + return RustAnalyzer(config, logger, repository_root_path) elif config.code_language == Language.CSHARP: from solidlsp.language_servers.omnisharp.omnisharp import OmniSharp + return OmniSharp(config, logger, repository_root_path) elif config.code_language in [Language.TYPESCRIPT, Language.JAVASCRIPT]: from solidlsp.language_servers.typescript_language_server.typescript_language_server import ( TypeScriptLanguageServer, ) + return TypeScriptLanguageServer(config, logger, repository_root_path) elif config.code_language == Language.GO: from solidlsp.language_servers.gopls.gopls import Gopls + return Gopls(config, logger, repository_root_path) elif config.code_language == Language.RUBY: from solidlsp.language_servers.solargraph.solargraph import Solargraph + return Solargraph(config, logger, repository_root_path) elif config.code_language == Language.DART: from solidlsp.language_servers.dart_language_server.dart_language_server import DartLanguageServer + return DartLanguageServer(config, logger, repository_root_path) elif config.code_language == Language.CPP: from solidlsp.language_servers.clangd_language_server.clangd_language_server import ClangdLanguageServer + return ClangdLanguageServer(config, logger, repository_root_path) elif config.code_language == Language.PHP: from solidlsp.language_servers.intelephense.intelephense import Intelephense + return Intelephense(config, logger, repository_root_path) else: @@ -128,12 +142,12 @@ class SolidLanguageServer(ABC): raise MultilspyException(f"Language {config.code_language} is not supported") def __init__( - self, - config: MultilspyConfig, - logger: MultilspyLogger, - repository_root_path: str, - process_launch_info: ProcessLaunchInfo, - language_id: str, + self, + config: MultilspyConfig, + logger: MultilspyLogger, + repository_root_path: str, + process_launch_info: ProcessLaunchInfo, + language_id: str, ): """ Initializes a LanguageServer instance. @@ -150,14 +164,19 @@ class SolidLanguageServer(ABC): """ 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) + self.logger.log( + f"Creating language server instance for {repository_root_path=} with {language_id=} and process launch info: {process_launch_info}", + logging.DEBUG, + ) self.language_id = language_id - self.open_file_buffers: Dict[str, LSPFileBuffer] = {} + self.open_file_buffers: dict[str, LSPFileBuffer] = {} self.language = Language(language_id) # load cache first to prevent any racing conditions due to asyncio stuff - self._document_symbols_cache: dict[str, Tuple[str, Tuple[List[multilspy_types.UnifiedSymbolInformation], List[multilspy_types.UnifiedSymbolInformation]]]] = {} + self._document_symbols_cache: dict[ + str, tuple[str, tuple[list[multilspy_types.UnifiedSymbolInformation], list[multilspy_types.UnifiedSymbolInformation]]] + ] = {} """Maps file paths to a tuple of (file_content_hash, result_of_request_document_symbols)""" self._cache_lock = threading.Lock() self._cache_has_changed: bool = False @@ -166,36 +185,35 @@ class SolidLanguageServer(ABC): self.server_started = False self.completions_available = threading.Event() if config.trace_lsp_communication: + def logging_fn(source: str, target: str, msg: StringDict | str): self.logger.log(f"LSP: {source} -> {target}: {str(msg)[:90]}...", self.logger.logger.level) + else: logging_fn = None - # 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) + self.logger.log( + f"Creating language server instance with {language_id=} and process launch info: {process_launch_info}", logging.DEBUG + ) self.server = SolidLanguageServerHandler( process_launch_info, logger=logging_fn, start_independent_lsp_process=config.start_independent_lsp_process, ) - # Set up the pathspec matcher for the ignored paths # for all absolute paths in ignored_paths, convert them to relative paths processed_patterns = [] for pattern in set(config.ignored_paths): # Normalize separators (pathspec expects forward slashes) - pattern = pattern.replace(os.path.sep, '/') + 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) # Create a pathspec matcher from the processed patterns - self._ignore_spec = pathspec.PathSpec.from_lines( - pathspec.patterns.GitWildMatchPattern, - processed_patterns - ) + self._ignore_spec = pathspec.PathSpec.from_lines(pathspec.patterns.GitWildMatchPattern, processed_patterns) self._server_context = None @@ -247,12 +265,12 @@ class SolidLanguageServer(ABC): # Use pathspec for gitignore-style pattern matching # Normalize path separators for pathspec (it expects forward slashes) - normalized_path = str(rel_path).replace(os.path.sep, '/') + normalized_path = str(rel_path).replace(os.path.sep, "/") # pathspec can't handle the matching of directories if they don't end with a slash! # see https://github.com/cpburnz/python-pathspec/issues/89 - if os.path.isdir(os.path.join(self.repository_root_path, normalized_path)) and not normalized_path.endswith('/'): - normalized_path = normalized_path + '/' + if os.path.isdir(os.path.join(self.repository_root_path, normalized_path)) and not normalized_path.endswith("/"): + normalized_path = normalized_path + "/" # Use the pathspec matcher to check if the path matches any ignore pattern if self.get_ignore_spec().match_file(normalized_path): @@ -271,7 +289,6 @@ class SolidLanguageServer(ABC): self.logger.log(f"Initiating final robust shutdown with a {timeout}s timeout...", logging.INFO) process = self.server.process - reader_tasks = list(self.server.tasks.values()) # --- Main Shutdown Logic --- # Stage 1: Graceful Termination Request @@ -281,50 +298,11 @@ class SolidLanguageServer(ABC): if process.stdin and not process.stdin.is_closing(): process.stdin.close() except Exception: - pass # Ignore errors here, we are proceeding to terminate anyway. + pass # Ignore errors here, we are proceeding to terminate anyway. # Stage 2: Terminate and Concurrently Drain stdout/stderr process.terminate() - """ - except asyncio.TimeoutError: - # Stage 3: Forceful Kill - self.logger.log("Graceful termination failed. Forcefully killing process...", logging.WARNING) - if self.server.is_running(): - try: - process.kill() - # Wait for the killed process to be reaped by the OS. - await process.wait() - except Exception as e: - self.logger.log(f"Error during forceful kill: {e}", logging.ERROR) - except Exception as e: - self.logger.log(f"An unexpected error occurred during shutdown logic: {e}", logging.ERROR) - - finally: - # === STAGE 4: EXPLICIT TASK & PIPE CLEANUP === - self.logger.log("Performing final task cancellation and pipe handle cleanup...", logging.DEBUG) - - # 1. Cancel any lingering reader tasks. - for task in reader_tasks: - if not task.done(): - task.cancel() - # Wait for cancellations to complete. - await asyncio.gather(*reader_tasks, return_exceptions=True) - self.server.tasks = {} - - # 2. Explicitly close each pipe to release OS handles. - for pipe in [process.stdin, process.stdout, process.stderr]: - if pipe and hasattr(pipe, 'is_closing') and not pipe.is_closing(): - try: - pipe.close() - except Exception: - pass - - # 3. Null out the process object in the handler. - self.server.process = None - self.logger.log("Shutdown sequence fully finished.", logging.DEBUG) - """ - @contextmanager def start_server(self) -> Iterator["SolidLanguageServer"]: self.start() @@ -393,10 +371,10 @@ class SolidLanguageServer(ABC): del self.open_file_buffers[uri] def insert_text_at_position( - self, relative_file_path: str, line: int, column: int, text_to_be_inserted: str + self, relative_file_path: str, line: int, column: int, text_to_be_inserted: str ) -> multilspy_types.Position: """ - Insert text at the given line and column in the given file and return + Insert text at the given line and column in the given file and return the updated cursor position after inserting the text. :param relative_file_path: The relative path of the file to open. @@ -442,10 +420,10 @@ class SolidLanguageServer(ABC): return multilspy_types.Position(line=new_l, character=new_c) def delete_text_between_positions( - self, - relative_file_path: str, - start: multilspy_types.Position, - end: multilspy_types.Position, + self, + relative_file_path: str, + start: multilspy_types.Position, + end: multilspy_types.Position, ) -> str: """ Delete text between the given start and end positions in the given file and return the deleted text. @@ -465,7 +443,9 @@ class SolidLanguageServer(ABC): file_buffer = self.open_file_buffers[uri] file_buffer.version += 1 - new_contents, deleted_text = TextUtils.delete_text_between_positions(file_buffer.contents, start_line=start["line"], start_col=start["character"], end_line=end["line"], end_col=end["character"]) + new_contents, deleted_text = TextUtils.delete_text_between_positions( + file_buffer.contents, start_line=start["line"], start_col=start["character"], end_line=end["line"], end_col=end["character"] + ) file_buffer.contents = new_contents self.server.notify.did_change_text_document( { @@ -478,12 +458,10 @@ class SolidLanguageServer(ABC): ) return deleted_text - def _send_definition_request(self, definition_params: DefinitionParams) -> Union[Definition, List[LocationLink], None]: + def _send_definition_request(self, definition_params: DefinitionParams) -> Definition | list[LocationLink] | None: return self.server.send.definition(definition_params) - def request_definition( - self, relative_file_path: str, line: int, column: int - ) -> List[multilspy_types.Location]: + def request_definition(self, relative_file_path: str, line: int, column: int) -> list[multilspy_types.Location]: """ Raise a [textDocument/definition](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition) request to the Language Server for the symbol at the given line and column in the given file. Wait for the response and return the result. @@ -494,7 +472,6 @@ 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( "find_function_definition called before Language Server started", @@ -504,20 +481,21 @@ class SolidLanguageServer(ABC): with self.open_file(relative_file_path): # sending request to the language server and waiting for response - definition_params = cast(DefinitionParams, { - LSPConstants.TEXT_DOCUMENT: { - LSPConstants.URI: pathlib.Path( - str(PurePath(self.repository_root_path, relative_file_path)) - ).as_uri() + definition_params = cast( + DefinitionParams, + { + LSPConstants.TEXT_DOCUMENT: { + LSPConstants.URI: pathlib.Path(str(PurePath(self.repository_root_path, relative_file_path))).as_uri() + }, + LSPConstants.POSITION: { + LSPConstants.LINE: line, + LSPConstants.CHARACTER: column, + }, }, - LSPConstants.POSITION: { - LSPConstants.LINE: line, - LSPConstants.CHARACTER: column, - }, - }) + ) response = self._send_definition_request(definition_params) - ret: List[multilspy_types.Location] = [] + ret: list[multilspy_types.Location] = [] if isinstance(response, list): # response is either of type Location[] or LocationLink[] for item in response: @@ -529,10 +507,10 @@ class SolidLanguageServer(ABC): new_item["relativePath"] = PathUtils.get_relative_path(new_item["absolutePath"], self.repository_root_path) ret.append(multilspy_types.Location(new_item)) elif ( - LSPConstants.ORIGIN_SELECTION_RANGE in item - and LSPConstants.TARGET_URI in item - and LSPConstants.TARGET_RANGE in item - and LSPConstants.TARGET_SELECTION_RANGE in item + LSPConstants.ORIGIN_SELECTION_RANGE in item + and LSPConstants.TARGET_URI in item + and LSPConstants.TARGET_RANGE in item + and LSPConstants.TARGET_SELECTION_RANGE in item ): new_item: multilspy_types.Location = {} new_item["uri"] = item[LSPConstants.TARGET_URI] @@ -565,7 +543,7 @@ class SolidLanguageServer(ABC): return ret # Some LS cause problems with this, so the call is isolated from the rest to allow overriding in subclasses - def _send_references_request(self, relative_file_path: str, line: int, column: int) -> List[lsp_types.Location] | None: + def _send_references_request(self, relative_file_path: str, line: int, column: int) -> list[lsp_types.Location] | None: return self.server.send.references( { "textDocument": {"uri": PathUtils.path_to_uri(os.path.join(self.repository_root_path, relative_file_path))}, @@ -574,9 +552,7 @@ class SolidLanguageServer(ABC): } ) - def request_references( - self, relative_file_path: str, line: int, column: int - ) -> List[multilspy_types.Location]: + def request_references(self, relative_file_path: str, line: int, column: int) -> list[multilspy_types.Location]: """ Raise a [textDocument/references](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references) request to the Language Server to find references to the symbol at the given line and column in the given file. Wait for the response and return the result. @@ -588,7 +564,6 @@ 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", @@ -596,13 +571,12 @@ class SolidLanguageServer(ABC): ) raise MultilspyException("Language Server not started") - with self.open_file(relative_file_path): try: response = self._send_references_request(relative_file_path, line=line, column=column) except Exception as e: # Catch LSP internal error (-32603) and raise a more informative exception - if isinstance(e, Error) and getattr(e, 'code', None) == -32603: + if isinstance(e, Error) and getattr(e, "code", None) == -32603: raise RuntimeError( f"LSP internal error (-32603) when requesting references for {relative_file_path}:{line}:{column}. " "This often occurs when requesting references for a symbol not referenced in the expected way. " @@ -611,7 +585,7 @@ class SolidLanguageServer(ABC): if response is None: return [] - ret: List[multilspy_types.Location] = [] + ret: list[multilspy_types.Location] = [] assert isinstance(response, list), f"Unexpected response from Language Server (expected list, got {type(response)}): {response}" for item in response: assert isinstance(item, dict), f"Unexpected response from Language Server (expected dict, got {type(item)}): {item}" @@ -633,8 +607,8 @@ class SolidLanguageServer(ABC): return ret def request_references_with_content( - self, relative_file_path: str, line: int, column: int, context_lines_before: int = 0, context_lines_after: int = 0 - ) -> List[MatchedConsecutiveLines]: + self, relative_file_path: str, line: int, column: int, context_lines_before: int = 0, context_lines_after: int = 0 + ) -> list[MatchedConsecutiveLines]: """ Like request_references, but returns the content of the lines containing the references, not just the locations. @@ -647,7 +621,10 @@ class SolidLanguageServer(ABC): :return: A list of MatchedConsecutiveLines objects, one for each reference. """ references = self.request_references(relative_file_path, line, column) - return [self.retrieve_content_around_line(ref["relativePath"], ref["range"]["start"]["line"], context_lines_before, context_lines_after) for ref in references] + return [ + self.retrieve_content_around_line(ref["relativePath"], ref["range"]["start"]["line"], context_lines_before, context_lines_after) + for ref in references + ] def retrieve_full_file_content(self, relative_file_path: str) -> str: """ @@ -656,7 +633,9 @@ class SolidLanguageServer(ABC): with self.open_file(relative_file_path) as file_data: return file_data.contents - def retrieve_content_around_line(self, relative_file_path: str, line: int, context_lines_before: int = 0, context_lines_after: int = 0) -> MatchedConsecutiveLines: + def retrieve_content_around_line( + self, relative_file_path: str, line: int, context_lines_before: int = 0, context_lines_after: int = 0 + ) -> MatchedConsecutiveLines: """ Retrieve the content of the given file around the given line. @@ -669,11 +648,17 @@ class SolidLanguageServer(ABC): """ with self.open_file(relative_file_path) as file_data: file_contents = file_data.contents - return MatchedConsecutiveLines.from_file_contents(file_contents, line=line, context_lines_before=context_lines_before, context_lines_after=context_lines_after, source_file_path=relative_file_path) + return MatchedConsecutiveLines.from_file_contents( + file_contents, + line=line, + context_lines_before=context_lines_before, + context_lines_after=context_lines_after, + source_file_path=relative_file_path, + ) def request_completions( - self, relative_file_path: str, line: int, column: int, allow_incomplete: bool = False - ) -> List[multilspy_types.CompletionItem]: + self, relative_file_path: str, line: int, column: int, allow_incomplete: bool = False + ) -> list[multilspy_types.CompletionItem]: """ Raise a [textDocument/completion](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion) request to the Language Server to find completions at the given line and column in the given file. Wait for the response and return the result. @@ -685,39 +670,35 @@ class SolidLanguageServer(ABC): :return List[multilspy_types.CompletionItem]: A list of completions """ with self.open_file(relative_file_path): - open_file_buffer = self.open_file_buffers[ - pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri() - ] + open_file_buffer = self.open_file_buffers[pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri()] completion_params: LSPTypes.CompletionParams = { "position": {"line": line, "character": column}, "textDocument": {"uri": open_file_buffer.uri}, "context": {"triggerKind": LSPTypes.CompletionTriggerKind.Invoked}, } - response: Union[List[LSPTypes.CompletionItem], LSPTypes.CompletionList, None] = None + response: list[LSPTypes.CompletionItem] | LSPTypes.CompletionList | None = None num_retries = 0 while response is None or (response["isIncomplete"] and num_retries < 30): self.completions_available.wait() - response: Union[ - List[LSPTypes.CompletionItem], LSPTypes.CompletionList, None - ] = self.server.send.completion(completion_params) + response: list[LSPTypes.CompletionItem] | LSPTypes.CompletionList | None = self.server.send.completion(completion_params) if isinstance(response, list): response = {"items": response, "isIncomplete": False} num_retries += 1 # TODO: Understand how to appropriately handle `isIncomplete` - if response is None or (response["isIncomplete"] and not(allow_incomplete)): + if response is None or (response["isIncomplete"] and not (allow_incomplete)): return [] if "items" in response: response = response["items"] - response: List[LSPTypes.CompletionItem] = response + response = cast(list[LSPTypes.CompletionItem], response) # TODO: Handle the case when the completion is a keyword items = [item for item in response if item["kind"] != LSPTypes.CompletionItemKind.Keyword] - completions_list: List[multilspy_types.CompletionItem] = [] + completions_list: list[multilspy_types.CompletionItem] = [] for item in items: assert "insertText" in item or "textEdit" in item @@ -745,8 +726,7 @@ class SolidLanguageServer(ABC): item["textEdit"]["range"]["start"]["line"] == new_dot_lineno, item["textEdit"]["range"]["start"]["character"] == new_dot_colno, item["textEdit"]["range"]["start"]["line"] == item["textEdit"]["range"]["end"]["line"], - item["textEdit"]["range"]["start"]["character"] - == item["textEdit"]["range"]["end"]["character"], + item["textEdit"]["range"]["start"]["character"] == item["textEdit"]["range"]["end"]["character"], ) ) @@ -760,12 +740,11 @@ class SolidLanguageServer(ABC): completion_item = multilspy_types.CompletionItem(**completion_item) completions_list.append(completion_item) - return [ - json.loads(json_repr) - for json_repr in set([json.dumps(item, sort_keys=True) for item in completions_list]) - ] + return [json.loads(json_repr) for json_repr in set(json.dumps(item, sort_keys=True) for item in completions_list)] - def request_document_symbols(self, relative_file_path: str, include_body: bool = False) -> Tuple[List[multilspy_types.UnifiedSymbolInformation], List[multilspy_types.UnifiedSymbolInformation]]: + def request_document_symbols( + self, relative_file_path: str, include_body: bool = False + ) -> tuple[list[multilspy_types.UnifiedSymbolInformation], list[multilspy_types.UnifiedSymbolInformation]]: """ Raise a [textDocument/documentSymbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol) request to the Language Server to find symbols in the given file. Wait for the response and return the result. @@ -797,13 +776,12 @@ class SolidLanguageServer(ABC): self.logger.log(f"Requesting document symbols for {relative_file_path} from the Language Server", logging.DEBUG) response = self.server.send.document_symbol( - { - "textDocument": { - "uri": pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri() - } - } + {"textDocument": {"uri": pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri()}} + ) + self.logger.log( + f"Received {len(response) if response is not None else None} document symbols for {relative_file_path} from the Language Server", + logging.DEBUG, ) - self.logger.log(f"Received {len(response) if response is not None else None} document symbols for {relative_file_path} from the Language Server", logging.DEBUG) def turn_item_into_symbol_with_children(item: GenericDocumentSymbol): item = cast(multilspy_types.UnifiedSymbolInformation, item) @@ -815,18 +793,18 @@ class SolidLanguageServer(ABC): assert "range" in item tree_location = multilspy_types.Location( uri=uri, - range=item['range'], + range=item["range"], absolutePath=absolute_path, relativePath=relative_file_path, ) - item['location'] = tree_location + item["location"] = tree_location location = item["location"] if "absolutePath" not in location: location["absolutePath"] = absolute_path if "relativePath" not in location: location["relativePath"] = relative_file_path if include_body: - item['body'] = self.retrieve_symbol_body(item) + item["body"] = self.retrieve_symbol_body(item) # handle missing selectionRange if "selectionRange" not in item: if "range" in item: @@ -838,9 +816,9 @@ class SolidLanguageServer(ABC): child["parent"] = item item[LSPConstants.CHILDREN] = children - flat_all_symbol_list: List[multilspy_types.UnifiedSymbolInformation] = [] + flat_all_symbol_list: list[multilspy_types.UnifiedSymbolInformation] = [] assert isinstance(response, list), f"Unexpected response from Language Server: {response}" - root_nodes: List[multilspy_types.UnifiedSymbolInformation] = [] + root_nodes: list[multilspy_types.UnifiedSymbolInformation] = [] for root_item in response: if "range" not in root_item and "location" not in root_item: if root_item["kind"] in [SymbolKind.File, SymbolKind.Module]: @@ -861,9 +839,9 @@ class SolidLanguageServer(ABC): if LSPConstants.CHILDREN in root_symbol: # TODO: l_tree should be a list of TreeRepr. Define the following function to return TreeRepr as well - def visit_tree_nodes_and_build_tree_repr(node: GenericDocumentSymbol) -> List[multilspy_types.UnifiedSymbolInformation]: + def visit_tree_nodes_and_build_tree_repr(node: GenericDocumentSymbol) -> list[multilspy_types.UnifiedSymbolInformation]: node = cast(multilspy_types.UnifiedSymbolInformation, node) - l: List[multilspy_types.UnifiedSymbolInformation] = [] + l: list[multilspy_types.UnifiedSymbolInformation] = [] turn_item_into_symbol_with_children(node) assert LSPConstants.CHILDREN in node children = node[LSPConstants.CHILDREN] @@ -883,9 +861,11 @@ class SolidLanguageServer(ABC): self._cache_has_changed = True return result - def request_full_symbol_tree(self, within_relative_path: str | None = None, include_body: bool = False) -> List[multilspy_types.UnifiedSymbolInformation]: + def request_full_symbol_tree( + self, within_relative_path: str | None = None, include_body: bool = False + ) -> list[multilspy_types.UnifiedSymbolInformation]: """ - Will go through all files in the project or within a relative path and build a tree of symbols. + Will go through all files in the project or within a relative path and build a tree of symbols. Note: this may be slow the first time it is called, especially if `within_relative_path` is not used to restrict the search. For each file, a symbol of kind File (2) will be created. For directories, a symbol of kind Package (4) will be created. @@ -902,21 +882,23 @@ class SolidLanguageServer(ABC): :return: A list of root symbols representing the top-level packages/modules in the project. """ - if within_relative_path is not None: within_abs_path = os.path.join(self.repository_root_path, within_relative_path) if not os.path.exists(within_abs_path): raise FileNotFoundError(f"File or directory not found: {within_abs_path}") if os.path.isfile(within_abs_path): if self.is_ignored_path(within_relative_path): - self.logger.log(f"You passed a file explicitly, but it is ignored. This is probably an error. File: {within_relative_path}", logging.ERROR) + self.logger.log( + f"You passed a file explicitly, but it is ignored. This is probably an error. File: {within_relative_path}", + logging.ERROR, + ) return [] else: _, root_nodes = self.request_document_symbols(within_relative_path, include_body=include_body) return root_nodes # Helper function to recursively process directories - def process_directory(rel_dir_path: str) -> List[multilspy_types.UnifiedSymbolInformation]: + def process_directory(rel_dir_path: str) -> list[multilspy_types.UnifiedSymbolInformation]: abs_dir_path = self.repository_root_path if rel_dir_path == "." else os.path.join(self.repository_root_path, rel_dir_path) abs_dir_path = os.path.realpath(abs_dir_path) @@ -931,7 +913,7 @@ class SolidLanguageServer(ABC): return [] # Create package symbol for directory - package_symbol = multilspy_types.UnifiedSymbolInformation( # type: ignore + package_symbol = multilspy_types.UnifiedSymbolInformation( # type: ignore name=os.path.basename(abs_dir_path), kind=multilspy_types.SymbolKind.Package, location=multilspy_types.Location( @@ -940,7 +922,7 @@ class SolidLanguageServer(ABC): absolutePath=str(abs_dir_path), relativePath=str(Path(abs_dir_path).resolve().relative_to(self.repository_root_path)), ), - children=[] + children=[], ) result.append(package_symbol) @@ -964,7 +946,7 @@ class SolidLanguageServer(ABC): file_rel_path = str(Path(contained_dir_or_file_abs_path).resolve().relative_to(self.repository_root_path)) with self.open_file(file_rel_path) as file_data: fileRange = self._get_range_from_file_content(file_data.contents) - file_symbol = multilspy_types.UnifiedSymbolInformation( # type: ignore + file_symbol = multilspy_types.UnifiedSymbolInformation( # type: ignore name=os.path.splitext(contained_dir_or_file_name)[0], kind=multilspy_types.SymbolKind.File, range=fileRange, @@ -985,7 +967,7 @@ class SolidLanguageServer(ABC): package_symbol["children"].append(file_symbol) # TODO: Not sure if this is actually still needed given recent changes to relative path handling - def fix_relative_path(nodes: List[multilspy_types.UnifiedSymbolInformation]): + def fix_relative_path(nodes: list[multilspy_types.UnifiedSymbolInformation]): for node in nodes: if "location" in node and "relativePath" in node["location"]: path = Path(node["location"]["relativePath"]) @@ -1015,8 +997,7 @@ class SolidLanguageServer(ABC): end_line = len(lines) end_column = len(lines[-1]) return multilspy_types.Range( - start=multilspy_types.Position(line=0, character=0), - end=multilspy_types.Position(line=end_line, character=end_column) + start=multilspy_types.Position(line=0, character=0), end=multilspy_types.Position(line=end_line, character=end_column) ) def request_dir_overview(self, relative_dir_path: str) -> dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]]: @@ -1038,12 +1019,14 @@ class SolidLanguageServer(ABC): assert "location" in child assert "selectionRange" in child path = Path(child["location"]["absolutePath"]).resolve().relative_to(self.repository_root_path) - result[str(path)].append(( - child["name"], - child["kind"], - child["selectionRange"]["start"]["line"], - child["selectionRange"]["start"]["character"] - )) + result[str(path)].append( + ( + child["name"], + child["kind"], + child["selectionRange"]["start"]["line"], + child["selectionRange"]["start"]["character"], + ) + ) # For package/directory symbols, process their children for child in symbol["children"]: process_symbol(child) @@ -1063,15 +1046,10 @@ class SolidLanguageServer(ABC): for root in document_roots: try: result.append( - ( root["name"], - root["kind"], - root["selectionRange"]["start"]["line"], - root["selectionRange"]["start"]["character"],) + (root["name"], root["kind"], root["selectionRange"]["start"]["line"], root["selectionRange"]["start"]["character"]) ) except KeyError as e: - raise KeyError( - f"Could not process symbol of name {root.get('name', 'unknown')} in {relative_file_path=}" - ) from e + raise KeyError(f"Could not process symbol of name {root.get('name', 'unknown')} in {relative_file_path=}") from e return result def request_overview(self, within_relative_path: str) -> dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]]: @@ -1091,7 +1069,7 @@ class SolidLanguageServer(ABC): else: return self.request_dir_overview(within_relative_path) - def request_hover(self, relative_file_path: str, line: int, column: int) -> Union[multilspy_types.Hover, None]: + def request_hover(self, relative_file_path: str, line: int, column: int) -> multilspy_types.Hover | None: """ Raise a [textDocument/hover](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover) request to the Language Server to find the hover information at the given line and column in the given file. Wait for the response and return the result. @@ -1105,9 +1083,7 @@ class SolidLanguageServer(ABC): with self.open_file(relative_file_path): response = self.server.send.hover( { - "textDocument": { - "uri": pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri() - }, + "textDocument": {"uri": pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri()}, "position": { "line": line, "character": column, @@ -1124,7 +1100,9 @@ class SolidLanguageServer(ABC): # ----------------------------- FROM HERE ON MODIFICATIONS BY MISCHA -------------------- - def retrieve_symbol_body(self, symbol: multilspy_types.UnifiedSymbolInformation | LSPTypes.DocumentSymbol | LSPTypes.SymbolInformation) -> str: + def retrieve_symbol_body( + self, symbol: multilspy_types.UnifiedSymbolInformation | LSPTypes.DocumentSymbol | LSPTypes.SymbolInformation + ) -> str: """ Load the body of the given symbol. If the body is already contained in the symbol, just return it. """ @@ -1138,7 +1116,7 @@ class SolidLanguageServer(ABC): assert "relativePath" in symbol["location"] symbol_file = self.retrieve_full_file_content(symbol["location"]["relativePath"]) symbol_lines = symbol_file.split("\n") - symbol_body = "\n".join(symbol_lines[symbol_start_line:symbol_end_line+1]) + symbol_body = "\n".join(symbol_lines[symbol_start_line : symbol_end_line + 1]) # remove leading indentation symbol_start_column = symbol["location"]["range"]["start"]["character"] @@ -1163,12 +1141,12 @@ class SolidLanguageServer(ABC): return rel_file_paths def search_files_for_pattern( - self, - pattern: re.Pattern | str, - context_lines_before: int = 0, - context_lines_after: int = 0, - paths_include_glob: str | None = None, - paths_exclude_glob: str | None = None, + self, + pattern: re.Pattern | str, + context_lines_before: int = 0, + context_lines_after: int = 0, + paths_include_glob: str | None = None, + paths_exclude_glob: str | None = None, ) -> list[MatchedConsecutiveLines]: """ Search for a pattern across all files analyzed by the Language Server. @@ -1191,19 +1169,19 @@ class SolidLanguageServer(ABC): context_lines_before=context_lines_before, context_lines_after=context_lines_after, paths_include_glob=paths_include_glob, - paths_exclude_glob=paths_exclude_glob + paths_exclude_glob=paths_exclude_glob, ) def request_referencing_symbols( - self, - relative_file_path: str, - line: int, - column: int, - include_imports: bool = True, - include_self: bool = False, - include_body: bool = False, - include_file_symbols: bool = False, - ) -> List[ReferenceInSymbol]: + self, + relative_file_path: str, + line: int, + column: int, + include_imports: bool = True, + include_self: bool = False, + include_body: bool = False, + include_file_symbols: bool = False, + ) -> list[ReferenceInSymbol]: """ Finds all symbols that reference the symbol at the given location. This is similar to request_references but filters to only include symbols @@ -1244,9 +1222,7 @@ class SolidLanguageServer(ABC): with self.open_file(ref_path) as file_data: # Get the containing symbol for this reference - containing_symbol = self.request_containing_symbol( - ref_path, ref_line, ref_col, include_body=include_body - ) + containing_symbol = self.request_containing_symbol(ref_path, ref_line, ref_col, include_body=include_body) if containing_symbol is None: # TODO: HORRIBLE HACK! I don't know how to do it better for now... # THIS IS BOUND TO BREAK IN MANY CASES! IT IS ALSO SPECIFIC TO PYTHON! @@ -1276,7 +1252,7 @@ class SolidLanguageServer(ABC): 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 + logging.WARNING, ) fileRange = self._get_range_from_file_content(file_data.contents) location = multilspy_types.Location( @@ -1301,7 +1277,7 @@ class SolidLanguageServer(ABC): children=[], body=body, ) - if containing_symbol is None or not include_file_symbols and containing_symbol["kind"] == multilspy_types.SymbolKind.File: + if containing_symbol is None or (not include_file_symbols and containing_symbol["kind"] == multilspy_types.SymbolKind.File): continue assert "location" in containing_symbol @@ -1309,31 +1285,31 @@ class SolidLanguageServer(ABC): # Checking for self-reference if ( - containing_symbol["location"]["relativePath"] == relative_file_path - and containing_symbol["selectionRange"]["start"]["line"] == ref_line - and containing_symbol["selectionRange"]["start"]["character"] == ref_col + containing_symbol["location"]["relativePath"] == relative_file_path + and containing_symbol["selectionRange"]["start"]["line"] == ref_line + and containing_symbol["selectionRange"]["start"]["character"] == ref_col ): incoming_symbol = containing_symbol if include_self: result.append(ReferenceInSymbol(symbol=containing_symbol, line=ref_line, character=ref_col)) continue - else: - self.logger.log(f"Found self-reference for {incoming_symbol['name']}, skipping it since {include_self=}", logging.DEBUG) - continue + self.logger.log(f"Found self-reference for {incoming_symbol['name']}, skipping it since {include_self=}", logging.DEBUG) + continue # checking whether reference is an import # This is neither really safe nor elegant, but if we don't do it, # there is no way to distinguish between definitions and imports as import is not a symbol-type # and we get the type referenced symbol resulting from imports... - if (not include_imports \ - and incoming_symbol is not None \ - and containing_symbol["name"] == incoming_symbol["name"] \ - and containing_symbol["kind"] == incoming_symbol["kind"] \ - ): + if ( + not include_imports + and incoming_symbol is not None + and containing_symbol["name"] == incoming_symbol["name"] + and containing_symbol["kind"] == incoming_symbol["kind"] + ): self.logger.log( f"Found import of referenced symbol {incoming_symbol['name']}" f"in {containing_symbol['location']['relativePath']}, skipping", - logging.DEBUG + logging.DEBUG, ) continue @@ -1342,12 +1318,12 @@ class SolidLanguageServer(ABC): return result def request_containing_symbol( - self, - relative_file_path: str, - line: int, - column: Optional[int] = None, - strict: bool = False, - include_body: bool = False, + self, + relative_file_path: str, + line: int, + column: int | None = None, + strict: bool = False, + include_body: bool = False, ) -> multilspy_types.UnifiedSymbolInformation | None: """ Finds the first symbol containing the position for the given file. @@ -1377,9 +1353,7 @@ class SolidLanguageServer(ABC): """ # checking if the line is empty, unfortunately ugly and duplicating code, but I don't want to refactor with self.open_file(relative_file_path): - absolute_file_path = str( - PurePath(self.repository_root_path, relative_file_path) - ) + absolute_file_path = str(PurePath(self.repository_root_path, relative_file_path)) content = FileUtils.read_file(self.logger, absolute_file_path) if content.split("\n")[line].strip() == "": self.logger.log( @@ -1411,11 +1385,7 @@ class SolidLanguageServer(ABC): location["uri"] = Path(absolute_file_path).as_uri() # Allowed container kinds, currently only for Python - container_symbol_kinds = { - multilspy_types.SymbolKind.Method, - multilspy_types.SymbolKind.Function, - multilspy_types.SymbolKind.Class - } + container_symbol_kinds = {multilspy_types.SymbolKind.Method, multilspy_types.SymbolKind.Function, multilspy_types.SymbolKind.Class} def is_position_in_range(line: int, range_d: multilspy_types.Range) -> bool: start = range_d["start"] @@ -1434,11 +1404,11 @@ class SolidLanguageServer(ABC): # Only consider containers that are not one-liners (otherwise we may get imports) candidate_containers = [ - s for s in symbols if s["kind"] in container_symbol_kinds and s["location"]["range"]["start"]["line"] != s["location"]["range"]["end"]["line"] - ] - var_containers = [ - s for s in symbols if s["kind"] == multilspy_types.SymbolKind.Variable + s + for s in symbols + if s["kind"] in container_symbol_kinds and s["location"]["range"]["start"]["line"] != s["location"]["range"]["end"]["line"] ] + var_containers = [s for s in symbols if s["kind"] == multilspy_types.SymbolKind.Variable] candidate_containers.extend(var_containers) if not candidate_containers: @@ -1461,7 +1431,9 @@ class SolidLanguageServer(ABC): else: return None - def request_container_of_symbol(self, symbol: multilspy_types.UnifiedSymbolInformation, include_body: bool = False) -> multilspy_types.UnifiedSymbolInformation | None: + def request_container_of_symbol( + self, symbol: multilspy_types.UnifiedSymbolInformation, include_body: bool = False + ) -> multilspy_types.UnifiedSymbolInformation | None: """ Finds the container of the given symbol if there is one. If the parent attribute is present, the parent is returned without further searching. @@ -1482,12 +1454,12 @@ class SolidLanguageServer(ABC): ) def request_defining_symbol( - self, - relative_file_path: str, - line: int, - column: int, - include_body: bool = False, - ) -> Optional[multilspy_types.UnifiedSymbolInformation]: + self, + relative_file_path: str, + line: int, + column: int, + include_body: bool = False, + ) -> multilspy_types.UnifiedSymbolInformation | None: """ Finds the symbol that defines the symbol at the given location. @@ -1519,9 +1491,7 @@ class SolidLanguageServer(ABC): def_col = definition["range"]["start"]["character"] # Find the symbol at or containing this location - defining_symbol = self.request_containing_symbol( - def_path, def_line, def_col, strict=False, include_body=include_body - ) + defining_symbol = self.request_containing_symbol(def_path, def_line, def_col, strict=False, include_body=include_body) return defining_symbol @@ -1587,7 +1557,7 @@ class SolidLanguageServer(ABC): logging.ERROR, ) - def request_workspace_symbol(self, query: str) -> Union[List[multilspy_types.UnifiedSymbolInformation], None]: + def request_workspace_symbol(self, query: str) -> list[multilspy_types.UnifiedSymbolInformation] | None: """ Raise a [workspace/symbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol) request to the Language Server to find symbols across the whole workspace. Wait for the response and return the result. @@ -1602,7 +1572,7 @@ class SolidLanguageServer(ABC): assert isinstance(response, list) - ret: List[multilspy_types.UnifiedSymbolInformation] = [] + ret: list[multilspy_types.UnifiedSymbolInformation] = [] for item in response: assert isinstance(item, dict) @@ -1620,7 +1590,10 @@ 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) + self.logger.log( + f"Starting language server with language {self.language_server.language} for {self.language_server.repository_root_path}", + logging.INFO, + ) self._server_context = self._start_server_process() return self diff --git a/src/solidlsp/ls_handler.py b/src/solidlsp/ls_handler.py index b34571f9..0bc32555 100644 --- a/src/solidlsp/ls_handler.py +++ b/src/solidlsp/ls_handler.py @@ -5,16 +5,29 @@ import os import subprocess import threading import time +from collections.abc import Callable from dataclasses import dataclass from queue import Queue -from typing import Any, Callable, Dict, Optional +from typing import Any import psutil from multilspy.lsp_protocol_handler.lsp_requests import LspNotification from multilspy.lsp_protocol_handler.lsp_types import ErrorCodes -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo, StringDict, content_length, ENCODING, Error, MessageType, \ - make_notification, PayloadLike, make_response, make_error_response, make_request, create_message +from multilspy.lsp_protocol_handler.server import ( + ENCODING, + Error, + MessageType, + PayloadLike, + ProcessLaunchInfo, + StringDict, + content_length, + create_message, + make_error_response, + make_notification, + make_request, + make_response, +) from multilspy.multilspy_exceptions import MultilspyException from solidlsp.lsp_request import SolidLspRequest @@ -25,8 +38,8 @@ class Request: @dataclass class Result: - payload: Optional[PayloadLike] = None - error: Optional[Error] = None + payload: PayloadLike | None = None + error: Error | None = None def is_error(self) -> bool: return self.error is not None @@ -81,13 +94,14 @@ class SolidLanguageServerHandler: language server process in an independent process group. Default is `True`. Setting it to `False` means that the language server process will be in the same process group as the the current process, and any SIGINT and SIGTERM signals will be sent to both processes. + """ def __init__( - self, - process_launch_info: ProcessLaunchInfo, - logger: Optional[Callable[[str, str, StringDict | str], None]] = None, - start_independent_lsp_process=True, + self, + process_launch_info: ProcessLaunchInfo, + logger: Callable[[str, str, StringDict | str], None] | None = None, + start_independent_lsp_process=True, ) -> None: """ Params: @@ -103,7 +117,7 @@ class SolidLanguageServerHandler: self._received_shutdown = False self.request_id = 1 - self._response_handlers: Dict[Any, Request] = {} + self._response_handlers: dict[Any, Request] = {} self.on_request_handlers = {} self.on_notification_handlers = {} self.logger = logger @@ -141,7 +155,7 @@ class SolidLanguageServerHandler: env=child_proc_env, cwd=self.process_launch_info.cwd, start_new_session=self.start_independent_lsp_process, - shell=True + shell=True, ) # Check if process terminated immediately @@ -149,7 +163,7 @@ class SolidLanguageServerHandler: log.error("Language server has already terminated/could not be started") # Process has already terminated stderr_data = self.process.stderr.read() - error_message = stderr_data.decode('utf-8', errors='replace') + error_message = stderr_data.decode("utf-8", errors="replace") raise RuntimeError(f"Process terminated immediately with code {self.process.returncode}. Error: {error_message}") # start threads to read stdout and stderr of the process @@ -284,7 +298,7 @@ class SolidLanguageServerHandler: # Process has terminated, check if we can still read pass - data = b'' + data = b"" while len(data) < num_bytes: chunk = stream.read(num_bytes - len(data)) if not chunk: @@ -332,7 +346,7 @@ class SolidLanguageServerHandler: line = self.process.stderr.readline() if not line: continue - self._log("LSP stderr: " + line.decode(ENCODING, errors='replace')) + self._log("LSP stderr: " + line.decode(ENCODING, errors="replace")) except (BrokenPipeError, ConnectionResetError): pass @@ -342,7 +356,7 @@ class SolidLanguageServerHandler: """ try: self._receive_payload(json.loads(body)) - except IOError as ex: + except OSError as ex: self._log(f"malformed {ENCODING}: {ex}") except UnicodeDecodeError as ex: self._log(f"malformed {ENCODING}: {ex}") @@ -368,7 +382,7 @@ class SolidLanguageServerHandler: except Exception as err: self._log(f"Error handling server payload: {err}") - def send_notification(self, method: str, params: Optional[dict] = None) -> None: + def send_notification(self, method: str, params: dict | None = None) -> None: """ Send notification pertaining to the given method to the server with the given parameters """ @@ -387,7 +401,7 @@ class SolidLanguageServerHandler: # Use lock to prevent race conditions on tasks and task_counter self._send_payload(make_error_response(request_id, err)) - def send_request(self, method: str, params: Optional[dict] = None) -> PayloadLike: + def send_request(self, method: str, params: dict | None = None) -> PayloadLike: """ Send request to the server, register the request id, and wait for the response """ @@ -406,10 +420,12 @@ class SolidLanguageServerHandler: self._log(f"Waiting for response to request {method} with params:\n{params}") result = request.get_result() - self._log(f"Processing result") + self._log("Processing result") if result.is_error(): - raise MultilspyException(f"Could not process request {method} with params:\n{params}.\n Language server error: {result.error}") from result.error - + raise MultilspyException( + f"Could not process request {method} with params:\n{params}.\n Language server error: {result.error}" + ) from result.error + self._log(f"Returning non-error result, which is:\n{result.payload}") return result.payload @@ -472,7 +488,7 @@ class SolidLanguageServerHandler: request_id, Error( ErrorCodes.MethodNotFound, - "method '{}' not handled on client.".format(method), + f"method '{method}' not handled on client.", ), ) return diff --git a/src/solidlsp/lsp_request.py b/src/solidlsp/lsp_request.py index 984407b5..6320fe5a 100644 --- a/src/solidlsp/lsp_request.py +++ b/src/solidlsp/lsp_request.py @@ -1,4 +1,5 @@ -from typing import List, Union +from typing import Union + from multilspy.lsp_protocol_handler import lsp_types @@ -6,258 +7,230 @@ class SolidLspRequest: def __init__(self, send_request): self.send_request = send_request - def implementation( - self, params: lsp_types.ImplementationParams - ) -> Union["lsp_types.Definition", List["lsp_types.LocationLink"], None]: + def implementation(self, params: lsp_types.ImplementationParams) -> Union["lsp_types.Definition", list["lsp_types.LocationLink"], None]: """A request to resolve the implementation locations of a symbol at a given text document position. The request's parameter is of type [TextDocumentPositionParams] (#TextDocumentPositionParams) the response is of type {@link Definition} or a - Thenable that resolves to such.""" + Thenable that resolves to such. + """ return self.send_request("textDocument/implementation", params) def type_definition( - self, params: lsp_types.TypeDefinitionParams - ) -> Union["lsp_types.Definition", List["lsp_types.LocationLink"], None]: + self, params: lsp_types.TypeDefinitionParams + ) -> Union["lsp_types.Definition", list["lsp_types.LocationLink"], None]: """A request to resolve the type definition locations of a symbol at a given text document position. The request's parameter is of type [TextDocumentPositionParams] (#TextDocumentPositionParams) the response is of type {@link Definition} or a - Thenable that resolves to such.""" + Thenable that resolves to such. + """ return self.send_request("textDocument/typeDefinition", params) - def document_color( - self, params: lsp_types.DocumentColorParams - ) -> List["lsp_types.ColorInformation"]: + def document_color(self, params: lsp_types.DocumentColorParams) -> list["lsp_types.ColorInformation"]: """A request to list all color symbols found in a given text document. The request's parameter is of type {@link DocumentColorParams} the response is of type {@link ColorInformation ColorInformation[]} or a Thenable - that resolves to such.""" + that resolves to such. + """ return self.send_request("textDocument/documentColor", params) - def color_presentation( - self, params: lsp_types.ColorPresentationParams - ) -> List["lsp_types.ColorPresentation"]: + def color_presentation(self, params: lsp_types.ColorPresentationParams) -> list["lsp_types.ColorPresentation"]: """A request to list all presentation for a color. The request's parameter is of type {@link ColorPresentationParams} the response is of type {@link ColorInformation ColorInformation[]} or a Thenable - that resolves to such.""" + that resolves to such. + """ return self.send_request("textDocument/colorPresentation", params) - def folding_range( - self, params: lsp_types.FoldingRangeParams - ) -> Union[List["lsp_types.FoldingRange"], None]: + def folding_range(self, params: lsp_types.FoldingRangeParams) -> list["lsp_types.FoldingRange"] | None: """A request to provide folding ranges in a document. The request's parameter is of type {@link FoldingRangeParams}, the response is of type {@link FoldingRangeList} or a Thenable - that resolves to such.""" + that resolves to such. + """ return self.send_request("textDocument/foldingRange", params) - def declaration( - self, params: lsp_types.DeclarationParams - ) -> Union["lsp_types.Declaration", List["lsp_types.LocationLink"], None]: + def declaration(self, params: lsp_types.DeclarationParams) -> Union["lsp_types.Declaration", list["lsp_types.LocationLink"], None]: """A request to resolve the type definition locations of a symbol at a given text document position. The request's parameter is of type [TextDocumentPositionParams] (#TextDocumentPositionParams) the response is of type {@link Declaration} or a typed array of {@link DeclarationLink} or a Thenable that resolves - to such.""" + to such. + """ return self.send_request("textDocument/declaration", params) - def selection_range( - self, params: lsp_types.SelectionRangeParams - ) -> Union[List["lsp_types.SelectionRange"], None]: + def selection_range(self, params: lsp_types.SelectionRangeParams) -> list["lsp_types.SelectionRange"] | None: """A request to provide selection ranges in a document. The request's parameter is of type {@link SelectionRangeParams}, the response is of type {@link SelectionRange SelectionRange[]} or a Thenable - that resolves to such.""" + that resolves to such. + """ return self.send_request("textDocument/selectionRange", params) - def prepare_call_hierarchy( - self, params: lsp_types.CallHierarchyPrepareParams - ) -> Union[List["lsp_types.CallHierarchyItem"], None]: + def prepare_call_hierarchy(self, params: lsp_types.CallHierarchyPrepareParams) -> list["lsp_types.CallHierarchyItem"] | None: """A request to result a `CallHierarchyItem` in a document at a given position. Can be used as an input to an incoming or outgoing call hierarchy. - @since 3.16.0""" + @since 3.16.0 + """ return self.send_request("textDocument/prepareCallHierarchy", params) - def incoming_calls( - self, params: lsp_types.CallHierarchyIncomingCallsParams - ) -> Union[List["lsp_types.CallHierarchyIncomingCall"], None]: + def incoming_calls(self, params: lsp_types.CallHierarchyIncomingCallsParams) -> list["lsp_types.CallHierarchyIncomingCall"] | None: """A request to resolve the incoming calls for a given `CallHierarchyItem`. - @since 3.16.0""" + @since 3.16.0 + """ return self.send_request("callHierarchy/incomingCalls", params) - def outgoing_calls( - self, params: lsp_types.CallHierarchyOutgoingCallsParams - ) -> Union[List["lsp_types.CallHierarchyOutgoingCall"], None]: + def outgoing_calls(self, params: lsp_types.CallHierarchyOutgoingCallsParams) -> list["lsp_types.CallHierarchyOutgoingCall"] | None: """A request to resolve the outgoing calls for a given `CallHierarchyItem`. - @since 3.16.0""" + @since 3.16.0 + """ return self.send_request("callHierarchy/outgoingCalls", params) - def semantic_tokens_full( - self, params: lsp_types.SemanticTokensParams - ) -> Union["lsp_types.SemanticTokens", None]: + def semantic_tokens_full(self, params: lsp_types.SemanticTokensParams) -> Union["lsp_types.SemanticTokens", None]: """@since 3.16.0""" return self.send_request("textDocument/semanticTokens/full", params) def semantic_tokens_delta( - self, params: lsp_types.SemanticTokensDeltaParams + self, params: lsp_types.SemanticTokensDeltaParams ) -> Union["lsp_types.SemanticTokens", "lsp_types.SemanticTokensDelta", None]: """@since 3.16.0""" return self.send_request("textDocument/semanticTokens/full/delta", params) - def semantic_tokens_range( - self, params: lsp_types.SemanticTokensRangeParams - ) -> Union["lsp_types.SemanticTokens", None]: + def semantic_tokens_range(self, params: lsp_types.SemanticTokensRangeParams) -> Union["lsp_types.SemanticTokens", None]: """@since 3.16.0""" return self.send_request("textDocument/semanticTokens/range", params) - def linked_editing_range( - self, params: lsp_types.LinkedEditingRangeParams - ) -> Union["lsp_types.LinkedEditingRanges", None]: + def linked_editing_range(self, params: lsp_types.LinkedEditingRangeParams) -> Union["lsp_types.LinkedEditingRanges", None]: """A request to provide ranges that can be edited together. - @since 3.16.0""" + @since 3.16.0 + """ return self.send_request("textDocument/linkedEditingRange", params) - def will_create_files( - self, params: lsp_types.CreateFilesParams - ) -> Union["lsp_types.WorkspaceEdit", None]: + def will_create_files(self, params: lsp_types.CreateFilesParams) -> Union["lsp_types.WorkspaceEdit", None]: """The will create files request is sent from the client to the server before files are actually created as long as the creation is triggered from within the client. - @since 3.16.0""" + @since 3.16.0 + """ return self.send_request("workspace/willCreateFiles", params) - def will_rename_files( - self, params: lsp_types.RenameFilesParams - ) -> Union["lsp_types.WorkspaceEdit", None]: + def will_rename_files(self, params: lsp_types.RenameFilesParams) -> Union["lsp_types.WorkspaceEdit", None]: """The will rename files request is sent from the client to the server before files are actually renamed as long as the rename is triggered from within the client. - @since 3.16.0""" + @since 3.16.0 + """ return self.send_request("workspace/willRenameFiles", params) - def will_delete_files( - self, params: lsp_types.DeleteFilesParams - ) -> Union["lsp_types.WorkspaceEdit", None]: + def will_delete_files(self, params: lsp_types.DeleteFilesParams) -> Union["lsp_types.WorkspaceEdit", None]: """The did delete files notification is sent from the client to the server when files were deleted from within the client. - @since 3.16.0""" + @since 3.16.0 + """ return self.send_request("workspace/willDeleteFiles", params) - def moniker( - self, params: lsp_types.MonikerParams - ) -> Union[List["lsp_types.Moniker"], None]: + def moniker(self, params: lsp_types.MonikerParams) -> list["lsp_types.Moniker"] | None: """A request to get the moniker of a symbol at a given text document position. The request parameter is of type {@link TextDocumentPositionParams}. - The response is of type {@link Moniker Moniker[]} or `null`.""" + The response is of type {@link Moniker Moniker[]} or `null`. + """ return self.send_request("textDocument/moniker", params) - def prepare_type_hierarchy( - self, params: lsp_types.TypeHierarchyPrepareParams - ) -> Union[List["lsp_types.TypeHierarchyItem"], None]: + def prepare_type_hierarchy(self, params: lsp_types.TypeHierarchyPrepareParams) -> list["lsp_types.TypeHierarchyItem"] | None: """A request to result a `TypeHierarchyItem` in a document at a given position. Can be used as an input to a subtypes or supertypes type hierarchy. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_request("textDocument/prepareTypeHierarchy", params) - def type_hierarchy_supertypes( - self, params: lsp_types.TypeHierarchySupertypesParams - ) -> Union[List["lsp_types.TypeHierarchyItem"], None]: + def type_hierarchy_supertypes(self, params: lsp_types.TypeHierarchySupertypesParams) -> list["lsp_types.TypeHierarchyItem"] | None: """A request to resolve the supertypes for a given `TypeHierarchyItem`. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_request("typeHierarchy/supertypes", params) - def type_hierarchy_subtypes( - self, params: lsp_types.TypeHierarchySubtypesParams - ) -> Union[List["lsp_types.TypeHierarchyItem"], None]: + def type_hierarchy_subtypes(self, params: lsp_types.TypeHierarchySubtypesParams) -> list["lsp_types.TypeHierarchyItem"] | None: """A request to resolve the subtypes for a given `TypeHierarchyItem`. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_request("typeHierarchy/subtypes", params) - def inline_value( - self, params: lsp_types.InlineValueParams - ) -> Union[List["lsp_types.InlineValue"], None]: + def inline_value(self, params: lsp_types.InlineValueParams) -> list["lsp_types.InlineValue"] | None: """A request to provide inline values in a document. The request's parameter is of type {@link InlineValueParams}, the response is of type {@link InlineValue InlineValue[]} or a Thenable that resolves to such. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_request("textDocument/inlineValue", params) - def inlay_hint( - self, params: lsp_types.InlayHintParams - ) -> Union[List["lsp_types.InlayHint"], None]: + def inlay_hint(self, params: lsp_types.InlayHintParams) -> list["lsp_types.InlayHint"] | None: """A request to provide inlay hints in a document. The request's parameter is of type {@link InlayHintsParams}, the response is of type {@link InlayHint InlayHint[]} or a Thenable that resolves to such. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_request("textDocument/inlayHint", params) - def resolve_inlay_hint( - self, params: lsp_types.InlayHint - ) -> "lsp_types.InlayHint": + def resolve_inlay_hint(self, params: lsp_types.InlayHint) -> "lsp_types.InlayHint": """A request to resolve additional properties for an inlay hint. The request's parameter is of type {@link InlayHint}, the response is of type {@link InlayHint} or a Thenable that resolves to such. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_request("inlayHint/resolve", params) - def text_document_diagnostic( - self, params: lsp_types.DocumentDiagnosticParams - ) -> "lsp_types.DocumentDiagnosticReport": + def text_document_diagnostic(self, params: lsp_types.DocumentDiagnosticParams) -> "lsp_types.DocumentDiagnosticReport": """The document diagnostic request definition. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_request("textDocument/diagnostic", params) - def workspace_diagnostic( - self, params: lsp_types.WorkspaceDiagnosticParams - ) -> "lsp_types.WorkspaceDiagnosticReport": + def workspace_diagnostic(self, params: lsp_types.WorkspaceDiagnosticParams) -> "lsp_types.WorkspaceDiagnosticReport": """The workspace diagnostic request definition. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_request("workspace/diagnostic", params) - def initialize( - self, params: lsp_types.InitializeParams - ) -> "lsp_types.InitializeResult": + def initialize(self, params: lsp_types.InitializeParams) -> "lsp_types.InitializeResult": """The initialize request is sent from the client to the server. It is sent once as the request after starting up the server. The requests parameter is of type {@link InitializeParams} the response if of type {@link InitializeResult} of a Thenable that - resolves to such.""" + resolves to such. + """ return self.send_request("initialize", params) def shutdown(self) -> None: """A shutdown request is sent from the client to the server. It is sent once when the client decides to shutdown the server. The only notification that is sent after a shutdown request - is the exit event.""" + is the exit event. + """ return self.send_request("shutdown") - def will_save_wait_until( - self, params: lsp_types.WillSaveTextDocumentParams - ) -> Union[List["lsp_types.TextEdit"], None]: + def will_save_wait_until(self, params: lsp_types.WillSaveTextDocumentParams) -> list["lsp_types.TextEdit"] | None: """A document will save request is sent from the client to the server before the document is actually saved. The request can return an array of TextEdits which will be applied to the text document before it is saved. Please note that clients might drop results if computing the text edits took too long or if a server constantly fails on this request. This is done to keep the save fast and - reliable.""" + reliable. + """ return self.send_request("textDocument/willSaveWaitUntil", params) - def completion( - self, params: lsp_types.CompletionParams - ) -> Union[List["lsp_types.CompletionItem"], "lsp_types.CompletionList", None]: + def completion(self, params: lsp_types.CompletionParams) -> Union[list["lsp_types.CompletionItem"], "lsp_types.CompletionList", None]: """Request to request completion at a given text document position. The request's parameter is of type {@link TextDocumentPosition} the response is of type {@link CompletionItem CompletionItem[]} or {@link CompletionList} @@ -270,85 +243,72 @@ class SolidLspRequest: """ return self.send_request("textDocument/completion", params) - def resolve_completion_item( - self, params: lsp_types.CompletionItem - ) -> "lsp_types.CompletionItem": + def resolve_completion_item(self, params: lsp_types.CompletionItem) -> "lsp_types.CompletionItem": """Request to resolve additional information for a given completion item.The request's parameter is of type {@link CompletionItem} the response - is of type {@link CompletionItem} or a Thenable that resolves to such.""" + is of type {@link CompletionItem} or a Thenable that resolves to such. + """ return self.send_request("completionItem/resolve", params) - def hover( - self, params: lsp_types.HoverParams - ) -> Union["lsp_types.Hover", None]: + def hover(self, params: lsp_types.HoverParams) -> Union["lsp_types.Hover", None]: """Request to request hover information at a given text document position. The request's parameter is of type {@link TextDocumentPosition} the response is of - type {@link Hover} or a Thenable that resolves to such.""" + type {@link Hover} or a Thenable that resolves to such. + """ return self.send_request("textDocument/hover", params) - def signature_help( - self, params: lsp_types.SignatureHelpParams - ) -> Union["lsp_types.SignatureHelp", None]: + def signature_help(self, params: lsp_types.SignatureHelpParams) -> Union["lsp_types.SignatureHelp", None]: return self.send_request("textDocument/signatureHelp", params) - def definition( - self, params: lsp_types.DefinitionParams - ) -> Union["lsp_types.Definition", List["lsp_types.LocationLink"], None]: + def definition(self, params: lsp_types.DefinitionParams) -> Union["lsp_types.Definition", list["lsp_types.LocationLink"], None]: """A request to resolve the definition location of a symbol at a given text document position. The request's parameter is of type [TextDocumentPosition] (#TextDocumentPosition) the response is of either type {@link Definition} or a typed array of {@link DefinitionLink} or a Thenable that resolves - to such.""" + to such. + """ return self.send_request("textDocument/definition", params) - def references( - self, params: lsp_types.ReferenceParams - ) -> Union[List["lsp_types.Location"], None]: + def references(self, params: lsp_types.ReferenceParams) -> list["lsp_types.Location"] | None: """A request to resolve project-wide references for the symbol denoted by the given text document position. The request's parameter is of type {@link ReferenceParams} the response is of type - {@link Location Location[]} or a Thenable that resolves to such.""" + {@link Location Location[]} or a Thenable that resolves to such. + """ return self.send_request("textDocument/references", params) - def document_highlight( - self, params: lsp_types.DocumentHighlightParams - ) -> Union[List["lsp_types.DocumentHighlight"], None]: + def document_highlight(self, params: lsp_types.DocumentHighlightParams) -> list["lsp_types.DocumentHighlight"] | None: """Request to resolve a {@link DocumentHighlight} for a given text document position. The request's parameter is of type [TextDocumentPosition] (#TextDocumentPosition) the request response is of type [DocumentHighlight[]] - (#DocumentHighlight) or a Thenable that resolves to such.""" + (#DocumentHighlight) or a Thenable that resolves to such. + """ return self.send_request("textDocument/documentHighlight", params) def document_symbol( - self, params: lsp_types.DocumentSymbolParams - ) -> Union[ - List["lsp_types.SymbolInformation"], List["lsp_types.DocumentSymbol"], None - ]: + self, params: lsp_types.DocumentSymbolParams + ) -> list["lsp_types.SymbolInformation"] | list["lsp_types.DocumentSymbol"] | None: """A request to list all symbols found in a given text document. The request's parameter is of type {@link TextDocumentIdentifier} the response is of type {@link SymbolInformation SymbolInformation[]} or a Thenable - that resolves to such.""" + that resolves to such. + """ return self.send_request("textDocument/documentSymbol", params) - def code_action( - self, params: lsp_types.CodeActionParams - ) -> Union[List[Union["lsp_types.Command", "lsp_types.CodeAction"]], None]: + def code_action(self, params: lsp_types.CodeActionParams) -> list[Union["lsp_types.Command", "lsp_types.CodeAction"]] | None: """A request to provide commands for the given text document and range.""" return self.send_request("textDocument/codeAction", params) - def resolve_code_action( - self, params: lsp_types.CodeAction - ) -> "lsp_types.CodeAction": + def resolve_code_action(self, params: lsp_types.CodeAction) -> "lsp_types.CodeAction": """Request to resolve additional information for a given code action.The request's parameter is of type {@link CodeAction} the response - is of type {@link CodeAction} or a Thenable that resolves to such.""" + is of type {@link CodeAction} or a Thenable that resolves to such. + """ return self.send_request("codeAction/resolve", params) def workspace_symbol( - self, params: lsp_types.WorkspaceSymbolParams - ) -> Union[ - List["lsp_types.SymbolInformation"], List["lsp_types.WorkspaceSymbol"], None - ]: + self, params: lsp_types.WorkspaceSymbolParams + ) -> list["lsp_types.SymbolInformation"] | list["lsp_types.WorkspaceSymbol"] | None: """A request to list project-wide symbols matching the query string given by the {@link WorkspaceSymbolParams}. The response is of type {@link SymbolInformation SymbolInformation[]} or a Thenable that @@ -360,76 +320,58 @@ class SolidLspRequest: """ return self.send_request("workspace/symbol", params) - def resolve_workspace_symbol( - self, params: lsp_types.WorkspaceSymbol - ) -> "lsp_types.WorkspaceSymbol": + def resolve_workspace_symbol(self, params: lsp_types.WorkspaceSymbol) -> "lsp_types.WorkspaceSymbol": """A request to resolve the range inside the workspace symbol's location. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_request("workspaceSymbol/resolve", params) - def code_lens( - self, params: lsp_types.CodeLensParams - ) -> Union[List["lsp_types.CodeLens"], None]: + def code_lens(self, params: lsp_types.CodeLensParams) -> list["lsp_types.CodeLens"] | None: """A request to provide code lens for the given text document.""" return self.send_request("textDocument/codeLens", params) - def resolve_code_lens( - self, params: lsp_types.CodeLens - ) -> "lsp_types.CodeLens": + def resolve_code_lens(self, params: lsp_types.CodeLens) -> "lsp_types.CodeLens": """A request to resolve a command for a given code lens.""" return self.send_request("codeLens/resolve", params) - def document_link( - self, params: lsp_types.DocumentLinkParams - ) -> Union[List["lsp_types.DocumentLink"], None]: + def document_link(self, params: lsp_types.DocumentLinkParams) -> list["lsp_types.DocumentLink"] | None: """A request to provide document links""" return self.send_request("textDocument/documentLink", params) - def resolve_document_link( - self, params: lsp_types.DocumentLink - ) -> "lsp_types.DocumentLink": + def resolve_document_link(self, params: lsp_types.DocumentLink) -> "lsp_types.DocumentLink": """Request to resolve additional information for a given document link. The request's parameter is of type {@link DocumentLink} the response - is of type {@link DocumentLink} or a Thenable that resolves to such.""" + is of type {@link DocumentLink} or a Thenable that resolves to such. + """ return self.send_request("documentLink/resolve", params) - def formatting( - self, params: lsp_types.DocumentFormattingParams - ) -> Union[List["lsp_types.TextEdit"], None]: + def formatting(self, params: lsp_types.DocumentFormattingParams) -> list["lsp_types.TextEdit"] | None: """A request to to format a whole document.""" return self.send_request("textDocument/formatting", params) - def range_formatting( - self, params: lsp_types.DocumentRangeFormattingParams - ) -> Union[List["lsp_types.TextEdit"], None]: + def range_formatting(self, params: lsp_types.DocumentRangeFormattingParams) -> list["lsp_types.TextEdit"] | None: """A request to to format a range in a document.""" return self.send_request("textDocument/rangeFormatting", params) - def on_type_formatting( - self, params: lsp_types.DocumentOnTypeFormattingParams - ) -> Union[List["lsp_types.TextEdit"], None]: + def on_type_formatting(self, params: lsp_types.DocumentOnTypeFormattingParams) -> list["lsp_types.TextEdit"] | None: """A request to format a document on type.""" return self.send_request("textDocument/onTypeFormatting", params) - def rename( - self, params: lsp_types.RenameParams - ) -> Union["lsp_types.WorkspaceEdit", None]: + def rename(self, params: lsp_types.RenameParams) -> Union["lsp_types.WorkspaceEdit", None]: """A request to rename a symbol.""" return self.send_request("textDocument/rename", params) - def prepare_rename( - self, params: lsp_types.PrepareRenameParams - ) -> Union["lsp_types.PrepareRenameResult", None]: + def prepare_rename(self, params: lsp_types.PrepareRenameParams) -> Union["lsp_types.PrepareRenameResult", None]: """A request to test and perform the setup necessary for a rename. - @since 3.16 - support for default behavior""" + @since 3.16 - support for default behavior + """ return self.send_request("textDocument/prepareRename", params) - def execute_command( - self, params: lsp_types.ExecuteCommandParams - ) -> Union["lsp_types.LSPAny", None]: + def execute_command(self, params: lsp_types.ExecuteCommandParams) -> Union["lsp_types.LSPAny", None]: """A request send from the client to the server to execute a command. The request might return - a workspace edit which the client will apply to the workspace.""" + a workspace edit which the client will apply to the workspace. + """ return self.send_request("workspace/executeCommand", params) From 27826d89c6831c2091a43aeeeabdd6fb7c339f31 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Mon, 23 Jun 2025 16:25:19 +0200 Subject: [PATCH 03/11] Move components from multilspy to solidlsp --- src/multilspy/__init__.py | 2 +- src/multilspy/language_server.py | 47 ++++++++++--------- .../clangd_language_server.py | 12 ++--- .../dart_language_server.py | 5 +- .../eclipse_jdtls/eclipse_jdtls.py | 14 +++--- src/multilspy/language_servers/gopls/gopls.py | 13 ++--- .../intelephense/intelephense.py | 10 ++-- .../jedi_language_server/jedi_server.py | 10 ++-- .../kotlin_language_server.py | 13 +++-- .../language_servers/omnisharp/omnisharp.py | 12 ++--- .../pyright_language_server/pyright_server.py | 11 ++--- .../rust_analyzer/rust_analyzer.py | 12 ++--- .../language_servers/solargraph/solargraph.py | 10 ++-- .../typescript_language_server.py | 10 ++-- src/serena/agent.py | 6 +-- src/serena/symbol.py | 2 +- src/serena/util/inspection.py | 2 +- .../clangd_language_server.py | 10 ++-- .../dart_language_server.py | 6 +-- .../eclipse_jdtls/eclipse_jdtls.py | 12 ++--- src/solidlsp/language_servers/gopls/gopls.py | 8 ++-- .../intelephense/intelephense.py | 10 ++-- .../jedi_language_server/jedi_server.py | 8 ++-- .../kotlin_language_server.py | 10 ++-- .../language_servers/omnisharp/omnisharp.py | 12 ++--- .../pyright_language_server/pyright_server.py | 8 ++-- .../rust_analyzer/rust_analyzer.py | 10 ++-- .../language_servers/solargraph/solargraph.py | 8 ++-- .../typescript_language_server.py | 10 ++-- src/solidlsp/ls.py | 28 ++++++----- src/solidlsp/ls_handler.py | 8 ++-- .../lsp_protocol_handler/lsp_constants.py | 0 .../lsp_protocol_handler/lsp_requests.py | 3 +- .../lsp_protocol_handler/lsp_types.py | 0 .../lsp_protocol_handler/server.py | 0 src/solidlsp/lsp_request.py | 2 +- .../multilspy_config.py | 0 .../multilspy_exceptions.py | 0 .../multilspy_logger.py | 0 .../multilspy_settings.py | 0 .../multilspy_types.py | 0 .../multilspy_utils.py | 6 +-- src/{multilspy => solidlsp}/type_helpers.py | 0 test/conftest.py | 4 +- test/multilspy/go/test_go_basic.py | 4 +- test/multilspy/java/test_java_basic.py | 4 +- test/multilspy/php/test_php_basic.py | 2 +- test/multilspy/python/test_python_basic.py | 2 +- .../test_retrieval_with_ignored_dirs.py | 2 +- .../multilspy/python/test_symbol_retrieval.py | 4 +- test/multilspy/rust/test_rust_basic.py | 4 +- .../typescript/test_typescript_basic.py | 4 +- test/serena/test_serena_agent.py | 2 +- test/serena/test_symbol_editing.py | 2 +- 54 files changed, 192 insertions(+), 192 deletions(-) rename src/{multilspy => solidlsp}/lsp_protocol_handler/lsp_constants.py (100%) rename src/{multilspy => solidlsp}/lsp_protocol_handler/lsp_requests.py (99%) rename src/{multilspy => solidlsp}/lsp_protocol_handler/lsp_types.py (100%) rename src/{multilspy => solidlsp}/lsp_protocol_handler/server.py (100%) rename src/{multilspy => solidlsp}/multilspy_config.py (100%) rename src/{multilspy => solidlsp}/multilspy_exceptions.py (100%) rename src/{multilspy => solidlsp}/multilspy_logger.py (100%) rename src/{multilspy => solidlsp}/multilspy_settings.py (100%) rename src/{multilspy => solidlsp}/multilspy_types.py (100%) rename src/{multilspy => solidlsp}/multilspy_utils.py (98%) rename src/{multilspy => solidlsp}/type_helpers.py (100%) diff --git a/src/multilspy/__init__.py b/src/multilspy/__init__.py index a8229e9f..0bd17583 100644 --- a/src/multilspy/__init__.py +++ b/src/multilspy/__init__.py @@ -2,7 +2,7 @@ This module contains the multilspy API """ -from . import multilspy_types as Types +from solidlsp import multilspy_types as Types from .language_server import LanguageServer, SyncLanguageServer __all__ = ["LanguageServer", "Types", "SyncLanguageServer"] diff --git a/src/multilspy/language_server.py b/src/multilspy/language_server.py index db9c1cbe..b584b899 100644 --- a/src/multilspy/language_server.py +++ b/src/multilspy/language_server.py @@ -19,28 +19,26 @@ from collections import defaultdict from contextlib import asynccontextmanager, contextmanager from copy import copy from pathlib import Path, PurePath -import time from typing import AsyncIterator, Dict, Iterator, List, Optional, Tuple, Union, cast import pathspec import tqdm -from . import multilspy_types -from .lsp_protocol_handler import lsp_types as LSPTypes -from .lsp_protocol_handler.lsp_constants import LSPConstants -from .lsp_protocol_handler.lsp_types import Definition, DefinitionParams, LocationLink, SymbolKind -from .lsp_protocol_handler.server import ( +from solidlsp import multilspy_types +from solidlsp.lsp_protocol_handler import lsp_types as LSPTypes, lsp_types +from solidlsp.lsp_protocol_handler.lsp_constants import LSPConstants +from solidlsp.lsp_protocol_handler.lsp_types import Definition, DefinitionParams, LocationLink, SymbolKind +from solidlsp.lsp_protocol_handler.server import ( Error, LanguageServerHandler, ProcessLaunchInfo, StringDict, ) -from .multilspy_config import Language, MultilspyConfig -from .multilspy_exceptions import MultilspyException -from .multilspy_logger import MultilspyLogger -from .multilspy_utils import FileUtils, PathUtils, TextUtils -from .type_helpers import ensure_all_methods_implemented -from .lsp_protocol_handler import lsp_types +from solidlsp.multilspy_config import Language, MultilspyConfig +from solidlsp.multilspy_exceptions import MultilspyException +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_utils import FileUtils, PathUtils, TextUtils +from solidlsp.type_helpers import ensure_all_methods_implemented # Serena dependencies # We will need to watch out for circular imports, but it's probably better to not @@ -49,7 +47,7 @@ from .lsp_protocol_handler import lsp_types # since it caches (in-memory) file contents, so we can avoid reading from disk. # Moreover, the way we want to use the language server (for retrieving actual content), # it makes sense to have more content-related utils directly in it. -from serena.text_utils import LineType, MatchedConsecutiveLines, TextLine, search_files +from serena.text_utils import MatchedConsecutiveLines, search_files @@ -217,7 +215,8 @@ class LanguageServer: self.language = Language(language_id) # load cache first to prevent any racing conditions due to asyncio stuff - self._document_symbols_cache: dict[str, Tuple[str, Tuple[List[multilspy_types.UnifiedSymbolInformation], List[multilspy_types.UnifiedSymbolInformation]]]] = {} + self._document_symbols_cache: dict[str, Tuple[str, Tuple[List[multilspy_types.UnifiedSymbolInformation], List[ + multilspy_types.UnifiedSymbolInformation]]]] = {} """Maps file paths to a tuple of (file_content_hash, result_of_request_document_symbols)""" self._cache_lock = threading.Lock() self._cache_has_changed: bool = False @@ -629,7 +628,8 @@ class LanguageServer: return ret # Some LS cause problems with this, so the call is isolated from the rest to allow overriding in subclasses - async def _send_references_request(self, relative_file_path: str, line: int, column: int) -> List[lsp_types.Location] | None: + async def _send_references_request(self, relative_file_path: str, line: int, column: int) -> List[ + lsp_types.Location] | None: return await self.server.send.references( { "textDocument": {"uri": PathUtils.path_to_uri(os.path.join(self.repository_root_path, relative_file_path))}, @@ -830,7 +830,8 @@ class LanguageServer: for json_repr in set([json.dumps(item, sort_keys=True) for item in completions_list]) ] - async def request_document_symbols(self, relative_file_path: str, include_body: bool = False) -> Tuple[List[multilspy_types.UnifiedSymbolInformation], List[multilspy_types.UnifiedSymbolInformation]]: + async def request_document_symbols(self, relative_file_path: str, include_body: bool = False) -> Tuple[List[ + multilspy_types.UnifiedSymbolInformation], List[multilspy_types.UnifiedSymbolInformation]]: """ Raise a [textDocument/documentSymbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol) request to the Language Server to find symbols in the given file. Wait for the response and return the result. @@ -926,7 +927,8 @@ class LanguageServer: if LSPConstants.CHILDREN in root_symbol: # TODO: l_tree should be a list of TreeRepr. Define the following function to return TreeRepr as well - def visit_tree_nodes_and_build_tree_repr(node: GenericDocumentSymbol) -> List[multilspy_types.UnifiedSymbolInformation]: + def visit_tree_nodes_and_build_tree_repr(node: GenericDocumentSymbol) -> List[ + multilspy_types.UnifiedSymbolInformation]: node = cast(multilspy_types.UnifiedSymbolInformation, node) l: List[multilspy_types.UnifiedSymbolInformation] = [] turn_item_into_symbol_with_children(node) @@ -948,7 +950,8 @@ class LanguageServer: self._cache_has_changed = True return result - async def request_full_symbol_tree(self, within_relative_path: str | None = None, include_body: bool = False) -> List[multilspy_types.UnifiedSymbolInformation]: + async def request_full_symbol_tree(self, within_relative_path: str | None = None, include_body: bool = False) -> List[ + multilspy_types.UnifiedSymbolInformation]: """ Will go through all files in the project or within a relative path and build a tree of symbols. Note: this may be slow the first time it is called, especially if `within_relative_path` is not used to restrict the search. @@ -1817,7 +1820,7 @@ class SyncLanguageServer: self.language_server.request_references(file_path, line, column), self.loop ).result(timeout=self.timeout) except Exception as e: - from multilspy.lsp_protocol_handler.server import Error + from solidlsp.lsp_protocol_handler.server import Error if isinstance(e, Error) and getattr(e, 'code', None) == -32603: raise RuntimeError( f"LSP internal error (-32603) when requesting references for {file_path}:{line}:{column}. " @@ -1865,7 +1868,8 @@ class SyncLanguageServer: ).result(timeout=self.timeout) return result - def request_document_symbols(self, relative_file_path: str, include_body: bool = False) -> Tuple[List[multilspy_types.UnifiedSymbolInformation], List[multilspy_types.UnifiedSymbolInformation]]: + def request_document_symbols(self, relative_file_path: str, include_body: bool = False) -> Tuple[List[ + multilspy_types.UnifiedSymbolInformation], List[multilspy_types.UnifiedSymbolInformation]]: """ Raise a [textDocument/documentSymbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol) request to the Language Server to find symbols in the given file. Wait for the response and return the result. @@ -1879,7 +1883,8 @@ class SyncLanguageServer: ).result() return result - def request_full_symbol_tree(self, within_relative_path: str | None = None, include_body: bool = False) -> List[multilspy_types.UnifiedSymbolInformation]: + def request_full_symbol_tree(self, within_relative_path: str | None = None, include_body: bool = False) -> List[ + multilspy_types.UnifiedSymbolInformation]: """ Will go through all files in the project and build a tree of symbols. Note: this may be slow the first time it is called. diff --git a/src/multilspy/language_servers/clangd_language_server/clangd_language_server.py b/src/multilspy/language_servers/clangd_language_server/clangd_language_server.py index e28aa2c0..6960b9f5 100644 --- a/src/multilspy/language_servers/clangd_language_server/clangd_language_server.py +++ b/src/multilspy/language_servers/clangd_language_server/clangd_language_server.py @@ -11,13 +11,13 @@ import pathlib from contextlib import asynccontextmanager from typing import AsyncIterator -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_logger import MultilspyLogger from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_utils import FileUtils -from multilspy.multilspy_utils import PlatformUtils +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_utils import FileUtils +from solidlsp.multilspy_utils import PlatformUtils class ClangdLanguageServer(LanguageServer): diff --git a/src/multilspy/language_servers/dart_language_server/dart_language_server.py b/src/multilspy/language_servers/dart_language_server/dart_language_server.py index cd8ea518..8f9aaab0 100644 --- a/src/multilspy/language_servers/dart_language_server/dart_language_server.py +++ b/src/multilspy/language_servers/dart_language_server/dart_language_server.py @@ -2,13 +2,12 @@ from contextlib import asynccontextmanager import logging import os import pathlib -import shutil import stat from typing import AsyncIterator from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo import json -from multilspy.multilspy_utils import FileUtils, PlatformUtils +from solidlsp.multilspy_utils import FileUtils, PlatformUtils class DartLanguageServer(LanguageServer): diff --git a/src/multilspy/language_servers/eclipse_jdtls/eclipse_jdtls.py b/src/multilspy/language_servers/eclipse_jdtls/eclipse_jdtls.py index a88dc1b6..103c9760 100644 --- a/src/multilspy/language_servers/eclipse_jdtls/eclipse_jdtls.py +++ b/src/multilspy/language_servers/eclipse_jdtls/eclipse_jdtls.py @@ -16,14 +16,14 @@ from typing import AsyncIterator from overrides import override -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_logger import MultilspyLogger from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_settings import MultilspySettings -from multilspy.multilspy_utils import FileUtils -from multilspy.multilspy_utils import PlatformUtils +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_settings import MultilspySettings +from solidlsp.multilspy_utils import FileUtils +from solidlsp.multilspy_utils import PlatformUtils from pathlib import PurePath diff --git a/src/multilspy/language_servers/gopls/gopls.py b/src/multilspy/language_servers/gopls/gopls.py index 0e5574a0..117f5c25 100644 --- a/src/multilspy/language_servers/gopls/gopls.py +++ b/src/multilspy/language_servers/gopls/gopls.py @@ -5,18 +5,15 @@ import os import pathlib import subprocess from contextlib import asynccontextmanager -from typing import AsyncIterator, List +from typing import AsyncIterator from overrides import override -from multilspy import multilspy_types -from multilspy.lsp_protocol_handler import lsp_types -from multilspy.multilspy_exceptions import MultilspyException -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_logger import MultilspyLogger from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import Error, ProcessLaunchInfo -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.multilspy_config import MultilspyConfig +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.multilspy_config import MultilspyConfig class Gopls(LanguageServer): diff --git a/src/multilspy/language_servers/intelephense/intelephense.py b/src/multilspy/language_servers/intelephense/intelephense.py index d2ad9117..5a023fcf 100644 --- a/src/multilspy/language_servers/intelephense/intelephense.py +++ b/src/multilspy/language_servers/intelephense/intelephense.py @@ -15,12 +15,12 @@ from typing import AsyncIterator from overrides import override -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_logger import MultilspyLogger from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.lsp_protocol_handler.lsp_types import DefinitionParams, InitializeParams -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_utils import PlatformUtils, PlatformId +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.lsp_types import DefinitionParams, InitializeParams +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_utils import PlatformUtils, PlatformId class Intelephense(LanguageServer): """ diff --git a/src/multilspy/language_servers/jedi_language_server/jedi_server.py b/src/multilspy/language_servers/jedi_language_server/jedi_server.py index 28c9d91d..6df83653 100644 --- a/src/multilspy/language_servers/jedi_language_server/jedi_server.py +++ b/src/multilspy/language_servers/jedi_language_server/jedi_server.py @@ -7,15 +7,15 @@ import logging import os import pathlib from contextlib import asynccontextmanager -from typing import AsyncIterator, Tuple +from typing import AsyncIterator from overrides import override -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_logger import MultilspyLogger from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.multilspy_config import MultilspyConfig +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.multilspy_config import MultilspyConfig class JediServer(LanguageServer): diff --git a/src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py b/src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py index ca4e8666..0b2906ca 100644 --- a/src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py +++ b/src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py @@ -2,7 +2,6 @@ Provides Kotlin specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Kotlin. """ -import asyncio import dataclasses import json import logging @@ -12,13 +11,13 @@ import pathlib from contextlib import asynccontextmanager from typing import AsyncIterator -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_logger import MultilspyLogger from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_utils import FileUtils -from multilspy.multilspy_utils import PlatformUtils +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_utils import FileUtils +from solidlsp.multilspy_utils import PlatformUtils @dataclasses.dataclass diff --git a/src/multilspy/language_servers/omnisharp/omnisharp.py b/src/multilspy/language_servers/omnisharp/omnisharp.py index df3cf0cd..b376de7d 100644 --- a/src/multilspy/language_servers/omnisharp/omnisharp.py +++ b/src/multilspy/language_servers/omnisharp/omnisharp.py @@ -13,13 +13,13 @@ from typing import AsyncIterator, Iterable from overrides import override -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_logger import MultilspyLogger from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_exceptions import MultilspyException -from multilspy.multilspy_utils import FileUtils, PlatformUtils, PlatformId, DotnetVersion +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_exceptions import MultilspyException +from solidlsp.multilspy_utils import FileUtils, PlatformUtils, PlatformId, DotnetVersion def breadth_first_file_scan(root) -> Iterable[str]: diff --git a/src/multilspy/language_servers/pyright_language_server/pyright_server.py b/src/multilspy/language_servers/pyright_language_server/pyright_server.py index 48855831..0cb9533a 100644 --- a/src/multilspy/language_servers/pyright_language_server/pyright_server.py +++ b/src/multilspy/language_servers/pyright_language_server/pyright_server.py @@ -3,21 +3,20 @@ Provides Python specific instantiation of the LanguageServer class. Contains var """ import asyncio -import json import logging import os import pathlib import re from contextlib import asynccontextmanager -from typing import AsyncIterator, Tuple +from typing import AsyncIterator from overrides import override -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_logger import MultilspyLogger from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.multilspy_config import MultilspyConfig +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.multilspy_config import MultilspyConfig class PyrightServer(LanguageServer): diff --git a/src/multilspy/language_servers/rust_analyzer/rust_analyzer.py b/src/multilspy/language_servers/rust_analyzer/rust_analyzer.py index 65f3598f..60d71035 100644 --- a/src/multilspy/language_servers/rust_analyzer/rust_analyzer.py +++ b/src/multilspy/language_servers/rust_analyzer/rust_analyzer.py @@ -13,13 +13,13 @@ from typing import AsyncIterator from overrides import override -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_logger import MultilspyLogger from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_utils import FileUtils -from multilspy.multilspy_utils import PlatformUtils +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_utils import FileUtils +from solidlsp.multilspy_utils import PlatformUtils class RustAnalyzer(LanguageServer): diff --git a/src/multilspy/language_servers/solargraph/solargraph.py b/src/multilspy/language_servers/solargraph/solargraph.py index 099ff9d6..5f3d135b 100644 --- a/src/multilspy/language_servers/solargraph/solargraph.py +++ b/src/multilspy/language_servers/solargraph/solargraph.py @@ -13,13 +13,11 @@ import pathlib from contextlib import asynccontextmanager from typing import AsyncIterator, override -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_logger import MultilspyLogger from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_utils import FileUtils -from multilspy.multilspy_utils import PlatformUtils, PlatformId +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.multilspy_config import MultilspyConfig class Solargraph(LanguageServer): diff --git a/src/multilspy/language_servers/typescript_language_server/typescript_language_server.py b/src/multilspy/language_servers/typescript_language_server/typescript_language_server.py index 479936c9..edd3b306 100644 --- a/src/multilspy/language_servers/typescript_language_server/typescript_language_server.py +++ b/src/multilspy/language_servers/typescript_language_server/typescript_language_server.py @@ -16,11 +16,11 @@ from time import sleep from overrides import override from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import PlatformId, PlatformUtils +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_utils import PlatformId, PlatformUtils # Platform-specific imports if os.name != 'nt': # Unix-like systems diff --git a/src/serena/agent.py b/src/serena/agent.py index ef6e2264..4952aa24 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -34,9 +34,9 @@ from sensai.util.logging import LOG_DEFAULT_FORMAT, FallbackHandler from sensai.util.string import ToStringMixin, dict_string from multilspy import SyncLanguageServer -from multilspy.multilspy_config import Language, MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_types import SymbolKind +from solidlsp.multilspy_config import Language, MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_types import SymbolKind from serena import serena_version from serena.config import SerenaAgentContext, SerenaAgentMode from serena.constants import ( diff --git a/src/serena/symbol.py b/src/serena/symbol.py index f7cb194c..334671db 100644 --- a/src/serena/symbol.py +++ b/src/serena/symbol.py @@ -11,7 +11,7 @@ from sensai.util.string import ToStringMixin from multilspy import SyncLanguageServer from multilspy.language_server import ReferenceInSymbol as LSPReferenceInSymbol -from multilspy.multilspy_types import Position, SymbolKind, UnifiedSymbolInformation +from solidlsp.multilspy_types import Position, SymbolKind, UnifiedSymbolInformation if TYPE_CHECKING: from .agent import SerenaAgent diff --git a/src/serena/util/inspection.py b/src/serena/util/inspection.py index 509da8e1..1e5abd57 100644 --- a/src/serena/util/inspection.py +++ b/src/serena/util/inspection.py @@ -3,7 +3,7 @@ import os from collections.abc import Generator from typing import TypeVar -from multilspy.multilspy_config import Language +from solidlsp.multilspy_config import Language from serena.util.file_system import find_all_non_ignored_files T = TypeVar("T") diff --git a/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py index 54d364e1..c3d34976 100644 --- a/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py @@ -9,11 +9,11 @@ import pathlib import stat import threading -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import FileUtils, PlatformUtils +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_utils import FileUtils, PlatformUtils from solidlsp.ls import SolidLanguageServer diff --git a/src/solidlsp/language_servers/dart_language_server/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server/dart_language_server.py index ec81c8c7..710f40a0 100644 --- a/src/solidlsp/language_servers/dart_language_server/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server/dart_language_server.py @@ -4,9 +4,9 @@ import os import pathlib import stat -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import FileUtils, PlatformUtils +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_utils import FileUtils, PlatformUtils from solidlsp.ls import SolidLanguageServer diff --git a/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py index 8bce13be..eb0bafcb 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py @@ -15,12 +15,12 @@ from pathlib import PurePath from overrides import override -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_settings import MultilspySettings -from multilspy.multilspy_utils import FileUtils, PlatformUtils +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_settings import MultilspySettings +from solidlsp.multilspy_utils import FileUtils, PlatformUtils from solidlsp.ls import SolidLanguageServer diff --git a/src/solidlsp/language_servers/gopls/gopls.py b/src/solidlsp/language_servers/gopls/gopls.py index 78f15caf..04e423aa 100644 --- a/src/solidlsp/language_servers/gopls/gopls.py +++ b/src/solidlsp/language_servers/gopls/gopls.py @@ -7,10 +7,10 @@ import threading from overrides import override -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.ls import SolidLanguageServer diff --git a/src/solidlsp/language_servers/intelephense/intelephense.py b/src/solidlsp/language_servers/intelephense/intelephense.py index fa415734..d78e483c 100644 --- a/src/solidlsp/language_servers/intelephense/intelephense.py +++ b/src/solidlsp/language_servers/intelephense/intelephense.py @@ -12,11 +12,11 @@ from time import sleep from overrides import override -from multilspy.lsp_protocol_handler.lsp_types import DefinitionParams, InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import PlatformId, PlatformUtils +from solidlsp.lsp_protocol_handler.lsp_types import DefinitionParams, InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_utils import PlatformId, PlatformUtils from solidlsp.ls import SolidLanguageServer diff --git a/src/solidlsp/language_servers/jedi_language_server/jedi_server.py b/src/solidlsp/language_servers/jedi_language_server/jedi_server.py index 65fbe623..e935954e 100644 --- a/src/solidlsp/language_servers/jedi_language_server/jedi_server.py +++ b/src/solidlsp/language_servers/jedi_language_server/jedi_server.py @@ -9,10 +9,10 @@ import pathlib from overrides import override -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.ls import SolidLanguageServer diff --git a/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py b/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py index bdb13330..bab71d13 100644 --- a/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py +++ b/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py @@ -9,11 +9,11 @@ import os import pathlib import stat -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import FileUtils, PlatformUtils +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_utils import FileUtils, PlatformUtils from solidlsp.ls import SolidLanguageServer diff --git a/src/solidlsp/language_servers/omnisharp/omnisharp.py b/src/solidlsp/language_servers/omnisharp/omnisharp.py index cc10f58a..f5580338 100644 --- a/src/solidlsp/language_servers/omnisharp/omnisharp.py +++ b/src/solidlsp/language_servers/omnisharp/omnisharp.py @@ -12,12 +12,12 @@ from collections.abc import Iterable from overrides import override -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_exceptions import MultilspyException -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_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.multilspy_config import MultilspyConfig +from solidlsp.multilspy_exceptions import MultilspyException +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_utils import DotnetVersion, FileUtils, PlatformId, PlatformUtils from solidlsp.ls import SolidLanguageServer diff --git a/src/solidlsp/language_servers/pyright_language_server/pyright_server.py b/src/solidlsp/language_servers/pyright_language_server/pyright_server.py index 1307e0e1..70811094 100644 --- a/src/solidlsp/language_servers/pyright_language_server/pyright_server.py +++ b/src/solidlsp/language_servers/pyright_language_server/pyright_server.py @@ -10,10 +10,10 @@ import threading from overrides import override -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.ls import SolidLanguageServer diff --git a/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py index e0b1a8ca..a5a14233 100644 --- a/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py @@ -11,11 +11,11 @@ import threading from overrides import override -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import FileUtils, PlatformUtils +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_utils import FileUtils, PlatformUtils from solidlsp.ls import SolidLanguageServer diff --git a/src/solidlsp/language_servers/solargraph/solargraph.py b/src/solidlsp/language_servers/solargraph/solargraph.py index 96fc7045..c3ea6199 100644 --- a/src/solidlsp/language_servers/solargraph/solargraph.py +++ b/src/solidlsp/language_servers/solargraph/solargraph.py @@ -12,10 +12,10 @@ import subprocess import threading from typing import override -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.ls import SolidLanguageServer diff --git a/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py index 7d5fae62..1d6c3f71 100644 --- a/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py @@ -13,11 +13,11 @@ from time import sleep from overrides import override -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import PlatformId, PlatformUtils +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.multilspy_config import MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_utils import PlatformId, PlatformUtils from solidlsp.ls import SolidLanguageServer # Platform-specific imports diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index 11c9febb..2ff2edf6 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -16,21 +16,20 @@ from typing import Self, cast import pathspec import tqdm -from multilspy import multilspy_types +from solidlsp import multilspy_types from multilspy.language_server import GenericDocumentSymbol, LSPFileBuffer, ReferenceInSymbol -from multilspy.lsp_protocol_handler import lsp_types -from multilspy.lsp_protocol_handler import lsp_types as LSPTypes -from multilspy.lsp_protocol_handler.lsp_constants import LSPConstants -from multilspy.lsp_protocol_handler.lsp_types import Definition, DefinitionParams, LocationLink, SymbolKind -from multilspy.lsp_protocol_handler.server import ( +from solidlsp.lsp_protocol_handler import lsp_types, lsp_types as LSPTypes +from solidlsp.lsp_protocol_handler.lsp_constants import LSPConstants +from solidlsp.lsp_protocol_handler.lsp_types import Definition, DefinitionParams, LocationLink, SymbolKind +from solidlsp.lsp_protocol_handler.server import ( Error, ProcessLaunchInfo, StringDict, ) -from multilspy.multilspy_config import Language, MultilspyConfig -from multilspy.multilspy_exceptions import MultilspyException -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_utils import FileUtils, PathUtils, TextUtils +from solidlsp.multilspy_config import Language, MultilspyConfig +from solidlsp.multilspy_exceptions import MultilspyException +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_utils import FileUtils, PathUtils, TextUtils from serena.text_utils import MatchedConsecutiveLines, search_files from solidlsp.ls_handler import SolidLanguageServerHandler @@ -175,7 +174,8 @@ class SolidLanguageServer(ABC): # load cache first to prevent any racing conditions due to asyncio stuff self._document_symbols_cache: dict[ - str, tuple[str, tuple[list[multilspy_types.UnifiedSymbolInformation], list[multilspy_types.UnifiedSymbolInformation]]] + str, tuple[str, tuple[list[multilspy_types.UnifiedSymbolInformation], list[ + multilspy_types.UnifiedSymbolInformation]]] ] = {} """Maps file paths to a tuple of (file_content_hash, result_of_request_document_symbols)""" self._cache_lock = threading.Lock() @@ -543,7 +543,8 @@ class SolidLanguageServer(ABC): return ret # Some LS cause problems with this, so the call is isolated from the rest to allow overriding in subclasses - def _send_references_request(self, relative_file_path: str, line: int, column: int) -> list[lsp_types.Location] | None: + def _send_references_request(self, relative_file_path: str, line: int, column: int) -> list[ + lsp_types.Location] | None: return self.server.send.references( { "textDocument": {"uri": PathUtils.path_to_uri(os.path.join(self.repository_root_path, relative_file_path))}, @@ -839,7 +840,8 @@ class SolidLanguageServer(ABC): if LSPConstants.CHILDREN in root_symbol: # TODO: l_tree should be a list of TreeRepr. Define the following function to return TreeRepr as well - def visit_tree_nodes_and_build_tree_repr(node: GenericDocumentSymbol) -> list[multilspy_types.UnifiedSymbolInformation]: + def visit_tree_nodes_and_build_tree_repr(node: GenericDocumentSymbol) -> list[ + multilspy_types.UnifiedSymbolInformation]: node = cast(multilspy_types.UnifiedSymbolInformation, node) l: list[multilspy_types.UnifiedSymbolInformation] = [] turn_item_into_symbol_with_children(node) diff --git a/src/solidlsp/ls_handler.py b/src/solidlsp/ls_handler.py index 0bc32555..a0910433 100644 --- a/src/solidlsp/ls_handler.py +++ b/src/solidlsp/ls_handler.py @@ -12,9 +12,9 @@ from typing import Any import psutil -from multilspy.lsp_protocol_handler.lsp_requests import LspNotification -from multilspy.lsp_protocol_handler.lsp_types import ErrorCodes -from multilspy.lsp_protocol_handler.server import ( +from solidlsp.lsp_protocol_handler.lsp_requests import LspNotification +from solidlsp.lsp_protocol_handler.lsp_types import ErrorCodes +from solidlsp.lsp_protocol_handler.server import ( ENCODING, Error, MessageType, @@ -28,7 +28,7 @@ from multilspy.lsp_protocol_handler.server import ( make_request, make_response, ) -from multilspy.multilspy_exceptions import MultilspyException +from solidlsp.multilspy_exceptions import MultilspyException from solidlsp.lsp_request import SolidLspRequest log = logging.getLogger(__name__) diff --git a/src/multilspy/lsp_protocol_handler/lsp_constants.py b/src/solidlsp/lsp_protocol_handler/lsp_constants.py similarity index 100% rename from src/multilspy/lsp_protocol_handler/lsp_constants.py rename to src/solidlsp/lsp_protocol_handler/lsp_constants.py diff --git a/src/multilspy/lsp_protocol_handler/lsp_requests.py b/src/solidlsp/lsp_protocol_handler/lsp_requests.py similarity index 99% rename from src/multilspy/lsp_protocol_handler/lsp_requests.py rename to src/solidlsp/lsp_protocol_handler/lsp_requests.py index 852df21d..206f252a 100644 --- a/src/multilspy/lsp_protocol_handler/lsp_requests.py +++ b/src/solidlsp/lsp_protocol_handler/lsp_requests.py @@ -30,7 +30,8 @@ SOFTWARE. """ from typing import List, Union -from multilspy.lsp_protocol_handler import lsp_types +from solidlsp.lsp_protocol_handler import lsp_types + class LspRequest: def __init__(self, send_request): diff --git a/src/multilspy/lsp_protocol_handler/lsp_types.py b/src/solidlsp/lsp_protocol_handler/lsp_types.py similarity index 100% rename from src/multilspy/lsp_protocol_handler/lsp_types.py rename to src/solidlsp/lsp_protocol_handler/lsp_types.py diff --git a/src/multilspy/lsp_protocol_handler/server.py b/src/solidlsp/lsp_protocol_handler/server.py similarity index 100% rename from src/multilspy/lsp_protocol_handler/server.py rename to src/solidlsp/lsp_protocol_handler/server.py diff --git a/src/solidlsp/lsp_request.py b/src/solidlsp/lsp_request.py index 6320fe5a..11fc67b9 100644 --- a/src/solidlsp/lsp_request.py +++ b/src/solidlsp/lsp_request.py @@ -1,6 +1,6 @@ from typing import Union -from multilspy.lsp_protocol_handler import lsp_types +from solidlsp.lsp_protocol_handler import lsp_types class SolidLspRequest: diff --git a/src/multilspy/multilspy_config.py b/src/solidlsp/multilspy_config.py similarity index 100% rename from src/multilspy/multilspy_config.py rename to src/solidlsp/multilspy_config.py diff --git a/src/multilspy/multilspy_exceptions.py b/src/solidlsp/multilspy_exceptions.py similarity index 100% rename from src/multilspy/multilspy_exceptions.py rename to src/solidlsp/multilspy_exceptions.py diff --git a/src/multilspy/multilspy_logger.py b/src/solidlsp/multilspy_logger.py similarity index 100% rename from src/multilspy/multilspy_logger.py rename to src/solidlsp/multilspy_logger.py diff --git a/src/multilspy/multilspy_settings.py b/src/solidlsp/multilspy_settings.py similarity index 100% rename from src/multilspy/multilspy_settings.py rename to src/solidlsp/multilspy_settings.py diff --git a/src/multilspy/multilspy_types.py b/src/solidlsp/multilspy_types.py similarity index 100% rename from src/multilspy/multilspy_types.py rename to src/solidlsp/multilspy_types.py diff --git a/src/multilspy/multilspy_utils.py b/src/solidlsp/multilspy_utils.py similarity index 98% rename from src/multilspy/multilspy_utils.py rename to src/solidlsp/multilspy_utils.py index 20959c4e..92cca5ff 100644 --- a/src/multilspy/multilspy_utils.py +++ b/src/solidlsp/multilspy_utils.py @@ -14,10 +14,10 @@ import platform import subprocess from enum import Enum -from multilspy.multilspy_exceptions import MultilspyException +from solidlsp.multilspy_exceptions import MultilspyException from pathlib import PurePath, Path -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.multilspy_types import UnifiedSymbolInformation +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_types import UnifiedSymbolInformation class TextUtils: diff --git a/src/multilspy/type_helpers.py b/src/solidlsp/type_helpers.py similarity index 100% rename from src/multilspy/type_helpers.py rename to src/solidlsp/type_helpers.py diff --git a/test/conftest.py b/test/conftest.py index 75bc8c74..9630cc6a 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -6,8 +6,8 @@ import pytest from sensai.util.logging import configure from multilspy.language_server import SyncLanguageServer -from multilspy.multilspy_config import Language, MultilspyConfig -from multilspy.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_config import Language, MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger from serena.constants import USE_SOLID_LSP from serena.util.file_system import GitignoreParser from solidlsp.ls import SolidLanguageServer diff --git a/test/multilspy/go/test_go_basic.py b/test/multilspy/go/test_go_basic.py index 6b5e316b..535b3ad2 100644 --- a/test/multilspy/go/test_go_basic.py +++ b/test/multilspy/go/test_go_basic.py @@ -3,8 +3,8 @@ import os import pytest from multilspy import SyncLanguageServer -from multilspy.multilspy_config import Language -from multilspy.multilspy_utils import SymbolUtils +from solidlsp.multilspy_config import Language +from solidlsp.multilspy_utils import SymbolUtils @pytest.mark.go diff --git a/test/multilspy/java/test_java_basic.py b/test/multilspy/java/test_java_basic.py index e7ff5a8c..2403d122 100644 --- a/test/multilspy/java/test_java_basic.py +++ b/test/multilspy/java/test_java_basic.py @@ -3,8 +3,8 @@ import os import pytest from multilspy import SyncLanguageServer -from multilspy.multilspy_config import Language -from multilspy.multilspy_utils import SymbolUtils +from solidlsp.multilspy_config import Language +from solidlsp.multilspy_utils import SymbolUtils @pytest.mark.java diff --git a/test/multilspy/php/test_php_basic.py b/test/multilspy/php/test_php_basic.py index 942281fe..2bfed7b3 100644 --- a/test/multilspy/php/test_php_basic.py +++ b/test/multilspy/php/test_php_basic.py @@ -3,7 +3,7 @@ from pathlib import Path import pytest from multilspy.language_server import SyncLanguageServer -from multilspy.multilspy_config import Language +from solidlsp.multilspy_config import Language @pytest.mark.php diff --git a/test/multilspy/python/test_python_basic.py b/test/multilspy/python/test_python_basic.py index 5af4f953..20ddabb2 100644 --- a/test/multilspy/python/test_python_basic.py +++ b/test/multilspy/python/test_python_basic.py @@ -10,7 +10,7 @@ import os import pytest from multilspy.language_server import SyncLanguageServer -from multilspy.multilspy_config import Language +from solidlsp.multilspy_config import Language from serena.text_utils import LineType diff --git a/test/multilspy/python/test_retrieval_with_ignored_dirs.py b/test/multilspy/python/test_retrieval_with_ignored_dirs.py index 0db668ef..f6b25c9a 100644 --- a/test/multilspy/python/test_retrieval_with_ignored_dirs.py +++ b/test/multilspy/python/test_retrieval_with_ignored_dirs.py @@ -4,7 +4,7 @@ from pathlib import Path import pytest from multilspy.language_server import SyncLanguageServer -from multilspy.multilspy_config import Language +from solidlsp.multilspy_config import Language from test.conftest import create_ls # This mark will be applied to all tests in this module diff --git a/test/multilspy/python/test_symbol_retrieval.py b/test/multilspy/python/test_symbol_retrieval.py index ba5bd615..80229ef2 100644 --- a/test/multilspy/python/test_symbol_retrieval.py +++ b/test/multilspy/python/test_symbol_retrieval.py @@ -11,8 +11,8 @@ import os import pytest from multilspy.language_server import SyncLanguageServer -from multilspy.multilspy_config import Language -from multilspy.multilspy_types import SymbolKind +from solidlsp.multilspy_config import Language +from solidlsp.multilspy_types import SymbolKind pytestmark = pytest.mark.python diff --git a/test/multilspy/rust/test_rust_basic.py b/test/multilspy/rust/test_rust_basic.py index 02e9d502..20aa6e0a 100644 --- a/test/multilspy/rust/test_rust_basic.py +++ b/test/multilspy/rust/test_rust_basic.py @@ -3,8 +3,8 @@ import os import pytest from multilspy import SyncLanguageServer -from multilspy.multilspy_config import Language -from multilspy.multilspy_utils import SymbolUtils +from solidlsp.multilspy_config import Language +from solidlsp.multilspy_utils import SymbolUtils @pytest.mark.rust diff --git a/test/multilspy/typescript/test_typescript_basic.py b/test/multilspy/typescript/test_typescript_basic.py index 442b5a1e..fca1e546 100644 --- a/test/multilspy/typescript/test_typescript_basic.py +++ b/test/multilspy/typescript/test_typescript_basic.py @@ -3,8 +3,8 @@ import os import pytest from multilspy import SyncLanguageServer -from multilspy.multilspy_config import Language -from multilspy.multilspy_utils import SymbolUtils +from solidlsp.multilspy_config import Language +from solidlsp.multilspy_utils import SymbolUtils @pytest.mark.typescript diff --git a/test/serena/test_serena_agent.py b/test/serena/test_serena_agent.py index 853d86a1..1642b389 100644 --- a/test/serena/test_serena_agent.py +++ b/test/serena/test_serena_agent.py @@ -5,7 +5,7 @@ from dataclasses import dataclass import pytest -from multilspy.multilspy_config import Language +from solidlsp.multilspy_config import Language from serena.agent import FindReferencingSymbolsTool, FindSymbolTool, Project, ProjectConfig, SerenaAgent, SerenaConfigBase from serena.process_isolated_agent import ProcessIsolatedSerenaAgent from test.conftest import get_repo_path diff --git a/test/serena/test_symbol_editing.py b/test/serena/test_symbol_editing.py index 12be047e..ba00ad12 100644 --- a/test/serena/test_symbol_editing.py +++ b/test/serena/test_symbol_editing.py @@ -11,7 +11,7 @@ from typing import Literal import pytest -from multilspy.multilspy_config import Language +from solidlsp.multilspy_config import Language from serena.symbol import CodeDiff from src.serena.symbol import SymbolManager from test.conftest import create_ls, get_repo_path From c2593a1171fb616569c54905d2acd707ef8f82e8 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Mon, 23 Jun 2025 16:34:56 +0200 Subject: [PATCH 04/11] Apply black & ruff to modules copied from multilspy --- pyproject.toml | 4 + src/serena/agent.py | 6 +- src/serena/util/inspection.py | 2 +- .../clangd_language_server.py | 2 +- .../dart_language_server.py | 2 +- .../eclipse_jdtls/eclipse_jdtls.py | 2 +- src/solidlsp/language_servers/gopls/gopls.py | 2 +- .../intelephense/intelephense.py | 2 +- .../jedi_language_server/jedi_server.py | 2 +- .../kotlin_language_server.py | 2 +- .../language_servers/omnisharp/omnisharp.py | 2 +- .../pyright_language_server/pyright_server.py | 2 +- .../rust_analyzer/rust_analyzer.py | 2 +- .../language_servers/solargraph/solargraph.py | 2 +- .../typescript_language_server.py | 2 +- src/solidlsp/ls.py | 18 +- src/solidlsp/ls_handler.py | 2 +- .../lsp_protocol_handler/lsp_constants.py | 1 + .../lsp_protocol_handler/lsp_requests.py | 399 ++++---- .../lsp_protocol_handler/lsp_types.py | 865 +++++++++--------- src/solidlsp/lsp_protocol_handler/server.py | 90 +- src/solidlsp/multilspy_config.py | 11 +- src/solidlsp/multilspy_exceptions.py | 3 +- src/solidlsp/multilspy_logger.py | 3 +- src/solidlsp/multilspy_settings.py | 2 + src/solidlsp/multilspy_types.py | 68 +- src/solidlsp/multilspy_utils.py | 99 +- src/solidlsp/type_helpers.py | 11 +- test/conftest.py | 4 +- test/multilspy/python/test_python_basic.py | 2 +- test/serena/test_serena_agent.py | 2 +- test/serena/test_symbol_editing.py | 2 +- 32 files changed, 816 insertions(+), 802 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0b0f9f95..d3ba68a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -241,6 +241,10 @@ ignore = [ "SIM105", # forbids empty/general except clause "SIM113", # wants to enforce use of enumerate "E712", # forbids equality comparison with True/False + "UP007", # forbids some uses of Union + "TID252", # forbids relative imports + "B904", # forces use of raise from other_exception + "RUF012", # forbids mutable attributes as ClassVar ] unfixable = [ "F841", diff --git a/src/serena/agent.py b/src/serena/agent.py index 4952aa24..97bf63e1 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -34,9 +34,6 @@ from sensai.util.logging import LOG_DEFAULT_FORMAT, FallbackHandler from sensai.util.string import ToStringMixin, dict_string from multilspy import SyncLanguageServer -from solidlsp.multilspy_config import Language, MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_types import SymbolKind from serena import serena_version from serena.config import SerenaAgentContext, SerenaAgentMode from serena.constants import ( @@ -57,6 +54,9 @@ from serena.util.inspection import determine_programming_language_composition, i from serena.util.shell import execute_shell_command from serena.util.thread import ExecutionResult, execute_with_timeout from solidlsp.ls import SolidLanguageServer +from solidlsp.multilspy_config import Language, MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.multilspy_types import SymbolKind if TYPE_CHECKING: from serena.gui_log_viewer import GuiLogViewerHandler diff --git a/src/serena/util/inspection.py b/src/serena/util/inspection.py index 1e5abd57..4847fd19 100644 --- a/src/serena/util/inspection.py +++ b/src/serena/util/inspection.py @@ -3,8 +3,8 @@ import os from collections.abc import Generator from typing import TypeVar -from solidlsp.multilspy_config import Language from serena.util.file_system import find_all_non_ignored_files +from solidlsp.multilspy_config import Language T = TypeVar("T") diff --git a/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py index c3d34976..a61ba13f 100644 --- a/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py @@ -9,12 +9,12 @@ import pathlib import stat import threading +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_config import MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_utils import FileUtils, PlatformUtils -from solidlsp.ls import SolidLanguageServer class ClangdLanguageServer(SolidLanguageServer): diff --git a/src/solidlsp/language_servers/dart_language_server/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server/dart_language_server.py index 710f40a0..bf406995 100644 --- a/src/solidlsp/language_servers/dart_language_server/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server/dart_language_server.py @@ -4,10 +4,10 @@ import os import pathlib import stat +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_utils import FileUtils, PlatformUtils -from solidlsp.ls import SolidLanguageServer class DartLanguageServer(SolidLanguageServer): diff --git a/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py index eb0bafcb..bd635945 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py @@ -15,13 +15,13 @@ from pathlib import PurePath from overrides import override +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_config import MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_settings import MultilspySettings from solidlsp.multilspy_utils import FileUtils, PlatformUtils -from solidlsp.ls import SolidLanguageServer @dataclasses.dataclass diff --git a/src/solidlsp/language_servers/gopls/gopls.py b/src/solidlsp/language_servers/gopls/gopls.py index 04e423aa..cd7f6e0b 100644 --- a/src/solidlsp/language_servers/gopls/gopls.py +++ b/src/solidlsp/language_servers/gopls/gopls.py @@ -7,11 +7,11 @@ import threading from overrides import override +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_config import MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.ls import SolidLanguageServer class Gopls(SolidLanguageServer): diff --git a/src/solidlsp/language_servers/intelephense/intelephense.py b/src/solidlsp/language_servers/intelephense/intelephense.py index d78e483c..7e7e8db3 100644 --- a/src/solidlsp/language_servers/intelephense/intelephense.py +++ b/src/solidlsp/language_servers/intelephense/intelephense.py @@ -12,12 +12,12 @@ from time import sleep from overrides import override +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import DefinitionParams, InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_config import MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_utils import PlatformId, PlatformUtils -from solidlsp.ls import SolidLanguageServer class Intelephense(SolidLanguageServer): diff --git a/src/solidlsp/language_servers/jedi_language_server/jedi_server.py b/src/solidlsp/language_servers/jedi_language_server/jedi_server.py index e935954e..9ec7554c 100644 --- a/src/solidlsp/language_servers/jedi_language_server/jedi_server.py +++ b/src/solidlsp/language_servers/jedi_language_server/jedi_server.py @@ -9,11 +9,11 @@ import pathlib from overrides import override +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_config import MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.ls import SolidLanguageServer class JediServer(SolidLanguageServer): diff --git a/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py b/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py index bab71d13..cf4022c2 100644 --- a/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py +++ b/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py @@ -9,12 +9,12 @@ import os import pathlib import stat +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_config import MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_utils import FileUtils, PlatformUtils -from solidlsp.ls import SolidLanguageServer @dataclasses.dataclass diff --git a/src/solidlsp/language_servers/omnisharp/omnisharp.py b/src/solidlsp/language_servers/omnisharp/omnisharp.py index f5580338..b8b33eab 100644 --- a/src/solidlsp/language_servers/omnisharp/omnisharp.py +++ b/src/solidlsp/language_servers/omnisharp/omnisharp.py @@ -12,13 +12,13 @@ from collections.abc import Iterable from overrides import override +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_config import MultilspyConfig from solidlsp.multilspy_exceptions import MultilspyException from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_utils import DotnetVersion, FileUtils, PlatformId, PlatformUtils -from solidlsp.ls import SolidLanguageServer def breadth_first_file_scan(root) -> Iterable[str]: diff --git a/src/solidlsp/language_servers/pyright_language_server/pyright_server.py b/src/solidlsp/language_servers/pyright_language_server/pyright_server.py index 70811094..b7ddd075 100644 --- a/src/solidlsp/language_servers/pyright_language_server/pyright_server.py +++ b/src/solidlsp/language_servers/pyright_language_server/pyright_server.py @@ -10,11 +10,11 @@ import threading from overrides import override +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_config import MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.ls import SolidLanguageServer class PyrightServer(SolidLanguageServer): diff --git a/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py index a5a14233..d88cc598 100644 --- a/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py @@ -11,12 +11,12 @@ import threading from overrides import override +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_config import MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_utils import FileUtils, PlatformUtils -from solidlsp.ls import SolidLanguageServer class RustAnalyzer(SolidLanguageServer): diff --git a/src/solidlsp/language_servers/solargraph/solargraph.py b/src/solidlsp/language_servers/solargraph/solargraph.py index c3ea6199..f733e306 100644 --- a/src/solidlsp/language_servers/solargraph/solargraph.py +++ b/src/solidlsp/language_servers/solargraph/solargraph.py @@ -12,11 +12,11 @@ import subprocess import threading from typing import override +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_config import MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.ls import SolidLanguageServer class Solargraph(SolidLanguageServer): diff --git a/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py index 1d6c3f71..3fe2d86e 100644 --- a/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py @@ -13,12 +13,12 @@ from time import sleep from overrides import override +from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo from solidlsp.multilspy_config import MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_utils import PlatformId, PlatformUtils -from solidlsp.ls import SolidLanguageServer # Platform-specific imports if os.name != "nt": # Unix-like systems diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index 2ff2edf6..cc792db6 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -16,9 +16,12 @@ from typing import Self, cast import pathspec import tqdm -from solidlsp import multilspy_types from multilspy.language_server import GenericDocumentSymbol, LSPFileBuffer, ReferenceInSymbol -from solidlsp.lsp_protocol_handler import lsp_types, lsp_types as LSPTypes +from serena.text_utils import MatchedConsecutiveLines, search_files +from solidlsp import multilspy_types +from solidlsp.ls_handler import SolidLanguageServerHandler +from solidlsp.lsp_protocol_handler import lsp_types +from solidlsp.lsp_protocol_handler import lsp_types as LSPTypes from solidlsp.lsp_protocol_handler.lsp_constants import LSPConstants from solidlsp.lsp_protocol_handler.lsp_types import Definition, DefinitionParams, LocationLink, SymbolKind from solidlsp.lsp_protocol_handler.server import ( @@ -30,8 +33,6 @@ from solidlsp.multilspy_config import Language, MultilspyConfig from solidlsp.multilspy_exceptions import MultilspyException from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_utils import FileUtils, PathUtils, TextUtils -from serena.text_utils import MatchedConsecutiveLines, search_files -from solidlsp.ls_handler import SolidLanguageServerHandler class SolidLanguageServer(ABC): @@ -174,8 +175,7 @@ class SolidLanguageServer(ABC): # load cache first to prevent any racing conditions due to asyncio stuff self._document_symbols_cache: dict[ - str, tuple[str, tuple[list[multilspy_types.UnifiedSymbolInformation], list[ - multilspy_types.UnifiedSymbolInformation]]] + str, tuple[str, tuple[list[multilspy_types.UnifiedSymbolInformation], list[multilspy_types.UnifiedSymbolInformation]]] ] = {} """Maps file paths to a tuple of (file_content_hash, result_of_request_document_symbols)""" self._cache_lock = threading.Lock() @@ -543,8 +543,7 @@ class SolidLanguageServer(ABC): return ret # Some LS cause problems with this, so the call is isolated from the rest to allow overriding in subclasses - def _send_references_request(self, relative_file_path: str, line: int, column: int) -> list[ - lsp_types.Location] | None: + def _send_references_request(self, relative_file_path: str, line: int, column: int) -> list[lsp_types.Location] | None: return self.server.send.references( { "textDocument": {"uri": PathUtils.path_to_uri(os.path.join(self.repository_root_path, relative_file_path))}, @@ -840,8 +839,7 @@ class SolidLanguageServer(ABC): if LSPConstants.CHILDREN in root_symbol: # TODO: l_tree should be a list of TreeRepr. Define the following function to return TreeRepr as well - def visit_tree_nodes_and_build_tree_repr(node: GenericDocumentSymbol) -> list[ - multilspy_types.UnifiedSymbolInformation]: + def visit_tree_nodes_and_build_tree_repr(node: GenericDocumentSymbol) -> list[multilspy_types.UnifiedSymbolInformation]: node = cast(multilspy_types.UnifiedSymbolInformation, node) l: list[multilspy_types.UnifiedSymbolInformation] = [] turn_item_into_symbol_with_children(node) diff --git a/src/solidlsp/ls_handler.py b/src/solidlsp/ls_handler.py index a0910433..e4c8b688 100644 --- a/src/solidlsp/ls_handler.py +++ b/src/solidlsp/ls_handler.py @@ -28,8 +28,8 @@ from solidlsp.lsp_protocol_handler.server import ( make_request, make_response, ) -from solidlsp.multilspy_exceptions import MultilspyException from solidlsp.lsp_request import SolidLspRequest +from solidlsp.multilspy_exceptions import MultilspyException log = logging.getLogger(__name__) diff --git a/src/solidlsp/lsp_protocol_handler/lsp_constants.py b/src/solidlsp/lsp_protocol_handler/lsp_constants.py index 4ca5c3d1..329c0039 100644 --- a/src/solidlsp/lsp_protocol_handler/lsp_constants.py +++ b/src/solidlsp/lsp_protocol_handler/lsp_constants.py @@ -2,6 +2,7 @@ This module contains constants used in the LSP protocol. """ + class LSPConstants: """ This class contains constants used in the LSP protocol. diff --git a/src/solidlsp/lsp_protocol_handler/lsp_requests.py b/src/solidlsp/lsp_protocol_handler/lsp_requests.py index 206f252a..383e4566 100644 --- a/src/solidlsp/lsp_protocol_handler/lsp_requests.py +++ b/src/solidlsp/lsp_protocol_handler/lsp_requests.py @@ -29,7 +29,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from typing import List, Union +from typing import Union + from solidlsp.lsp_protocol_handler import lsp_types @@ -39,96 +40,94 @@ class LspRequest: async def implementation( self, params: lsp_types.ImplementationParams - ) -> Union["lsp_types.Definition", List["lsp_types.LocationLink"], None]: + ) -> Union["lsp_types.Definition", list["lsp_types.LocationLink"], None]: """A request to resolve the implementation locations of a symbol at a given text document position. The request's parameter is of type [TextDocumentPositionParams] (#TextDocumentPositionParams) the response is of type {@link Definition} or a - Thenable that resolves to such.""" + Thenable that resolves to such. + """ return await self.send_request("textDocument/implementation", params) async def type_definition( self, params: lsp_types.TypeDefinitionParams - ) -> Union["lsp_types.Definition", List["lsp_types.LocationLink"], None]: + ) -> Union["lsp_types.Definition", list["lsp_types.LocationLink"], None]: """A request to resolve the type definition locations of a symbol at a given text document position. The request's parameter is of type [TextDocumentPositionParams] (#TextDocumentPositionParams) the response is of type {@link Definition} or a - Thenable that resolves to such.""" + Thenable that resolves to such. + """ return await self.send_request("textDocument/typeDefinition", params) - async def document_color( - self, params: lsp_types.DocumentColorParams - ) -> List["lsp_types.ColorInformation"]: + async def document_color(self, params: lsp_types.DocumentColorParams) -> list["lsp_types.ColorInformation"]: """A request to list all color symbols found in a given text document. The request's parameter is of type {@link DocumentColorParams} the response is of type {@link ColorInformation ColorInformation[]} or a Thenable - that resolves to such.""" + that resolves to such. + """ return await self.send_request("textDocument/documentColor", params) - async def color_presentation( - self, params: lsp_types.ColorPresentationParams - ) -> List["lsp_types.ColorPresentation"]: + async def color_presentation(self, params: lsp_types.ColorPresentationParams) -> list["lsp_types.ColorPresentation"]: """A request to list all presentation for a color. The request's parameter is of type {@link ColorPresentationParams} the response is of type {@link ColorInformation ColorInformation[]} or a Thenable - that resolves to such.""" + that resolves to such. + """ return await self.send_request("textDocument/colorPresentation", params) - async def folding_range( - self, params: lsp_types.FoldingRangeParams - ) -> Union[List["lsp_types.FoldingRange"], None]: + async def folding_range(self, params: lsp_types.FoldingRangeParams) -> list["lsp_types.FoldingRange"] | None: """A request to provide folding ranges in a document. The request's parameter is of type {@link FoldingRangeParams}, the response is of type {@link FoldingRangeList} or a Thenable - that resolves to such.""" + that resolves to such. + """ return await self.send_request("textDocument/foldingRange", params) async def declaration( self, params: lsp_types.DeclarationParams - ) -> Union["lsp_types.Declaration", List["lsp_types.LocationLink"], None]: + ) -> Union["lsp_types.Declaration", list["lsp_types.LocationLink"], None]: """A request to resolve the type definition locations of a symbol at a given text document position. The request's parameter is of type [TextDocumentPositionParams] (#TextDocumentPositionParams) the response is of type {@link Declaration} or a typed array of {@link DeclarationLink} or a Thenable that resolves - to such.""" + to such. + """ return await self.send_request("textDocument/declaration", params) - async def selection_range( - self, params: lsp_types.SelectionRangeParams - ) -> Union[List["lsp_types.SelectionRange"], None]: + async def selection_range(self, params: lsp_types.SelectionRangeParams) -> list["lsp_types.SelectionRange"] | None: """A request to provide selection ranges in a document. The request's parameter is of type {@link SelectionRangeParams}, the response is of type {@link SelectionRange SelectionRange[]} or a Thenable - that resolves to such.""" + that resolves to such. + """ return await self.send_request("textDocument/selectionRange", params) - async def prepare_call_hierarchy( - self, params: lsp_types.CallHierarchyPrepareParams - ) -> Union[List["lsp_types.CallHierarchyItem"], None]: + async def prepare_call_hierarchy(self, params: lsp_types.CallHierarchyPrepareParams) -> list["lsp_types.CallHierarchyItem"] | None: """A request to result a `CallHierarchyItem` in a document at a given position. Can be used as an input to an incoming or outgoing call hierarchy. - @since 3.16.0""" + @since 3.16.0 + """ return await self.send_request("textDocument/prepareCallHierarchy", params) async def incoming_calls( self, params: lsp_types.CallHierarchyIncomingCallsParams - ) -> Union[List["lsp_types.CallHierarchyIncomingCall"], None]: + ) -> list["lsp_types.CallHierarchyIncomingCall"] | None: """A request to resolve the incoming calls for a given `CallHierarchyItem`. - @since 3.16.0""" + @since 3.16.0 + """ return await self.send_request("callHierarchy/incomingCalls", params) async def outgoing_calls( self, params: lsp_types.CallHierarchyOutgoingCallsParams - ) -> Union[List["lsp_types.CallHierarchyOutgoingCall"], None]: + ) -> list["lsp_types.CallHierarchyOutgoingCall"] | None: """A request to resolve the outgoing calls for a given `CallHierarchyItem`. - @since 3.16.0""" + @since 3.16.0 + """ return await self.send_request("callHierarchy/outgoingCalls", params) - async def semantic_tokens_full( - self, params: lsp_types.SemanticTokensParams - ) -> Union["lsp_types.SemanticTokens", None]: + async def semantic_tokens_full(self, params: lsp_types.SemanticTokensParams) -> Union["lsp_types.SemanticTokens", None]: """@since 3.16.0""" return await self.send_request("textDocument/semanticTokens/full", params) @@ -138,157 +137,143 @@ class LspRequest: """@since 3.16.0""" return await self.send_request("textDocument/semanticTokens/full/delta", params) - async def semantic_tokens_range( - self, params: lsp_types.SemanticTokensRangeParams - ) -> Union["lsp_types.SemanticTokens", None]: + async def semantic_tokens_range(self, params: lsp_types.SemanticTokensRangeParams) -> Union["lsp_types.SemanticTokens", None]: """@since 3.16.0""" return await self.send_request("textDocument/semanticTokens/range", params) - async def linked_editing_range( - self, params: lsp_types.LinkedEditingRangeParams - ) -> Union["lsp_types.LinkedEditingRanges", None]: + async def linked_editing_range(self, params: lsp_types.LinkedEditingRangeParams) -> Union["lsp_types.LinkedEditingRanges", None]: """A request to provide ranges that can be edited together. - @since 3.16.0""" + @since 3.16.0 + """ return await self.send_request("textDocument/linkedEditingRange", params) - async def will_create_files( - self, params: lsp_types.CreateFilesParams - ) -> Union["lsp_types.WorkspaceEdit", None]: + async def will_create_files(self, params: lsp_types.CreateFilesParams) -> Union["lsp_types.WorkspaceEdit", None]: """The will create files request is sent from the client to the server before files are actually created as long as the creation is triggered from within the client. - @since 3.16.0""" + @since 3.16.0 + """ return await self.send_request("workspace/willCreateFiles", params) - async def will_rename_files( - self, params: lsp_types.RenameFilesParams - ) -> Union["lsp_types.WorkspaceEdit", None]: + async def will_rename_files(self, params: lsp_types.RenameFilesParams) -> Union["lsp_types.WorkspaceEdit", None]: """The will rename files request is sent from the client to the server before files are actually renamed as long as the rename is triggered from within the client. - @since 3.16.0""" + @since 3.16.0 + """ return await self.send_request("workspace/willRenameFiles", params) - async def will_delete_files( - self, params: lsp_types.DeleteFilesParams - ) -> Union["lsp_types.WorkspaceEdit", None]: + async def will_delete_files(self, params: lsp_types.DeleteFilesParams) -> Union["lsp_types.WorkspaceEdit", None]: """The did delete files notification is sent from the client to the server when files were deleted from within the client. - @since 3.16.0""" + @since 3.16.0 + """ return await self.send_request("workspace/willDeleteFiles", params) - async def moniker( - self, params: lsp_types.MonikerParams - ) -> Union[List["lsp_types.Moniker"], None]: + async def moniker(self, params: lsp_types.MonikerParams) -> list["lsp_types.Moniker"] | None: """A request to get the moniker of a symbol at a given text document position. The request parameter is of type {@link TextDocumentPositionParams}. - The response is of type {@link Moniker Moniker[]} or `null`.""" + The response is of type {@link Moniker Moniker[]} or `null`. + """ return await self.send_request("textDocument/moniker", params) - async def prepare_type_hierarchy( - self, params: lsp_types.TypeHierarchyPrepareParams - ) -> Union[List["lsp_types.TypeHierarchyItem"], None]: + async def prepare_type_hierarchy(self, params: lsp_types.TypeHierarchyPrepareParams) -> list["lsp_types.TypeHierarchyItem"] | None: """A request to result a `TypeHierarchyItem` in a document at a given position. Can be used as an input to a subtypes or supertypes type hierarchy. - @since 3.17.0""" + @since 3.17.0 + """ return await self.send_request("textDocument/prepareTypeHierarchy", params) async def type_hierarchy_supertypes( self, params: lsp_types.TypeHierarchySupertypesParams - ) -> Union[List["lsp_types.TypeHierarchyItem"], None]: + ) -> list["lsp_types.TypeHierarchyItem"] | None: """A request to resolve the supertypes for a given `TypeHierarchyItem`. - @since 3.17.0""" + @since 3.17.0 + """ return await self.send_request("typeHierarchy/supertypes", params) - async def type_hierarchy_subtypes( - self, params: lsp_types.TypeHierarchySubtypesParams - ) -> Union[List["lsp_types.TypeHierarchyItem"], None]: + async def type_hierarchy_subtypes(self, params: lsp_types.TypeHierarchySubtypesParams) -> list["lsp_types.TypeHierarchyItem"] | None: """A request to resolve the subtypes for a given `TypeHierarchyItem`. - @since 3.17.0""" + @since 3.17.0 + """ return await self.send_request("typeHierarchy/subtypes", params) - async def inline_value( - self, params: lsp_types.InlineValueParams - ) -> Union[List["lsp_types.InlineValue"], None]: + async def inline_value(self, params: lsp_types.InlineValueParams) -> list["lsp_types.InlineValue"] | None: """A request to provide inline values in a document. The request's parameter is of type {@link InlineValueParams}, the response is of type {@link InlineValue InlineValue[]} or a Thenable that resolves to such. - @since 3.17.0""" + @since 3.17.0 + """ return await self.send_request("textDocument/inlineValue", params) - async def inlay_hint( - self, params: lsp_types.InlayHintParams - ) -> Union[List["lsp_types.InlayHint"], None]: + async def inlay_hint(self, params: lsp_types.InlayHintParams) -> list["lsp_types.InlayHint"] | None: """A request to provide inlay hints in a document. The request's parameter is of type {@link InlayHintsParams}, the response is of type {@link InlayHint InlayHint[]} or a Thenable that resolves to such. - @since 3.17.0""" + @since 3.17.0 + """ return await self.send_request("textDocument/inlayHint", params) - async def resolve_inlay_hint( - self, params: lsp_types.InlayHint - ) -> "lsp_types.InlayHint": + async def resolve_inlay_hint(self, params: lsp_types.InlayHint) -> "lsp_types.InlayHint": """A request to resolve additional properties for an inlay hint. The request's parameter is of type {@link InlayHint}, the response is of type {@link InlayHint} or a Thenable that resolves to such. - @since 3.17.0""" + @since 3.17.0 + """ return await self.send_request("inlayHint/resolve", params) - async def text_document_diagnostic( - self, params: lsp_types.DocumentDiagnosticParams - ) -> "lsp_types.DocumentDiagnosticReport": + async def text_document_diagnostic(self, params: lsp_types.DocumentDiagnosticParams) -> "lsp_types.DocumentDiagnosticReport": """The document diagnostic request definition. - @since 3.17.0""" + @since 3.17.0 + """ return await self.send_request("textDocument/diagnostic", params) - async def workspace_diagnostic( - self, params: lsp_types.WorkspaceDiagnosticParams - ) -> "lsp_types.WorkspaceDiagnosticReport": + async def workspace_diagnostic(self, params: lsp_types.WorkspaceDiagnosticParams) -> "lsp_types.WorkspaceDiagnosticReport": """The workspace diagnostic request definition. - @since 3.17.0""" + @since 3.17.0 + """ return await self.send_request("workspace/diagnostic", params) - async def initialize( - self, params: lsp_types.InitializeParams - ) -> "lsp_types.InitializeResult": + async def initialize(self, params: lsp_types.InitializeParams) -> "lsp_types.InitializeResult": """The initialize request is sent from the client to the server. It is sent once as the request after starting up the server. The requests parameter is of type {@link InitializeParams} the response if of type {@link InitializeResult} of a Thenable that - resolves to such.""" + resolves to such. + """ return await self.send_request("initialize", params) async def shutdown(self) -> None: """A shutdown request is sent from the client to the server. It is sent once when the client decides to shutdown the server. The only notification that is sent after a shutdown request - is the exit event.""" + is the exit event. + """ return await self.send_request("shutdown") - async def will_save_wait_until( - self, params: lsp_types.WillSaveTextDocumentParams - ) -> Union[List["lsp_types.TextEdit"], None]: + async def will_save_wait_until(self, params: lsp_types.WillSaveTextDocumentParams) -> list["lsp_types.TextEdit"] | None: """A document will save request is sent from the client to the server before the document is actually saved. The request can return an array of TextEdits which will be applied to the text document before it is saved. Please note that clients might drop results if computing the text edits took too long or if a server constantly fails on this request. This is done to keep the save fast and - reliable.""" + reliable. + """ return await self.send_request("textDocument/willSaveWaitUntil", params) async def completion( self, params: lsp_types.CompletionParams - ) -> Union[List["lsp_types.CompletionItem"], "lsp_types.CompletionList", None]: + ) -> Union[list["lsp_types.CompletionItem"], "lsp_types.CompletionList", None]: """Request to request completion at a given text document position. The request's parameter is of type {@link TextDocumentPosition} the response is of type {@link CompletionItem CompletionItem[]} or {@link CompletionList} @@ -301,85 +286,72 @@ class LspRequest: """ return await self.send_request("textDocument/completion", params) - async def resolve_completion_item( - self, params: lsp_types.CompletionItem - ) -> "lsp_types.CompletionItem": + async def resolve_completion_item(self, params: lsp_types.CompletionItem) -> "lsp_types.CompletionItem": """Request to resolve additional information for a given completion item.The request's parameter is of type {@link CompletionItem} the response - is of type {@link CompletionItem} or a Thenable that resolves to such.""" + is of type {@link CompletionItem} or a Thenable that resolves to such. + """ return await self.send_request("completionItem/resolve", params) - async def hover( - self, params: lsp_types.HoverParams - ) -> Union["lsp_types.Hover", None]: + async def hover(self, params: lsp_types.HoverParams) -> Union["lsp_types.Hover", None]: """Request to request hover information at a given text document position. The request's parameter is of type {@link TextDocumentPosition} the response is of - type {@link Hover} or a Thenable that resolves to such.""" + type {@link Hover} or a Thenable that resolves to such. + """ return await self.send_request("textDocument/hover", params) - async def signature_help( - self, params: lsp_types.SignatureHelpParams - ) -> Union["lsp_types.SignatureHelp", None]: + async def signature_help(self, params: lsp_types.SignatureHelpParams) -> Union["lsp_types.SignatureHelp", None]: return await self.send_request("textDocument/signatureHelp", params) - async def definition( - self, params: lsp_types.DefinitionParams - ) -> Union["lsp_types.Definition", List["lsp_types.LocationLink"], None]: + async def definition(self, params: lsp_types.DefinitionParams) -> Union["lsp_types.Definition", list["lsp_types.LocationLink"], None]: """A request to resolve the definition location of a symbol at a given text document position. The request's parameter is of type [TextDocumentPosition] (#TextDocumentPosition) the response is of either type {@link Definition} or a typed array of {@link DefinitionLink} or a Thenable that resolves - to such.""" + to such. + """ return await self.send_request("textDocument/definition", params) - async def references( - self, params: lsp_types.ReferenceParams - ) -> Union[List["lsp_types.Location"], None]: + async def references(self, params: lsp_types.ReferenceParams) -> list["lsp_types.Location"] | None: """A request to resolve project-wide references for the symbol denoted by the given text document position. The request's parameter is of type {@link ReferenceParams} the response is of type - {@link Location Location[]} or a Thenable that resolves to such.""" + {@link Location Location[]} or a Thenable that resolves to such. + """ return await self.send_request("textDocument/references", params) - async def document_highlight( - self, params: lsp_types.DocumentHighlightParams - ) -> Union[List["lsp_types.DocumentHighlight"], None]: + async def document_highlight(self, params: lsp_types.DocumentHighlightParams) -> list["lsp_types.DocumentHighlight"] | None: """Request to resolve a {@link DocumentHighlight} for a given text document position. The request's parameter is of type [TextDocumentPosition] (#TextDocumentPosition) the request response is of type [DocumentHighlight[]] - (#DocumentHighlight) or a Thenable that resolves to such.""" + (#DocumentHighlight) or a Thenable that resolves to such. + """ return await self.send_request("textDocument/documentHighlight", params) async def document_symbol( self, params: lsp_types.DocumentSymbolParams - ) -> Union[ - List["lsp_types.SymbolInformation"], List["lsp_types.DocumentSymbol"], None - ]: + ) -> list["lsp_types.SymbolInformation"] | list["lsp_types.DocumentSymbol"] | None: """A request to list all symbols found in a given text document. The request's parameter is of type {@link TextDocumentIdentifier} the response is of type {@link SymbolInformation SymbolInformation[]} or a Thenable - that resolves to such.""" + that resolves to such. + """ return await self.send_request("textDocument/documentSymbol", params) - async def code_action( - self, params: lsp_types.CodeActionParams - ) -> Union[List[Union["lsp_types.Command", "lsp_types.CodeAction"]], None]: + async def code_action(self, params: lsp_types.CodeActionParams) -> list[Union["lsp_types.Command", "lsp_types.CodeAction"]] | None: """A request to provide commands for the given text document and range.""" return await self.send_request("textDocument/codeAction", params) - async def resolve_code_action( - self, params: lsp_types.CodeAction - ) -> "lsp_types.CodeAction": + async def resolve_code_action(self, params: lsp_types.CodeAction) -> "lsp_types.CodeAction": """Request to resolve additional information for a given code action.The request's parameter is of type {@link CodeAction} the response - is of type {@link CodeAction} or a Thenable that resolves to such.""" + is of type {@link CodeAction} or a Thenable that resolves to such. + """ return await self.send_request("codeAction/resolve", params) async def workspace_symbol( self, params: lsp_types.WorkspaceSymbolParams - ) -> Union[ - List["lsp_types.SymbolInformation"], List["lsp_types.WorkspaceSymbol"], None - ]: + ) -> list["lsp_types.SymbolInformation"] | list["lsp_types.WorkspaceSymbol"] | None: """A request to list project-wide symbols matching the query string given by the {@link WorkspaceSymbolParams}. The response is of type {@link SymbolInformation SymbolInformation[]} or a Thenable that @@ -391,78 +363,60 @@ class LspRequest: """ return await self.send_request("workspace/symbol", params) - async def resolve_workspace_symbol( - self, params: lsp_types.WorkspaceSymbol - ) -> "lsp_types.WorkspaceSymbol": + async def resolve_workspace_symbol(self, params: lsp_types.WorkspaceSymbol) -> "lsp_types.WorkspaceSymbol": """A request to resolve the range inside the workspace symbol's location. - @since 3.17.0""" + @since 3.17.0 + """ return await self.send_request("workspaceSymbol/resolve", params) - async def code_lens( - self, params: lsp_types.CodeLensParams - ) -> Union[List["lsp_types.CodeLens"], None]: + async def code_lens(self, params: lsp_types.CodeLensParams) -> list["lsp_types.CodeLens"] | None: """A request to provide code lens for the given text document.""" return await self.send_request("textDocument/codeLens", params) - async def resolve_code_lens( - self, params: lsp_types.CodeLens - ) -> "lsp_types.CodeLens": + async def resolve_code_lens(self, params: lsp_types.CodeLens) -> "lsp_types.CodeLens": """A request to resolve a command for a given code lens.""" return await self.send_request("codeLens/resolve", params) - async def document_link( - self, params: lsp_types.DocumentLinkParams - ) -> Union[List["lsp_types.DocumentLink"], None]: + async def document_link(self, params: lsp_types.DocumentLinkParams) -> list["lsp_types.DocumentLink"] | None: """A request to provide document links""" return await self.send_request("textDocument/documentLink", params) - async def resolve_document_link( - self, params: lsp_types.DocumentLink - ) -> "lsp_types.DocumentLink": + async def resolve_document_link(self, params: lsp_types.DocumentLink) -> "lsp_types.DocumentLink": """Request to resolve additional information for a given document link. The request's parameter is of type {@link DocumentLink} the response - is of type {@link DocumentLink} or a Thenable that resolves to such.""" + is of type {@link DocumentLink} or a Thenable that resolves to such. + """ return await self.send_request("documentLink/resolve", params) - async def formatting( - self, params: lsp_types.DocumentFormattingParams - ) -> Union[List["lsp_types.TextEdit"], None]: + async def formatting(self, params: lsp_types.DocumentFormattingParams) -> list["lsp_types.TextEdit"] | None: """A request to to format a whole document.""" return await self.send_request("textDocument/formatting", params) - async def range_formatting( - self, params: lsp_types.DocumentRangeFormattingParams - ) -> Union[List["lsp_types.TextEdit"], None]: + async def range_formatting(self, params: lsp_types.DocumentRangeFormattingParams) -> list["lsp_types.TextEdit"] | None: """A request to to format a range in a document.""" return await self.send_request("textDocument/rangeFormatting", params) - async def on_type_formatting( - self, params: lsp_types.DocumentOnTypeFormattingParams - ) -> Union[List["lsp_types.TextEdit"], None]: + async def on_type_formatting(self, params: lsp_types.DocumentOnTypeFormattingParams) -> list["lsp_types.TextEdit"] | None: """A request to format a document on type.""" return await self.send_request("textDocument/onTypeFormatting", params) - async def rename( - self, params: lsp_types.RenameParams - ) -> Union["lsp_types.WorkspaceEdit", None]: + async def rename(self, params: lsp_types.RenameParams) -> Union["lsp_types.WorkspaceEdit", None]: """A request to rename a symbol.""" return await self.send_request("textDocument/rename", params) - async def prepare_rename( - self, params: lsp_types.PrepareRenameParams - ) -> Union["lsp_types.PrepareRenameResult", None]: + async def prepare_rename(self, params: lsp_types.PrepareRenameParams) -> Union["lsp_types.PrepareRenameResult", None]: """A request to test and perform the setup necessary for a rename. - @since 3.16 - support for default behavior""" + @since 3.16 - support for default behavior + """ return await self.send_request("textDocument/prepareRename", params) - async def execute_command( - self, params: lsp_types.ExecuteCommandParams - ) -> Union["lsp_types.LSPAny", None]: + async def execute_command(self, params: lsp_types.ExecuteCommandParams) -> Union["lsp_types.LSPAny", None]: """A request send from the client to the server to execute a command. The request might return - a workspace edit which the client will apply to the workspace.""" + a workspace edit which the client will apply to the workspace. + """ return await self.send_request("workspace/executeCommand", params) @@ -470,92 +424,87 @@ class LspNotification: def __init__(self, send_notification): self.send_notification = send_notification - def did_change_workspace_folders( - self, params: lsp_types.DidChangeWorkspaceFoldersParams - ) -> None: + def did_change_workspace_folders(self, params: lsp_types.DidChangeWorkspaceFoldersParams) -> None: """The `workspace/didChangeWorkspaceFolders` notification is sent from the client to the server when the workspace - folder configuration changes.""" + folder configuration changes. + """ return self.send_notification("workspace/didChangeWorkspaceFolders", params) - def cancel_work_done_progress( - self, params: lsp_types.WorkDoneProgressCancelParams - ) -> None: + def cancel_work_done_progress(self, params: lsp_types.WorkDoneProgressCancelParams) -> None: """The `window/workDoneProgress/cancel` notification is sent from the client to the server to cancel a progress - initiated on the server side.""" + initiated on the server side. + """ return self.send_notification("window/workDoneProgress/cancel", params) def did_create_files(self, params: lsp_types.CreateFilesParams) -> None: """The did create files notification is sent from the client to the server when files were created from within the client. - @since 3.16.0""" + @since 3.16.0 + """ return self.send_notification("workspace/didCreateFiles", params) def did_rename_files(self, params: lsp_types.RenameFilesParams) -> None: """The did rename files notification is sent from the client to the server when files were renamed from within the client. - @since 3.16.0""" + @since 3.16.0 + """ return self.send_notification("workspace/didRenameFiles", params) def did_delete_files(self, params: lsp_types.DeleteFilesParams) -> None: """The will delete files request is sent from the client to the server before files are actually deleted as long as the deletion is triggered from within the client. - @since 3.16.0""" + @since 3.16.0 + """ return self.send_notification("workspace/didDeleteFiles", params) - def did_open_notebook_document( - self, params: lsp_types.DidOpenNotebookDocumentParams - ) -> None: + def did_open_notebook_document(self, params: lsp_types.DidOpenNotebookDocumentParams) -> None: """A notification sent when a notebook opens. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_notification("notebookDocument/didOpen", params) - def did_change_notebook_document( - self, params: lsp_types.DidChangeNotebookDocumentParams - ) -> None: + def did_change_notebook_document(self, params: lsp_types.DidChangeNotebookDocumentParams) -> None: return self.send_notification("notebookDocument/didChange", params) - def did_save_notebook_document( - self, params: lsp_types.DidSaveNotebookDocumentParams - ) -> None: + def did_save_notebook_document(self, params: lsp_types.DidSaveNotebookDocumentParams) -> None: """A notification sent when a notebook document is saved. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_notification("notebookDocument/didSave", params) - def did_close_notebook_document( - self, params: lsp_types.DidCloseNotebookDocumentParams - ) -> None: + def did_close_notebook_document(self, params: lsp_types.DidCloseNotebookDocumentParams) -> None: """A notification sent when a notebook closes. - @since 3.17.0""" + @since 3.17.0 + """ return self.send_notification("notebookDocument/didClose", params) def initialized(self, params: lsp_types.InitializedParams) -> None: """The initialized notification is sent from the client to the server after the client is fully initialized and the server - is allowed to send requests from the server to the client.""" + is allowed to send requests from the server to the client. + """ return self.send_notification("initialized", params) def exit(self) -> None: """The exit event is sent from the client to the server to - ask the server to exit its process.""" + ask the server to exit its process. + """ return self.send_notification("exit") - def workspace_did_change_configuration( - self, params: lsp_types.DidChangeConfigurationParams - ) -> None: + def workspace_did_change_configuration(self, params: lsp_types.DidChangeConfigurationParams) -> None: """The configuration change notification is sent from the client to the server when the client's configuration has changed. The notification contains - the changed configuration as defined by the language client.""" + the changed configuration as defined by the language client. + """ return self.send_notification("workspace/didChangeConfiguration", params) - def did_open_text_document( - self, params: lsp_types.DidOpenTextDocumentParams - ) -> None: + def did_open_text_document(self, params: lsp_types.DidOpenTextDocumentParams) -> None: """The document open notification is sent from the client to the server to signal newly opened text documents. The document's truth is now managed by the client and the server must not try to read the document's truth using the document's @@ -563,47 +512,43 @@ class LspNotification: mean that its content is presented in an editor. An open notification must not be sent more than once without a corresponding close notification send before. This means open and close notification must be balanced and the max open count - is one.""" + is one. + """ return self.send_notification("textDocument/didOpen", params) - def did_change_text_document( - self, params: lsp_types.DidChangeTextDocumentParams - ) -> None: + def did_change_text_document(self, params: lsp_types.DidChangeTextDocumentParams) -> None: """The document change notification is sent from the client to the server to signal - changes to a text document.""" + changes to a text document. + """ return self.send_notification("textDocument/didChange", params) - def did_close_text_document( - self, params: lsp_types.DidCloseTextDocumentParams - ) -> None: + def did_close_text_document(self, params: lsp_types.DidCloseTextDocumentParams) -> None: """The document close notification is sent from the client to the server when the document got closed in the client. The document's truth now exists where the document's uri points to (e.g. if the document's uri is a file uri the truth now exists on disk). As with the open notification the close notification is about managing the document's content. Receiving a close notification doesn't mean that the document was open in an editor before. A close - notification requires a previous open notification to be sent.""" + notification requires a previous open notification to be sent. + """ return self.send_notification("textDocument/didClose", params) - def did_save_text_document( - self, params: lsp_types.DidSaveTextDocumentParams - ) -> None: + def did_save_text_document(self, params: lsp_types.DidSaveTextDocumentParams) -> None: """The document save notification is sent from the client to the server when - the document got saved in the client.""" + the document got saved in the client. + """ return self.send_notification("textDocument/didSave", params) - def will_save_text_document( - self, params: lsp_types.WillSaveTextDocumentParams - ) -> None: + def will_save_text_document(self, params: lsp_types.WillSaveTextDocumentParams) -> None: """A document will save notification is sent from the client to the server before - the document is actually saved.""" + the document is actually saved. + """ return self.send_notification("textDocument/willSave", params) - def did_change_watched_files( - self, params: lsp_types.DidChangeWatchedFilesParams - ) -> None: + def did_change_watched_files(self, params: lsp_types.DidChangeWatchedFilesParams) -> None: """The watched files notification is sent from the client to the server when - the client detects changes to file watched by the language client.""" + the client detects changes to file watched by the language client. + """ return self.send_notification("workspace/didChangeWatchedFiles", params) def set_trace(self, params: lsp_types.SetTraceParams) -> None: diff --git a/src/solidlsp/lsp_protocol_handler/lsp_types.py b/src/solidlsp/lsp_protocol_handler/lsp_types.py index 6d6a7546..5488c358 100644 --- a/src/solidlsp/lsp_protocol_handler/lsp_types.py +++ b/src/solidlsp/lsp_protocol_handler/lsp_types.py @@ -30,8 +30,9 @@ SOFTWARE. """ from enum import Enum, IntEnum, IntFlag -from typing import Dict, List, Literal, Union -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, Union + +from typing_extensions import TypedDict URI = str DocumentUri = str @@ -44,7 +45,8 @@ class SemanticTokenTypes(Enum): an clients can specify additional token types via the corresponding client capabilities. - @since 3.16.0""" + @since 3.16.0 + """ Namespace = "namespace" Type = "type" @@ -79,7 +81,8 @@ class SemanticTokenModifiers(Enum): an clients can specify additional token types via the corresponding client capabilities. - @since 3.16.0""" + @since 3.16.0 + """ Declaration = "declaration" Definition = "definition" @@ -96,7 +99,8 @@ class SemanticTokenModifiers(Enum): class DocumentDiagnosticReportKind(Enum): """The document diagnostic report kinds. - @since 3.17.0""" + @since 3.17.0 + """ Full = "full" """ A diagnostic report with a full @@ -191,8 +195,8 @@ class SymbolKind(IntEnum): Event = 24 Operator = 25 TypeParameter = 26 - - @classmethod + + @classmethod def from_int(cls, value: int) -> "SymbolKind": for symbol_kind in cls: if symbol_kind.value == value: @@ -203,7 +207,8 @@ class SymbolKind(IntEnum): class SymbolTag(IntEnum): """Symbol tags are extra annotations that tweak the rendering of a symbol. - @since 3.16""" + @since 3.16 + """ Deprecated = 1 """ Render a symbol as obsolete, usually using a strike-out. """ @@ -212,7 +217,8 @@ class SymbolTag(IntEnum): class UniquenessLevel(Enum): """Moniker uniqueness level to define scope of the moniker. - @since 3.16.0""" + @since 3.16.0 + """ Document = "document" """ The moniker is only unique inside a document """ @@ -229,7 +235,8 @@ class UniquenessLevel(Enum): class MonikerKind(Enum): """The moniker kind. - @since 3.16.0""" + @since 3.16.0 + """ Import = "import" """ The moniker represent a symbol that is imported into a project """ @@ -243,7 +250,8 @@ class MonikerKind(Enum): class InlayHintKind(IntEnum): """Inlay hint kinds. - @since 3.17.0""" + @since 3.17.0 + """ Type = 1 """ An inlay hint that for a type annotation. """ @@ -266,7 +274,8 @@ class MessageType(IntEnum): class TextDocumentSyncKind(IntEnum): """Defines how the host (editor) should sync - document changes to the language server.""" + document changes to the language server. + """ None_ = 0 """ Documents should not be synced at all. """ @@ -325,7 +334,8 @@ class CompletionItemTag(IntEnum): """Completion item tags are extra annotations that tweak the rendering of a completion item. - @since 3.15.0""" + @since 3.15.0 + """ Deprecated = 1 """ Render a completion as obsolete, usually using a strike-out. """ @@ -333,7 +343,8 @@ class CompletionItemTag(IntEnum): class InsertTextFormat(IntEnum): """Defines whether the insert text in a completion item should be interpreted as - plain text or a snippet.""" + plain text or a snippet. + """ PlainText = 1 """ The primary text to be inserted is treated as a plain string. """ @@ -352,7 +363,8 @@ class InsertTextMode(IntEnum): """How whitespace and indentation is handled during completion item insertion. - @since 3.16.0""" + @since 3.16.0 + """ AsIs = 1 """ The insertion or replace strings is taken as it is. If the @@ -449,7 +461,8 @@ class MarkupKind(Enum): result literals like `Hover`, `ParameterInfo` or `CompletionItem`. Please note that `MarkupKinds` must not start with a `$`. This kinds - are reserved for internal usage.""" + are reserved for internal usage. + """ PlainText = "plaintext" """ Plain text is supported as a content format """ @@ -460,7 +473,8 @@ class MarkupKind(Enum): class PositionEncodingKind(Enum): """A set of predefined position encoding kinds. - @since 3.17.0""" + @since 3.17.0 + """ UTF8 = "utf-8" """ Character offsets count UTF-8 code units. """ @@ -513,7 +527,8 @@ class DiagnosticSeverity(IntEnum): class DiagnosticTag(IntEnum): """The diagnostic tags. - @since 3.15.0""" + @since 3.15.0 + """ Unnecessary = 1 """ Unused or unnecessary code. @@ -542,7 +557,8 @@ class CompletionTriggerKind(IntEnum): class SignatureHelpTriggerKind(IntEnum): """How a signature help was triggered. - @since 3.15.0""" + @since 3.15.0 + """ Invoked = 1 """ Signature help was invoked manually by the user or by a command. """ @@ -555,7 +571,8 @@ class SignatureHelpTriggerKind(IntEnum): class CodeActionTriggerKind(IntEnum): """The reason why code actions were requested. - @since 3.17.0""" + @since 3.17.0 + """ Invoked = 1 """ Code actions were explicitly requested by the user or by an extension. """ @@ -570,7 +587,8 @@ class FileOperationPatternKind(Enum): """A pattern kind describing if a glob pattern matches a file a folder or both. - @since 3.16.0""" + @since 3.16.0 + """ File = "file" """ The pattern matches a file only. """ @@ -581,7 +599,8 @@ class FileOperationPatternKind(Enum): class NotebookCellKind(IntEnum): """A notebook cell kind. - @since 3.17.0""" + @since 3.17.0 + """ Markup = 1 """ A markup-cell is formatted source that is used for display. """ @@ -624,7 +643,7 @@ class TokenFormat(Enum): Relative = "relative" -Definition = Union["Location", List["Location"]] +Definition = Union["Location", list["Location"]] """ The definition of a symbol represented as one or many {@link Location locations}. For most programming languages there is only one location at which a symbol is defined. @@ -638,7 +657,7 @@ DefinitionLink = "LocationLink" Provides additional metadata over normal {@link Location location} definitions, including the range of the defining symbol """ -LSPArray = List["LSPAny"] +LSPArray = list["LSPAny"] """ LSP arrays. @since 3.17.0 """ @@ -650,7 +669,7 @@ convenience it is allowed and assumed that all these properties are optional as well. @since 3.17.0 """ -Declaration = Union["Location", List["Location"]] +Declaration = Union["Location", list["Location"]] """ The declaration of a symbol representation as one or many {@link Location locations}. """ DeclarationLink = "LocationLink" @@ -662,9 +681,7 @@ the declaring symbol. Servers should prefer returning `DeclarationLink` over `Declaration` if supported by the client. """ -InlineValue = Union[ - "InlineValueText", "InlineValueVariableLookup", "InlineValueEvaluatableExpression" -] +InlineValue = Union["InlineValueText", "InlineValueVariableLookup", "InlineValueEvaluatableExpression"] """ Inline value information can be provided by different means: - directly as a text value (class InlineValueText). - as a name to use for a variable lookup (class InlineValueVariableLookup) @@ -673,9 +690,7 @@ The InlineValue types combines all inline value types into one type. @since 3.17.0 """ -DocumentDiagnosticReport = Union[ - "RelatedFullDocumentDiagnosticReport", "RelatedUnchangedDocumentDiagnosticReport" -] +DocumentDiagnosticReport = Union["RelatedFullDocumentDiagnosticReport", "RelatedUnchangedDocumentDiagnosticReport"] """ The result of a document diagnostic pull request. A report can either be a full report containing all diagnostics for the requested document or an unchanged report indicating that nothing @@ -684,14 +699,12 @@ pull request. @since 3.17.0 """ -PrepareRenameResult = Union[ - "Range", "__PrepareRenameResult_Type_1", "__PrepareRenameResult_Type_2" -] +PrepareRenameResult = Union["Range", "__PrepareRenameResult_Type_1", "__PrepareRenameResult_Type_2"] -DocumentSelector = List["DocumentFilter"] +DocumentSelector = list["DocumentFilter"] """ A document selector is the combination of one or many document filters. -@sample `let sel:DocumentSelector = [{ language: 'typescript' }, { language: 'json', pattern: '**∕tsconfig.json' }]`; +@sample `let sel:DocumentSelector = [{ language: 'typescript' }, { language: 'json', pattern: '**/tsconfig.json' }]`; The use of a string as a document filter is deprecated @since 3.16.0. """ @@ -708,9 +721,7 @@ WorkspaceDocumentDiagnosticReport = Union[ @since 3.17.0 """ -TextDocumentContentChangeEvent = Union[ - "__TextDocumentContentChangeEvent_Type_1", "__TextDocumentContentChangeEvent_Type_2" -] +TextDocumentContentChangeEvent = Union["__TextDocumentContentChangeEvent_Type_1", "__TextDocumentContentChangeEvent_Type_2"] """ An event describing a change to a text document. If only a text is provided it is considered to be the full content of the document. """ @@ -734,7 +745,7 @@ a notebook cell document. @since 3.17.0 - proposed support for NotebookCellTextDocumentFilter. """ -LSPObject = Dict[str, "LSPAny"] +LSPObject = dict[str, "LSPAny"] """ LSP object definition. @since 3.17.0 """ @@ -756,7 +767,7 @@ Glob patterns can have the following syntax: - `*` to match one or more characters in a path segment - `?` to match on one character in a path segment - `**` to match any number of path segments, including none -- `{}` to group sub patterns into an OR expression. (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) +- `{}` to group sub patterns into an OR expression. (e.g. `**\u200b/*.{ts,js}` matches all TypeScript and JavaScript files) - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) @@ -781,7 +792,7 @@ Pattern = str - `*` to match one or more characters in a path segment - `?` to match on one character in a path segment - `**` to match any number of path segments, including none -- `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) +- `{}` to group conditions (e.g. `**\u200b/*.{ts,js}` matches all TypeScript and JavaScript files) - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) @@ -802,7 +813,8 @@ class ImplementationParams(TypedDict): class Location(TypedDict): """Represents a location inside a resource, such as a line - inside a text file.""" + inside a text file. + """ uri: "DocumentUri" range: "Range" @@ -858,7 +870,7 @@ class DidChangeWorkspaceFoldersParams(TypedDict): class ConfigurationParams(TypedDict): """The parameters of a configuration request.""" - items: List["ConfigurationItem"] + items: list["ConfigurationItem"] class DocumentColorParams(TypedDict): @@ -916,7 +928,7 @@ class ColorPresentation(TypedDict): """ An {@link TextEdit edit} which is applied to a document when selecting this presentation for the color. When `falsy` the {@link ColorPresentation.label label} is used. """ - additionalTextEdits: NotRequired[List["TextEdit"]] + additionalTextEdits: NotRequired[list["TextEdit"]] """ An optional array of additional {@link TextEdit text edits} that are applied when selecting this color presentation. Edits must not overlap with the main {@link ColorPresentation.textEdit edit} nor with themselves. """ @@ -1007,7 +1019,7 @@ class SelectionRangeParams(TypedDict): textDocument: "TextDocumentIdentifier" """ The text document. """ - positions: List["Position"] + positions: list["Position"] """ The positions inside the text document. """ workDoneToken: NotRequired["ProgressToken"] """ An optional token that a server can use to report work done progress. """ @@ -1018,7 +1030,8 @@ class SelectionRangeParams(TypedDict): class SelectionRange(TypedDict): """A selection range represents a part of a selection hierarchy. A selection range - may have a parent selection range that contains it.""" + may have a parent selection range that contains it. + """ range: "Range" """ The {@link Range range} of this selection range. """ @@ -1048,7 +1061,8 @@ class WorkDoneProgressCancelParams(TypedDict): class CallHierarchyPrepareParams(TypedDict): """The parameter of a `textDocument/prepareCallHierarchy` request. - @since 3.16.0""" + @since 3.16.0 + """ textDocument: "TextDocumentIdentifier" """ The text document. """ @@ -1062,13 +1076,14 @@ class CallHierarchyItem(TypedDict): """Represents programming constructs like functions or constructors in the context of call hierarchy. - @since 3.16.0""" + @since 3.16.0 + """ name: str """ The name of this item. """ kind: "SymbolKind" """ The kind of this item. """ - tags: NotRequired[List["SymbolTag"]] + tags: NotRequired[list["SymbolTag"]] """ Tags for this item. """ detail: NotRequired[str] """ More detail for this item, e.g. the signature of a function. """ @@ -1087,7 +1102,8 @@ class CallHierarchyItem(TypedDict): class CallHierarchyRegistrationOptions(TypedDict): """Call hierarchy options used during static or dynamic registration. - @since 3.16.0""" + @since 3.16.0 + """ documentSelector: Union["DocumentSelector", None] """ A document selector to identify the scope of the registration. If set to null @@ -1100,7 +1116,8 @@ class CallHierarchyRegistrationOptions(TypedDict): class CallHierarchyIncomingCallsParams(TypedDict): """The parameter of a `callHierarchy/incomingCalls` request. - @since 3.16.0""" + @since 3.16.0 + """ item: "CallHierarchyItem" workDoneToken: NotRequired["ProgressToken"] @@ -1117,7 +1134,7 @@ CallHierarchyIncomingCall = TypedDict( "from": "CallHierarchyItem", # The ranges at which the calls appear. This is relative to the caller # denoted by {@link CallHierarchyIncomingCall.from `this.from`}. - "fromRanges": List["Range"], + "fromRanges": list["Range"], }, ) """ Represents an incoming call, e.g. a caller of a method or constructor. @@ -1128,7 +1145,8 @@ CallHierarchyIncomingCall = TypedDict( class CallHierarchyOutgoingCallsParams(TypedDict): """The parameter of a `callHierarchy/outgoingCalls` request. - @since 3.16.0""" + @since 3.16.0 + """ item: "CallHierarchyItem" workDoneToken: NotRequired["ProgressToken"] @@ -1141,11 +1159,12 @@ class CallHierarchyOutgoingCallsParams(TypedDict): class CallHierarchyOutgoingCall(TypedDict): """Represents an outgoing call, e.g. calling a getter from a method or a method from a constructor etc. - @since 3.16.0""" + @since 3.16.0 + """ to: "CallHierarchyItem" """ The item that is called. """ - fromRanges: List["Range"] + fromRanges: list["Range"] """ The range at which this item is called. This is the range relative to the caller, e.g the item passed to {@link CallHierarchyItemProvider.provideCallHierarchyOutgoingCalls `provideCallHierarchyOutgoingCalls`} and not {@link CallHierarchyOutgoingCall.to `this.to`}. """ @@ -1171,14 +1190,14 @@ class SemanticTokens(TypedDict): the client will include the result id in the next semantic token request. A server can then instead of computing all semantic tokens again simply send a delta. """ - data: List[Uint] + data: list[Uint] """ The actual tokens. """ class SemanticTokensPartialResult(TypedDict): """@since 3.16.0""" - data: List[Uint] + data: list[Uint] class SemanticTokensRegistrationOptions(TypedDict): @@ -1189,7 +1208,7 @@ class SemanticTokensRegistrationOptions(TypedDict): the document selector provided on the client side will be used. """ legend: "SemanticTokensLegend" """ The legend used by the server """ - range: NotRequired[Union[bool, dict]] + range: NotRequired[bool | dict] """ Server supports providing semantic tokens for a specific range of a document. """ full: NotRequired[Union[bool, "__SemanticTokensOptions_full_Type_1"]] @@ -1218,14 +1237,14 @@ class SemanticTokensDelta(TypedDict): """@since 3.16.0""" resultId: NotRequired[str] - edits: List["SemanticTokensEdit"] + edits: list["SemanticTokensEdit"] """ The semantic token edits to transform a previous result into a new result. """ class SemanticTokensDeltaPartialResult(TypedDict): """@since 3.16.0""" - edits: List["SemanticTokensEdit"] + edits: list["SemanticTokensEdit"] class SemanticTokensRangeParams(TypedDict): @@ -1245,7 +1264,8 @@ class SemanticTokensRangeParams(TypedDict): class ShowDocumentParams(TypedDict): """Params to show a document. - @since 3.16.0""" + @since 3.16.0 + """ uri: "URI" """ The document uri to show. """ @@ -1268,7 +1288,8 @@ class ShowDocumentParams(TypedDict): class ShowDocumentResult(TypedDict): """The result of a showDocument request. - @since 3.16.0""" + @since 3.16.0 + """ success: bool """ A boolean indicating if the show was successful. """ @@ -1286,9 +1307,10 @@ class LinkedEditingRangeParams(TypedDict): class LinkedEditingRanges(TypedDict): """The result of a linked editing range request. - @since 3.16.0""" + @since 3.16.0 + """ - ranges: List["Range"] + ranges: list["Range"] """ A list of ranges that can be edited together. The ranges must have identical length and contain identical text content. The ranges cannot overlap. """ wordPattern: NotRequired[str] @@ -1310,9 +1332,10 @@ class CreateFilesParams(TypedDict): """The parameters sent in notifications/requests for user-initiated creation of files. - @since 3.16.0""" + @since 3.16.0 + """ - files: List["FileCreate"] + files: list["FileCreate"] """ An array of all files/folders created in this operation. """ @@ -1328,13 +1351,12 @@ class WorkspaceEdit(TypedDict): An invalid sequence (e.g. (1) delete file a.txt and (2) insert text into file a.txt) will cause failure of the operation. How the client recovers from the failure is described by - the client capability: `workspace.workspaceEdit.failureHandling`""" + the client capability: `workspace.workspaceEdit.failureHandling` + """ - changes: NotRequired[Dict["DocumentUri", List["TextEdit"]]] + changes: NotRequired[dict["DocumentUri", list["TextEdit"]]] """ Holds changes to existing resources. """ - documentChanges: NotRequired[ - List[Union["TextDocumentEdit", "CreateFile", "RenameFile", "DeleteFile"]] - ] + documentChanges: NotRequired[list[Union["TextDocumentEdit", "CreateFile", "RenameFile", "DeleteFile"]]] """ Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes are either an array of `TextDocumentEdit`s to express changes to n different text documents where each text document edit addresses a specific version of a text document. Or it can contain @@ -1345,9 +1367,7 @@ class WorkspaceEdit(TypedDict): If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then only plain `TextEdit`s using the `changes` property are supported. """ - changeAnnotations: NotRequired[ - Dict["ChangeAnnotationIdentifier", "ChangeAnnotation"] - ] + changeAnnotations: NotRequired[dict["ChangeAnnotationIdentifier", "ChangeAnnotation"]] """ A map of change annotations that can be referenced in `AnnotatedTextEdit`s or create, rename and delete file / folder operations. @@ -1359,9 +1379,10 @@ class WorkspaceEdit(TypedDict): class FileOperationRegistrationOptions(TypedDict): """The options to register for file operations. - @since 3.16.0""" + @since 3.16.0 + """ - filters: List["FileOperationFilter"] + filters: list["FileOperationFilter"] """ The actual filters. """ @@ -1369,9 +1390,10 @@ class RenameFilesParams(TypedDict): """The parameters sent in notifications/requests for user-initiated renames of files. - @since 3.16.0""" + @since 3.16.0 + """ - files: List["FileRename"] + files: list["FileRename"] """ An array of all files/folders renamed in this operation. When a folder is renamed, only the folder will be included, and not its children. """ @@ -1380,9 +1402,10 @@ class DeleteFilesParams(TypedDict): """The parameters sent in notifications/requests for user-initiated deletes of files. - @since 3.16.0""" + @since 3.16.0 + """ - files: List["FileDelete"] + files: list["FileDelete"] """ An array of all files/folders deleted in this operation. """ @@ -1401,7 +1424,8 @@ class MonikerParams(TypedDict): class Moniker(TypedDict): """Moniker definition to match LSIF 0.5 moniker definition. - @since 3.16.0""" + @since 3.16.0 + """ scheme: str """ The scheme of the moniker. For example tsc or .Net """ @@ -1423,7 +1447,8 @@ class MonikerRegistrationOptions(TypedDict): class TypeHierarchyPrepareParams(TypedDict): """The parameter of a `textDocument/prepareTypeHierarchy` request. - @since 3.17.0""" + @since 3.17.0 + """ textDocument: "TextDocumentIdentifier" """ The text document. """ @@ -1440,7 +1465,7 @@ class TypeHierarchyItem(TypedDict): """ The name of this item. """ kind: "SymbolKind" """ The kind of this item. """ - tags: NotRequired[List["SymbolTag"]] + tags: NotRequired[list["SymbolTag"]] """ Tags for this item. """ detail: NotRequired[str] """ More detail for this item, e.g. the signature of a function. """ @@ -1463,7 +1488,8 @@ class TypeHierarchyItem(TypedDict): class TypeHierarchyRegistrationOptions(TypedDict): """Type hierarchy options used during static or dynamic registration. - @since 3.17.0""" + @since 3.17.0 + """ documentSelector: Union["DocumentSelector", None] """ A document selector to identify the scope of the registration. If set to null @@ -1476,7 +1502,8 @@ class TypeHierarchyRegistrationOptions(TypedDict): class TypeHierarchySupertypesParams(TypedDict): """The parameter of a `typeHierarchy/supertypes` request. - @since 3.17.0""" + @since 3.17.0 + """ item: "TypeHierarchyItem" workDoneToken: NotRequired["ProgressToken"] @@ -1489,7 +1516,8 @@ class TypeHierarchySupertypesParams(TypedDict): class TypeHierarchySubtypesParams(TypedDict): """The parameter of a `typeHierarchy/subtypes` request. - @since 3.17.0""" + @since 3.17.0 + """ item: "TypeHierarchyItem" workDoneToken: NotRequired["ProgressToken"] @@ -1502,7 +1530,8 @@ class TypeHierarchySubtypesParams(TypedDict): class InlineValueParams(TypedDict): """A parameter literal used in inline value requests. - @since 3.17.0""" + @since 3.17.0 + """ textDocument: "TextDocumentIdentifier" """ The text document. """ @@ -1518,7 +1547,8 @@ class InlineValueParams(TypedDict): class InlineValueRegistrationOptions(TypedDict): """Inline value options used during static or dynamic registration. - @since 3.17.0""" + @since 3.17.0 + """ documentSelector: Union["DocumentSelector", None] """ A document selector to identify the scope of the registration. If set to null @@ -1531,7 +1561,8 @@ class InlineValueRegistrationOptions(TypedDict): class InlayHintParams(TypedDict): """A parameter literal used in inlay hint requests. - @since 3.17.0""" + @since 3.17.0 + """ textDocument: "TextDocumentIdentifier" """ The text document. """ @@ -1544,11 +1575,12 @@ class InlayHintParams(TypedDict): class InlayHint(TypedDict): """Inlay hint information. - @since 3.17.0""" + @since 3.17.0 + """ position: "Position" """ The position of this hint. """ - label: Union[str, List["InlayHintLabelPart"]] + label: str | list["InlayHintLabelPart"] """ The label of this hint. A human readable string or an array of InlayHintLabelPart label parts. @@ -1556,7 +1588,7 @@ class InlayHint(TypedDict): kind: NotRequired["InlayHintKind"] """ The kind of this hint. Can be omitted in which case the client should fall back to a reasonable default. """ - textEdits: NotRequired[List["TextEdit"]] + textEdits: NotRequired[list["TextEdit"]] """ Optional text edits that are performed when accepting this inlay hint. *Note* that edits are expected to change the document so that the inlay @@ -1584,7 +1616,8 @@ class InlayHint(TypedDict): class InlayHintRegistrationOptions(TypedDict): """Inlay hint options used during static or dynamic registration. - @since 3.17.0""" + @since 3.17.0 + """ resolveProvider: NotRequired[bool] """ The server provides support to resolve additional @@ -1600,7 +1633,8 @@ class InlayHintRegistrationOptions(TypedDict): class DocumentDiagnosticParams(TypedDict): """Parameters of the document diagnostic request. - @since 3.17.0""" + @since 3.17.0 + """ textDocument: "TextDocumentIdentifier" """ The text document. """ @@ -1618,9 +1652,10 @@ class DocumentDiagnosticParams(TypedDict): class DocumentDiagnosticReportPartialResult(TypedDict): """A partial result for a document diagnostic report. - @since 3.17.0""" + @since 3.17.0 + """ - relatedDocuments: Dict[ + relatedDocuments: dict[ "DocumentUri", Union["FullDocumentDiagnosticReport", "UnchangedDocumentDiagnosticReport"], ] @@ -1629,7 +1664,8 @@ class DocumentDiagnosticReportPartialResult(TypedDict): class DiagnosticServerCancellationData(TypedDict): """Cancellation data returned from a diagnostic request. - @since 3.17.0""" + @since 3.17.0 + """ retriggerRequest: bool @@ -1637,7 +1673,8 @@ class DiagnosticServerCancellationData(TypedDict): class DiagnosticRegistrationOptions(TypedDict): """Diagnostic registration options. - @since 3.17.0""" + @since 3.17.0 + """ documentSelector: Union["DocumentSelector", None] """ A document selector to identify the scope of the registration. If set to null @@ -1660,11 +1697,12 @@ class DiagnosticRegistrationOptions(TypedDict): class WorkspaceDiagnosticParams(TypedDict): """Parameters of the workspace diagnostic request. - @since 3.17.0""" + @since 3.17.0 + """ identifier: NotRequired[str] """ The additional identifier provided during registration. """ - previousResultIds: List["PreviousResultId"] + previousResultIds: list["PreviousResultId"] """ The currently known diagnostic reports with their previous result ids. """ workDoneToken: NotRequired["ProgressToken"] @@ -1677,27 +1715,30 @@ class WorkspaceDiagnosticParams(TypedDict): class WorkspaceDiagnosticReport(TypedDict): """A workspace diagnostic report. - @since 3.17.0""" + @since 3.17.0 + """ - items: List["WorkspaceDocumentDiagnosticReport"] + items: list["WorkspaceDocumentDiagnosticReport"] class WorkspaceDiagnosticReportPartialResult(TypedDict): """A partial result for a workspace diagnostic report. - @since 3.17.0""" + @since 3.17.0 + """ - items: List["WorkspaceDocumentDiagnosticReport"] + items: list["WorkspaceDocumentDiagnosticReport"] class DidOpenNotebookDocumentParams(TypedDict): """The params sent in an open notebook document notification. - @since 3.17.0""" + @since 3.17.0 + """ notebookDocument: "NotebookDocument" """ The notebook document that got opened. """ - cellTextDocuments: List["TextDocumentItem"] + cellTextDocuments: list["TextDocumentItem"] """ The text documents that represent the content of a notebook cell. """ @@ -1705,7 +1746,8 @@ class DidOpenNotebookDocumentParams(TypedDict): class DidChangeNotebookDocumentParams(TypedDict): """The params sent in a change notebook document notification. - @since 3.17.0""" + @since 3.17.0 + """ notebookDocument: "VersionedNotebookDocumentIdentifier" """ The notebook document that did change. The version number points @@ -1731,7 +1773,8 @@ class DidChangeNotebookDocumentParams(TypedDict): class DidSaveNotebookDocumentParams(TypedDict): """The params sent in a save notebook document notification. - @since 3.17.0""" + @since 3.17.0 + """ notebookDocument: "NotebookDocumentIdentifier" """ The notebook document that got saved. """ @@ -1740,25 +1783,26 @@ class DidSaveNotebookDocumentParams(TypedDict): class DidCloseNotebookDocumentParams(TypedDict): """The params sent in a close notebook document notification. - @since 3.17.0""" + @since 3.17.0 + """ notebookDocument: "NotebookDocumentIdentifier" """ The notebook document that got closed. """ - cellTextDocuments: List["TextDocumentIdentifier"] + cellTextDocuments: list["TextDocumentIdentifier"] """ The text documents that represent the content of a notebook cell that got closed. """ class RegistrationParams(TypedDict): - registrations: List["Registration"] + registrations: list["Registration"] class UnregistrationParams(TypedDict): - unregisterations: List["Unregistration"] + unregisterations: list["Unregistration"] class InitializeParams(TypedDict): - processId: Union[int, None] + processId: int | None """ The process Id of the parent process that started the server. @@ -1777,7 +1821,7 @@ class InitializeParams(TypedDict): (See https://en.wikipedia.org/wiki/IETF_language_tag) @since 3.16.0 """ - rootPath: NotRequired[Union[str, None]] + rootPath: NotRequired[str | None] """ The rootPath of the workspace. Is null if no folder is open. @@ -1794,7 +1838,7 @@ class InitializeParams(TypedDict): """ User provided initialization options. """ trace: NotRequired["TraceValues"] """ The initial trace setting. If omitted trace is disabled ('off'). """ - workspaceFolders: NotRequired[Union[List["WorkspaceFolder"], None]] + workspaceFolders: NotRequired[list["WorkspaceFolder"] | None] """ The workspace folders configured in the client when the server starts. This property is only available if the client supports workspace folders. @@ -1817,7 +1861,8 @@ class InitializeResult(TypedDict): class InitializeError(TypedDict): """The data type of the ResponseError if the - initialize request fails.""" + initialize request fails. + """ retry: bool """ Indicates whether the client execute the following retry logic: @@ -1838,7 +1883,7 @@ class DidChangeConfigurationParams(TypedDict): class DidChangeConfigurationRegistrationOptions(TypedDict): - section: NotRequired[Union[str, List[str]]] + section: NotRequired[str | list[str]] class ShowMessageParams(TypedDict): @@ -1855,7 +1900,7 @@ class ShowMessageRequestParams(TypedDict): """ The message type. See {@link MessageType} """ message: str """ The actual message. """ - actions: NotRequired[List["MessageActionItem"]] + actions: NotRequired[list["MessageActionItem"]] """ The message action items to present. """ @@ -1887,7 +1932,7 @@ class DidChangeTextDocumentParams(TypedDict): """ The document that did change. The version number points to the version after all provided content changes have been applied. """ - contentChanges: List["TextDocumentContentChangeEvent"] + contentChanges: list["TextDocumentContentChangeEvent"] """ The actual content changes. The content changes describe single state changes to the document. So if there are two content changes c1 (at array index 0) and c2 (at array index 1) for a document in state S then c1 moves the document from @@ -1961,14 +2006,14 @@ class TextEdit(TypedDict): class DidChangeWatchedFilesParams(TypedDict): """The watched files change notification's parameters.""" - changes: List["FileEvent"] + changes: list["FileEvent"] """ The actual file events. """ class DidChangeWatchedFilesRegistrationOptions(TypedDict): """Describe options to be used when registered for text document change events.""" - watchers: List["FileSystemWatcher"] + watchers: list["FileSystemWatcher"] """ The watchers to register. """ @@ -1981,7 +2026,7 @@ class PublishDiagnosticsParams(TypedDict): """ Optional the version number of the document the diagnostics are published for. @since 3.15.0 """ - diagnostics: List["Diagnostic"] + diagnostics: list["Diagnostic"] """ An array of diagnostic information items. """ @@ -2004,7 +2049,8 @@ class CompletionParams(TypedDict): class CompletionItem(TypedDict): """A completion item represents a text snippet that is - proposed to complete text that is being typed.""" + proposed to complete text that is being typed. + """ label: str """ The label of this completion item. @@ -2021,7 +2067,7 @@ class CompletionItem(TypedDict): kind: NotRequired["CompletionItemKind"] """ The kind of this completion item. Based of the kind an icon is chosen by the editor. """ - tags: NotRequired[List["CompletionItemTag"]] + tags: NotRequired[list["CompletionItemTag"]] """ Tags for this completion item. @since 3.15.0 """ @@ -2104,7 +2150,7 @@ class CompletionItem(TypedDict): property is used as a text. @since 3.17.0 """ - additionalTextEdits: NotRequired[List["TextEdit"]] + additionalTextEdits: NotRequired[list["TextEdit"]] """ An optional array of additional {@link TextEdit text edits} that are applied when selecting this completion. Edits must not overlap (including the same insert position) with the main {@link CompletionItem.textEdit edit} nor with themselves. @@ -2112,7 +2158,7 @@ class CompletionItem(TypedDict): Additional text edits should be used to change text unrelated to the current cursor position (for example adding an import statement at the top of the file if the completion item will insert an unqualified type). """ - commitCharacters: NotRequired[List[str]] + commitCharacters: NotRequired[list[str]] """ An optional set of characters that when pressed while this completion is active will accept it first and then type that character. *Note* that all commit characters should have `length=1` and that superfluous characters will be ignored. """ @@ -2127,7 +2173,8 @@ class CompletionItem(TypedDict): class CompletionList(TypedDict): """Represents a collection of {@link CompletionItem completion items} to be presented - in the editor.""" + in the editor. + """ isIncomplete: bool """ This list it not complete. Further typing results in recomputing this list. @@ -2148,7 +2195,7 @@ class CompletionList(TypedDict): capability. @since 3.17.0 """ - items: List["CompletionItem"] + items: list["CompletionItem"] """ The completion items. """ @@ -2158,7 +2205,7 @@ class CompletionRegistrationOptions(TypedDict): documentSelector: Union["DocumentSelector", None] """ A document selector to identify the scope of the registration. If set to null the document selector provided on the client side will be used. """ - triggerCharacters: NotRequired[List[str]] + triggerCharacters: NotRequired[list[str]] """ Most tools trigger completion request automatically without explicitly requesting it using a keyboard shortcut (e.g. Ctrl+Space). Typically they do so when the user starts to type an identifier. For example if the user types `c` in a JavaScript file @@ -2167,7 +2214,7 @@ class CompletionRegistrationOptions(TypedDict): If code complete should automatically be trigger on characters not being valid inside an identifier (for example `.` in JavaScript) list them in `triggerCharacters`. """ - allCommitCharacters: NotRequired[List[str]] + allCommitCharacters: NotRequired[list[str]] """ The list of all possible characters that commit a completion. This field can be used if clients don't support individual commit characters per completion item. See `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport` @@ -2200,7 +2247,7 @@ class HoverParams(TypedDict): class Hover(TypedDict): """The result of a hover request.""" - contents: Union["MarkupContent", "MarkedString", List["MarkedString"]] + contents: Union["MarkupContent", "MarkedString", list["MarkedString"]] """ The hover's content """ range: NotRequired["Range"] """ An optional range inside the text document that is used to @@ -2234,9 +2281,10 @@ class SignatureHelpParams(TypedDict): class SignatureHelp(TypedDict): """Signature help represents the signature of something callable. There can be multiple signature but only one - active and only one active parameter.""" + active and only one active parameter. + """ - signatures: List["SignatureInformation"] + signatures: list["SignatureInformation"] """ One or more signatures. """ activeSignature: NotRequired[Uint] """ The active signature. If omitted or the value lies outside the @@ -2264,9 +2312,9 @@ class SignatureHelpRegistrationOptions(TypedDict): documentSelector: Union["DocumentSelector", None] """ A document selector to identify the scope of the registration. If set to null the document selector provided on the client side will be used. """ - triggerCharacters: NotRequired[List[str]] + triggerCharacters: NotRequired[list[str]] """ List of characters that trigger signature help automatically. """ - retriggerCharacters: NotRequired[List[str]] + retriggerCharacters: NotRequired[list[str]] """ List of characters that re-trigger signature help. These trigger characters are only active when signature help is already showing. All trigger characters @@ -2337,7 +2385,8 @@ class DocumentHighlightParams(TypedDict): class DocumentHighlight(TypedDict): """A document highlight is a range inside a text document which deserves special attention. Usually a document highlight is visualized by changing - the background color of its range.""" + the background color of its range. + """ range: "Range" """ The range this highlight applies to. """ @@ -2367,7 +2416,8 @@ class DocumentSymbolParams(TypedDict): class SymbolInformation(TypedDict): """Represents information about programming constructs like variables, classes, - interfaces etc.""" + interfaces etc. + """ deprecated: NotRequired[bool] """ Indicates if this symbol is deprecated. @@ -2387,7 +2437,7 @@ class SymbolInformation(TypedDict): """ The name of this symbol. """ kind: "SymbolKind" """ The kind of this symbol. """ - tags: NotRequired[List["SymbolTag"]] + tags: NotRequired[list["SymbolTag"]] """ Tags for this symbol. @since 3.16.0 """ @@ -2402,7 +2452,8 @@ class DocumentSymbol(TypedDict): """Represents programming constructs like variables, classes, interfaces etc. that appear in a document. Document symbols can be hierarchical and they have two ranges: one that encloses its definition and one that points to - its most interesting range, e.g. the range of an identifier.""" + its most interesting range, e.g. the range of an identifier. + """ name: str """ The name of this symbol. Will be displayed in the user interface and therefore must not be @@ -2411,7 +2462,7 @@ class DocumentSymbol(TypedDict): """ More detail for this symbol, e.g the signature of a function. """ kind: "SymbolKind" """ The kind of this symbol. """ - tags: NotRequired[List["SymbolTag"]] + tags: NotRequired[list["SymbolTag"]] """ Tags for this document symbol. @since 3.16.0 """ @@ -2461,13 +2512,14 @@ class Command(TypedDict): """Represents a reference to a command. Provides a title which will be used to represent a command in the UI and, optionally, an array of arguments which will be passed to the command handler - function when invoked.""" + function when invoked. + """ title: str """ Title of the command, like `save`. """ command: str """ The identifier of the actual command handler. """ - arguments: NotRequired[List["LSPAny"]] + arguments: NotRequired[list["LSPAny"]] """ Arguments that the command handler should be invoked with. """ @@ -2485,7 +2537,7 @@ class CodeAction(TypedDict): """ The kind of the code action. Used to filter code actions. """ - diagnostics: NotRequired[List["Diagnostic"]] + diagnostics: NotRequired[list["Diagnostic"]] """ The diagnostics that this code action resolves. """ isPreferred: NotRequired[bool] """ Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted @@ -2530,7 +2582,7 @@ class CodeActionRegistrationOptions(TypedDict): documentSelector: Union["DocumentSelector", None] """ A document selector to identify the scope of the registration. If set to null the document selector provided on the client side will be used. """ - codeActionKinds: NotRequired[List["CodeActionKind"]] + codeActionKinds: NotRequired[list["CodeActionKind"]] """ CodeActionKinds that this server may return. The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server @@ -2560,7 +2612,8 @@ class WorkspaceSymbol(TypedDict): See also SymbolInformation. - @since 3.17.0""" + @since 3.17.0 + """ location: Union["Location", "__WorkspaceSymbol_location_Type_1"] """ The location of the symbol. Whether a server is allowed to @@ -2575,7 +2628,7 @@ class WorkspaceSymbol(TypedDict): """ The name of this symbol. """ kind: "SymbolKind" """ The kind of this symbol. """ - tags: NotRequired[List["SymbolTag"]] + tags: NotRequired[list["SymbolTag"]] """ Tags for this symbol. @since 3.16.0 """ @@ -2613,7 +2666,8 @@ class CodeLens(TypedDict): source text, like the number of references, a way to run tests, etc. A code lens is _unresolved_ when no command is associated to it. For performance - reasons the creation of a code lens and resolving should be done in two stages.""" + reasons the creation of a code lens and resolving should be done in two stages. + """ range: "Range" """ The range in which this code lens is valid. Should only span a single line. """ @@ -2649,7 +2703,8 @@ class DocumentLinkParams(TypedDict): class DocumentLink(TypedDict): """A document link is a range in a text document that links to an internal or external resource, like another - text document or a web site.""" + text document or a web site. + """ range: "Range" """ The range this link applies to. """ @@ -2744,7 +2799,7 @@ class DocumentOnTypeFormattingRegistrationOptions(TypedDict): the document selector provided on the client side will be used. """ firstTriggerCharacter: str """ A character on which formatting should be triggered, like `{`. """ - moreTriggerCharacter: NotRequired[List[str]] + moreTriggerCharacter: NotRequired[list[str]] """ More trigger characters. """ @@ -2789,7 +2844,7 @@ class ExecuteCommandParams(TypedDict): command: str """ The identifier of the actual command handler. """ - arguments: NotRequired[List["LSPAny"]] + arguments: NotRequired[list["LSPAny"]] """ Arguments that the command should be invoked with. """ workDoneToken: NotRequired["ProgressToken"] """ An optional token that a server can use to report work done progress. """ @@ -2798,7 +2853,7 @@ class ExecuteCommandParams(TypedDict): class ExecuteCommandRegistrationOptions(TypedDict): """Registration options for a {@link ExecuteCommandRequest}.""" - commands: List[str] + commands: list[str] """ The commands to be executed on the server """ @@ -2816,7 +2871,8 @@ class ApplyWorkspaceEditParams(TypedDict): class ApplyWorkspaceEditResult(TypedDict): """The result returned from the apply workspace edit request. - @since 3.17 renamed from ApplyWorkspaceEditResponse""" + @since 3.17 renamed from ApplyWorkspaceEditResponse + """ applied: bool """ Indicates whether the edit was applied or not. """ @@ -2895,7 +2951,7 @@ class LogTraceParams(TypedDict): class CancelParams(TypedDict): - id: Union[int, str] + id: int | str """ The request id to cancel. """ @@ -2908,7 +2964,8 @@ class ProgressParams(TypedDict): class TextDocumentPositionParams(TypedDict): """A parameter literal used in requests to pass a text document and a position inside that - document.""" + document. + """ textDocument: "TextDocumentIdentifier" """ The text document. """ @@ -2929,7 +2986,8 @@ class PartialResultParams(TypedDict): class LocationLink(TypedDict): """Represents the connection of two locations. Provides additional metadata over normal {@link Location locations}, - including an origin range.""" + including an origin range. + """ originSelectionRange: NotRequired["Range"] """ Span of the origin of this link. @@ -2958,7 +3016,8 @@ class Range(TypedDict): start: { line: 5, character: 23 } end : { line 6, character : 0 } } - ```""" + ``` + """ start: "Position" """ The range's start position. """ @@ -2972,7 +3031,8 @@ class ImplementationOptions(TypedDict): class StaticRegistrationOptions(TypedDict): """Static registration options to be returned in the initialize - request.""" + request. + """ id: NotRequired[str] """ The id used to register the request. The id can be used to deregister @@ -2986,9 +3046,9 @@ class TypeDefinitionOptions(TypedDict): class WorkspaceFoldersChangeEvent(TypedDict): """The workspace folder change event.""" - added: List["WorkspaceFolder"] + added: list["WorkspaceFolder"] """ The array of added workspace folders """ - removed: List["WorkspaceFolder"] + removed: list["WorkspaceFolder"] """ The array of the removed workspace folders """ @@ -3032,7 +3092,7 @@ class DeclarationOptions(TypedDict): class Position(TypedDict): - """Position in a text document expressed as zero-based line and character + r"""Position in a text document expressed as zero-based line and character offset. Prior to 3.17 the offsets were always based on a UTF-16 string representation. So a string of the form `a𐐀b` the character offset of the character `a` is 0, the character offset of `𐐀` is 1 and the character @@ -3058,7 +3118,8 @@ class Position(TypedDict): Positions are line end character agnostic. So you can not specify a position that denotes `\r|\n` or `\n|` where `|` represents the character offset. - @since 3.17.0 - support for negotiated position encoding.""" + @since 3.17.0 - support for negotiated position encoding. + """ line: Uint """ Line position in a document (zero-based). @@ -3082,7 +3143,8 @@ class SelectionRangeOptions(TypedDict): class CallHierarchyOptions(TypedDict): """Call hierarchy options used during static registration. - @since 3.16.0""" + @since 3.16.0 + """ workDoneProgress: NotRequired[bool] @@ -3092,7 +3154,7 @@ class SemanticTokensOptions(TypedDict): legend: "SemanticTokensLegend" """ The legend used by the server """ - range: NotRequired[Union[bool, dict]] + range: NotRequired[bool | dict] """ Server supports providing semantic tokens for a specific range of a document. """ full: NotRequired[Union[bool, "__SemanticTokensOptions_full_Type_2"]] @@ -3107,7 +3169,7 @@ class SemanticTokensEdit(TypedDict): """ The start offset of the edit. """ deleteCount: Uint """ The count of elements to remove. """ - data: NotRequired[List[Uint]] + data: NotRequired[list[Uint]] """ The elements to insert. """ @@ -3118,7 +3180,8 @@ class LinkedEditingRangeOptions(TypedDict): class FileCreate(TypedDict): """Represents information on a file/folder create. - @since 3.16.0""" + @since 3.16.0 + """ uri: str """ A file:// URI for the location of the file/folder being created. """ @@ -3128,11 +3191,12 @@ class TextDocumentEdit(TypedDict): """Describes textual changes on a text document. A TextDocumentEdit describes all changes on a document version Si and after they are applied move the document to version Si+1. So the creator of a TextDocumentEdit doesn't need to sort the array of edits or do any - kind of ordering. However the edits must be non overlapping.""" + kind of ordering. However the edits must be non overlapping. + """ textDocument: "OptionalVersionedTextDocumentIdentifier" """ The text document to change. """ - edits: List[Union["TextEdit", "AnnotatedTextEdit"]] + edits: list[Union["TextEdit", "AnnotatedTextEdit"]] """ The edits to be applied. @since 3.16.0 - support for AnnotatedTextEdit. This is guarded using a @@ -3189,7 +3253,8 @@ class DeleteFile(TypedDict): class ChangeAnnotation(TypedDict): """Additional information that describes document changes. - @since 3.16.0""" + @since 3.16.0 + """ label: str """ A human-readable string describing the actual change. The string @@ -3206,7 +3271,8 @@ class FileOperationFilter(TypedDict): """A filter to describe in which file operation requests or notifications the server is interested in receiving. - @since 3.16.0""" + @since 3.16.0 + """ scheme: NotRequired[str] """ A Uri scheme like `file` or `untitled`. """ @@ -3217,7 +3283,8 @@ class FileOperationFilter(TypedDict): class FileRename(TypedDict): """Represents information on a file/folder rename. - @since 3.16.0""" + @since 3.16.0 + """ oldUri: str """ A file:// URI for the original location of the file/folder being renamed. """ @@ -3228,7 +3295,8 @@ class FileRename(TypedDict): class FileDelete(TypedDict): """Represents information on a file/folder delete. - @since 3.16.0""" + @since 3.16.0 + """ uri: str """ A file:// URI for the location of the file/folder being deleted. """ @@ -3241,7 +3309,8 @@ class MonikerOptions(TypedDict): class TypeHierarchyOptions(TypedDict): """Type hierarchy options used during static registration. - @since 3.17.0""" + @since 3.17.0 + """ workDoneProgress: NotRequired[bool] @@ -3259,7 +3328,8 @@ class InlineValueContext(TypedDict): class InlineValueText(TypedDict): """Provide inline value as text. - @since 3.17.0""" + @since 3.17.0 + """ range: "Range" """ The document range for which the inline value applies. """ @@ -3272,7 +3342,8 @@ class InlineValueVariableLookup(TypedDict): If only a range is specified, the variable name will be extracted from the underlying document. An optional variable name can be used to override the extracted name. - @since 3.17.0""" + @since 3.17.0 + """ range: "Range" """ The document range for which the inline value applies. @@ -3288,7 +3359,8 @@ class InlineValueEvaluatableExpression(TypedDict): If only a range is specified, the expression will be extracted from the underlying document. An optional expression can be used to override the extracted expression. - @since 3.17.0""" + @since 3.17.0 + """ range: "Range" """ The document range for which the inline value applies. @@ -3300,7 +3372,8 @@ class InlineValueEvaluatableExpression(TypedDict): class InlineValueOptions(TypedDict): """Inline value options used during static registration. - @since 3.17.0""" + @since 3.17.0 + """ workDoneProgress: NotRequired[bool] @@ -3309,7 +3382,8 @@ class InlayHintLabelPart(TypedDict): """An inlay hint label part allows for interactive and composite labels of inlay hints. - @since 3.17.0""" + @since 3.17.0 + """ value: str """ The value of this label part. """ @@ -3337,7 +3411,7 @@ class InlayHintLabelPart(TypedDict): class MarkupContent(TypedDict): - """A `MarkupContent` literal represents a string value which content is interpreted base on its + r"""A `MarkupContent` literal represents a string value which content is interpreted base on its kind flag. Currently the protocol supports `plaintext` and `markdown` as markup kinds. If the kind is `markdown` then the value can contain fenced code blocks like in GitHub issues. @@ -3358,7 +3432,8 @@ class MarkupContent(TypedDict): ``` *Please Note* that clients might sanitize the return markdown. A client could decide to - remove HTML from the markdown to avoid script execution.""" + remove HTML from the markdown to avoid script execution. + """ kind: "MarkupKind" """ The type of the Markup """ @@ -3369,7 +3444,8 @@ class MarkupContent(TypedDict): class InlayHintOptions(TypedDict): """Inlay hint options used during static registration. - @since 3.17.0""" + @since 3.17.0 + """ resolveProvider: NotRequired[bool] """ The server provides support to resolve additional @@ -3380,10 +3456,11 @@ class InlayHintOptions(TypedDict): class RelatedFullDocumentDiagnosticReport(TypedDict): """A full diagnostic report with a set of related documents. - @since 3.17.0""" + @since 3.17.0 + """ relatedDocuments: NotRequired[ - Dict[ + dict[ "DocumentUri", Union["FullDocumentDiagnosticReport", "UnchangedDocumentDiagnosticReport"], ] @@ -3401,17 +3478,18 @@ class RelatedFullDocumentDiagnosticReport(TypedDict): """ An optional result id. If provided it will be sent on the next diagnostic request for the same document. """ - items: List["Diagnostic"] + items: list["Diagnostic"] """ The actual items. """ class RelatedUnchangedDocumentDiagnosticReport(TypedDict): """An unchanged diagnostic report with a set of related documents. - @since 3.17.0""" + @since 3.17.0 + """ relatedDocuments: NotRequired[ - Dict[ + dict[ "DocumentUri", Union["FullDocumentDiagnosticReport", "UnchangedDocumentDiagnosticReport"], ] @@ -3436,7 +3514,8 @@ class RelatedUnchangedDocumentDiagnosticReport(TypedDict): class FullDocumentDiagnosticReport(TypedDict): """A diagnostic report with a full set of problems. - @since 3.17.0""" + @since 3.17.0 + """ kind: Literal["full"] """ A full document diagnostic report. """ @@ -3444,7 +3523,7 @@ class FullDocumentDiagnosticReport(TypedDict): """ An optional result id. If provided it will be sent on the next diagnostic request for the same document. """ - items: List["Diagnostic"] + items: list["Diagnostic"] """ The actual items. """ @@ -3452,7 +3531,8 @@ class UnchangedDocumentDiagnosticReport(TypedDict): """A diagnostic report indicating that the last returned report is still accurate. - @since 3.17.0""" + @since 3.17.0 + """ kind: Literal["unchanged"] """ A document diagnostic report indicating @@ -3467,7 +3547,8 @@ class UnchangedDocumentDiagnosticReport(TypedDict): class DiagnosticOptions(TypedDict): """Diagnostic options. - @since 3.17.0""" + @since 3.17.0 + """ identifier: NotRequired[str] """ An optional identifier under which the diagnostics are @@ -3485,7 +3566,8 @@ class DiagnosticOptions(TypedDict): class PreviousResultId(TypedDict): """A previous result id in a workspace pull request. - @since 3.17.0""" + @since 3.17.0 + """ uri: "DocumentUri" """ The URI for which the client knowns a @@ -3497,7 +3579,8 @@ class PreviousResultId(TypedDict): class NotebookDocument(TypedDict): """A notebook document. - @since 3.17.0""" + @since 3.17.0 + """ uri: "URI" """ The notebook document's uri. """ @@ -3511,13 +3594,14 @@ class NotebookDocument(TypedDict): document. Note: should always be an object literal (e.g. LSPObject) """ - cells: List["NotebookCell"] + cells: list["NotebookCell"] """ The cells of a notebook. """ class TextDocumentItem(TypedDict): """An item to transfer a text document from the client to the - server.""" + server. + """ uri: "DocumentUri" """ The text document's uri. """ @@ -3533,7 +3617,8 @@ class TextDocumentItem(TypedDict): class VersionedNotebookDocumentIdentifier(TypedDict): """A versioned notebook document identifier. - @since 3.17.0""" + @since 3.17.0 + """ version: int """ The version number of this notebook document. """ @@ -3544,7 +3629,8 @@ class VersionedNotebookDocumentIdentifier(TypedDict): class NotebookDocumentChangeEvent(TypedDict): """A change event for a notebook document. - @since 3.17.0""" + @since 3.17.0 + """ metadata: NotRequired["LSPObject"] """ The changed meta data if any. @@ -3557,7 +3643,8 @@ class NotebookDocumentChangeEvent(TypedDict): class NotebookDocumentIdentifier(TypedDict): """A literal to identify a notebook document in the client. - @since 3.17.0""" + @since 3.17.0 + """ uri: "URI" """ The notebook document's uri. """ @@ -3586,7 +3673,7 @@ class Unregistration(TypedDict): class WorkspaceFoldersInitializeParams(TypedDict): - workspaceFolders: NotRequired[Union[List["WorkspaceFolder"], None]] + workspaceFolders: NotRequired[list["WorkspaceFolder"] | None] """ The workspace folders configured in the client when the server starts. This property is only available if the client supports workspace folders. @@ -3598,7 +3685,8 @@ class WorkspaceFoldersInitializeParams(TypedDict): class ServerCapabilities(TypedDict): """Defines the capabilities provided by a language - server.""" + server. + """ positionEncoding: NotRequired["PositionEncodingKind"] """ The position encoding the server picked from the encodings offered @@ -3610,15 +3698,11 @@ class ServerCapabilities(TypedDict): If omitted it defaults to 'utf-16'. @since 3.17.0 """ - textDocumentSync: NotRequired[ - Union["TextDocumentSyncOptions", "TextDocumentSyncKind"] - ] + textDocumentSync: NotRequired[Union["TextDocumentSyncOptions", "TextDocumentSyncKind"]] """ Defines how text documents are synced. Is either a detailed structure defining each notification or for backwards compatibility the TextDocumentSyncKind number. """ - notebookDocumentSync: NotRequired[ - Union["NotebookDocumentSyncOptions", "NotebookDocumentSyncRegistrationOptions"] - ] + notebookDocumentSync: NotRequired[Union["NotebookDocumentSyncOptions", "NotebookDocumentSyncRegistrationOptions"]] """ Defines how notebook documents are synced. @since 3.17.0 """ @@ -3628,19 +3712,13 @@ class ServerCapabilities(TypedDict): """ The server provides hover support. """ signatureHelpProvider: NotRequired["SignatureHelpOptions"] """ The server provides signature help support. """ - declarationProvider: NotRequired[ - Union[bool, "DeclarationOptions", "DeclarationRegistrationOptions"] - ] + declarationProvider: NotRequired[Union[bool, "DeclarationOptions", "DeclarationRegistrationOptions"]] """ The server provides Goto Declaration support. """ definitionProvider: NotRequired[Union[bool, "DefinitionOptions"]] """ The server provides goto definition support. """ - typeDefinitionProvider: NotRequired[ - Union[bool, "TypeDefinitionOptions", "TypeDefinitionRegistrationOptions"] - ] + typeDefinitionProvider: NotRequired[Union[bool, "TypeDefinitionOptions", "TypeDefinitionRegistrationOptions"]] """ The server provides Goto Type Definition support. """ - implementationProvider: NotRequired[ - Union[bool, "ImplementationOptions", "ImplementationRegistrationOptions"] - ] + implementationProvider: NotRequired[Union[bool, "ImplementationOptions", "ImplementationRegistrationOptions"]] """ The server provides Goto Implementation support. """ referencesProvider: NotRequired[Union[bool, "ReferenceOptions"]] """ The server provides find references support. """ @@ -3656,17 +3734,13 @@ class ServerCapabilities(TypedDict): """ The server provides code lens. """ documentLinkProvider: NotRequired["DocumentLinkOptions"] """ The server provides document link support. """ - colorProvider: NotRequired[ - Union[bool, "DocumentColorOptions", "DocumentColorRegistrationOptions"] - ] + colorProvider: NotRequired[Union[bool, "DocumentColorOptions", "DocumentColorRegistrationOptions"]] """ The server provides color provider support. """ workspaceSymbolProvider: NotRequired[Union[bool, "WorkspaceSymbolOptions"]] """ The server provides workspace symbol support. """ documentFormattingProvider: NotRequired[Union[bool, "DocumentFormattingOptions"]] """ The server provides document formatting. """ - documentRangeFormattingProvider: NotRequired[ - Union[bool, "DocumentRangeFormattingOptions"] - ] + documentRangeFormattingProvider: NotRequired[Union[bool, "DocumentRangeFormattingOptions"]] """ The server provides document range formatting. """ documentOnTypeFormattingProvider: NotRequired["DocumentOnTypeFormattingOptions"] """ The server provides document formatting on typing. """ @@ -3674,63 +3748,41 @@ class ServerCapabilities(TypedDict): """ The server provides rename support. RenameOptions may only be specified if the client states that it supports `prepareSupport` in its initial `initialize` request. """ - foldingRangeProvider: NotRequired[ - Union[bool, "FoldingRangeOptions", "FoldingRangeRegistrationOptions"] - ] + foldingRangeProvider: NotRequired[Union[bool, "FoldingRangeOptions", "FoldingRangeRegistrationOptions"]] """ The server provides folding provider support. """ - selectionRangeProvider: NotRequired[ - Union[bool, "SelectionRangeOptions", "SelectionRangeRegistrationOptions"] - ] + selectionRangeProvider: NotRequired[Union[bool, "SelectionRangeOptions", "SelectionRangeRegistrationOptions"]] """ The server provides selection range support. """ executeCommandProvider: NotRequired["ExecuteCommandOptions"] """ The server provides execute command support. """ - callHierarchyProvider: NotRequired[ - Union[bool, "CallHierarchyOptions", "CallHierarchyRegistrationOptions"] - ] + callHierarchyProvider: NotRequired[Union[bool, "CallHierarchyOptions", "CallHierarchyRegistrationOptions"]] """ The server provides call hierarchy support. @since 3.16.0 """ - linkedEditingRangeProvider: NotRequired[ - Union[ - bool, "LinkedEditingRangeOptions", "LinkedEditingRangeRegistrationOptions" - ] - ] + linkedEditingRangeProvider: NotRequired[Union[bool, "LinkedEditingRangeOptions", "LinkedEditingRangeRegistrationOptions"]] """ The server provides linked editing range support. @since 3.16.0 """ - semanticTokensProvider: NotRequired[ - Union["SemanticTokensOptions", "SemanticTokensRegistrationOptions"] - ] + semanticTokensProvider: NotRequired[Union["SemanticTokensOptions", "SemanticTokensRegistrationOptions"]] """ The server provides semantic tokens support. @since 3.16.0 """ - monikerProvider: NotRequired[ - Union[bool, "MonikerOptions", "MonikerRegistrationOptions"] - ] + monikerProvider: NotRequired[Union[bool, "MonikerOptions", "MonikerRegistrationOptions"]] """ The server provides moniker support. @since 3.16.0 """ - typeHierarchyProvider: NotRequired[ - Union[bool, "TypeHierarchyOptions", "TypeHierarchyRegistrationOptions"] - ] + typeHierarchyProvider: NotRequired[Union[bool, "TypeHierarchyOptions", "TypeHierarchyRegistrationOptions"]] """ The server provides type hierarchy support. @since 3.17.0 """ - inlineValueProvider: NotRequired[ - Union[bool, "InlineValueOptions", "InlineValueRegistrationOptions"] - ] + inlineValueProvider: NotRequired[Union[bool, "InlineValueOptions", "InlineValueRegistrationOptions"]] """ The server provides inline values. @since 3.17.0 """ - inlayHintProvider: NotRequired[ - Union[bool, "InlayHintOptions", "InlayHintRegistrationOptions"] - ] + inlayHintProvider: NotRequired[Union[bool, "InlayHintOptions", "InlayHintRegistrationOptions"]] """ The server provides inlay hints. @since 3.17.0 """ - diagnosticProvider: NotRequired[ - Union["DiagnosticOptions", "DiagnosticRegistrationOptions"] - ] + diagnosticProvider: NotRequired[Union["DiagnosticOptions", "DiagnosticRegistrationOptions"]] """ The server has support for pull model diagnostics. @since 3.17.0 """ @@ -3778,14 +3830,15 @@ class FileSystemWatcher(TypedDict): class Diagnostic(TypedDict): """Represents a diagnostic, such as a compiler error or warning. Diagnostic objects - are only valid in the scope of a resource.""" + are only valid in the scope of a resource. + """ range: "Range" """ The range at which the message applies """ severity: NotRequired["DiagnosticSeverity"] """ The diagnostic's severity. Can be omitted. If omitted it is up to the client to interpret diagnostics as error, warning, info or hint. """ - code: NotRequired[Union[int, str]] + code: NotRequired[int | str] """ The diagnostic's code, which usually appear in the user interface. """ codeDescription: NotRequired["CodeDescription"] """ An optional property to describe the error code. @@ -3798,11 +3851,11 @@ class Diagnostic(TypedDict): appears in the user interface. """ message: str """ The diagnostic's message. It usually appears in the user interface """ - tags: NotRequired[List["DiagnosticTag"]] + tags: NotRequired[list["DiagnosticTag"]] """ Additional metadata about the diagnostic. @since 3.15.0 """ - relatedInformation: NotRequired[List["DiagnosticRelatedInformation"]] + relatedInformation: NotRequired[list["DiagnosticRelatedInformation"]] """ An array of related diagnostic information, e.g. when symbol-names within a scope collide all definitions can be marked via this property. """ data: NotRequired["LSPAny"] @@ -3825,7 +3878,8 @@ class CompletionContext(TypedDict): class CompletionItemLabelDetails(TypedDict): """Additional details for a completion item label. - @since 3.17.0""" + @since 3.17.0 + """ detail: NotRequired[str] """ An optional string which is rendered less prominently directly after {@link CompletionItem.label label}, @@ -3838,7 +3892,8 @@ class CompletionItemLabelDetails(TypedDict): class InsertReplaceEdit(TypedDict): """A special text edit to provide an insert and a replace operation. - @since 3.16.0""" + @since 3.16.0 + """ newText: str """ The string to be inserted. """ @@ -3851,7 +3906,7 @@ class InsertReplaceEdit(TypedDict): class CompletionOptions(TypedDict): """Completion options.""" - triggerCharacters: NotRequired[List[str]] + triggerCharacters: NotRequired[list[str]] """ Most tools trigger completion request automatically without explicitly requesting it using a keyboard shortcut (e.g. Ctrl+Space). Typically they do so when the user starts to type an identifier. For example if the user types `c` in a JavaScript file @@ -3860,7 +3915,7 @@ class CompletionOptions(TypedDict): If code complete should automatically be trigger on characters not being valid inside an identifier (for example `.` in JavaScript) list them in `triggerCharacters`. """ - allCommitCharacters: NotRequired[List[str]] + allCommitCharacters: NotRequired[list[str]] """ The list of all possible characters that commit a completion. This field can be used if clients don't support individual commit characters per completion item. See `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport` @@ -3889,7 +3944,8 @@ class HoverOptions(TypedDict): class SignatureHelpContext(TypedDict): """Additional information about the context in which a signature help request was triggered. - @since 3.15.0""" + @since 3.15.0 + """ triggerKind: "SignatureHelpTriggerKind" """ Action that caused signature help to be triggered. """ @@ -3912,7 +3968,8 @@ class SignatureHelpContext(TypedDict): class SignatureInformation(TypedDict): """Represents the signature of something callable. A signature can have a label, like a function-name, a doc-comment, and - a set of parameters.""" + a set of parameters. + """ label: str """ The label of this signature. Will be shown in @@ -3920,7 +3977,7 @@ class SignatureInformation(TypedDict): documentation: NotRequired[Union[str, "MarkupContent"]] """ The human-readable doc-comment of this signature. Will be shown in the UI but can be omitted. """ - parameters: NotRequired[List["ParameterInformation"]] + parameters: NotRequired[list["ParameterInformation"]] """ The parameters of this signature. """ activeParameter: NotRequired[Uint] """ The index of the active parameter. @@ -3933,9 +3990,9 @@ class SignatureInformation(TypedDict): class SignatureHelpOptions(TypedDict): """Server Capabilities for a {@link SignatureHelpRequest}.""" - triggerCharacters: NotRequired[List[str]] + triggerCharacters: NotRequired[list[str]] """ List of characters that trigger signature help automatically. """ - retriggerCharacters: NotRequired[List[str]] + retriggerCharacters: NotRequired[list[str]] """ List of characters that re-trigger signature help. These trigger characters are only active when signature help is already showing. All trigger characters @@ -3953,7 +4010,8 @@ class DefinitionOptions(TypedDict): class ReferenceContext(TypedDict): """Value-object that contains additional information when - requesting references.""" + requesting references. + """ includeDeclaration: bool """ Include the declaration of the current symbol. """ @@ -3978,7 +4036,7 @@ class BaseSymbolInformation(TypedDict): """ The name of this symbol. """ kind: "SymbolKind" """ The kind of this symbol. """ - tags: NotRequired[List["SymbolTag"]] + tags: NotRequired[list["SymbolTag"]] """ Tags for this symbol. @since 3.16.0 """ @@ -4002,15 +4060,16 @@ class DocumentSymbolOptions(TypedDict): class CodeActionContext(TypedDict): """Contains additional diagnostic information about the context in which - a {@link CodeActionProvider.provideCodeActions code action} is run.""" + a {@link CodeActionProvider.provideCodeActions code action} is run. + """ - diagnostics: List["Diagnostic"] + diagnostics: list["Diagnostic"] """ An array of diagnostics known on the client side overlapping the range provided to the `textDocument/codeAction` request. They are provided so that the server knows which errors are currently presented to the user for the given range. There is no guarantee that these accurately reflect the error state of the resource. The primary parameter to compute code actions is the provided range. """ - only: NotRequired[List["CodeActionKind"]] + only: NotRequired[list["CodeActionKind"]] """ Requested kind of actions to return. Actions not of this kind are filtered out by the client before being shown. So servers @@ -4024,7 +4083,7 @@ class CodeActionContext(TypedDict): class CodeActionOptions(TypedDict): """Provider options for a {@link CodeActionRequest}.""" - codeActionKinds: NotRequired[List["CodeActionKind"]] + codeActionKinds: NotRequired[list["CodeActionKind"]] """ CodeActionKinds that this server may return. The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server @@ -4102,7 +4161,7 @@ class DocumentOnTypeFormattingOptions(TypedDict): firstTriggerCharacter: str """ A character on which formatting should be triggered, like `{`. """ - moreTriggerCharacter: NotRequired[List[str]] + moreTriggerCharacter: NotRequired[list[str]] """ More trigger characters. """ @@ -4119,7 +4178,7 @@ class RenameOptions(TypedDict): class ExecuteCommandOptions(TypedDict): """The server capabilities of a {@link ExecuteCommandRequest}.""" - commands: List[str] + commands: list[str] """ The commands to be executed on the server """ workDoneProgress: NotRequired[bool] @@ -4127,16 +4186,16 @@ class ExecuteCommandOptions(TypedDict): class SemanticTokensLegend(TypedDict): """@since 3.16.0""" - tokenTypes: List[str] + tokenTypes: list[str] """ The token types a server uses. """ - tokenModifiers: List[str] + tokenModifiers: list[str] """ The token modifiers a server uses. """ class OptionalVersionedTextDocumentIdentifier(TypedDict): """A text document identifier to optionally denote a specific version of a text document.""" - version: Union[int, None] + version: int | None """ The version number of this document. If a versioned text document identifier is sent from the server to the client and the file is not open in the editor (the server has not received an open notification before) the server can send @@ -4149,7 +4208,8 @@ class OptionalVersionedTextDocumentIdentifier(TypedDict): class AnnotatedTextEdit(TypedDict): """A special text edit with an additional change annotation. - @since 3.16.0.""" + @since 3.16.0. + """ annotationId: "ChangeAnnotationIdentifier" """ The actual identifier of the change annotation """ @@ -4203,14 +4263,15 @@ class FileOperationPattern(TypedDict): """A pattern to describe in which file operation requests or notifications the server is interested in receiving. - @since 3.16.0""" + @since 3.16.0 + """ glob: str """ The glob pattern to match. Glob patterns can have the following syntax: - `*` to match one or more characters in a path segment - `?` to match on one character in a path segment - `**` to match any number of path segments, including none - - `{}` to group sub patterns into an OR expression. (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + - `{}` to group sub patterns into an OR expression. (e.g. `**\u200b/*.{ts,js}` matches all TypeScript and JavaScript files) - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) """ matches: NotRequired["FileOperationPatternKind"] @@ -4224,11 +4285,12 @@ class FileOperationPattern(TypedDict): class WorkspaceFullDocumentDiagnosticReport(TypedDict): """A full document diagnostic report for a workspace diagnostic result. - @since 3.17.0""" + @since 3.17.0 + """ uri: "DocumentUri" """ The URI for which diagnostic information is reported. """ - version: Union[int, None] + version: int | None """ The version number for which the diagnostics are reported. If the document is not marked as open `null` can be provided. """ kind: Literal["full"] @@ -4237,18 +4299,19 @@ class WorkspaceFullDocumentDiagnosticReport(TypedDict): """ An optional result id. If provided it will be sent on the next diagnostic request for the same document. """ - items: List["Diagnostic"] + items: list["Diagnostic"] """ The actual items. """ class WorkspaceUnchangedDocumentDiagnosticReport(TypedDict): """An unchanged document diagnostic report for a workspace diagnostic result. - @since 3.17.0""" + @since 3.17.0 + """ uri: "DocumentUri" """ The URI for which diagnostic information is reported. """ - version: Union[int, None] + version: int | None """ The version number for which the diagnostics are reported. If the document is not marked as open `null` can be provided. """ kind: Literal["unchanged"] @@ -4268,7 +4331,8 @@ class NotebookCell(TypedDict): cells and can therefore be used to uniquely identify a notebook cell or the cell's text document. - @since 3.17.0""" + @since 3.17.0 + """ kind: "NotebookCellKind" """ The cell's kind """ @@ -4288,13 +4352,14 @@ class NotebookCellArrayChange(TypedDict): """A change describing how to move a `NotebookCell` array from state S to S'. - @since 3.17.0""" + @since 3.17.0 + """ start: Uint """ The start oftest of the cell that changed. """ deleteCount: Uint """ The deleted cells """ - cells: NotRequired[List["NotebookCell"]] + cells: NotRequired[list["NotebookCell"]] """ The new cells, if any """ @@ -4350,9 +4415,10 @@ class NotebookDocumentSyncOptions(TypedDict): document that contain at least one matching cell will be synced. - @since 3.17.0""" + @since 3.17.0 + """ - notebookSelector: List[ + notebookSelector: list[ Union[ "__NotebookDocumentSyncOptions_notebookSelector_Type_1", "__NotebookDocumentSyncOptions_notebookSelector_Type_2", @@ -4367,9 +4433,10 @@ class NotebookDocumentSyncOptions(TypedDict): class NotebookDocumentSyncRegistrationOptions(TypedDict): """Registration options specific to a notebook. - @since 3.17.0""" + @since 3.17.0 + """ - notebookSelector: List[ + notebookSelector: list[ Union[ "__NotebookDocumentSyncOptions_notebookSelector_Type_3", "__NotebookDocumentSyncOptions_notebookSelector_Type_4", @@ -4387,7 +4454,7 @@ class NotebookDocumentSyncRegistrationOptions(TypedDict): class WorkspaceFoldersServerCapabilities(TypedDict): supported: NotRequired[bool] """ The server has support for workspace folders """ - changeNotifications: NotRequired[Union[str, bool]] + changeNotifications: NotRequired[str | bool] """ Whether the server wants to receive workspace folder change notifications. @@ -4400,7 +4467,8 @@ class WorkspaceFoldersServerCapabilities(TypedDict): class FileOperationOptions(TypedDict): """Options for notifications/requests for user operations on files. - @since 3.16.0""" + @since 3.16.0 + """ didCreate: NotRequired["FileOperationRegistrationOptions"] """ The server is interested in receiving didCreateFiles notifications. """ @@ -4419,7 +4487,8 @@ class FileOperationOptions(TypedDict): class CodeDescription(TypedDict): """Structure to capture a description for an error code. - @since 3.16.0""" + @since 3.16.0 + """ href: "URI" """ An URI to open with more information about the diagnostic error. """ @@ -4428,7 +4497,8 @@ class CodeDescription(TypedDict): class DiagnosticRelatedInformation(TypedDict): """Represents a related message and source code location for a diagnostic. This should be used to point to code locations that cause or related to a diagnostics, e.g when duplicating - a symbol in a scope.""" + a symbol in a scope. + """ location: "Location" """ The location of this related diagnostic information. """ @@ -4438,9 +4508,10 @@ class DiagnosticRelatedInformation(TypedDict): class ParameterInformation(TypedDict): """Represents a parameter of a callable-signature. A parameter can - have a label and a doc-comment.""" + have a label and a doc-comment. + """ - label: Union[str, List[Union[Uint, Uint]]] + label: str | list[Uint | Uint] """ The label of this parameter information. Either a string or an inclusive start and exclusive end offsets within its containing @@ -4458,7 +4529,8 @@ class NotebookCellTextDocumentFilter(TypedDict): """A notebook cell text document filter denotes a cell text document by different properties. - @since 3.17.0""" + @since 3.17.0 + """ notebook: Union[str, "NotebookDocumentFilter"] """ A filter that matches against the notebook @@ -4475,7 +4547,8 @@ class NotebookCellTextDocumentFilter(TypedDict): class FileOperationPatternOptions(TypedDict): """Matching options for the file operation pattern. - @since 3.16.0""" + @since 3.16.0 + """ ignoreCase: NotRequired[bool] """ The pattern should be matched ignoring casing. """ @@ -4644,7 +4717,8 @@ class TextDocumentClientCapabilities(TypedDict): class NotebookDocumentClientCapabilities(TypedDict): """Capabilities specific to the notebook document support. - @since 3.17.0""" + @since 3.17.0 + """ synchronization: "NotebookDocumentSyncClientCapabilities" """ Capabilities specific to notebook document synchronization @@ -4676,11 +4750,10 @@ class WindowClientCapabilities(TypedDict): class GeneralClientCapabilities(TypedDict): """General client capabilities. - @since 3.16.0""" + @since 3.16.0 + """ - staleRequestSupport: NotRequired[ - "__GeneralClientCapabilities_staleRequestSupport_Type_1" - ] + staleRequestSupport: NotRequired["__GeneralClientCapabilities_staleRequestSupport_Type_1"] """ Client capability that signals how the client handles stale requests (e.g. a request for which the client will not process the response @@ -4695,7 +4768,7 @@ class GeneralClientCapabilities(TypedDict): """ Client capabilities specific to the client's markdown parser. @since 3.16.0 """ - positionEncodings: NotRequired[List["PositionEncodingKind"]] + positionEncodings: NotRequired[list["PositionEncodingKind"]] """ The position encodings supported by the client. Client and server have to agree on the same position encoding to ensure that offsets (e.g. character position in a line) are interpreted the same on both @@ -4721,7 +4794,8 @@ class RelativePattern(TypedDict): relatively to a base URI. The common value for a `baseUri` is a workspace folder root, but it can be another absolute URI as well. - @since 3.17.0""" + @since 3.17.0 + """ baseUri: Union["WorkspaceFolder", "URI"] """ A workspace folder or a base URI to which this pattern will be matched @@ -4733,7 +4807,7 @@ class RelativePattern(TypedDict): class WorkspaceEditClientCapabilities(TypedDict): documentChanges: NotRequired[bool] """ The client supports versioned document changes in `WorkspaceEdit`s """ - resourceOperations: NotRequired[List["ResourceOperationKind"]] + resourceOperations: NotRequired[list["ResourceOperationKind"]] """ The resource operations the client supports. Clients should at least support 'create', 'rename' and 'delete' files and folders. @@ -4751,9 +4825,7 @@ class WorkspaceEditClientCapabilities(TypedDict): character. @since 3.16.0 """ - changeAnnotationSupport: NotRequired[ - "__WorkspaceEditClientCapabilities_changeAnnotationSupport_Type_1" - ] + changeAnnotationSupport: NotRequired["__WorkspaceEditClientCapabilities_changeAnnotationSupport_Type_1"] """ Whether the client in general supports change annotations on text edits, create file, rename file and delete file changes. @@ -4789,9 +4861,7 @@ class WorkspaceSymbolClientCapabilities(TypedDict): Clients supporting tags have to handle unknown tags gracefully. @since 3.16.0 """ - resolveSupport: NotRequired[ - "__WorkspaceSymbolClientCapabilities_resolveSupport_Type_1" - ] + resolveSupport: NotRequired["__WorkspaceSymbolClientCapabilities_resolveSupport_Type_1"] """ The client support partial workspace symbols. The client will send the request `workspaceSymbol/resolve` to the server to resolve additional properties. @@ -4838,7 +4908,8 @@ class FileOperationClientCapabilities(TypedDict): These events do not come from the file system, they come from user operations like renaming a file in the UI. - @since 3.16.0""" + @since 3.16.0 + """ dynamicRegistration: NotRequired[bool] """ Whether the client supports dynamic registration for file requests/notifications. """ @@ -4859,7 +4930,8 @@ class FileOperationClientCapabilities(TypedDict): class InlineValueWorkspaceClientCapabilities(TypedDict): """Client workspace capabilities specific to inline values. - @since 3.17.0""" + @since 3.17.0 + """ refreshSupport: NotRequired[bool] """ Whether the client implementation supports a refresh request sent from the @@ -4874,7 +4946,8 @@ class InlineValueWorkspaceClientCapabilities(TypedDict): class InlayHintWorkspaceClientCapabilities(TypedDict): """Client workspace capabilities specific to inlay hints. - @since 3.17.0""" + @since 3.17.0 + """ refreshSupport: NotRequired[bool] """ Whether the client implementation supports a refresh request sent from @@ -4889,7 +4962,8 @@ class InlayHintWorkspaceClientCapabilities(TypedDict): class DiagnosticWorkspaceClientCapabilities(TypedDict): """Workspace client capabilities specific to diagnostic pull requests. - @since 3.17.0""" + @since 3.17.0 + """ refreshSupport: NotRequired[bool] """ Whether the client implementation supports a refresh request sent from @@ -4922,9 +4996,7 @@ class CompletionClientCapabilities(TypedDict): completionItem: NotRequired["__CompletionClientCapabilities_completionItem_Type_1"] """ The client supports the following `CompletionItem` specific capabilities. """ - completionItemKind: NotRequired[ - "__CompletionClientCapabilities_completionItemKind_Type_1" - ] + completionItemKind: NotRequired["__CompletionClientCapabilities_completionItemKind_Type_1"] insertTextMode: NotRequired["InsertTextMode"] """ Defines how the client handles whitespace and indentation when accepting a completion item that uses multi line @@ -4944,7 +5016,7 @@ class CompletionClientCapabilities(TypedDict): class HoverClientCapabilities(TypedDict): dynamicRegistration: NotRequired[bool] """ Whether hover supports dynamic registration. """ - contentFormat: NotRequired[List["MarkupKind"]] + contentFormat: NotRequired[list["MarkupKind"]] """ Client supports the following content formats for the content property. The order describes the preferred format of the client. """ @@ -4954,9 +5026,7 @@ class SignatureHelpClientCapabilities(TypedDict): dynamicRegistration: NotRequired[bool] """ Whether signature help supports dynamic registration. """ - signatureInformation: NotRequired[ - "__SignatureHelpClientCapabilities_signatureInformation_Type_1" - ] + signatureInformation: NotRequired["__SignatureHelpClientCapabilities_signatureInformation_Type_1"] """ The client supports the following `SignatureInformation` specific properties. """ contextSupport: NotRequired[bool] @@ -5058,9 +5128,7 @@ class CodeActionClientCapabilities(TypedDict): dynamicRegistration: NotRequired[bool] """ Whether code action supports dynamic registration. """ - codeActionLiteralSupport: NotRequired[ - "__CodeActionClientCapabilities_codeActionLiteralSupport_Type_1" - ] + codeActionLiteralSupport: NotRequired["__CodeActionClientCapabilities_codeActionLiteralSupport_Type_1"] """ The client support code action literals of type `CodeAction` as a valid response of the `textDocument/codeAction` request. If the property is not set the request can only return `Command` literals. @@ -5180,9 +5248,7 @@ class FoldingRangeClientCapabilities(TypedDict): """ If set, the client signals that it only supports folding complete lines. If set, client will ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange. """ - foldingRangeKind: NotRequired[ - "__FoldingRangeClientCapabilities_foldingRangeKind_Type_1" - ] + foldingRangeKind: NotRequired["__FoldingRangeClientCapabilities_foldingRangeKind_Type_1"] """ Specific options for the folding range kind. @since 3.17.0 """ @@ -5251,11 +5317,11 @@ class SemanticTokensClientCapabilities(TypedDict): `request.range` are both set to true but the server only provides a range provider the client might not render a minimap correctly or might even decide to not show any semantic tokens at all. """ - tokenTypes: List[str] + tokenTypes: list[str] """ The token types that the client supports. """ - tokenModifiers: List[str] + tokenModifiers: list[str] """ The token modifiers that the client supports. """ - formats: List["TokenFormat"] + formats: list["TokenFormat"] """ The token formats the clients supports. """ overlappingTokenSupport: NotRequired[bool] """ Whether the client supports tokens that can overlap each other. """ @@ -5284,7 +5350,8 @@ class SemanticTokensClientCapabilities(TypedDict): class LinkedEditingRangeClientCapabilities(TypedDict): """Client capabilities for the linked editing range request. - @since 3.16.0""" + @since 3.16.0 + """ dynamicRegistration: NotRequired[bool] """ Whether implementation supports dynamic registration. If this is set to `true` @@ -5295,7 +5362,8 @@ class LinkedEditingRangeClientCapabilities(TypedDict): class MonikerClientCapabilities(TypedDict): """Client capabilities specific to the moniker request. - @since 3.16.0""" + @since 3.16.0 + """ dynamicRegistration: NotRequired[bool] """ Whether moniker supports dynamic registration. If this is set to `true` @@ -5315,7 +5383,8 @@ class TypeHierarchyClientCapabilities(TypedDict): class InlineValueClientCapabilities(TypedDict): """Client capabilities specific to inline values. - @since 3.17.0""" + @since 3.17.0 + """ dynamicRegistration: NotRequired[bool] """ Whether implementation supports dynamic registration for inline value providers. """ @@ -5324,7 +5393,8 @@ class InlineValueClientCapabilities(TypedDict): class InlayHintClientCapabilities(TypedDict): """Inlay hint client capabilities. - @since 3.17.0""" + @since 3.17.0 + """ dynamicRegistration: NotRequired[bool] """ Whether inlay hints support dynamic registration. """ @@ -5336,7 +5406,8 @@ class InlayHintClientCapabilities(TypedDict): class DiagnosticClientCapabilities(TypedDict): """Client capabilities specific to diagnostic pull requests. - @since 3.17.0""" + @since 3.17.0 + """ dynamicRegistration: NotRequired[bool] """ Whether implementation supports dynamic registration. If this is set to `true` @@ -5349,7 +5420,8 @@ class DiagnosticClientCapabilities(TypedDict): class NotebookDocumentSyncClientCapabilities(TypedDict): """Notebook specific client capabilities. - @since 3.17.0""" + @since 3.17.0 + """ dynamicRegistration: NotRequired[bool] """ Whether implementation supports dynamic registration. If this is @@ -5363,16 +5435,15 @@ class NotebookDocumentSyncClientCapabilities(TypedDict): class ShowMessageRequestClientCapabilities(TypedDict): """Show message request client capabilities""" - messageActionItem: NotRequired[ - "__ShowMessageRequestClientCapabilities_messageActionItem_Type_1" - ] + messageActionItem: NotRequired["__ShowMessageRequestClientCapabilities_messageActionItem_Type_1"] """ Capabilities specific to the `MessageActionItem` type. """ class ShowDocumentClientCapabilities(TypedDict): """Client capabilities for the showDocument request. - @since 3.16.0""" + @since 3.16.0 + """ support: bool """ The client has support for the showDocument @@ -5382,7 +5453,8 @@ class ShowDocumentClientCapabilities(TypedDict): class RegularExpressionsClientCapabilities(TypedDict): """Client capabilities specific to regular expressions. - @since 3.16.0""" + @since 3.16.0 + """ engine: str """ The engine's name. """ @@ -5393,13 +5465,14 @@ class RegularExpressionsClientCapabilities(TypedDict): class MarkdownClientCapabilities(TypedDict): """Client capabilities specific to the used markdown parser. - @since 3.16.0""" + @since 3.16.0 + """ parser: str """ The name of the parser. """ version: NotRequired[str] """ The version of the parser. """ - allowedTags: NotRequired[List[str]] + allowedTags: NotRequired[list[str]] """ A list of HTML tags that the client allows / supports in Markdown. @@ -5412,10 +5485,8 @@ class __CodeActionClientCapabilities_codeActionLiteralSupport_Type_1(TypedDict): set. """ -class __CodeActionClientCapabilities_codeActionLiteralSupport_codeActionKind_Type_1( - TypedDict -): - valueSet: List["CodeActionKind"] +class __CodeActionClientCapabilities_codeActionLiteralSupport_codeActionKind_Type_1(TypedDict): + valueSet: list["CodeActionKind"] """ The code action kind values the client supports. When this property exists the client also guarantees that it will handle values outside its set gracefully and falls back @@ -5423,7 +5494,7 @@ class __CodeActionClientCapabilities_codeActionLiteralSupport_codeActionKind_Typ class __CodeActionClientCapabilities_resolveSupport_Type_1(TypedDict): - properties: List[str] + properties: list[str] """ The properties that a client can resolve lazily. """ @@ -5435,7 +5506,7 @@ class __CodeAction_disabled_Type_1(TypedDict): class __CompletionClientCapabilities_completionItemKind_Type_1(TypedDict): - valueSet: NotRequired[List["CompletionItemKind"]] + valueSet: NotRequired[list["CompletionItemKind"]] """ The completion item kind values the client supports. When this property exists the client also guarantees that it will handle values outside its set gracefully and falls back @@ -5456,16 +5527,14 @@ class __CompletionClientCapabilities_completionItem_Type_1(TypedDict): that is typing in one will update others too. """ commitCharactersSupport: NotRequired[bool] """ Client supports commit characters on a completion item. """ - documentationFormat: NotRequired[List["MarkupKind"]] + documentationFormat: NotRequired[list["MarkupKind"]] """ Client supports the following content formats for the documentation property. The order describes the preferred format of the client. """ deprecatedSupport: NotRequired[bool] """ Client supports the deprecated property on a completion item. """ preselectSupport: NotRequired[bool] """ Client supports the preselect property on a completion item. """ - tagSupport: NotRequired[ - "__CompletionClientCapabilities_completionItem_tagSupport_Type_1" - ] + tagSupport: NotRequired["__CompletionClientCapabilities_completionItem_tagSupport_Type_1"] """ Client supports the tag property on a completion item. Clients supporting tags have to handle unknown tags gracefully. Clients especially need to preserve unknown tags when sending a completion item back to the server in @@ -5477,17 +5546,13 @@ class __CompletionClientCapabilities_completionItem_Type_1(TypedDict): completion item is inserted in the text or should replace text. @since 3.16.0 """ - resolveSupport: NotRequired[ - "__CompletionClientCapabilities_completionItem_resolveSupport_Type_1" - ] + resolveSupport: NotRequired["__CompletionClientCapabilities_completionItem_resolveSupport_Type_1"] """ Indicates which properties a client can resolve lazily on a completion item. Before version 3.16.0 only the predefined properties `documentation` and `details` could be resolved lazily. @since 3.16.0 """ - insertTextModeSupport: NotRequired[ - "__CompletionClientCapabilities_completionItem_insertTextModeSupport_Type_1" - ] + insertTextModeSupport: NotRequired["__CompletionClientCapabilities_completionItem_insertTextModeSupport_Type_1"] """ The client supports the `insertTextMode` property on a completion item to override the whitespace handling mode as defined by the client (see `insertTextMode`). @@ -5500,24 +5565,22 @@ class __CompletionClientCapabilities_completionItem_Type_1(TypedDict): @since 3.17.0 """ -class __CompletionClientCapabilities_completionItem_insertTextModeSupport_Type_1( - TypedDict -): - valueSet: List["InsertTextMode"] +class __CompletionClientCapabilities_completionItem_insertTextModeSupport_Type_1(TypedDict): + valueSet: list["InsertTextMode"] class __CompletionClientCapabilities_completionItem_resolveSupport_Type_1(TypedDict): - properties: List[str] + properties: list[str] """ The properties that a client can resolve lazily. """ class __CompletionClientCapabilities_completionItem_tagSupport_Type_1(TypedDict): - valueSet: List["CompletionItemTag"] + valueSet: list["CompletionItemTag"] """ The tags supported by the client. """ class __CompletionClientCapabilities_completionList_Type_1(TypedDict): - itemDefaults: NotRequired[List[str]] + itemDefaults: NotRequired[list[str]] """ The client supports the following itemDefaults on a completion list. @@ -5529,13 +5592,11 @@ class __CompletionClientCapabilities_completionList_Type_1(TypedDict): class __CompletionList_itemDefaults_Type_1(TypedDict): - commitCharacters: NotRequired[List[str]] + commitCharacters: NotRequired[list[str]] """ A default commit character set. @since 3.17.0 """ - editRange: NotRequired[ - Union["Range", "__CompletionList_itemDefaults_editRange_Type_1"] - ] + editRange: NotRequired[Union["Range", "__CompletionList_itemDefaults_editRange_Type_1"]] """ A default edit range. @since 3.17.0 """ @@ -5577,7 +5638,7 @@ class __CompletionOptions_completionItem_Type_2(TypedDict): class __DocumentSymbolClientCapabilities_symbolKind_Type_1(TypedDict): - valueSet: NotRequired[List["SymbolKind"]] + valueSet: NotRequired[list["SymbolKind"]] """ The symbol kind values the client supports. When this property exists the client also guarantees that it will handle values outside its set gracefully and falls back @@ -5589,12 +5650,12 @@ class __DocumentSymbolClientCapabilities_symbolKind_Type_1(TypedDict): class __DocumentSymbolClientCapabilities_tagSupport_Type_1(TypedDict): - valueSet: List["SymbolTag"] + valueSet: list["SymbolTag"] """ The tags supported by the client. """ class __FoldingRangeClientCapabilities_foldingRangeKind_Type_1(TypedDict): - valueSet: NotRequired[List["FoldingRangeKind"]] + valueSet: NotRequired[list["FoldingRangeKind"]] """ The folding range kind values the client supports. When this property exists the client also guarantees that it will handle values outside its set gracefully and falls back @@ -5612,7 +5673,7 @@ class __FoldingRangeClientCapabilities_foldingRange_Type_1(TypedDict): class __GeneralClientCapabilities_staleRequestSupport_Type_1(TypedDict): cancel: bool """ The client will actively cancel the request. """ - retryOnContentModified: List[str] + retryOnContentModified: list[str] """ The list of requests for which the client will retry the request if it receives a response with error code `ContentModified` """ @@ -5626,7 +5687,7 @@ class __InitializeResult_serverInfo_Type_1(TypedDict): class __InlayHintClientCapabilities_resolveSupport_Type_1(TypedDict): - properties: List[str] + properties: list[str] """ The properties that a client can resolve lazily. """ @@ -5639,27 +5700,25 @@ class __NotebookDocumentChangeEvent_cells_Type_1(TypedDict): structure: NotRequired["__NotebookDocumentChangeEvent_cells_structure_Type_1"] """ Changes to the cell structure to add or remove cells. """ - data: NotRequired[List["NotebookCell"]] + data: NotRequired[list["NotebookCell"]] """ Changes to notebook cells properties like its kind, execution summary or metadata. """ - textContent: NotRequired[ - List["__NotebookDocumentChangeEvent_cells_textContent_Type_1"] - ] + textContent: NotRequired[list["__NotebookDocumentChangeEvent_cells_textContent_Type_1"]] """ Changes to the text content of notebook cells. """ class __NotebookDocumentChangeEvent_cells_structure_Type_1(TypedDict): array: "NotebookCellArrayChange" """ The change to the cell array. """ - didOpen: NotRequired[List["TextDocumentItem"]] + didOpen: NotRequired[list["TextDocumentItem"]] """ Additional opened cell text documents. """ - didClose: NotRequired[List["TextDocumentIdentifier"]] + didClose: NotRequired[list["TextDocumentIdentifier"]] """ Additional closed cell text documents. """ class __NotebookDocumentChangeEvent_cells_textContent_Type_1(TypedDict): document: "VersionedTextDocumentIdentifier" - changes: List["TextDocumentContentChangeEvent"] + changes: list["TextDocumentContentChangeEvent"] class __NotebookDocumentFilter_Type_1(TypedDict): @@ -5694,9 +5753,7 @@ class __NotebookDocumentSyncOptions_notebookSelector_Type_1(TypedDict): """ The notebook to be synced If a string value is provided it matches against the notebook type. '*' matches every notebook. """ - cells: NotRequired[ - List["__NotebookDocumentSyncOptions_notebookSelector_cells_Type_1"] - ] + cells: NotRequired[list["__NotebookDocumentSyncOptions_notebookSelector_cells_Type_1"]] """ The cells of the matching notebook to be synced. """ @@ -5705,7 +5762,7 @@ class __NotebookDocumentSyncOptions_notebookSelector_Type_2(TypedDict): """ The notebook to be synced If a string value is provided it matches against the notebook type. '*' matches every notebook. """ - cells: List["__NotebookDocumentSyncOptions_notebookSelector_cells_Type_2"] + cells: list["__NotebookDocumentSyncOptions_notebookSelector_cells_Type_2"] """ The cells of the matching notebook to be synced. """ @@ -5714,9 +5771,7 @@ class __NotebookDocumentSyncOptions_notebookSelector_Type_3(TypedDict): """ The notebook to be synced If a string value is provided it matches against the notebook type. '*' matches every notebook. """ - cells: NotRequired[ - List["__NotebookDocumentSyncOptions_notebookSelector_cells_Type_3"] - ] + cells: NotRequired[list["__NotebookDocumentSyncOptions_notebookSelector_cells_Type_3"]] """ The cells of the matching notebook to be synced. """ @@ -5725,7 +5780,7 @@ class __NotebookDocumentSyncOptions_notebookSelector_Type_4(TypedDict): """ The notebook to be synced If a string value is provided it matches against the notebook type. '*' matches every notebook. """ - cells: List["__NotebookDocumentSyncOptions_notebookSelector_cells_Type_4"] + cells: list["__NotebookDocumentSyncOptions_notebookSelector_cells_Type_4"] """ The cells of the matching notebook to be synced. """ @@ -5755,17 +5810,15 @@ class __PrepareRenameResult_Type_2(TypedDict): class __PublishDiagnosticsClientCapabilities_tagSupport_Type_1(TypedDict): - valueSet: List["DiagnosticTag"] + valueSet: list["DiagnosticTag"] """ The tags supported by the client. """ class __SemanticTokensClientCapabilities_requests_Type_1(TypedDict): - range: NotRequired[Union[bool, dict]] + range: NotRequired[bool | dict] """ The client will send the `textDocument/semanticTokens/range` request if the server provides a corresponding handler. """ - full: NotRequired[ - Union[bool, "__SemanticTokensClientCapabilities_requests_full_Type_1"] - ] + full: NotRequired[Union[bool, "__SemanticTokensClientCapabilities_requests_full_Type_1"]] """ The client will send the `textDocument/semanticTokens/full` request if the server provides a corresponding handler. """ @@ -5805,12 +5858,10 @@ class __ShowMessageRequestClientCapabilities_messageActionItem_Type_1(TypedDict) class __SignatureHelpClientCapabilities_signatureInformation_Type_1(TypedDict): - documentationFormat: NotRequired[List["MarkupKind"]] + documentationFormat: NotRequired[list["MarkupKind"]] """ Client supports the following content formats for the documentation property. The order describes the preferred format of the client. """ - parameterInformation: NotRequired[ - "__SignatureHelpClientCapabilities_signatureInformation_parameterInformation_Type_1" - ] + parameterInformation: NotRequired["__SignatureHelpClientCapabilities_signatureInformation_parameterInformation_Type_1"] """ Client capabilities specific to parameter information. """ activeParameterSupport: NotRequired[bool] """ The client supports the `activeParameter` property on `SignatureInformation` @@ -5819,9 +5870,7 @@ class __SignatureHelpClientCapabilities_signatureInformation_Type_1(TypedDict): @since 3.16.0 """ -class __SignatureHelpClientCapabilities_signatureInformation_parameterInformation_Type_1( - TypedDict -): +class __SignatureHelpClientCapabilities_signatureInformation_parameterInformation_Type_1(TypedDict): labelOffsetSupport: NotRequired[bool] """ The client supports processing label offsets instead of a simple label string. @@ -5880,13 +5929,13 @@ class __WorkspaceEditClientCapabilities_changeAnnotationSupport_Type_1(TypedDict class __WorkspaceSymbolClientCapabilities_resolveSupport_Type_1(TypedDict): - properties: List[str] + properties: list[str] """ The properties that a client can resolve lazily. Usually `location.range` """ class __WorkspaceSymbolClientCapabilities_symbolKind_Type_1(TypedDict): - valueSet: NotRequired[List["SymbolKind"]] + valueSet: NotRequired[list["SymbolKind"]] """ The symbol kind values the client supports. When this property exists the client also guarantees that it will handle values outside its set gracefully and falls back @@ -5898,7 +5947,7 @@ class __WorkspaceSymbolClientCapabilities_symbolKind_Type_1(TypedDict): class __WorkspaceSymbolClientCapabilities_tagSupport_Type_1(TypedDict): - valueSet: List["SymbolTag"] + valueSet: list["SymbolTag"] """ The tags supported by the client. """ diff --git a/src/solidlsp/lsp_protocol_handler/server.py b/src/solidlsp/lsp_protocol_handler/server.py index a2241433..c628d434 100644 --- a/src/solidlsp/lsp_protocol_handler/server.py +++ b/src/solidlsp/lsp_protocol_handler/server.py @@ -1,8 +1,8 @@ """ -This file provides the implementation of the JSON-RPC client, that launches and +This file provides the implementation of the JSON-RPC client, that launches and communicates with the language server. -The initial implementation of this file was obtained from +The initial implementation of this file was obtained from https://github.com/predragnikolic/OLSP under the MIT License with the following terms: MIT License @@ -34,16 +34,17 @@ import json import logging import os import threading +from collections.abc import Callable +from typing import Any, Union import psutil -from typing import Any, Callable, Dict, List, Optional, Union +from ..multilspy_exceptions import MultilspyException from .lsp_requests import LspNotification, LspRequest from .lsp_types import ErrorCodes -from ..multilspy_exceptions import MultilspyException -StringDict = Dict[str, Any] -PayloadLike = Union[List[StringDict], StringDict, None] +StringDict = dict[str, Any] +PayloadLike = Union[list[StringDict], StringDict, None] CONTENT_LENGTH = "Content-Length: " ENCODING = "utf-8" log = logging.getLogger(__name__) @@ -59,7 +60,7 @@ class ProcessLaunchInfo: cmd: str # The environment variables to set for the process - env: Dict[str, str] = dataclasses.field(default_factory=dict) + env: dict[str, str] = dataclasses.field(default_factory=dict) # The working directory for the process cwd: str = os.getcwd() @@ -120,8 +121,8 @@ class MessageType: class Request: def __init__(self) -> None: self.cv = asyncio.Condition() - self.result: Optional[PayloadLike] = None - self.error: Optional[Error] = None + self.result: PayloadLike | None = None + self.error: Error | None = None async def on_result(self, params: PayloadLike) -> None: self.result = params @@ -134,14 +135,14 @@ class Request: self.cv.notify() -def content_length(line: bytes) -> Optional[int]: +def content_length(line: bytes) -> int | None: if line.startswith(b"Content-Length: "): _, value = line.split(b"Content-Length: ") value = value.strip() try: return int(value) except ValueError: - raise ValueError("Invalid Content-Length header: {}".format(value)) + raise ValueError(f"Invalid Content-Length header: {value}") return None @@ -181,12 +182,13 @@ class LanguageServerHandler: language server process in an independent process group. Default is `True`. Setting it to `False` means that the language server process will be in the same process group as the the current process, and any SIGINT and SIGTERM signals will be sent to both processes. + """ def __init__( self, process_launch_info: ProcessLaunchInfo, - logger: Optional[Callable[[str, str, StringDict | str], None]] = None, + logger: Callable[[str, str, StringDict | str], None] | None = None, start_independent_lsp_process=True, ) -> None: """ @@ -203,7 +205,7 @@ class LanguageServerHandler: self._received_shutdown = False self.request_id = 1 - self._response_handlers: Dict[Any, Request] = {} + self._response_handlers: dict[Any, Request] = {} self.on_request_handlers = {} self.on_notification_handlers = {} self.logger = logger @@ -218,8 +220,6 @@ class LanguageServerHandler: self._response_handlers_lock = threading.Lock() self._tasks_lock = threading.Lock() - - def is_running(self) -> bool: """ Checks if the language server process is currently running. @@ -250,7 +250,7 @@ class LanguageServerHandler: log.error("Language server has already terminated/could not be started") # Process has already terminated stderr_data = await self.process.stderr.read() - error_message = stderr_data.decode('utf-8', errors='replace') + error_message = stderr_data.decode("utf-8", errors="replace") raise RuntimeError(f"Process terminated immediately with code {self.process.returncode}. Error: {error_message}") self.loop = asyncio.get_event_loop() @@ -262,21 +262,19 @@ class LanguageServerHandler: self.tasks[self.task_counter] = self.loop.create_task(self.run_forever_stderr()) self.task_counter += 1 - - async def stop(self) -> None: """ Sends the terminate signal to the language server process and waits for it to exit, with a timeout, killing it if necessary """ # First cancel all tasks await self._cancel_pending_tasks() - + process = self.process self.process = None - + if not process: return - + # Clean up the process await self._cleanup_process(process) @@ -294,7 +292,7 @@ class LanguageServerHandler: if pending_tasks: try: await asyncio.wait_for(asyncio.gather(*pending_tasks, return_exceptions=True), timeout=5.0) - except (asyncio.TimeoutError, Exception): + except (TimeoutError, Exception): pass # Clear tasks dictionary under lock @@ -306,18 +304,18 @@ class LanguageServerHandler: # Close stdin first to prevent deadlocks # See: https://bugs.python.org/issue35539 self._safely_close_pipe(process.stdin) - + # Terminate/kill the process if it's still running if process.returncode is None: await self._terminate_or_kill_process(process) - + # Close stdout and stderr pipes after process has exited # This is essential to prevent "I/O operation on closed pipe" errors and # "Event loop is closed" errors during garbage collection # See: https://bugs.python.org/issue41320 and https://github.com/python/cpython/issues/88050 self._safely_close_pipe(process.stdout) self._safely_close_pipe(process.stderr) - + # Small delay to ensure OS has released file handles await asyncio.sleep(0.5) @@ -333,11 +331,11 @@ class LanguageServerHandler: """Try to terminate the process gracefully, then forcefully if necessary.""" # First try to terminate the process tree gracefully self._signal_process_tree(process, terminate=True) - + # Wait for the process to exit (with timeout) try: await asyncio.wait_for(process.wait(), timeout=10) - except (asyncio.TimeoutError, Exception): + except (TimeoutError, Exception): # If termination failed, forcefully kill the process tree self._signal_process_tree(process, terminate=False) try: @@ -349,14 +347,14 @@ class LanguageServerHandler: def _signal_process_tree(self, process, terminate=True): """Send signal (terminate or kill) to the process and all its children.""" signal_method = "terminate" if terminate else "kill" - + # Try to get the parent process parent = None try: parent = psutil.Process(process.pid) except (psutil.NoSuchProcess, psutil.AccessDenied, Exception): pass - + # If we have the parent process and it's running, signal the entire tree if parent and parent.is_running(): # Signal children first @@ -365,7 +363,7 @@ class LanguageServerHandler: getattr(child, signal_method)() except (psutil.NoSuchProcess, psutil.AccessDenied, Exception): pass - + # Then signal the parent try: getattr(parent, signal_method)() @@ -378,7 +376,6 @@ class LanguageServerHandler: except Exception: pass - async def shutdown(self) -> None: """ Perform the shutdown sequence for the client, including sending the shutdown request to the server and notifying it of exit @@ -433,7 +430,6 @@ class LanguageServerHandler: pass return self._received_shutdown - async def run_forever_stderr(self) -> None: """ Continuously read from the language server process stderr and log the messages @@ -443,7 +439,7 @@ class LanguageServerHandler: line = await self.process.stderr.readline() if not line: continue - self._log("LSP stderr: " + line.decode(ENCODING, errors='replace')) + self._log("LSP stderr: " + line.decode(ENCODING, errors="replace")) except (BrokenPipeError, ConnectionResetError, StopLoopException): pass @@ -453,7 +449,7 @@ class LanguageServerHandler: """ try: await self._receive_payload(json.loads(body)) - except IOError as ex: + except OSError as ex: self._log(f"malformed {ENCODING}: {ex}") except UnicodeDecodeError as ex: self._log(f"malformed {ENCODING}: {ex}") @@ -479,7 +475,7 @@ class LanguageServerHandler: except Exception as err: self._log(f"Error handling server payload: {err}") - def send_notification(self, method: str, params: Optional[dict] = None) -> None: + def send_notification(self, method: str, params: dict | None = None) -> None: """ Send notification pertaining to the given method to the server with the given parameters """ @@ -491,25 +487,19 @@ class LanguageServerHandler: """ # Use lock to prevent race conditions on tasks and task_counter with self._tasks_lock: - self.tasks[self.task_counter] = asyncio.get_event_loop().create_task( - self._send_payload(make_response(request_id, params)) - ) + self.tasks[self.task_counter] = asyncio.get_event_loop().create_task(self._send_payload(make_response(request_id, params))) self.task_counter += 1 - def send_error_response(self, request_id: Any, err: Error) -> None: """ Send error response to the given request id to the server with the given error """ # Use lock to prevent race conditions on tasks and task_counter with self._tasks_lock: - self.tasks[self.task_counter] = asyncio.get_event_loop().create_task( - self._send_payload(make_error_response(request_id, err)) - ) + self.tasks[self.task_counter] = asyncio.get_event_loop().create_task(self._send_payload(make_error_response(request_id, err))) self.task_counter += 1 - - async def send_request(self, method: str, params: Optional[dict] = None) -> PayloadLike: + async def send_request(self, method: str, params: dict | None = None) -> PayloadLike: """ Send request to the server, register the request id, and wait for the response """ @@ -527,13 +517,14 @@ class LanguageServerHandler: await self._send_payload(make_request(method, request_id, params)) self._log(f"Waiting for asyncio condition for request {method} with params:\n{params}") await request.cv.wait() - self._log(f"Finished waiting, processing result") + self._log("Finished waiting, processing result") if isinstance(request.error, Error): - raise MultilspyException(f"Could not process request {method} with params:\n{params}.\n Language server error: {request.error}") from request.error + raise MultilspyException( + f"Could not process request {method} with params:\n{params}.\n Language server error: {request.error}" + ) from request.error self._log(f"Returning non-error result, which is:\n{request.result}") return request.result - def _send_payload_sync(self, payload: StringDict) -> None: """ Send the payload to the server by writing to its stdin synchronously @@ -554,7 +545,6 @@ class LanguageServerHandler: self.logger("client", "logger", f"Failed to write to stdin: {e}") return - async def _send_payload(self, payload: StringDict) -> None: """ Send the payload to the server by writing to its stdin asynchronously. @@ -575,7 +565,6 @@ class LanguageServerHandler: self.logger("client", "logger", f"Failed to write to stdin: {e}") return - def on_request(self, method: str, cb) -> None: """ Register the callback function to handle requests from the server to the client for the given method @@ -602,7 +591,6 @@ class LanguageServerHandler: else: await request.on_error(Error(ErrorCodes.InvalidRequest, "")) - async def _request_handler(self, response: StringDict) -> None: """ Handle the request received from the server: call the appropriate callback function and return the result @@ -616,7 +604,7 @@ class LanguageServerHandler: request_id, Error( ErrorCodes.MethodNotFound, - "method '{}' not handled on client.".format(method), + f"method '{method}' not handled on client.", ), ) return diff --git a/src/solidlsp/multilspy_config.py b/src/solidlsp/multilspy_config.py index c1d77ae0..f676e52a 100644 --- a/src/solidlsp/multilspy_config.py +++ b/src/solidlsp/multilspy_config.py @@ -1,10 +1,10 @@ """ Configuration parameters for Multilspy. """ + import fnmatch -from enum import Enum -from typing import List from dataclasses import dataclass, field +from enum import Enum class FilenameMatcher: @@ -77,6 +77,7 @@ class MultilspyConfig: """ Configuration parameters """ + code_language: Language trace_lsp_communication: bool = False start_independent_lsp_process: bool = True @@ -89,7 +90,5 @@ class MultilspyConfig: Create a MultilspyConfig instance from a dictionary """ import inspect - return cls(**{ - k: v for k, v in env.items() - if k in inspect.signature(cls).parameters - }) + + return cls(**{k: v for k, v in env.items() if k in inspect.signature(cls).parameters}) diff --git a/src/solidlsp/multilspy_exceptions.py b/src/solidlsp/multilspy_exceptions.py index d645d130..1dc12ed4 100644 --- a/src/solidlsp/multilspy_exceptions.py +++ b/src/solidlsp/multilspy_exceptions.py @@ -2,6 +2,7 @@ This module contains the exceptions raised by the Multilspy framework. """ + class MultilspyException(Exception): """ Exceptions raised by the Multilspy framework. @@ -11,4 +12,4 @@ class MultilspyException(Exception): """ Initializes the exception with the given message. """ - super().__init__(message) \ No newline at end of file + super().__init__(message) diff --git a/src/solidlsp/multilspy_logger.py b/src/solidlsp/multilspy_logger.py index c6135a55..993aa3b8 100644 --- a/src/solidlsp/multilspy_logger.py +++ b/src/solidlsp/multilspy_logger.py @@ -1,9 +1,11 @@ """ Multilspy logger module. """ + import inspect import logging from datetime import datetime + from pydantic import BaseModel @@ -34,7 +36,6 @@ class MultilspyLogger: """ 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", " ") diff --git a/src/solidlsp/multilspy_settings.py b/src/solidlsp/multilspy_settings.py index 5d7ebfab..b4040301 100644 --- a/src/solidlsp/multilspy_settings.py +++ b/src/solidlsp/multilspy_settings.py @@ -5,10 +5,12 @@ Defines the settings for multilspy. import os import pathlib + class MultilspySettings: """ Provides the various settings for multilspy. """ + @staticmethod def get_language_server_directory() -> str: """Returns the directory for language servers""" diff --git a/src/solidlsp/multilspy_types.py b/src/solidlsp/multilspy_types.py index dad01167..3d4d4658 100644 --- a/src/solidlsp/multilspy_types.py +++ b/src/solidlsp/multilspy_types.py @@ -4,16 +4,19 @@ Defines wrapper objects around the types returned by LSP to ensure decoupling be from __future__ import annotations -from enum import IntEnum, Enum -from typing_extensions import NotRequired, TypedDict, List, Dict, Union +from enum import Enum, IntEnum +from typing import NotRequired, Union + +from typing_extensions import TypedDict URI = str DocumentUri = str Uint = int RegExp = str + class Position(TypedDict): - """Position in a text document expressed as zero-based line and character + r"""Position in a text document expressed as zero-based line and character offset. Prior to 3.17 the offsets were always based on a UTF-16 string representation. So a string of the form `a𐐀b` the character offset of the character `a` is 0, the character offset of `𐐀` is 1 and the character @@ -39,7 +42,8 @@ class Position(TypedDict): Positions are line end character agnostic. So you can not specify a position that denotes `\r|\n` or `\n|` where `|` represents the character offset. - @since 3.17.0 - support for negotiated position encoding.""" + @since 3.17.0 - support for negotiated position encoding. + """ line: Uint """ Line position in a document (zero-based). @@ -67,7 +71,8 @@ class Range(TypedDict): start: { line: 5, character: 23 } end : { line 6, character : 0 } } - ```""" + ``` + """ start: Position """ The range's start position. """ @@ -77,12 +82,14 @@ class Range(TypedDict): class Location(TypedDict): """Represents a location inside a resource, such as a line - inside a text file.""" + inside a text file. + """ uri: DocumentUri range: Range absolutePath: str - relativePath: Union[str, None] + relativePath: str | None + class CompletionItemKind(IntEnum): """The kind of a completion entry.""" @@ -113,9 +120,11 @@ class CompletionItemKind(IntEnum): Operator = 24 TypeParameter = 25 + class CompletionItem(TypedDict): """A completion item represents a text snippet that is - proposed to complete text that is being typed.""" + proposed to complete text that is being typed. + """ completionText: str """ The completionText of this completion item. @@ -131,6 +140,7 @@ class CompletionItem(TypedDict): """ A human-readable string with additional information about this item, like type or symbol information. """ + class SymbolKind(IntEnum): """A symbol kind.""" @@ -161,17 +171,21 @@ class SymbolKind(IntEnum): Operator = 25 TypeParameter = 26 + class SymbolTag(IntEnum): """Symbol tags are extra annotations that tweak the rendering of a symbol. - @since 3.16""" + @since 3.16 + """ Deprecated = 1 """ Render a symbol as obsolete, usually using a strike-out. """ + class UnifiedSymbolInformation(TypedDict): """Represents information about programming constructs like variables, classes, - interfaces etc.""" + interfaces etc. + """ deprecated: NotRequired[bool] """ Indicates if this symbol is deprecated. @@ -191,7 +205,7 @@ class UnifiedSymbolInformation(TypedDict): """ The name of this symbol. """ kind: SymbolKind """ The kind of this symbol. """ - tags: NotRequired[List[SymbolTag]] + tags: NotRequired[list[SymbolTag]] """ Tags for this symbol. @since 3.16.0 """ @@ -207,7 +221,7 @@ class UnifiedSymbolInformation(TypedDict): detail: NotRequired[str] """ More detail for this symbol, e.g the signature of a function. """ - + range: NotRequired[Range] """ The range enclosing this symbol not including leading/trailing whitespace but everything else like comments. This information is typically used to determine if the clients cursor is @@ -215,37 +229,40 @@ class UnifiedSymbolInformation(TypedDict): selectionRange: NotRequired[Range] """ The range that should be selected and revealed when this symbol is being picked, e.g the name of a function. Must be contained by the `range`. """ - + body: NotRequired[str] """ The body of the symbol. """ - - children: List[UnifiedSymbolInformation] + + children: list[UnifiedSymbolInformation] """ The children of the symbol. Added to be compatible with `lsp_types.DocumentSymbol`, since it is sometimes useful to have the children of the symbol as a user-facing feature.""" - + parent: NotRequired[UnifiedSymbolInformation | None] """The parent of the symbol, if there is any. Added with Serena, not part of the LSP. All symbols except the root packages will have a parent. """ - + class MarkupKind(Enum): """Describes the content type that a client supports in various result literals like `Hover`, `ParameterInfo` or `CompletionItem`. Please note that `MarkupKinds` must not start with a `$`. This kinds - are reserved for internal usage.""" + are reserved for internal usage. + """ PlainText = "plaintext" """ Plain text is supported as a content format """ Markdown = "markdown" """ Markdown is supported as a content format """ + class __MarkedString_Type_1(TypedDict): language: str value: str + MarkedString = Union[str, "__MarkedString_Type_1"] """ MarkedString can be used to render human readable text. It is either a markdown string or a code-block that provides a language and a code snippet. The language identifier @@ -260,8 +277,9 @@ ${value} Note that markdown strings will be sanitized - that means html will be escaped. @deprecated use MarkupContent instead. """ + class MarkupContent(TypedDict): - """A `MarkupContent` literal represents a string value which content is interpreted base on its + r"""A `MarkupContent` literal represents a string value which content is interpreted base on its kind flag. Currently the protocol supports `plaintext` and `markdown` as markup kinds. If the kind is `markdown` then the value can contain fenced code blocks like in GitHub issues. @@ -282,18 +300,20 @@ class MarkupContent(TypedDict): ``` *Please Note* that clients might sanitize the return markdown. A client could decide to - remove HTML from the markdown to avoid script execution.""" + remove HTML from the markdown to avoid script execution. + """ - kind: "MarkupKind" + kind: MarkupKind """ The type of the Markup """ value: str """ The content itself """ + class Hover(TypedDict): """The result of a hover request.""" - contents: Union["MarkupContent", "MarkedString", List["MarkedString"]] + contents: MarkupContent | MarkedString | list[MarkedString] """ The hover's content """ - range: NotRequired["Range"] + range: NotRequired[Range] """ An optional range inside the text document that is used to - visualize the hover, e.g. by changing the background color. """ \ No newline at end of file + visualize the hover, e.g. by changing the background color. """ diff --git a/src/solidlsp/multilspy_utils.py b/src/solidlsp/multilspy_utils.py index 92cca5ff..e1b71c2c 100644 --- a/src/solidlsp/multilspy_utils.py +++ b/src/solidlsp/multilspy_utils.py @@ -5,17 +5,16 @@ This file contains various utility functions like I/O operations, handling paths import gzip import logging import os -from typing import Tuple, Union -import requests -import shutil -import uuid - import platform +import shutil import subprocess +import uuid from enum import Enum +from pathlib import Path, PurePath + +import requests from solidlsp.multilspy_exceptions import MultilspyException -from pathlib import PurePath, Path from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_types import UnifiedSymbolInformation @@ -24,8 +23,9 @@ class TextUtils: """ Utilities for text operations. """ + @staticmethod - def get_line_col_from_index(text: str, index: int) -> Tuple[int, int]: + def get_line_col_from_index(text: str, index: int) -> tuple[int, int]: """ Returns the zero-indexed line and column number of the given index in the given text """ @@ -33,7 +33,7 @@ class TextUtils: c = 0 idx = 0 while idx < index: - if text[idx] == '\n': + if text[idx] == "\n": l += 1 c = 0 else: @@ -41,7 +41,7 @@ class TextUtils: idx += 1 return l, c - + @staticmethod def get_index_from_line_col(text: str, line: int, col: int) -> int: """ @@ -55,35 +55,35 @@ class TextUtils: idx += 1 idx += col return idx - + @staticmethod - def _get_updated_position_from_line_and_column_and_edit(l: int, c: int, text_to_be_inserted: str) -> Tuple[int, int]: + def _get_updated_position_from_line_and_column_and_edit(l: int, c: int, text_to_be_inserted: str) -> tuple[int, int]: """ Utility function to get the position of the cursor after inserting text at a given line and column. """ - num_newlines_in_gen_text = text_to_be_inserted.count('\n') + num_newlines_in_gen_text = text_to_be_inserted.count("\n") if num_newlines_in_gen_text > 0: l += num_newlines_in_gen_text - c = len(text_to_be_inserted.split('\n')[-1]) + c = len(text_to_be_inserted.split("\n")[-1]) else: c += len(text_to_be_inserted) return (l, c) - + @staticmethod - def delete_text_between_positions(text: str, start_line: int, start_col: int, end_line: int, end_col: int) -> Tuple[str, str]: + def delete_text_between_positions(text: str, start_line: int, start_col: int, end_line: int, end_col: int) -> tuple[str, str]: """ Deletes the text between the given start and end positions. Returns the modified text and the deleted text. """ del_start_idx = TextUtils.get_index_from_line_col(text, start_line, start_col) del_end_idx = TextUtils.get_index_from_line_col(text, end_line, end_col) - + deleted_text = text[del_start_idx:del_end_idx] new_text = text[:del_start_idx] + text[del_end_idx:] return new_text, deleted_text - + @staticmethod - def insert_text_at_position(text: str, line: int, col: int, text_to_be_inserted: str) -> Tuple[str, int, int]: + def insert_text_at_position(text: str, line: int, col: int, text_to_be_inserted: str) -> tuple[str, int, int]: """ Inserts the given text at the given line and column. Returns the modified text and the new line and column. @@ -98,6 +98,7 @@ class PathUtils: """ Utilities for platform-agnostic path operations. """ + @staticmethod def uri_to_path(uri: str) -> str: """ @@ -106,14 +107,15 @@ class PathUtils: This method was obtained from https://stackoverflow.com/a/61922504 """ try: - from urllib.parse import urlparse, unquote + from urllib.parse import unquote, urlparse from urllib.request import url2pathname except ImportError: # backwards compatibility - from urlparse import urlparse from urllib import unquote, url2pathname + + from urlparse import urlparse parsed = urlparse(uri) - host = "{0}{0}{mnt}{0}".format(os.path.sep, mnt=parsed.netloc) + host = f"{os.path.sep}{os.path.sep}{parsed.netloc}{os.path.sep}" return os.path.normpath(os.path.join(host, url2pathname(unquote(parsed.path)))) @staticmethod @@ -126,10 +128,10 @@ class PathUtils: @staticmethod def is_glob_pattern(pattern: str) -> bool: """Check if a pattern contains glob-specific characters.""" - return any(c in pattern for c in '*?[]!') + return any(c in pattern for c in "*?[]!") @staticmethod - def get_relative_path(path: str, base_path: str) -> Union[str, None]: + def get_relative_path(path: str, base_path: str) -> str | None: """ Gets relative path if it's possible (paths should be on the same drive), returns `None` otherwise. @@ -153,12 +155,12 @@ class FileUtils: logger.log(f"File read '{file_path}' failed: File does not exist.", logging.ERROR) raise MultilspyException(f"File read '{file_path}' failed: File does not exist.") try: - with open(file_path, "r", encoding="utf-8") as inp_file: + with open(file_path, encoding="utf-8") as inp_file: return inp_file.read() except Exception as exc: logger.log(f"File read '{file_path}' failed to read with encoding 'utf-8': {exc}", logging.ERROR) raise MultilspyException("File read failed.") from None - + @staticmethod def download_file(logger: MultilspyLogger, url: str, target_path: str) -> None: """ @@ -215,6 +217,7 @@ class PlatformId(str, Enum): """ multilspy supported platforms """ + WIN_x86 = "win-x86" WIN_x64 = "win-x64" WIN_arm64 = "win-arm64" @@ -232,6 +235,7 @@ class DotnetVersion(str, Enum): """ multilspy supported dotnet versions """ + V4 = "4" V6 = "6" V7 = "7" @@ -260,7 +264,7 @@ class PlatformUtils: platform_id = system_map[system] + "-" + machine_map[machine] if system == "Linux" and bitness == "64bit": libc = platform.libc_ver()[0] - if libc != 'glibc': + if libc != "glibc": platform_id += "-" + libc return PlatformId(platform_id) else: @@ -274,13 +278,13 @@ class PlatformUtils: class SYSTEM_INFO(ctypes.Structure): class _U(ctypes.Union): class _S(ctypes.Structure): - _fields_ = [("wProcessorArchitecture", wintypes.WORD), - ("wReserved", wintypes.WORD)] - _fields_ = [("dwOemId", wintypes.DWORD), - ("s", _S)] + _fields_ = [("wProcessorArchitecture", wintypes.WORD), ("wReserved", wintypes.WORD)] + + _fields_ = [("dwOemId", wintypes.DWORD), ("s", _S)] _anonymous_ = ("s",) - _fields_ = [("u", _U), + _fields_ = [ + ("u", _U), ("dwPageSize", wintypes.DWORD), ("lpMinimumApplicationAddress", wintypes.LPVOID), ("lpMaximumApplicationAddress", wintypes.LPVOID), @@ -289,22 +293,22 @@ class PlatformUtils: ("dwProcessorType", wintypes.DWORD), ("dwAllocationGranularity", wintypes.DWORD), ("wProcessorLevel", wintypes.WORD), - ("wProcessorRevision", wintypes.WORD)] + ("wProcessorRevision", wintypes.WORD), + ] _anonymous_ = ("u",) sys_info = SYSTEM_INFO() ctypes.windll.kernel32.GetNativeSystemInfo(ctypes.byref(sys_info)) arch_map = { - 9: 'AMD64', - 5: 'ARM', - 12: 'arm64', - 6: 'Intel Itanium-based', - 0: 'i386', + 9: "AMD64", + 5: "ARM", + 12: "arm64", + 6: "Intel Itanium-based", + 0: "i386", } - return arch_map.get(sys_info.wProcessorArchitecture, f'Unknown ({sys_info.wProcessorArchitecture})') - + return arch_map.get(sys_info.wProcessorArchitecture, f"Unknown ({sys_info.wProcessorArchitecture})") @staticmethod def get_dotnet_version() -> DotnetVersion: @@ -314,14 +318,14 @@ class PlatformUtils: try: result = subprocess.run(["dotnet", "--list-runtimes"], capture_output=True, check=True) available_version_cmd_output = [] - for line in result.stdout.decode('utf-8').split('\n'): - if line.startswith('Microsoft.NETCore.App'): - version_cmd_output = line.split(' ')[1] + for line in result.stdout.decode("utf-8").split("\n"): + if line.startswith("Microsoft.NETCore.App"): + version_cmd_output = line.split(" ")[1] available_version_cmd_output.append(version_cmd_output) - + if not available_version_cmd_output: raise MultilspyException("dotnet not found on the system") - + # Check for supported versions in order of preference (latest first) for version_cmd_output in available_version_cmd_output: if version_cmd_output.startswith("8"): @@ -335,9 +339,11 @@ class PlatformUtils: for version_cmd_output in available_version_cmd_output: if version_cmd_output.startswith("4"): return DotnetVersion.V4 - + # If no supported version found, raise exception with all available versions - raise MultilspyException(f"No supported dotnet version found. Available versions: {', '.join(available_version_cmd_output)}. Supported versions: 4, 6, 7, 8") + raise MultilspyException( + f"No supported dotnet version found. Available versions: {', '.join(available_version_cmd_output)}. Supported versions: 4, 6, 7, 8" + ) except (FileNotFoundError, subprocess.CalledProcessError): try: result = subprocess.run(["mono", "--version"], capture_output=True, check=True) @@ -346,7 +352,6 @@ class PlatformUtils: raise MultilspyException("dotnet or mono not found on the system") - class SymbolUtils: @staticmethod def symbol_tree_contains_name(roots: list[UnifiedSymbolInformation], name: str) -> bool: diff --git a/src/solidlsp/type_helpers.py b/src/solidlsp/type_helpers.py index 0a805e43..53a10de2 100644 --- a/src/solidlsp/type_helpers.py +++ b/src/solidlsp/type_helpers.py @@ -3,14 +3,15 @@ This module provides type-helpers used across multilspy implementation """ import inspect - -from typing import Callable, TypeVar, Type +from collections.abc import Callable +from typing import TypeVar R = TypeVar("R", bound=object) + def ensure_all_methods_implemented( - source_cls: Type[object], -) -> Callable[[Type[R]], Type[R]]: + source_cls: type[object], +) -> Callable[[type[R]], type[R]]: """ A decorator to ensure that all methods of source_cls class are implemented in the decorated class. """ @@ -24,4 +25,4 @@ def ensure_all_methods_implemented( return target_cls - return check_all_methods_implemented \ No newline at end of file + return check_all_methods_implemented diff --git a/test/conftest.py b/test/conftest.py index 9630cc6a..0eeda4a1 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -6,11 +6,11 @@ import pytest from sensai.util.logging import configure from multilspy.language_server import SyncLanguageServer -from solidlsp.multilspy_config import Language, MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger from serena.constants import USE_SOLID_LSP from serena.util.file_system import GitignoreParser from solidlsp.ls import SolidLanguageServer +from solidlsp.multilspy_config import Language, MultilspyConfig +from solidlsp.multilspy_logger import MultilspyLogger configure(level=logging.DEBUG) diff --git a/test/multilspy/python/test_python_basic.py b/test/multilspy/python/test_python_basic.py index 20ddabb2..a3edbea0 100644 --- a/test/multilspy/python/test_python_basic.py +++ b/test/multilspy/python/test_python_basic.py @@ -10,8 +10,8 @@ import os import pytest from multilspy.language_server import SyncLanguageServer -from solidlsp.multilspy_config import Language from serena.text_utils import LineType +from solidlsp.multilspy_config import Language @pytest.mark.python diff --git a/test/serena/test_serena_agent.py b/test/serena/test_serena_agent.py index 1642b389..a31ccd42 100644 --- a/test/serena/test_serena_agent.py +++ b/test/serena/test_serena_agent.py @@ -5,9 +5,9 @@ from dataclasses import dataclass import pytest -from solidlsp.multilspy_config import Language from serena.agent import FindReferencingSymbolsTool, FindSymbolTool, Project, ProjectConfig, SerenaAgent, SerenaConfigBase from serena.process_isolated_agent import ProcessIsolatedSerenaAgent +from solidlsp.multilspy_config import Language from test.conftest import get_repo_path diff --git a/test/serena/test_symbol_editing.py b/test/serena/test_symbol_editing.py index ba00ad12..bf6e2a33 100644 --- a/test/serena/test_symbol_editing.py +++ b/test/serena/test_symbol_editing.py @@ -11,8 +11,8 @@ from typing import Literal import pytest -from solidlsp.multilspy_config import Language from serena.symbol import CodeDiff +from solidlsp.multilspy_config import Language from src.serena.symbol import SymbolManager from test.conftest import create_ls, get_repo_path From 541998e912f3657b7828c3e87b5fe118bb0ebafe Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Mon, 23 Jun 2025 16:38:11 +0200 Subject: [PATCH 05/11] Move definitions from multilspy.language_server to solidlsp --- src/multilspy/language_server.py | 38 ---------------------------- src/solidlsp/ls.py | 43 ++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/multilspy/language_server.py b/src/multilspy/language_server.py index b584b899..e6faf7af 100644 --- a/src/multilspy/language_server.py +++ b/src/multilspy/language_server.py @@ -51,44 +51,6 @@ from serena.text_utils import MatchedConsecutiveLines, search_files -GenericDocumentSymbol = Union[LSPTypes.DocumentSymbol, LSPTypes.SymbolInformation, multilspy_types.UnifiedSymbolInformation] - -@dataclasses.dataclass(kw_only=True) -class ReferenceInSymbol: - """A symbol retrieved when requesting reference to a symbol, together with the location of the reference""" - symbol: multilspy_types.UnifiedSymbolInformation - line: int - character: int - -@dataclasses.dataclass -class LSPFileBuffer: - """ - This class is used to store the contents of an open LSP file in memory. - """ - - # uri of the file - uri: str - - # The contents of the file - contents: str - - # The version of the file - version: int - - # The language id of the file - language_id: str - - # reference count of the file - ref_count: int - - # --------------------------------- MODIFICATIONS BY MISCHA --------------------------------- - - content_hash: str = "" - - def __post_init__(self): - self.content_hash = hashlib.md5(self.contents.encode('utf-8')).hexdigest() - - class LanguageServer: """ The LanguageServer class provides a language agnostic interface to the Language Server Protocol. diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index cc792db6..610c60e4 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -1,3 +1,5 @@ +import dataclasses +import hashlib import json import logging import os @@ -11,12 +13,11 @@ from collections.abc import Iterator from contextlib import contextmanager from copy import copy from pathlib import Path, PurePath -from typing import Self, cast +from typing import Self, Union, cast import pathspec import tqdm -from multilspy.language_server import GenericDocumentSymbol, LSPFileBuffer, ReferenceInSymbol from serena.text_utils import MatchedConsecutiveLines, search_files from solidlsp import multilspy_types from solidlsp.ls_handler import SolidLanguageServerHandler @@ -34,6 +35,44 @@ from solidlsp.multilspy_exceptions import MultilspyException from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_utils import FileUtils, PathUtils, TextUtils +GenericDocumentSymbol = Union[LSPTypes.DocumentSymbol, LSPTypes.SymbolInformation, multilspy_types.UnifiedSymbolInformation] + + +@dataclasses.dataclass(kw_only=True) +class ReferenceInSymbol: + """A symbol retrieved when requesting reference to a symbol, together with the location of the reference""" + + symbol: multilspy_types.UnifiedSymbolInformation + line: int + character: int + + +@dataclasses.dataclass +class LSPFileBuffer: + """ + This class is used to store the contents of an open LSP file in memory. + """ + + # uri of the file + uri: str + + # The contents of the file + contents: str + + # The version of the file + version: int + + # The language id of the file + language_id: str + + # reference count of the file + ref_count: int + + content_hash: str = "" + + def __post_init__(self): + self.content_hash = hashlib.md5(self.contents.encode("utf-8")).hexdigest() + class SolidLanguageServer(ABC): """ From 1bdc0240401ab07dd6b7ac8b0641c0d2e8bb8e61 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Mon, 23 Jun 2025 16:57:31 +0200 Subject: [PATCH 06/11] Change imports to use SolidLanguageServer instead of SyncLanguageServer --- src/serena/agent.py | 2 +- src/serena/symbol.py | 4 ++-- src/solidlsp/__init__.py | 1 + test/conftest.py | 2 +- test/multilspy/go/test_go_basic.py | 2 +- test/multilspy/java/test_java_basic.py | 2 +- test/multilspy/php/test_php_basic.py | 2 +- test/multilspy/python/test_python_basic.py | 2 +- test/multilspy/python/test_retrieval_with_ignored_dirs.py | 2 +- test/multilspy/python/test_symbol_retrieval.py | 2 +- test/multilspy/rust/test_rust_basic.py | 2 +- test/multilspy/typescript/test_typescript_basic.py | 2 +- 12 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 src/solidlsp/__init__.py diff --git a/src/serena/agent.py b/src/serena/agent.py index 97bf63e1..1ac3ffb6 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -33,7 +33,7 @@ from sensai.util import logging from sensai.util.logging import LOG_DEFAULT_FORMAT, FallbackHandler from sensai.util.string import ToStringMixin, dict_string -from multilspy import SyncLanguageServer +from solidlsp import SolidLanguageServer as SyncLanguageServer from serena import serena_version from serena.config import SerenaAgentContext, SerenaAgentMode from serena.constants import ( diff --git a/src/serena/symbol.py b/src/serena/symbol.py index 334671db..ec94db29 100644 --- a/src/serena/symbol.py +++ b/src/serena/symbol.py @@ -9,8 +9,8 @@ from typing import TYPE_CHECKING, Any, Literal, NamedTuple, Self, Union from sensai.util.string import ToStringMixin -from multilspy import SyncLanguageServer -from multilspy.language_server import ReferenceInSymbol as LSPReferenceInSymbol +from solidlsp import SolidLanguageServer as SyncLanguageServer +from solidlsp.ls import ReferenceInSymbol as LSPReferenceInSymbol from solidlsp.multilspy_types import Position, SymbolKind, UnifiedSymbolInformation if TYPE_CHECKING: diff --git a/src/solidlsp/__init__.py b/src/solidlsp/__init__.py new file mode 100644 index 00000000..fbd65b10 --- /dev/null +++ b/src/solidlsp/__init__.py @@ -0,0 +1 @@ +from .ls import SolidLanguageServer diff --git a/test/conftest.py b/test/conftest.py index 0eeda4a1..8e086cf7 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -5,7 +5,7 @@ from typing import cast import pytest from sensai.util.logging import configure -from multilspy.language_server import SyncLanguageServer +from solidlsp import SolidLanguageServer as SyncLanguageServer from serena.constants import USE_SOLID_LSP from serena.util.file_system import GitignoreParser from solidlsp.ls import SolidLanguageServer diff --git a/test/multilspy/go/test_go_basic.py b/test/multilspy/go/test_go_basic.py index 535b3ad2..e5d82de5 100644 --- a/test/multilspy/go/test_go_basic.py +++ b/test/multilspy/go/test_go_basic.py @@ -2,7 +2,7 @@ import os import pytest -from multilspy import SyncLanguageServer +from solidlsp import SolidLanguageServer as SyncLanguageServer from solidlsp.multilspy_config import Language from solidlsp.multilspy_utils import SymbolUtils diff --git a/test/multilspy/java/test_java_basic.py b/test/multilspy/java/test_java_basic.py index 2403d122..679a7a8e 100644 --- a/test/multilspy/java/test_java_basic.py +++ b/test/multilspy/java/test_java_basic.py @@ -2,7 +2,7 @@ import os import pytest -from multilspy import SyncLanguageServer +from solidlsp import SolidLanguageServer as SyncLanguageServer from solidlsp.multilspy_config import Language from solidlsp.multilspy_utils import SymbolUtils diff --git a/test/multilspy/php/test_php_basic.py b/test/multilspy/php/test_php_basic.py index 2bfed7b3..ff4bf324 100644 --- a/test/multilspy/php/test_php_basic.py +++ b/test/multilspy/php/test_php_basic.py @@ -2,7 +2,7 @@ from pathlib import Path import pytest -from multilspy.language_server import SyncLanguageServer +from solidlsp import SolidLanguageServer as SyncLanguageServer from solidlsp.multilspy_config import Language diff --git a/test/multilspy/python/test_python_basic.py b/test/multilspy/python/test_python_basic.py index a3edbea0..c5522e5d 100644 --- a/test/multilspy/python/test_python_basic.py +++ b/test/multilspy/python/test_python_basic.py @@ -9,7 +9,7 @@ import os import pytest -from multilspy.language_server import SyncLanguageServer +from solidlsp import SolidLanguageServer as SyncLanguageServer from serena.text_utils import LineType from solidlsp.multilspy_config import Language diff --git a/test/multilspy/python/test_retrieval_with_ignored_dirs.py b/test/multilspy/python/test_retrieval_with_ignored_dirs.py index f6b25c9a..d0446f9a 100644 --- a/test/multilspy/python/test_retrieval_with_ignored_dirs.py +++ b/test/multilspy/python/test_retrieval_with_ignored_dirs.py @@ -3,7 +3,7 @@ from pathlib import Path import pytest -from multilspy.language_server import SyncLanguageServer +from solidlsp import SolidLanguageServer as SyncLanguageServer from solidlsp.multilspy_config import Language from test.conftest import create_ls diff --git a/test/multilspy/python/test_symbol_retrieval.py b/test/multilspy/python/test_symbol_retrieval.py index 80229ef2..5bca8f64 100644 --- a/test/multilspy/python/test_symbol_retrieval.py +++ b/test/multilspy/python/test_symbol_retrieval.py @@ -10,7 +10,7 @@ import os import pytest -from multilspy.language_server import SyncLanguageServer +from solidlsp import SolidLanguageServer as SyncLanguageServer from solidlsp.multilspy_config import Language from solidlsp.multilspy_types import SymbolKind diff --git a/test/multilspy/rust/test_rust_basic.py b/test/multilspy/rust/test_rust_basic.py index 20aa6e0a..ffe1245c 100644 --- a/test/multilspy/rust/test_rust_basic.py +++ b/test/multilspy/rust/test_rust_basic.py @@ -2,7 +2,7 @@ import os import pytest -from multilspy import SyncLanguageServer +from solidlsp import SolidLanguageServer as SyncLanguageServer from solidlsp.multilspy_config import Language from solidlsp.multilspy_utils import SymbolUtils diff --git a/test/multilspy/typescript/test_typescript_basic.py b/test/multilspy/typescript/test_typescript_basic.py index fca1e546..e135f88d 100644 --- a/test/multilspy/typescript/test_typescript_basic.py +++ b/test/multilspy/typescript/test_typescript_basic.py @@ -2,7 +2,7 @@ import os import pytest -from multilspy import SyncLanguageServer +from solidlsp import SolidLanguageServer as SyncLanguageServer from solidlsp.multilspy_config import Language from solidlsp.multilspy_utils import SymbolUtils From 605f8fba71cfd4c134ae568a45fdea5eb85241f0 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Mon, 23 Jun 2025 16:57:49 +0200 Subject: [PATCH 07/11] Remove multilspy package --- src/multilspy/.syncCommitId.remote | 1 - src/multilspy/.syncCommitId.this | 1 - src/multilspy/__init__.py | 8 - src/multilspy/language_server.py | 2311 ----------------- .../clangd_language_server.py | 190 -- .../initialize_params.json | 36 - .../runtime_dependencies.json | 29 - .../dart_language_server.py | 142 - .../initialize_params.json | 23 - .../runtime_dependencies.json | 45 - .../eclipse_jdtls/eclipse_jdtls.py | 422 --- .../eclipse_jdtls/initialize_params.json | 849 ------ .../eclipse_jdtls/runtime_dependencies.json | 72 - src/multilspy/language_servers/gopls/gopls.py | 151 -- .../gopls/initialize_params.json | 46 - .../intelephense/initialize_params.json | 36 - .../intelephense/intelephense.py | 200 -- .../intelephense/runtime_dependencies.json | 10 - .../initialize_params.json | 905 ------- .../jedi_language_server/jedi_server.py | 123 - .../initialize_params.json | 521 ---- .../kotlin_language_server.py | 226 -- .../runtime_dependencies.json | 41 - .../omnisharp/initialize_params.json | 631 ----- .../language_servers/omnisharp/omnisharp.py | 406 --- .../omnisharp/runtime_dependencies.json | 441 ---- .../workspace_did_change_configuration.json | 111 - .../pyright_language_server/pyright_server.py | 245 -- .../rust_analyzer/initialize_params.json | 917 ------- .../rust_analyzer/runtime_dependencies.json | 29 - .../rust_analyzer/rust_analyzer.py | 185 -- .../solargraph/initialize_params.json | 15 - .../solargraph/runtime_dependencies.json | 11 - .../language_servers/solargraph/solargraph.py | 187 -- .../initialize_params.json | 906 ------- .../runtime_dependencies.json | 16 - .../typescript_language_server.py | 245 -- 37 files changed, 10733 deletions(-) delete mode 100644 src/multilspy/.syncCommitId.remote delete mode 100644 src/multilspy/.syncCommitId.this delete mode 100644 src/multilspy/__init__.py delete mode 100644 src/multilspy/language_server.py delete mode 100644 src/multilspy/language_servers/clangd_language_server/clangd_language_server.py delete mode 100644 src/multilspy/language_servers/clangd_language_server/initialize_params.json delete mode 100644 src/multilspy/language_servers/clangd_language_server/runtime_dependencies.json delete mode 100644 src/multilspy/language_servers/dart_language_server/dart_language_server.py delete mode 100644 src/multilspy/language_servers/dart_language_server/initialize_params.json delete mode 100644 src/multilspy/language_servers/dart_language_server/runtime_dependencies.json delete mode 100644 src/multilspy/language_servers/eclipse_jdtls/eclipse_jdtls.py delete mode 100644 src/multilspy/language_servers/eclipse_jdtls/initialize_params.json delete mode 100644 src/multilspy/language_servers/eclipse_jdtls/runtime_dependencies.json delete mode 100644 src/multilspy/language_servers/gopls/gopls.py delete mode 100644 src/multilspy/language_servers/gopls/initialize_params.json delete mode 100644 src/multilspy/language_servers/intelephense/initialize_params.json delete mode 100644 src/multilspy/language_servers/intelephense/intelephense.py delete mode 100644 src/multilspy/language_servers/intelephense/runtime_dependencies.json delete mode 100644 src/multilspy/language_servers/jedi_language_server/initialize_params.json delete mode 100644 src/multilspy/language_servers/jedi_language_server/jedi_server.py delete mode 100644 src/multilspy/language_servers/kotlin_language_server/initialize_params.json delete mode 100644 src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py delete mode 100644 src/multilspy/language_servers/kotlin_language_server/runtime_dependencies.json delete mode 100644 src/multilspy/language_servers/omnisharp/initialize_params.json delete mode 100644 src/multilspy/language_servers/omnisharp/omnisharp.py delete mode 100644 src/multilspy/language_servers/omnisharp/runtime_dependencies.json delete mode 100644 src/multilspy/language_servers/omnisharp/workspace_did_change_configuration.json delete mode 100644 src/multilspy/language_servers/pyright_language_server/pyright_server.py delete mode 100644 src/multilspy/language_servers/rust_analyzer/initialize_params.json delete mode 100644 src/multilspy/language_servers/rust_analyzer/runtime_dependencies.json delete mode 100644 src/multilspy/language_servers/rust_analyzer/rust_analyzer.py delete mode 100644 src/multilspy/language_servers/solargraph/initialize_params.json delete mode 100644 src/multilspy/language_servers/solargraph/runtime_dependencies.json delete mode 100644 src/multilspy/language_servers/solargraph/solargraph.py delete mode 100644 src/multilspy/language_servers/typescript_language_server/initialize_params.json delete mode 100644 src/multilspy/language_servers/typescript_language_server/runtime_dependencies.json delete mode 100644 src/multilspy/language_servers/typescript_language_server/typescript_language_server.py diff --git a/src/multilspy/.syncCommitId.remote b/src/multilspy/.syncCommitId.remote deleted file mode 100644 index 20a711e0..00000000 --- a/src/multilspy/.syncCommitId.remote +++ /dev/null @@ -1 +0,0 @@ -606542ed8766bfbcc6490b3115eb0aea78c693e5 \ No newline at end of file diff --git a/src/multilspy/.syncCommitId.this b/src/multilspy/.syncCommitId.this deleted file mode 100644 index 84678199..00000000 --- a/src/multilspy/.syncCommitId.this +++ /dev/null @@ -1 +0,0 @@ -2f5651bd7846ebb6abde121223233bf975ce98ee \ No newline at end of file diff --git a/src/multilspy/__init__.py b/src/multilspy/__init__.py deleted file mode 100644 index 0bd17583..00000000 --- a/src/multilspy/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -This module contains the multilspy API -""" - -from solidlsp import multilspy_types as Types -from .language_server import LanguageServer, SyncLanguageServer - -__all__ = ["LanguageServer", "Types", "SyncLanguageServer"] diff --git a/src/multilspy/language_server.py b/src/multilspy/language_server.py deleted file mode 100644 index e6faf7af..00000000 --- a/src/multilspy/language_server.py +++ /dev/null @@ -1,2311 +0,0 @@ -""" -This file contains the main interface and the public API for multilspy. -The abstract class LanguageServer provides a factory method, creator that is -intended for creating instantiations of language specific clients. -The details of Language Specific configuration are not exposed to the user. -""" - -import asyncio -import dataclasses -import hashlib -import json -import logging -import os -import pathlib -import pickle -import re -import threading -from collections import defaultdict -from contextlib import asynccontextmanager, contextmanager -from copy import copy -from pathlib import Path, PurePath -from typing import AsyncIterator, Dict, Iterator, List, Optional, Tuple, Union, cast - -import pathspec -import tqdm - -from solidlsp import multilspy_types -from solidlsp.lsp_protocol_handler import lsp_types as LSPTypes, lsp_types -from solidlsp.lsp_protocol_handler.lsp_constants import LSPConstants -from solidlsp.lsp_protocol_handler.lsp_types import Definition, DefinitionParams, LocationLink, SymbolKind -from solidlsp.lsp_protocol_handler.server import ( - Error, - LanguageServerHandler, - ProcessLaunchInfo, - StringDict, -) -from solidlsp.multilspy_config import Language, MultilspyConfig -from solidlsp.multilspy_exceptions import MultilspyException -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_utils import FileUtils, PathUtils, TextUtils -from solidlsp.type_helpers import ensure_all_methods_implemented - -# Serena dependencies -# We will need to watch out for circular imports, but it's probably better to not -# move all generic util code from serena into multilspy. -# It does however make sense to integrate many text-related utils into the language server -# since it caches (in-memory) file contents, so we can avoid reading from disk. -# Moreover, the way we want to use the language server (for retrieving actual content), -# it makes sense to have more content-related utils directly in it. -from serena.text_utils import MatchedConsecutiveLines, search_files - - - -class LanguageServer: - """ - The LanguageServer class provides a language agnostic interface to the Language Server Protocol. - It is used to communicate with Language Servers of different programming languages. - """ - - # To be overridden and extended by subclasses - def is_ignored_dirname(self, dirname: str) -> bool: - """ - A language-specific condition for directories that should always be ignored. For example, venv - in Python and node_modules in JS/TS should be ignored always. - """ - return dirname.startswith('.') - - @classmethod - def create(cls, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str) -> "LanguageServer": - """ - Creates a language specific LanguageServer instance based on the given configuration, and appropriate settings for the programming language. - - If language is Java, then ensure that jdk-17.0.6 or higher is installed, `java` is in PATH, and JAVA_HOME is set to the installation directory. - If language is JS/TS, then ensure that node (v18.16.0 or higher) is installed and in PATH. - - :param repository_root_path: The root path of the repository. - :param config: The Multilspy configuration. - :param logger: The logger to use. - :return LanguageServer: A language specific LanguageServer instance. - """ - if config.code_language == Language.PYTHON: - from multilspy.language_servers.pyright_language_server.pyright_server import ( - PyrightServer, - ) - - return PyrightServer(config, logger, repository_root_path) - # It used to be jedi, but pyright is a bit faster, and also more actively maintained - # Keeping the previous code for reference - # from multilspy.language_servers.jedi_language_server.jedi_server import ( - # JediServer, - # ) - - # return JediServer(config, logger, repository_root_path) - elif config.code_language == Language.JAVA: - from multilspy.language_servers.eclipse_jdtls.eclipse_jdtls import ( - EclipseJDTLS, - ) - - return EclipseJDTLS(config, logger, repository_root_path) - elif config.code_language == Language.KOTLIN: - from multilspy.language_servers.kotlin_language_server.kotlin_language_server import ( - KotlinLanguageServer, - ) - - return KotlinLanguageServer(config, logger, repository_root_path) - elif config.code_language == Language.RUST: - from multilspy.language_servers.rust_analyzer.rust_analyzer import ( - RustAnalyzer, - ) - - return RustAnalyzer(config, logger, repository_root_path) - elif config.code_language == Language.CSHARP: - from multilspy.language_servers.omnisharp.omnisharp import OmniSharp - - return OmniSharp(config, logger, repository_root_path) - elif config.code_language in [Language.TYPESCRIPT, Language.JAVASCRIPT]: - from multilspy.language_servers.typescript_language_server.typescript_language_server import ( - TypeScriptLanguageServer, - ) - return TypeScriptLanguageServer(config, logger, repository_root_path) - elif config.code_language == Language.GO: - from multilspy.language_servers.gopls.gopls import Gopls - - return Gopls(config, logger, repository_root_path) - elif config.code_language == Language.RUBY: - from multilspy.language_servers.solargraph.solargraph import Solargraph - - return Solargraph(config, logger, repository_root_path) - elif config.code_language == Language.DART: - from multilspy.language_servers.dart_language_server.dart_language_server import DartLanguageServer - - return DartLanguageServer(config, logger, repository_root_path) - elif config.code_language == Language.CPP: - from multilspy.language_servers.clangd_language_server.clangd_language_server import ClangdLanguageServer - - return ClangdLanguageServer(config, logger, repository_root_path) - elif config.code_language == Language.PHP: - from multilspy.language_servers.intelephense.intelephense import Intelephense - - return Intelephense(config, logger, repository_root_path) - else: - logger.log(f"Language {config.code_language} is not supported", logging.ERROR) - raise MultilspyException(f"Language {config.code_language} is not supported") - - def __init__( - self, - config: MultilspyConfig, - logger: MultilspyLogger, - repository_root_path: str, - process_launch_info: ProcessLaunchInfo, - language_id: str, - ): - """ - Initializes a LanguageServer instance. - - Do not instantiate this class directly. Use `LanguageServer.create` method instead. - - :param config: The Multilspy configuration. - :param logger: The logger to use. - :param repository_root_path: The root path of the repository. - :param process_launch_info: Each language server has a specific command used to start the server. - This parameter is the command to launch the language server process. - The command must pass appropriate flags to the binary, so that it runs in the stdio mode, - as opposed to HTTP, TCP modes supported by some language servers. - """ - if type(self) == LanguageServer: - raise MultilspyException( - "LanguageServer is an abstract class and cannot be instantiated directly. Use LanguageServer.create method instead." - ) - - 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) - - self.language_id = language_id - self.open_file_buffers: Dict[str, LSPFileBuffer] = {} - self.language = Language(language_id) - - # load cache first to prevent any racing conditions due to asyncio stuff - self._document_symbols_cache: dict[str, Tuple[str, Tuple[List[multilspy_types.UnifiedSymbolInformation], List[ - multilspy_types.UnifiedSymbolInformation]]]] = {} - """Maps file paths to a tuple of (file_content_hash, result_of_request_document_symbols)""" - self._cache_lock = threading.Lock() - self._cache_has_changed: bool = False - self.load_cache() - - self.server_started = False - self.completions_available = asyncio.Event() - if config.trace_lsp_communication: - def logging_fn(source: str, target: str, msg: StringDict | str): - self.logger.log(f"LSP: {source} -> {target}: {str(msg)[:90]}...", self.logger.logger.level) - else: - logging_fn = None - - - # 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) - self.server = LanguageServerHandler( - process_launch_info, - logger=logging_fn, - start_independent_lsp_process=config.start_independent_lsp_process, - ) - - - # Set up the pathspec matcher for the ignored paths - # for all absolute paths in ignored_paths, convert them to relative paths - processed_patterns = [] - for pattern in set(config.ignored_paths): - # 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) - - # Create a pathspec matcher from the processed patterns - self._ignore_spec = pathspec.PathSpec.from_lines( - pathspec.patterns.GitWildMatchPattern, - processed_patterns - ) - - def get_ignore_spec(self) -> pathspec.PathSpec: - """Returns the pathspec matcher for the paths that were configured to be ignored through - the multilspy config. - - This is is a subset of the full language-specific ignore spec that determines - which files are relevant for the language server. - - This matcher is useful for operations outside of the language server, - such as when searching for relevant non-language files in the project. - """ - return self._ignore_spec - - def is_ignored_path(self, relative_path: str, ignore_unsupported_files: bool = True) -> bool: - """ - Determine if a path should be ignored based on file type - and ignore patterns. - - :param relative_path: Relative path to check - :param ignore_unsupported_files: whether files that are not supported source files should be ignored - - :return: True if the path should be ignored, False otherwise - """ - abs_path = os.path.join(self.repository_root_path, relative_path) - if not os.path.exists(abs_path): - raise FileNotFoundError(f"File {abs_path} not found, the ignore check cannot be performed") - - # Check file extension if it's a file - is_file = os.path.isfile(abs_path) - if is_file and ignore_unsupported_files: - fn_matcher = self.language.get_source_fn_matcher() - if not fn_matcher.is_relevant_filename(abs_path): - return True - - # Create normalized path for consistent handling - rel_path = Path(relative_path) - - # Check each part of the path against always fulfilled ignore conditions - dir_parts = rel_path.parts - if is_file: - dir_parts = dir_parts[:-1] - for part in dir_parts: - if not part: # Skip empty parts (e.g., from leading '/') - continue - if self.is_ignored_dirname(part): - return True - - # Use pathspec for gitignore-style pattern matching - # Normalize path separators for pathspec (it expects forward slashes) - normalized_path = str(rel_path).replace(os.path.sep, '/') - - # pathspec can't handle the matching of directories if they don't end with a slash! - # see https://github.com/cpburnz/python-pathspec/issues/89 - if os.path.isdir(os.path.join(self.repository_root_path, normalized_path)) and not normalized_path.endswith('/'): - normalized_path = normalized_path + '/' - - # Use the pathspec matcher to check if the path matches any ignore pattern - if self.get_ignore_spec().match_file(normalized_path): - return True - - return False - - async def _shutdown(self, timeout: float = 5.0): - """ - A robust shutdown process designed to terminate cleanly on all platforms, including Windows, - by explicitly closing all I/O pipes. - """ - if not self.server.is_running(): - self.logger.log("Server process not running, skipping shutdown.", logging.DEBUG) - return - - self.logger.log(f"Initiating final robust shutdown with a {timeout}s timeout...", logging.INFO) - process = self.server.process - reader_tasks = list(self.server.tasks.values()) - - # --- Main Shutdown Logic --- - try: - # Stage 1: Graceful Termination Request - # Send LSP shutdown and close stdin to signal no more input. - try: - await asyncio.wait_for(self.server.shutdown(), timeout=2.0) - if process.stdin and not process.stdin.is_closing(): - process.stdin.close() - except Exception: - pass # Ignore errors here, we are proceeding to terminate anyway. - - # Stage 2: Terminate and Concurrently Drain stdout/stderr - process.terminate() - - # Wait for the process to exit AND for the output pipes to be drained. - # The reader tasks will exit when they hit EOF. - await asyncio.wait_for( - asyncio.gather(process.wait(), *reader_tasks), - timeout=timeout - 2.0 - ) - self.logger.log("Process terminated and output pipes drained.", logging.INFO) - - except asyncio.TimeoutError: - # Stage 3: Forceful Kill - self.logger.log("Graceful termination failed. Forcefully killing process...", logging.WARNING) - if self.server.is_running(): - try: - process.kill() - # Wait for the killed process to be reaped by the OS. - await process.wait() - except Exception as e: - self.logger.log(f"Error during forceful kill: {e}", logging.ERROR) - - except Exception as e: - self.logger.log(f"An unexpected error occurred during shutdown logic: {e}", logging.ERROR) - - finally: - # === STAGE 4: EXPLICIT TASK & PIPE CLEANUP === - self.logger.log("Performing final task cancellation and pipe handle cleanup...", logging.DEBUG) - - # 1. Cancel any lingering reader tasks. - for task in reader_tasks: - if not task.done(): - task.cancel() - # Wait for cancellations to complete. - await asyncio.gather(*reader_tasks, return_exceptions=True) - self.server.tasks = {} - - # 2. Explicitly close each pipe to release OS handles. - for pipe in [process.stdin, process.stdout, process.stderr]: - if pipe and hasattr(pipe, 'is_closing') and not pipe.is_closing(): - try: - pipe.close() - except Exception: - pass - - # 3. Null out the process object in the handler. - self.server.process = None - self.logger.log("Shutdown sequence fully finished.", logging.DEBUG) - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["LanguageServer"]: - """ - Starts the Language Server and yields the LanguageServer instance. - """ - self.server_started = True - try: - yield self - finally: - self.server_started = False - await self._shutdown() - - @contextmanager - def open_file(self, relative_file_path: str) -> Iterator[LSPFileBuffer]: - """ - Open a file in the Language Server. This is required before making any requests to the Language Server. - - :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, - ) - raise MultilspyException("Language Server not started") - - absolute_file_path = str(PurePath(self.repository_root_path, relative_file_path)) - uri = pathlib.Path(absolute_file_path).as_uri() - - if uri in self.open_file_buffers: - assert self.open_file_buffers[uri].uri == uri - assert self.open_file_buffers[uri].ref_count >= 1 - - self.open_file_buffers[uri].ref_count += 1 - yield self.open_file_buffers[uri] - self.open_file_buffers[uri].ref_count -= 1 - else: - contents = FileUtils.read_file(self.logger, absolute_file_path) - - version = 0 - self.open_file_buffers[uri] = LSPFileBuffer(uri, contents, version, self.language_id, 1) - - self.server.notify.did_open_text_document( - { - LSPConstants.TEXT_DOCUMENT: { - LSPConstants.URI: uri, - LSPConstants.LANGUAGE_ID: self.language_id, - LSPConstants.VERSION: 0, - LSPConstants.TEXT: contents, - } - } - ) - yield self.open_file_buffers[uri] - self.open_file_buffers[uri].ref_count -= 1 - - if self.open_file_buffers[uri].ref_count == 0: - self.server.notify.did_close_text_document( - { - LSPConstants.TEXT_DOCUMENT: { - LSPConstants.URI: uri, - } - } - ) - del self.open_file_buffers[uri] - - def insert_text_at_position( - self, relative_file_path: str, line: int, column: int, text_to_be_inserted: str - ) -> multilspy_types.Position: - """ - Insert text at the given line and column in the given file and return - the updated cursor position after inserting the text. - - :param relative_file_path: The relative path of the file to open. - :param line: The line number at which text should be inserted. - :param column: The column number at which text should be inserted. - :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, - ) - raise MultilspyException("Language Server not started") - - absolute_file_path = str(PurePath(self.repository_root_path, relative_file_path)) - uri = pathlib.Path(absolute_file_path).as_uri() - - # Ensure the file is open - assert uri in self.open_file_buffers - - file_buffer = self.open_file_buffers[uri] - file_buffer.version += 1 - - new_contents, new_l, new_c = TextUtils.insert_text_at_position(file_buffer.contents, line, column, text_to_be_inserted) - file_buffer.contents = new_contents - self.server.notify.did_change_text_document( - { - LSPConstants.TEXT_DOCUMENT: { - LSPConstants.VERSION: file_buffer.version, - LSPConstants.URI: file_buffer.uri, - }, - LSPConstants.CONTENT_CHANGES: [ - { - LSPConstants.RANGE: { - "start": {"line": line, "character": column}, - "end": {"line": line, "character": column}, - }, - "text": text_to_be_inserted, - } - ], - } - ) - return multilspy_types.Position(line=new_l, character=new_c) - - def delete_text_between_positions( - self, - relative_file_path: str, - start: multilspy_types.Position, - end: multilspy_types.Position, - ) -> str: - """ - 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, - ) - raise MultilspyException("Language Server not started") - - absolute_file_path = str(PurePath(self.repository_root_path, relative_file_path)) - uri = pathlib.Path(absolute_file_path).as_uri() - - # Ensure the file is open - assert uri in self.open_file_buffers - - file_buffer = self.open_file_buffers[uri] - file_buffer.version += 1 - new_contents, deleted_text = TextUtils.delete_text_between_positions(file_buffer.contents, start_line=start["line"], start_col=start["character"], end_line=end["line"], end_col=end["character"]) - file_buffer.contents = new_contents - self.server.notify.did_change_text_document( - { - LSPConstants.TEXT_DOCUMENT: { - LSPConstants.VERSION: file_buffer.version, - LSPConstants.URI: file_buffer.uri, - }, - LSPConstants.CONTENT_CHANGES: [{LSPConstants.RANGE: {"start": start, "end": end}, "text": ""}], - } - ) - return deleted_text - - async def _send_definition_request(self, definition_params: DefinitionParams) -> Union[Definition, List[LocationLink], None]: - return await self.server.send.definition(definition_params) - - async def request_definition( - self, relative_file_path: str, line: int, column: int - ) -> List[multilspy_types.Location]: - """ - Raise a [textDocument/definition](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition) request to the Language Server - for the symbol at the given line and column in the given file. Wait for the response and return the result. - - :param relative_file_path: The relative path of the file that has the symbol for which definition should be looked up - :param line: The line number of the symbol - :param column: The column number of the symbol - - :return List[multilspy_types.Location]: A list of locations where the symbol is defined - """ - - if not self.server_started: - self.logger.log( - "find_function_definition called before Language Server started", - logging.ERROR, - ) - raise MultilspyException("Language Server not started") - - with self.open_file(relative_file_path): - # sending request to the language server and waiting for response - definition_params = cast(DefinitionParams, { - LSPConstants.TEXT_DOCUMENT: { - LSPConstants.URI: pathlib.Path( - str(PurePath(self.repository_root_path, relative_file_path)) - ).as_uri() - }, - LSPConstants.POSITION: { - LSPConstants.LINE: line, - LSPConstants.CHARACTER: column, - }, - }) - response = await self._send_definition_request(definition_params) - - ret: List[multilspy_types.Location] = [] - if isinstance(response, list): - # response is either of type Location[] or LocationLink[] - for item in response: - assert isinstance(item, dict) - if LSPConstants.URI in item and LSPConstants.RANGE in item: - new_item: multilspy_types.Location = {} - new_item.update(item) - new_item["absolutePath"] = PathUtils.uri_to_path(new_item["uri"]) - new_item["relativePath"] = PathUtils.get_relative_path(new_item["absolutePath"], self.repository_root_path) - ret.append(multilspy_types.Location(new_item)) - elif ( - LSPConstants.ORIGIN_SELECTION_RANGE in item - and LSPConstants.TARGET_URI in item - and LSPConstants.TARGET_RANGE in item - and LSPConstants.TARGET_SELECTION_RANGE in item - ): - new_item: multilspy_types.Location = {} - new_item["uri"] = item[LSPConstants.TARGET_URI] - new_item["absolutePath"] = PathUtils.uri_to_path(new_item["uri"]) - new_item["relativePath"] = PathUtils.get_relative_path(new_item["absolutePath"], self.repository_root_path) - new_item["range"] = item[LSPConstants.TARGET_SELECTION_RANGE] - ret.append(multilspy_types.Location(**new_item)) - else: - assert False, f"Unexpected response from Language Server: {item}" - elif isinstance(response, dict): - # response is of type Location - assert LSPConstants.URI in response - assert LSPConstants.RANGE in response - - new_item: multilspy_types.Location = {} - new_item.update(response) - new_item["absolutePath"] = PathUtils.uri_to_path(new_item["uri"]) - new_item["relativePath"] = PathUtils.get_relative_path(new_item["absolutePath"], self.repository_root_path) - ret.append(multilspy_types.Location(**new_item)) - 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, - ) - else: - assert False, f"Unexpected response from Language Server: {response}" - - return ret - - # Some LS cause problems with this, so the call is isolated from the rest to allow overriding in subclasses - async def _send_references_request(self, relative_file_path: str, line: int, column: int) -> List[ - lsp_types.Location] | None: - return await self.server.send.references( - { - "textDocument": {"uri": PathUtils.path_to_uri(os.path.join(self.repository_root_path, relative_file_path))}, - "position": {"line": line, "character": column}, - "context": {"includeDeclaration": False}, - } - ) - - async def request_references( - self, relative_file_path: str, line: int, column: int - ) -> List[multilspy_types.Location]: - """ - Raise a [textDocument/references](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references) request to the Language Server - to find references to the symbol at the given line and column in the given file. Wait for the response and return the result. - Filters out references located in ignored directories. - - :param relative_file_path: The relative path of the file that has the symbol for which references should be looked up - :param line: The line number of the symbol - :param column: The column number of the symbol - - :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, - ) - raise MultilspyException("Language Server not started") - - - with self.open_file(relative_file_path): - try: - response = await self._send_references_request(relative_file_path, line=line, column=column) - except Exception as e: - # Catch LSP internal error (-32603) and raise a more informative exception - if isinstance(e, Error) and getattr(e, 'code', None) == -32603: - raise RuntimeError( - f"LSP internal error (-32603) when requesting references for {relative_file_path}:{line}:{column}. " - "This often occurs when requesting references for a symbol not referenced in the expected way. " - ) from e - raise - if response is None: - return [] - - ret: List[multilspy_types.Location] = [] - assert isinstance(response, list), f"Unexpected response from Language Server (expected list, got {type(response)}): {response}" - for item in response: - assert isinstance(item, dict), f"Unexpected response from Language Server (expected dict, got {type(item)}): {item}" - assert LSPConstants.URI in item - assert LSPConstants.RANGE in item - - abs_path = PathUtils.uri_to_path(item[LSPConstants.URI]) - 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) - continue - - new_item: multilspy_types.Location = {} - new_item.update(item) - new_item["absolutePath"] = str(abs_path) - new_item["relativePath"] = str(rel_path) - ret.append(multilspy_types.Location(**new_item)) - - return ret - - async def request_references_with_content( - self, relative_file_path: str, line: int, column: int, context_lines_before: int = 0, context_lines_after: int = 0 - ) -> List[MatchedConsecutiveLines]: - """ - Like request_references, but returns the content of the lines containing the references, not just the locations. - - :param relative_file_path: The relative path of the file that has the symbol for which references should be looked up - :param line: The line number of the symbol - :param column: The column number of the symbol - :param context_lines_before: The number of lines to include in the context before the line containing the reference - :param context_lines_after: The number of lines to include in the context after the line containing the reference - - :return: A list of MatchedConsecutiveLines objects, one for each reference. - """ - references = await self.request_references(relative_file_path, line, column) - return [self.retrieve_content_around_line(ref["relativePath"], ref["range"]["start"]["line"], context_lines_before, context_lines_after) for ref in references] - - def retrieve_full_file_content(self, relative_file_path: str) -> str: - """ - Retrieve the full content of the given file. - """ - with self.open_file(relative_file_path) as file_data: - return file_data.contents - - def retrieve_content_around_line(self, relative_file_path: str, line: int, context_lines_before: int = 0, context_lines_after: int = 0) -> MatchedConsecutiveLines: - """ - Retrieve the content of the given file around the given line. - - :param relative_file_path: The relative path of the file to retrieve the content from - :param line: The line number to retrieve the content around - :param context_lines_before: The number of lines to retrieve before the given line - :param context_lines_after: The number of lines to retrieve after the given line - - :return MatchedConsecutiveLines: A container with the desired lines. - """ - with self.open_file(relative_file_path) as file_data: - file_contents = file_data.contents - return MatchedConsecutiveLines.from_file_contents(file_contents, line=line, context_lines_before=context_lines_before, context_lines_after=context_lines_after, source_file_path=relative_file_path) - - - async def request_completions( - self, relative_file_path: str, line: int, column: int, allow_incomplete: bool = False - ) -> List[multilspy_types.CompletionItem]: - """ - Raise a [textDocument/completion](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion) request to the Language Server - to find completions at the given line and column in the given file. Wait for the response and return the result. - - :param relative_file_path: The relative path of the file that has the symbol for which completions should be looked up - :param line: The line number of the symbol - :param column: The column number of the symbol - - :return List[multilspy_types.CompletionItem]: A list of completions - """ - with self.open_file(relative_file_path): - open_file_buffer = self.open_file_buffers[ - pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri() - ] - completion_params: LSPTypes.CompletionParams = { - "position": {"line": line, "character": column}, - "textDocument": {"uri": open_file_buffer.uri}, - "context": {"triggerKind": LSPTypes.CompletionTriggerKind.Invoked}, - } - response: Union[List[LSPTypes.CompletionItem], LSPTypes.CompletionList, None] = None - - num_retries = 0 - while response is None or (response["isIncomplete"] and num_retries < 30): - await self.completions_available.wait() - response: Union[ - List[LSPTypes.CompletionItem], LSPTypes.CompletionList, None - ] = await self.server.send.completion(completion_params) - if isinstance(response, list): - response = {"items": response, "isIncomplete": False} - num_retries += 1 - - # TODO: Understand how to appropriately handle `isIncomplete` - if response is None or (response["isIncomplete"] and not(allow_incomplete)): - return [] - - if "items" in response: - response = response["items"] - - response: List[LSPTypes.CompletionItem] = response - - # TODO: Handle the case when the completion is a keyword - items = [item for item in response if item["kind"] != LSPTypes.CompletionItemKind.Keyword] - - completions_list: List[multilspy_types.CompletionItem] = [] - - for item in items: - assert "insertText" in item or "textEdit" in item - assert "kind" in item - completion_item = {} - if "detail" in item: - completion_item["detail"] = item["detail"] - - if "label" in item: - completion_item["completionText"] = item["label"] - completion_item["kind"] = item["kind"] - elif "insertText" in item: - completion_item["completionText"] = item["insertText"] - completion_item["kind"] = item["kind"] - elif "textEdit" in item and "newText" in item["textEdit"]: - completion_item["completionText"] = item["textEdit"]["newText"] - completion_item["kind"] = item["kind"] - elif "textEdit" in item and "range" in item["textEdit"]: - new_dot_lineno, new_dot_colno = ( - completion_params["position"]["line"], - completion_params["position"]["character"], - ) - assert all( - ( - item["textEdit"]["range"]["start"]["line"] == new_dot_lineno, - item["textEdit"]["range"]["start"]["character"] == new_dot_colno, - item["textEdit"]["range"]["start"]["line"] == item["textEdit"]["range"]["end"]["line"], - item["textEdit"]["range"]["start"]["character"] - == item["textEdit"]["range"]["end"]["character"], - ) - ) - - completion_item["completionText"] = item["textEdit"]["newText"] - completion_item["kind"] = item["kind"] - elif "textEdit" in item and "insert" in item["textEdit"]: - assert False - else: - assert False - - completion_item = multilspy_types.CompletionItem(**completion_item) - completions_list.append(completion_item) - - return [ - json.loads(json_repr) - for json_repr in set([json.dumps(item, sort_keys=True) for item in completions_list]) - ] - - async def request_document_symbols(self, relative_file_path: str, include_body: bool = False) -> Tuple[List[ - multilspy_types.UnifiedSymbolInformation], List[multilspy_types.UnifiedSymbolInformation]]: - """ - Raise a [textDocument/documentSymbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol) request to the Language Server - to find symbols in the given file. Wait for the response and return the result. - - :param relative_file_path: The relative path of the file that has the symbols - :param include_body: whether to include the body of the symbols in the result. - :return: A list of symbols in the file, and a list of root symbols that represent the tree structure of the symbols. - All symbols will have a location, a children, and a parent attribute, - where the parent attribute is None for root symbols. - Note that this is slightly different from the call to request_full_symbol_tree, - where the parent attribute will be the file symbol which in turn may have a package symbol as parent. - If you need a symbol tree that contains file symbols as well, you should use `request_full_symbol_tree` instead. - """ - # TODO: it's kinda dumb to not use the cache if include_body is False after include_body was True once - # Should be fixed in the future, it's a small performance optimization - cache_key = f"{relative_file_path}-{include_body}" - with self.open_file(relative_file_path) as file_data: - with self._cache_lock: - file_hash_and_result = self._document_symbols_cache.get(cache_key) - if file_hash_and_result is not None: - file_hash, result = file_hash_and_result - if file_hash == file_data.content_hash: - self.logger.log(f"Returning cached document symbols for {relative_file_path}", logging.DEBUG) - return result - else: - self.logger.log(f"Content for {relative_file_path} has changed. Will overwrite in-memory cache", logging.DEBUG) - else: - self.logger.log(f"No cache hit for symbols with {include_body=} in {relative_file_path}", logging.DEBUG) - - self.logger.log(f"Requesting document symbols for {relative_file_path} from the Language Server", logging.DEBUG) - response = await self.server.send.document_symbol( - { - "textDocument": { - "uri": pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri() - } - } - ) - self.logger.log(f"Received {len(response) if response is not None else None} document symbols for {relative_file_path} from the Language Server", logging.DEBUG) - - def turn_item_into_symbol_with_children(item: GenericDocumentSymbol): - item = cast(multilspy_types.UnifiedSymbolInformation, item) - absolute_path = os.path.join(self.repository_root_path, relative_file_path) - - # handle missing entries in location - if "location" not in item: - uri = pathlib.Path(absolute_path).as_uri() - assert "range" in item - tree_location = multilspy_types.Location( - uri=uri, - range=item['range'], - absolutePath=absolute_path, - relativePath=relative_file_path, - ) - item['location'] = tree_location - location = item["location"] - if "absolutePath" not in location: - location["absolutePath"] = absolute_path - if "relativePath" not in location: - location["relativePath"] = relative_file_path - if include_body: - item['body'] = self.retrieve_symbol_body(item) - # handle missing selectionRange - if "selectionRange" not in item: - if "range" in item: - item["selectionRange"] = item["range"] - else: - item["selectionRange"] = item["location"]["range"] - children = item.get(LSPConstants.CHILDREN, []) - for child in children: - child["parent"] = item - item[LSPConstants.CHILDREN] = children - - flat_all_symbol_list: List[multilspy_types.UnifiedSymbolInformation] = [] - assert isinstance(response, list), f"Unexpected response from Language Server: {response}" - root_nodes: List[multilspy_types.UnifiedSymbolInformation] = [] - for root_item in response: - if "range" not in root_item and "location" not in root_item: - if root_item["kind"] in [SymbolKind.File, SymbolKind.Module]: - ... - - # mutation is more convenient than creating a new dict, - # so we cast and rename the var after the mutating call to turn_item_into_symbol_with_children - # which turned and item into a "symbol" - turn_item_into_symbol_with_children(root_item) - root_symbol = cast(multilspy_types.UnifiedSymbolInformation, root_item) - root_symbol["parent"] = None - - root_nodes.append(root_symbol) - assert isinstance(root_symbol, dict) - assert LSPConstants.NAME in root_symbol - assert LSPConstants.KIND in root_symbol - - if LSPConstants.CHILDREN in root_symbol: - # TODO: l_tree should be a list of TreeRepr. Define the following function to return TreeRepr as well - - def visit_tree_nodes_and_build_tree_repr(node: GenericDocumentSymbol) -> List[ - multilspy_types.UnifiedSymbolInformation]: - node = cast(multilspy_types.UnifiedSymbolInformation, node) - l: List[multilspy_types.UnifiedSymbolInformation] = [] - turn_item_into_symbol_with_children(node) - assert LSPConstants.CHILDREN in node - children = node[LSPConstants.CHILDREN] - l.append(node) - for child in children: - l.extend(visit_tree_nodes_and_build_tree_repr(child)) - return l - - flat_all_symbol_list.extend(visit_tree_nodes_and_build_tree_repr(root_symbol)) - else: - flat_all_symbol_list.append(multilspy_types.UnifiedSymbolInformation(**root_symbol)) - - result = flat_all_symbol_list, root_nodes - self.logger.log(f"Caching document symbols for {relative_file_path}", logging.DEBUG) - with self._cache_lock: - self._document_symbols_cache[cache_key] = (file_data.content_hash, result) - self._cache_has_changed = True - return result - - async def request_full_symbol_tree(self, within_relative_path: str | None = None, include_body: bool = False) -> List[ - multilspy_types.UnifiedSymbolInformation]: - """ - Will go through all files in the project or within a relative path and build a tree of symbols. - Note: this may be slow the first time it is called, especially if `within_relative_path` is not used to restrict the search. - - For each file, a symbol of kind File (2) will be created. For directories, a symbol of kind Package (4) will be created. - All symbols will have a children attribute, thereby representing the tree structure of all symbols in the project - that are within the repository. - All symbols except the root packages will have a parent attribute. - Will ignore directories starting with '.', language-specific defaults - and user-configured directories (e.g. from .gitignore). - - :param within_relative_path: pass a relative path to only consider symbols within this path. - If a file is passed, only the symbols within this file will be considered. - If a directory is passed, all files within this directory will be considered. - :param include_body: whether to include the body of the symbols in the result. - - :return: A list of root symbols representing the top-level packages/modules in the project. - """ - - if within_relative_path is not None: - within_abs_path = os.path.join(self.repository_root_path, within_relative_path) - if not os.path.exists(within_abs_path): - raise FileNotFoundError(f"File or directory not found: {within_abs_path}") - if os.path.isfile(within_abs_path): - if self.is_ignored_path(within_relative_path): - self.logger.log(f"You passed a file explicitly, but it is ignored. This is probably an error. File: {within_relative_path}", logging.ERROR) - return [] - else: - _, root_nodes = await self.request_document_symbols(within_relative_path, include_body=include_body) - return root_nodes - - # Helper function to recursively process directories - async def process_directory(rel_dir_path: str) -> List[multilspy_types.UnifiedSymbolInformation]: - abs_dir_path = self.repository_root_path if rel_dir_path == "." else os.path.join(self.repository_root_path, rel_dir_path) - abs_dir_path = os.path.realpath(abs_dir_path) - - if self.is_ignored_path(str(Path(abs_dir_path).relative_to(self.repository_root_path))): - self.logger.log(f"Skipping directory: {rel_dir_path}\n(because it should be ignored)", logging.DEBUG) - return [] - - result = [] - try: - contained_dir_or_file_names = os.listdir(abs_dir_path) - except OSError: - return [] - - # Create package symbol for directory - package_symbol = multilspy_types.UnifiedSymbolInformation( # type: ignore - name=os.path.basename(abs_dir_path), - kind=multilspy_types.SymbolKind.Package, - location=multilspy_types.Location( - uri=str(pathlib.Path(abs_dir_path).as_uri()), - range={"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}, - absolutePath=str(abs_dir_path), - relativePath=str(Path(abs_dir_path).resolve().relative_to(self.repository_root_path)), - ), - children=[] - ) - result.append(package_symbol) - - for contained_dir_or_file_name in contained_dir_or_file_names: - contained_dir_or_file_abs_path = os.path.join(abs_dir_path, contained_dir_or_file_name) - contained_dir_or_file_rel_path = str(Path(contained_dir_or_file_abs_path).resolve().relative_to(self.repository_root_path)) - if self.is_ignored_path(contained_dir_or_file_rel_path): - self.logger.log(f"Skipping item: {contained_dir_or_file_rel_path}\n(because it should be ignored)", logging.DEBUG) - continue - - if os.path.isdir(contained_dir_or_file_abs_path): - child_symbols = await process_directory(contained_dir_or_file_rel_path) - package_symbol["children"].extend(child_symbols) - for child in child_symbols: - child["parent"] = package_symbol - - elif os.path.isfile(contained_dir_or_file_abs_path): - _, file_root_nodes = await self.request_document_symbols(contained_dir_or_file_rel_path, include_body=include_body) - - # Create file symbol, link with children - file_rel_path = str(Path(contained_dir_or_file_abs_path).resolve().relative_to(self.repository_root_path)) - with self.open_file(file_rel_path) as file_data: - fileRange = self._get_range_from_file_content(file_data.contents) - file_symbol = multilspy_types.UnifiedSymbolInformation( # type: ignore - name=os.path.splitext(contained_dir_or_file_name)[0], - kind=multilspy_types.SymbolKind.File, - range=fileRange, - selectionRange=fileRange, - location=multilspy_types.Location( - uri=str(pathlib.Path(contained_dir_or_file_abs_path).as_uri()), - range=fileRange, - absolutePath=str(contained_dir_or_file_abs_path), - relativePath=str(Path(contained_dir_or_file_abs_path).resolve().relative_to(self.repository_root_path)), - ), - children=file_root_nodes, - parent=package_symbol, - ) - for child in file_root_nodes: - child["parent"] = file_symbol - - # Link file symbol with package - package_symbol["children"].append(file_symbol) - - # TODO: Not sure if this is actually still needed given recent changes to relative path handling - def fix_relative_path(nodes: List[multilspy_types.UnifiedSymbolInformation]): - for node in nodes: - if "location" in node and "relativePath" in node["location"]: - path = Path(node["location"]["relativePath"]) - if path.is_absolute(): - try: - path = path.relative_to(self.repository_root_path) - node["location"]["relativePath"] = str(path) - except Exception: - pass - if "children" in node: - fix_relative_path(node["children"]) - - fix_relative_path(file_root_nodes) - - return result - - # Start from the root or the specified directory - start_rel_path = within_relative_path or "." - return await process_directory(start_rel_path) - - @staticmethod - def _get_range_from_file_content(file_content: str) -> multilspy_types.Range: - """ - Get the range for the given file. - """ - lines = file_content.split("\n") - end_line = len(lines) - end_column = len(lines[-1]) - return multilspy_types.Range( - start=multilspy_types.Position(line=0, character=0), - end=multilspy_types.Position(line=end_line, character=end_column) - ) - - async def request_dir_overview(self, relative_dir_path: str) -> dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]]: - """ - An overview of the given directory. - - Maps relative paths of all contained files to info about top-level symbols in the file - (name, kind, line, column). - """ - symbol_tree = await self.request_full_symbol_tree(relative_dir_path) - # Initialize result dictionary - result: dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]] = defaultdict(list) - - # Helper function to process a symbol and its children - def process_symbol(symbol: multilspy_types.UnifiedSymbolInformation): - if symbol["kind"] == multilspy_types.SymbolKind.File: - # For file symbols, process their children (top-level symbols) - for child in symbol["children"]: - assert "location" in child - assert "selectionRange" in child - path = Path(child["location"]["absolutePath"]).resolve().relative_to(self.repository_root_path) - result[str(path)].append(( - child["name"], - child["kind"], - child["selectionRange"]["start"]["line"], - child["selectionRange"]["start"]["character"] - )) - # For package/directory symbols, process their children - for child in symbol["children"]: - process_symbol(child) - - # Process each root symbol - for root in symbol_tree: - process_symbol(root) - return result - - async def request_document_overview(self, relative_file_path: str) -> list[tuple[str, multilspy_types.SymbolKind, int, int]]: - """ - An overview of the given file. - Returns the list of tuples (name, kind, line, column) of all top-level symbols in the file. - """ - _, document_roots = await self.request_document_symbols(relative_file_path) - result = [] - for root in document_roots: - try: - result.append( - ( root["name"], - root["kind"], - root["selectionRange"]["start"]["line"], - root["selectionRange"]["start"]["character"],) - ) - except KeyError as e: - raise KeyError( - f"Could not process symbol of name {root.get('name', 'unknown')} in {relative_file_path=}" - ) from e - return result - - async def request_overview(self, within_relative_path: str) -> dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]]: - """ - An overview of all symbols in the given file or directory. - - :param within_relative_path: the relative path to the file or directory to get the overview of. - :return: A mapping of all relative paths analyzed to lists of tuples (name, kind, line, column) of all top-level symbols in the corresponding file. - """ - abs_path = (Path(self.repository_root_path) / within_relative_path).resolve() - if not abs_path.exists(): - raise FileNotFoundError(f"File or directory not found: {abs_path}") - - if abs_path.is_file(): - symbols_overview = await self.request_document_overview(within_relative_path) - return {within_relative_path: symbols_overview} - else: - return await self.request_dir_overview(within_relative_path) - - async def request_hover(self, relative_file_path: str, line: int, column: int) -> Union[multilspy_types.Hover, None]: - """ - Raise a [textDocument/hover](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover) request to the Language Server - to find the hover information at the given line and column in the given file. Wait for the response and return the result. - - :param relative_file_path: The relative path of the file that has the hover information - :param line: The line number of the symbol - :param column: The column number of the symbol - - :return None - """ - with self.open_file(relative_file_path): - response = await self.server.send.hover( - { - "textDocument": { - "uri": pathlib.Path(os.path.join(self.repository_root_path, relative_file_path)).as_uri() - }, - "position": { - "line": line, - "character": column, - }, - } - ) - - if response is None: - return None - - assert isinstance(response, dict) - - return multilspy_types.Hover(**response) - - # ----------------------------- FROM HERE ON MODIFICATIONS BY MISCHA -------------------- - - def retrieve_symbol_body(self, symbol: multilspy_types.UnifiedSymbolInformation | LSPTypes.DocumentSymbol | LSPTypes.SymbolInformation) -> str: - """ - Load the body of the given symbol. If the body is already contained in the symbol, just return it. - """ - existing_body = symbol.get("body", None) - if existing_body: - return existing_body - - assert "location" in symbol - symbol_start_line = symbol["location"]["range"]["start"]["line"] - symbol_end_line = symbol["location"]["range"]["end"]["line"] - assert "relativePath" in symbol["location"] - symbol_file = self.retrieve_full_file_content(symbol["location"]["relativePath"]) - symbol_lines = symbol_file.split("\n") - symbol_body = "\n".join(symbol_lines[symbol_start_line:symbol_end_line+1]) - - # remove leading indentation - symbol_start_column = symbol["location"]["range"]["start"]["character"] - symbol_body = symbol_body[symbol_start_column:] - return symbol_body - - async def request_parsed_files(self) -> list[str]: - """Retrieves relative paths of all files analyzed by the Language Server.""" - if not self.server_started: - self.logger.log( - "request_parsed_files called before Language Server started", - logging.ERROR, - ) - raise MultilspyException("Language Server not started") - rel_file_paths = [] - for root, dirs, files in os.walk(self.repository_root_path): - dirs[:] = [d for d in dirs if not self.is_ignored_path(os.path.join(root, d))] - for file in files: - rel_file_path = os.path.relpath(os.path.join(root, file), start=self.repository_root_path) - if not self.is_ignored_path(rel_file_path): - rel_file_paths.append(rel_file_path) - return rel_file_paths - - async def search_files_for_pattern( - self, - pattern: re.Pattern | str, - context_lines_before: int = 0, - context_lines_after: int = 0, - paths_include_glob: str | None = None, - paths_exclude_glob: str | None = None, - ) -> list[MatchedConsecutiveLines]: - """ - Search for a pattern across all files analyzed by the Language Server. - - :param pattern: Regular expression pattern to search for, either as a compiled Pattern or string - :param context_lines_before: Number of lines of context to include before each match - :param context_lines_after: Number of lines of context to include after each match - :param paths_include_glob: Glob pattern to filter which files to include in the search - :param paths_exclude_glob: Glob pattern to filter which files to exclude from the search. Takes precedence over paths_include_glob. - :return: List of matched consecutive lines with context - """ - if isinstance(pattern, str): - pattern = re.compile(pattern) - - relative_file_paths = await self.request_parsed_files() - return search_files( - relative_file_paths, - pattern, - file_reader=self.retrieve_full_file_content, - context_lines_before=context_lines_before, - context_lines_after=context_lines_after, - paths_include_glob=paths_include_glob, - paths_exclude_glob=paths_exclude_glob - ) - - async def request_referencing_symbols( - self, - relative_file_path: str, - line: int, - column: int, - include_imports: bool = True, - include_self: bool = False, - include_body: bool = False, - include_file_symbols: bool = False, - ) -> List[ReferenceInSymbol]: - """ - Finds all symbols that reference the symbol at the given location. - This is similar to request_references but filters to only include symbols - (functions, methods, classes, etc.) that reference the target symbol. - - :param relative_file_path: The relative path to the file. - :param line: The 0-indexed line number. - :param column: The 0-indexed column number. - :param include_imports: whether to also include imports as references. - Unfortunately, the LSP does not have an import type, so the references corresponding to imports - will not be easily distinguishable from definitions. - :param include_self: whether to include the references that is the "input symbol" itself. - Only has an effect if the relative_file_path, line and column point to a symbol, for example a definition. - :param include_body: whether to include the body of the symbols in the result. - :param include_file_symbols: whether to include references that are file symbols. This - is often a fallback mechanism for when the reference cannot be resolved to a symbol. - :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, - ) - raise MultilspyException("Language Server not started") - - # First, get all references to the symbol - references = await self.request_references(relative_file_path, line, column) - if not references: - return [] - - # For each reference, find the containing symbol - result = [] - incoming_symbol = None - for ref in references: - ref_path = ref["relativePath"] - ref_line = ref["range"]["start"]["line"] - ref_col = ref["range"]["start"]["character"] - - with self.open_file(ref_path) as file_data: - # Get the containing symbol for this reference - containing_symbol = await self.request_containing_symbol( - ref_path, ref_line, ref_col, include_body=include_body - ) - if containing_symbol is None: - # TODO: HORRIBLE HACK! I don't know how to do it better for now... - # THIS IS BOUND TO BREAK IN MANY CASES! IT IS ALSO SPECIFIC TO PYTHON! - # Background: - # When a variable is used to change something, like - # - # instance = MyClass() - # instance.status = "new status" - # - # we can't find the containing symbol for the reference to `status` - # since there is no container on the line of the reference - # The hack is to try to find a variable symbol in the containing module - # by using the text of the reference to find the variable name (In a very heuristic way) - # and then look for a symbol with that name and kind Variable - ref_text = file_data.contents.split("\n")[ref_line] - if "." in ref_text: - containing_symbol_name = ref_text.split(".")[0] - all_symbols, _ = await self.request_document_symbols(ref_path) - for symbol in all_symbols: - if symbol["name"] == containing_symbol_name and symbol["kind"] == multilspy_types.SymbolKind.Variable: - containing_symbol = copy(symbol) - containing_symbol["location"] = ref - containing_symbol["range"] = ref["range"] - break - - # 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 - ) - fileRange = self._get_range_from_file_content(file_data.contents) - location = multilspy_types.Location( - uri=str(pathlib.Path(os.path.join(self.repository_root_path, ref_path)).as_uri()), - range=fileRange, - absolutePath=str(os.path.join(self.repository_root_path, ref_path)), - relativePath=ref_path, - ) - name = os.path.splitext(os.path.basename(ref_path))[0] - - if include_body: - body = self.retrieve_full_file_content(ref_path) - else: - body = "" - - containing_symbol = multilspy_types.UnifiedSymbolInformation( - kind=multilspy_types.SymbolKind.File, - range=fileRange, - selectionRange=fileRange, - location=location, - name=name, - children=[], - body=body, - ) - if containing_symbol is None or not include_file_symbols and containing_symbol["kind"] == multilspy_types.SymbolKind.File: - continue - - assert "location" in containing_symbol - assert "selectionRange" in containing_symbol - - # Checking for self-reference - if ( - containing_symbol["location"]["relativePath"] == relative_file_path - and containing_symbol["selectionRange"]["start"]["line"] == ref_line - and containing_symbol["selectionRange"]["start"]["character"] == ref_col - ): - incoming_symbol = containing_symbol - if include_self: - result.append(ReferenceInSymbol(symbol=containing_symbol, line=ref_line, character=ref_col)) - continue - else: - self.logger.log(f"Found self-reference for {incoming_symbol['name']}, skipping it since {include_self=}", logging.DEBUG) - continue - - # checking whether reference is an import - # This is neither really safe nor elegant, but if we don't do it, - # there is no way to distinguish between definitions and imports as import is not a symbol-type - # and we get the type referenced symbol resulting from imports... - if (not include_imports \ - and incoming_symbol is not None \ - and containing_symbol["name"] == incoming_symbol["name"] \ - and containing_symbol["kind"] == incoming_symbol["kind"] \ - ): - self.logger.log( - f"Found import of referenced symbol {incoming_symbol['name']}" - f"in {containing_symbol['location']['relativePath']}, skipping", - logging.DEBUG - ) - continue - - result.append(ReferenceInSymbol(symbol=containing_symbol, line=ref_line, character=ref_col)) - - return result - - async def request_containing_symbol( - self, - relative_file_path: str, - line: int, - column: Optional[int] = None, - strict: bool = False, - include_body: bool = False, - ) -> multilspy_types.UnifiedSymbolInformation | None: - """ - Finds the first symbol containing the position for the given file. - For Python, container symbols are considered to be those with kinds corresponding to - functions, methods, or classes (typically: Function (12), Method (6), Class (5)). - - The method operates as follows: - - Request the document symbols for the file. - - Filter symbols to those that start at or before the given line. - - From these, first look for symbols whose range contains the (line, column). - - If one or more symbols contain the position, return the one with the greatest starting position - (i.e. the innermost container). - - If none (strictly) contain the position, return the symbol with the greatest starting position - among those above the given line. - - If no container candidates are found, return None. - - :param relative_file_path: The relative path to the Python file. - :param line: The 0-indexed line number. - :param column: The 0-indexed column (also called character). If not passed, the lookup will be based - only on the line. - :param strict: If True, the position must be strictly within the range of the symbol. - Setting to True is useful for example for finding the parent of a symbol, as with strict=False, - and the line pointing to a symbol itself, the containing symbol will be the symbol itself - (and not the parent). - :param include_body: Whether to include the body of the symbol in the result. - :return: The container symbol (if found) or None. - """ - # checking if the line is empty, unfortunately ugly and duplicating code, but I don't want to refactor - with self.open_file(relative_file_path): - absolute_file_path = str( - PurePath(self.repository_root_path, relative_file_path) - ) - content = FileUtils.read_file(self.logger, absolute_file_path) - 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, - ) - return None - - symbols, _ = await self.request_document_symbols(relative_file_path) - - # make jedi and pyright api compatible - # the former has no location, the later has no range - # we will just always add location of the desired format to all symbols - for symbol in symbols: - if "location" not in symbol: - range = symbol["range"] - location = multilspy_types.Location( - uri=f"file:/{absolute_file_path}", - range=range, - absolutePath=absolute_file_path, - relativePath=relative_file_path, - ) - symbol["location"] = location - else: - location = symbol["location"] - assert "range" in location - location["absolutePath"] = absolute_file_path - location["relativePath"] = relative_file_path - location["uri"] = Path(absolute_file_path).as_uri() - - # Allowed container kinds, currently only for Python - container_symbol_kinds = { - multilspy_types.SymbolKind.Method, - multilspy_types.SymbolKind.Function, - multilspy_types.SymbolKind.Class - } - - def is_position_in_range(line: int, range_d: multilspy_types.Range) -> bool: - start = range_d["start"] - end = range_d["end"] - - column_condition = True - if strict: - line_condition = end["line"] >= line > start["line"] - if column is not None and line == start["line"]: - column_condition = column > start["character"] - else: - line_condition = end["line"] >= line >= start["line"] - if column is not None and line == start["line"]: - column_condition = column >= start["character"] - return line_condition and column_condition - - # Only consider containers that are not one-liners (otherwise we may get imports) - candidate_containers = [ - s for s in symbols if s["kind"] in container_symbol_kinds and s["location"]["range"]["start"]["line"] != s["location"]["range"]["end"]["line"] - ] - var_containers = [ - s for s in symbols if s["kind"] == multilspy_types.SymbolKind.Variable - ] - candidate_containers.extend(var_containers) - - if not candidate_containers: - return None - - # From the candidates, find those whose range contains the given position. - containing_symbols = [] - for symbol in candidate_containers: - s_range = symbol["location"]["range"] - if not is_position_in_range(line, s_range): - continue - containing_symbols.append(symbol) - - if containing_symbols: - # Return the one with the greatest starting position (i.e. the innermost container). - containing_symbol = max(containing_symbols, key=lambda s: s["location"]["range"]["start"]["line"]) - if include_body: - containing_symbol["body"] = self.retrieve_symbol_body(containing_symbol) - return containing_symbol - else: - return None - - async def request_container_of_symbol(self, symbol: multilspy_types.UnifiedSymbolInformation, include_body: bool = False) -> multilspy_types.UnifiedSymbolInformation | None: - """ - Finds the container of the given symbol if there is one. If the parent attribute is present, the parent is returned - without further searching. - - :param symbol: The symbol to find the container of. - :param include_body: whether to include the body of the symbol in the result. - :return: The container of the given symbol or None if no container is found. - """ - if "parent" in symbol: - return symbol["parent"] - assert "location" in symbol, f"Symbol {symbol} has no location and no parent attribute" - return await self.request_containing_symbol( - symbol["location"]["relativePath"], - symbol["location"]["range"]["start"]["line"], - symbol["location"]["range"]["start"]["character"], - strict=True, - include_body=include_body, - ) - - async def request_defining_symbol( - self, - relative_file_path: str, - line: int, - column: int, - include_body: bool = False, - ) -> Optional[multilspy_types.UnifiedSymbolInformation]: - """ - Finds the symbol that defines the symbol at the given location. - - This method first finds the definition of the symbol at the given position, - then retrieves the full symbol information for that definition. - - :param relative_file_path: The relative path to the file. - :param line: The 0-indexed line number. - :param column: The 0-indexed column number. - :param include_body: whether to include the body of the symbol in the result. - :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, - ) - raise MultilspyException("Language Server not started") - - # Get the definition location(s) - definitions = await self.request_definition(relative_file_path, line, column) - if not definitions: - return None - - # Use the first definition location - definition = definitions[0] - def_path = definition["relativePath"] - def_line = definition["range"]["start"]["line"] - def_col = definition["range"]["start"]["character"] - - # Find the symbol at or containing this location - defining_symbol = await self.request_containing_symbol( - def_path, def_line, def_col, strict=False, include_body=include_body - ) - - return defining_symbol - - @property - def cache_path(self) -> Path: - """ - The path to the cache file for the document symbols. - """ - return Path(self.repository_root_path) / ".serena" / "cache" / self.language_id / "document_symbols_cache_v20-05-25.pkl" - - async def index_repository(self, progress_bar: bool = True, save_after_n_files: int = 10) -> None: - """Will go through the entire repository and "index" all files, meaning save their symbols to the cache. - - :param progress_bar: Whether to show a progress bar while indexing the repository. - :param save_after_n_files: How many files to process before saving a checkpoint of the cache. - """ - parsed_files = await self.request_parsed_files() - files_processed = 0 - pbar = tqdm.tqdm(parsed_files, disable=not progress_bar) - for relative_file_path in pbar: - pbar.set_description(f"Indexing ({os.path.basename(relative_file_path)})") - await self.request_document_symbols(relative_file_path, include_body=False) - await self.request_document_symbols(relative_file_path, include_body=True) - files_processed += 1 - if files_processed % save_after_n_files == 0: - self.save_cache() - self.save_cache() - - def save_cache(self): - with self._cache_lock: - if not self._cache_has_changed: - self.logger.log("No changes to document symbols cache, skipping save", logging.DEBUG) - return - - self.logger.log(f"Saving updated document symbols cache to {self.cache_path}", logging.INFO) - self.cache_path.parent.mkdir(parents=True, exist_ok=True) - try: - with open(self.cache_path, "wb") as f: - pickle.dump(self._document_symbols_cache, f) - self._cache_has_changed = False - except Exception as e: - self.logger.log( - f"Failed to save document symbols cache to {self.cache_path}: {e}. " - "Note: this may have resulted in a corrupted cache file.", - logging.ERROR, - ) - - def load_cache(self): - if not self.cache_path.exists(): - return - - with self._cache_lock: - self.logger.log(f"Loading document symbols cache from {self.cache_path}", logging.INFO) - try: - with open(self.cache_path, "rb") as f: - self._document_symbols_cache = pickle.load(f) - self.logger.log(f"Loaded {len(self._document_symbols_cache)} document symbols from cache.", logging.INFO) - except Exception as e: - # cache often becomes corrupt, so just skip loading it - self.logger.log( - f"Failed to load document symbols cache from {self.cache_path}: {e}. Possible cause: the cache file is corrupted. " - "Check for any errors related to saving the cache in the logs.", - logging.ERROR, - ) - - - async def request_workspace_symbol(self, query: str) -> Union[List[multilspy_types.UnifiedSymbolInformation], None]: - """ - Raise a [workspace/symbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol) request to the Language Server - to find symbols across the whole workspace. Wait for the response and return the result. - - :param query: The query string to filter symbols by - - :return: A list of matching symbols - """ - response = await self.server.send.workspace_symbol({"query": query}) - if response is None: - return None - - assert isinstance(response, list) - - ret: List[multilspy_types.UnifiedSymbolInformation] = [] - for item in response: - assert isinstance(item, dict) - - assert LSPConstants.NAME in item - assert LSPConstants.KIND in item - assert LSPConstants.LOCATION in item - - ret.append(multilspy_types.UnifiedSymbolInformation(**item)) - - return ret - -@ensure_all_methods_implemented(LanguageServer) -class SyncLanguageServer: - """ - The SyncLanguageServer class provides a language agnostic interface to the Language Server Protocol. - It is used to communicate with Language Servers of different programming languages. - """ - - def __init__(self, language_server: LanguageServer, timeout: Optional[float] = None): - """ - :param language_server: the async language server being wrapped - :param timeout: the timeout, in seconds, to use for requests to the language server. - """ - self.language_server = language_server - self.loop = None - self.loop_thread = None - self.timeout = timeout - - self._server_context = None - - self._shutdown_lock = threading.Lock() - self._is_shutting_down = False - - @property - def cache_path(self) -> Path: - """ - The path to the cache file for the document symbols. - """ - return self.language_server.cache_path - - @classmethod - def create( - cls, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str, - timeout: Optional[float] = None - ) -> "SyncLanguageServer": - """ - Creates a language specific LanguageServer instance based on the given configuration, and appropriate settings for the programming language. - - If language is Java, then ensure that jdk-17.0.6 or higher is installed, `java` is in PATH, and JAVA_HOME is set to the installation directory. - - :param repository_root_path: The root path of the repository (must be absolute). - :param config: The Multilspy configuration. - :param logger: The logger to use. - :param timeout: the timeout, in seconds, to use for requests; if None, use no timeout - - :return SyncLanguageServer: A language specific LanguageServer instance. - """ - return SyncLanguageServer(LanguageServer.create(config, logger, repository_root_path), timeout=timeout) - - @property - def repository_root_path(self) -> str: - return self.language_server.repository_root_path - - @contextmanager - def open_file(self, relative_file_path: str) -> Iterator[LSPFileBuffer]: - """ - Open a file in the Language Server. This is required before making any requests to the Language Server. - - :param relative_file_path: The relative path of the file to open. - """ - with self.language_server.open_file(relative_file_path) as file_buffer: - yield file_buffer - - def insert_text_at_position( - self, relative_file_path: str, line: int, column: int, text_to_be_inserted: str - ) -> multilspy_types.Position: - """ - Insert text at the given line and column in the given file and return - the updated cursor position after inserting the text. - - :param relative_file_path: The relative path of the file to open. - :param line: The line number at which text should be inserted. - :param column: The column number at which text should be inserted. - :param text_to_be_inserted: The text to insert. - """ - return self.language_server.insert_text_at_position(relative_file_path, line, column, text_to_be_inserted) - - def delete_text_between_positions( - self, - relative_file_path: str, - start: multilspy_types.Position, - end: multilspy_types.Position, - ) -> str: - """ - Delete text between the given start and end positions in the given file and return the deleted text. - """ - return self.language_server.delete_text_between_positions(relative_file_path, start, end) - - @contextmanager - def start_server(self) -> Iterator["SyncLanguageServer"]: - """ - Starts the language server process and connects to it. - - :return: None - """ - self.start() - yield self - self.language_server.logger.log("SyncLS Startup: exiting LS context", logging.DEBUG) - self.stop() - - def request_definition(self, file_path: str, line: int, column: int) -> List[multilspy_types.Location]: - """ - Raise a [textDocument/definition](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition) request to the Language Server - for the symbol at the given line and column in the given file. Wait for the response and return the result. - - :param file_path: The path of the file relative to the repository root. - :param line: The line number (zero-indexed). - :param column: The column number (zero-indexed). - - :return: A list of Locations defining the symbol. - """ - if not self.is_running(): - raise RuntimeError("Language server is not running. Cannot make requests to a stopped language server.") - - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_definition(file_path, line, column), self.loop - ).result(timeout=self.timeout) - return result - - def request_references(self, file_path: str, line: int, column: int) -> List[multilspy_types.Location]: - """ - Raises a [textDocument/references](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references) request to the Language Server - for the symbol at the given line and column in the given file. Wait for the response and return the result. - - :param file_path: The path of the file relative to the repository root. - :param line: The line number (zero-indexed). - :param column: The column number (zero-indexed). - - :return: A list of Locations referencing the symbol. - """ - if not self.is_running(): - raise RuntimeError("Language server is not running. Cannot make requests to a stopped language server.") - - try: - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_references(file_path, line, column), self.loop - ).result(timeout=self.timeout) - except Exception as e: - from solidlsp.lsp_protocol_handler.server import Error - if isinstance(e, Error) and getattr(e, 'code', None) == -32603: - raise RuntimeError( - f"LSP internal error (-32603) when requesting references for {file_path}:{line}:{column}. " - "This often occurs when requesting references for a symbol not referenced in the expected way. " - ) from e - raise - return result - - - def request_references_with_content( - self, relative_file_path: str, line: int, column: int, context_lines_before: int = 0, context_lines_after: int = 0 - ) -> List[MatchedConsecutiveLines]: - """ - Like request_references, but returns the content of the lines containing the references, not just the locations. - - :param relative_file_path: The relative path of the file that has the symbol for which references should be looked up - :param line: The line number of the symbol - :param column: The column number of the symbol - :param context_lines_before: The number of lines to include in the context before the line containing the reference - :param context_lines_after: The number of lines to include in the context after the line containing the reference - - :return: A list of MatchedConsecutiveLines objects, one for each reference. - """ - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_references_with_content(relative_file_path, line, column, context_lines_before, context_lines_after), self.loop - ).result() - return result - - def request_completions( - self, relative_file_path: str, line: int, column: int, allow_incomplete: bool = False - ) -> List[multilspy_types.CompletionItem]: - """ - Raise a [textDocument/completion](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion) request to the Language Server - to find completions at the given line and column in the given file. Wait for the response and return the result. - - :param relative_file_path: The relative path of the file that has the symbol for which completions should be looked up - :param line: The line number of the symbol - :param column: The column number of the symbol - - :return List[multilspy_types.CompletionItem]: A list of completions - """ - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_completions(relative_file_path, line, column, allow_incomplete), - self.loop, - ).result(timeout=self.timeout) - return result - - def request_document_symbols(self, relative_file_path: str, include_body: bool = False) -> Tuple[List[ - multilspy_types.UnifiedSymbolInformation], List[multilspy_types.UnifiedSymbolInformation]]: - """ - Raise a [textDocument/documentSymbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol) request to the Language Server - to find symbols in the given file. Wait for the response and return the result. - - :param relative_file_path: The relative path of the file that has the symbols - :param include_body: whether to include the body of the symbols in the result. - :return: A list of symbols in the file, and a list of root symbols that represent the tree structure of the symbols. Each symbol in hierarchy starting from the roots has a children attribute. - """ - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_document_symbols(relative_file_path, include_body), self.loop - ).result() - return result - - def request_full_symbol_tree(self, within_relative_path: str | None = None, include_body: bool = False) -> List[ - multilspy_types.UnifiedSymbolInformation]: - """ - Will go through all files in the project and build a tree of symbols. Note: this may be slow the first time it is called. - - For each file, a symbol of kind Module (3) will be created. For directories, a symbol of kind Package (4) will be created. - All symbols will have a children attribute, thereby representing the tree structure of all symbols in the project - that are within the repository. - Will ignore directories starting with '.', language-specific defaults - and user-configured directories (e.g. from .gitignore). - - :param within_relative_path: pass a relative path to only consider symbols within this path. - If a file is passed, only the symbols within this file will be considered. - If a directory is passed, all files within this directory will be considered. - If None, the entire codebase will be considered. - :param include_body: whether to include the body of the symbols in the result. - - :return: A list of root symbols representing the top-level packages/modules in the project. - """ - if not self.is_running(): - raise RuntimeError("Language server is not running. Cannot make requests to a stopped language server.") - - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_full_symbol_tree(within_relative_path, include_body), self.loop - ).result(timeout=self.timeout) - return result - - def request_dir_overview(self, relative_dir_path: str) -> dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]]: - """ - An overview of the given directory. - - Maps relative paths of all contained files to info about top-level symbols in the file - (name, kind, line, column). - """ - if not self.is_running(): - raise RuntimeError("Language server is not running. Cannot make requests to a stopped language server.") - - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_dir_overview(relative_dir_path), self.loop - ).result(timeout=self.timeout) - return result - - def request_document_overview(self, relative_file_path: str) -> list[tuple[str, multilspy_types.SymbolKind, int, int]]: - """ - An overview of the given file. - - Returns the list of tuples (name, kind, line, column) of all top-level symbols in the file. - """ - assert self.loop - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_document_overview(relative_file_path), self.loop - ).result(timeout=self.timeout) - return result - - def request_overview(self, within_relative_path: str) -> dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]]: - """ - An overview of all symbols in the given file or directory. - - :param within_relative_path: the relative path to the file or directory to get the overview of. - :return: A mapping of all relative paths analyzed to lists of tuples (name, kind, line, column) of all top-level symbols in the corresponding file. - """ - assert self.loop - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_overview(within_relative_path), self.loop - ).result() - return result - - def request_hover(self, relative_file_path: str, line: int, column: int) -> Union[multilspy_types.Hover, None]: - """ - Raise a [textDocument/hover](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover) request to the Language Server - to find the hover information at the given line and column in the given file. Wait for the response and return the result. - - :param relative_file_path: The relative path of the file that has the hover information - :param line: The line number of the symbol - :param column: The column number of the symbol - - :return None - """ - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_hover(relative_file_path, line, column), self.loop - ).result(timeout=self.timeout) - return result - - def request_workspace_symbol(self, query: str) -> Union[List[multilspy_types.UnifiedSymbolInformation], None]: - """ - Raise a [workspace/symbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol) request to the Language Server - to find symbols across the whole workspace. Wait for the response and return the result. - - :param query: The query string to filter symbols by - - :return Union[List[multilspy_types.UnifiedSymbolInformation], None]: A list of matching symbols - """ - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_workspace_symbol(query), self.loop - ).result(timeout=self.timeout) - return result - - # ----------------------------- FROM HERE ON MODIFICATIONS BY MISCHA -------------------- - - def retrieve_symbol_body(self, symbol: multilspy_types.UnifiedSymbolInformation) -> str: - """ - Load the body of the given symbol. If the body is already contained in the symbol, just return it. - - :param symbol: The symbol to retrieve the body of. - :return: The body of the symbol. - """ - return self.language_server.retrieve_symbol_body(symbol) - - def request_parsed_files(self) -> list[str]: - """Retrieves relative paths of all files analyzed by the Language Server.""" - assert self.loop - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_parsed_files(), self.loop - ).result() - return result - - def request_referencing_symbols( - self, relative_file_path: str, line: int, column: int, - include_imports: bool = True, include_self: bool = False, - include_body: bool = False, - include_file_symbols: bool = False, - ) -> List[ReferenceInSymbol]: - """ - Finds all symbols that reference the symbol at the given location. - This is similar to request_references but filters to only include symbols - (functions, methods, classes, etc.) that reference the target symbol. - - :param relative_file_path: The relative path to the file. - :param line: The 0-indexed line number. - :param column: The 0-indexed column number. - :param include_imports: whether to also include imports as references. - Unfortunately, the LSP does not have an import type, so the references corresponding to imports - will not be easily distinguishable from definitions. - :param include_self: whether to include the references that is the "input symbol" itself. - Only has an effect if the relative_file_path, line and column point to a symbol, for example a definition. - :param include_body: whether to include the body of the symbols in the result. - :param include_file_symbols: whether to include references that are file symbols. This - is often a fallback mechanism for when the reference cannot be resolved to a symbol. - :return: List of objects containing the symbol and the location of the reference. - """ - assert self.loop - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_referencing_symbols( - relative_file_path, - line, - column, - include_imports=include_imports, - include_self=include_self, - include_body=include_body, - include_file_symbols=include_file_symbols, - ), - self.loop - ).result(timeout=self.timeout) - return result - - def request_containing_symbol( - self, relative_file_path: str, line: int, - column: Optional[int] = None, strict: bool = False, - include_body: bool = False, - ) -> multilspy_types.UnifiedSymbolInformation | None: - """ - Finds the first symbol containing the position for the given file. - For Python, container symbols are considered to be those with kinds corresponding to - functions, methods, or classes (typically: Function (12), Method (6), Class (5)). - - The method operates as follows: - - Request the document symbols for the file. - - Filter symbols to those that start at or before the given line. - - From these, first look for symbols whose range contains the (line, column). - - If one or more symbols contain the position, return the one with the greatest starting position - (i.e. the innermost container). - - If none (strictly) contain the position, return the symbol with the greatest starting position - among those above the given line. - - If no container candidates are found, return None. - - :param relative_file_path: The relative path to the Python file. - :param line: The 0-indexed line number. - :param column: The 0-indexed column (also called character). If not passed, the lookup will be based - only on the line. - :param strict: If True, the position must be strictly within the range of the symbol. - Setting to true is useful for example for finding the parent of a symbol, as with strict=False, - and the line pointing to a symbol itself, the containing symbol will be the symbol itself - (and not the parent). - :param include_body: whether to include the body of the symbol in the result. - :return: The container symbol (if found) or None. - """ - assert self.loop - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_containing_symbol(relative_file_path, line, column=column, strict=strict, include_body=include_body), self.loop - ).result(timeout=self.timeout) - return result - - def request_container_of_symbol(self, symbol: multilspy_types.UnifiedSymbolInformation, include_body: bool = False) -> multilspy_types.UnifiedSymbolInformation | None: - """ - Finds the container of the given symbol if there is one. - - :param symbol: The symbol to find the container of. - :param include_body: whether to include the body of the symbol in the result. - """ - assert self.loop - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_container_of_symbol(symbol, include_body=include_body), self.loop - ).result(timeout=self.timeout) - return result - - def request_defining_symbol( - self, relative_file_path: str, line: int, column: int, - include_body: bool = False, - ) -> Optional[multilspy_types.UnifiedSymbolInformation]: - """ - Finds the symbol that defines the symbol at the given location. - - This method first finds the definition of the symbol at the given position, - then retrieves the full symbol information for that definition. - - :param relative_file_path: The relative path to the file. - :param line: The 0-indexed line number. - :param column: The 0-indexed column number. - :param include_body: whether to include the body of the symbol in the result. - :return: The symbol information for the definition, or None if not found. - """ - assert self.loop - result = asyncio.run_coroutine_threadsafe( - self.language_server.request_defining_symbol(relative_file_path, line, column, include_body=include_body), self.loop - ).result(timeout=self.timeout) - return result - - def retrieve_full_file_content(self, relative_file_path: str) -> str: - """ - Retrieve the full content of the given file. - """ - return self.language_server.retrieve_full_file_content(relative_file_path) - - def retrieve_content_around_line(self, relative_file_path: str, line: int, context_lines_before: int = 0, context_lines_after: int = 0) -> MatchedConsecutiveLines: - """ - Retrieve the content of the given file around the given line. - - :param relative_file_path: The relative path of the file to retrieve the content from - :param line: The line number to retrieve the content around - :param context_lines_before: The number of lines to retrieve before the given line - :param context_lines_after: The number of lines to retrieve after the given line - :return MatchedConsecutiveLines: A container with the desired lines. - """ - return self.language_server.retrieve_content_around_line(relative_file_path, line, context_lines_before, context_lines_after) - - def search_files_for_pattern( - self, - pattern: re.Pattern | str, - context_lines_before: int = 0, - context_lines_after: int = 0, - paths_include_glob: str | None = None, - paths_exclude_glob: str | None = None, - ) -> list[MatchedConsecutiveLines]: - """ - Search for a pattern across all files analyzed by the Language Server. - - :param pattern: Regular expression pattern to search for, either as a compiled Pattern or string - :param context_lines_before: Number of lines of context to include before each match - :param context_lines_after: Number of lines of context to include after each match - :param paths_include_glob: Glob pattern to filter which files to include in the search - :param paths_exclude_glob: Glob pattern to filter which files to exclude from the search. Takes precedence over paths_include_glob. - :return: List of matched consecutive lines with context - """ - assert self.loop - result = asyncio.run_coroutine_threadsafe( - self.language_server.search_files_for_pattern(pattern, context_lines_before, context_lines_after, paths_include_glob, paths_exclude_glob), self.loop - ).result(timeout=self.timeout) - return result - - def index_repository(self, progress_bar: bool = True, timeout: float | None = None, save_after_n_files: int = 10) -> None: - """ - Will go through the entire repository and "index" all files, meaning save their symbols to the cache. - - :param progress_bar: Whether to show a progress bar while indexing the repository. - :param save_after_n_files: How many files to process before saving a checkpoint of the cache. - :param timeout: since this operation can take a long time, the default timeout (the attribute of the LS instance) is not used. - If you want to provide a timeout for indexing, you can pass it here explicitly. - :return: - """ - assert self.loop - asyncio.run_coroutine_threadsafe( - self.language_server.index_repository(progress_bar=progress_bar, save_after_n_files=save_after_n_files), self.loop - ).result(timeout=timeout) - - def start(self) -> "SyncLanguageServer": - """ - Starts the language server process and connects to it. Call shutdown when ready. - - :return: self for method chaining - """ - self.language_server.logger.log(f"Starting language server with language {self.language_server.language} for {self.language_server.repository_root_path}", logging.INFO) - self.language_server.logger.log("Creating new event loop", logging.DEBUG) - self.loop = asyncio.new_event_loop() - self.language_server.logger.log("Creating new thread for event loop", logging.DEBUG) - self.loop_thread = threading.Thread(target=self.loop.run_forever, daemon=True) - self.loop_thread.start() - self.language_server.logger.log("Starting server (async) context", logging.DEBUG) - self._server_context = self.language_server.start_server() - self.language_server.logger.log("Entering server context", logging.DEBUG) - asyncio.run_coroutine_threadsafe(self._server_context.__aenter__(), loop=self.loop).result(timeout=self.timeout) - return self - - def is_running(self) -> bool: - """ - Check if the language server is running. - """ - return self.loop is not None and self.loop_thread is not None and self.loop_thread.is_alive() - - async def _shutdown_and_stop_loop(self): - """A coroutine that performs the full shutdown and then stops the event loop.""" - try: - if self._server_context: - await self._server_context.__aexit__(None, None, None) - except Exception as e: - self.language_server.logger.log(f"Exception during async shutdown: {e}", logging.ERROR) - finally: - # Clean up tasks but DON'T stop the loop from here - # Let the main thread handle loop stopping to avoid coroutine deadlocks - if self.loop and self.loop.is_running(): - current_task = asyncio.current_task(self.loop) - pending_tasks = [task for task in asyncio.all_tasks(self.loop) - if not task.done() and task is not current_task] - - if pending_tasks: - self.language_server.logger.log(f"Cancelling {len(pending_tasks)} pending tasks", logging.DEBUG) - for task in pending_tasks: - try: - task.cancel() - except Exception: - pass - - self.language_server.logger.log("Async shutdown tasks completed, returning to main thread", logging.DEBUG) - # Note: NOT calling loop.stop() here - let main thread do it - - def stop(self, shutdown_timeout: float = 2.0) -> None: - """ - Stops the language server and robustly cleans up all associated resources, - including the asyncio event loop, to prevent hangs on process exit. - """ - # 1. Use a lock and flag to make the shutdown call thread-safe and idempotent, - # ensuring the shutdown logic runs only once. - with self._shutdown_lock: - if self._is_shutting_down: - self.language_server.logger.log("Already shutting down or stopped, skipping", logging.DEBUG) - return - self._is_shutting_down = True - - if not self.is_running(): - self.language_server.logger.log("Language server is not running, skipping", logging.DEBUG) - return - - self.language_server.logger.log("Initiating server shutdown...", logging.INFO) - self.save_cache() - - # Detect platform to choose shutdown strategy - is_windows = os.name == "nt" - - if not is_windows: - # 2. Graceful shutdown for Linux/Unix - try proper async cleanup first - self.language_server.logger.log("Starting graceful shutdown", logging.INFO) - shutdown_future = None - if self.loop and self.loop.is_running(): - shutdown_future = asyncio.run_coroutine_threadsafe(self._shutdown_and_stop_loop(), self.loop) - - # 3. Wait for the background thread to exit - if self.loop_thread: - self.language_server.logger.log("Waiting for event loop thread to exit", logging.INFO) - self.loop_thread.join(timeout=shutdown_timeout) - if self.loop_thread.is_alive(): - self.language_server.logger.log("Event loop thread did not terminate within timeout", logging.WARNING) - - # 4. Wait for the shutdown coroutine to complete if it was scheduled - if shutdown_future: - self.language_server.logger.log("Waiting for async shutdown to complete", logging.INFO) - try: - shutdown_future.result(timeout=shutdown_timeout) - self.language_server.logger.log("Graceful async shutdown completed", logging.DEBUG) - except Exception as e: - self.language_server.logger.log(f"Async shutdown failed: {e}", logging.WARNING) - - # 5. Close the loop properly after graceful shutdown - if self.loop and not self.loop.is_closed(): - self.language_server.logger.log("Closing event loop", logging.INFO) - try: - self.loop.close() - self.language_server.logger.log("Event loop closed successfully", logging.DEBUG) - except Exception as e: - self.language_server.logger.log(f"Event loop close failed: {e}", logging.WARNING) - - else: - # WINDOWS NUCLEAR OPTION: Skip graceful shutdown - go straight to nuclear - # Windows IocpProactor has too many issues with proper cleanup - self.language_server.logger.log("Windows detected - using nuclear shutdown to prevent zombies", logging.WARNING) - - # Force stop the loop immediately without waiting for async cleanup - if self.loop and self.loop.is_running(): - try: - self.loop.call_soon_threadsafe(lambda: setattr(self.loop, '_stopping', True)) - self.loop.call_soon_threadsafe(self.loop.stop) - except Exception: - pass - - # Force mark loop as closed IMMEDIATELY to prevent zombies - try: - if hasattr(self.loop, '_closed'): - self.loop._closed = True - if hasattr(self.loop, '_stopping'): - self.loop._stopping = True - if hasattr(self.loop, '_ready'): - self.loop._ready.clear() - except Exception: - pass - - # Give thread minimal time to exit, then abandon it - if self.loop_thread: - self.loop_thread.join(timeout=1.0) # Only 1 second on Windows - if self.loop_thread.is_alive(): - self.language_server.logger.log("Thread stuck - abandoning to prevent GC deadlock", logging.WARNING) - - # 6. Null out everything immediately - same for both platforms - self.loop = None - self.loop_thread = None - self._server_context = None - self._is_shutting_down = False - - shutdown_type = "Nuclear" if is_windows else "Graceful" - self.language_server.logger.log(f"{shutdown_type} shutdown complete - all references cleared", logging.INFO) - - def save_cache(self): - """ - Save the cache to a file. - """ - self.language_server.save_cache() - - def load_cache(self): - """ - Load the cache from a file. - """ - self.language_server.load_cache() - - def is_ignored_dirname(self, dirname: str) -> bool: - """ - A language-specific condition for directories that should be ignored always. For example, venv - in Python and node_modules in JS/TS should be ignored always. - """ - return self.language_server.is_ignored_dirname(dirname) - - def is_ignored_path(self, relative_path: str, ignore_unsupported_files: bool = True) -> bool: - """ - Whether the given path should be ignored. - """ - return self.language_server.is_ignored_path(relative_path, ignore_unsupported_files=ignore_unsupported_files) - - def get_ignore_spec(self) -> pathspec.PathSpec: - """Returns the pathspec matcher for the paths that were configured to be ignored through - the multilspy config. - - This is is a subset of the full language-specific ignore spec that determines - which files are relevant for the language server. - - This matcher is useful for operations outside of the language server, - such as when searching for relevant non-language files in the project. - """ - return self.language_server.get_ignore_spec() diff --git a/src/multilspy/language_servers/clangd_language_server/clangd_language_server.py b/src/multilspy/language_servers/clangd_language_server/clangd_language_server.py deleted file mode 100644 index 6960b9f5..00000000 --- a/src/multilspy/language_servers/clangd_language_server/clangd_language_server.py +++ /dev/null @@ -1,190 +0,0 @@ -""" -Provides C/C++ specific instantiation of the LanguageServer class. Contains various configurations and settings specific to C/C++. -""" - -import asyncio -import json -import logging -import os -import stat -import pathlib -from contextlib import asynccontextmanager -from typing import AsyncIterator - -from solidlsp.multilspy_logger import MultilspyLogger -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_utils import FileUtils -from solidlsp.multilspy_utils import PlatformUtils - - -class ClangdLanguageServer(LanguageServer): - """ - Provides C/C++ specific instantiation of the LanguageServer class. Contains various configurations and settings specific to C/C++. - As the project gets bigger in size, building index will take time. Try running clangd multiple times to ensure index is built properly. - Also make sure compile_commands.json is created at root of the source directory. Check clangd test case for example. - """ - - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): - """ - 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) - super().__init__( - config, - logger, - repository_root_path, - ProcessLaunchInfo(cmd=clangd_executable_path, cwd=repository_root_path), - "cpp", - ) - self.server_ready = asyncio.Event() - - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str: - """ - Setup runtime dependencies for ClangdLanguageServer. - """ - platform_id = PlatformUtils.get_platform_id() - - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r") as f: - d = json.load(f) - del d["_description"] - - assert platform_id.value in [ - "linux-x64", - "win-x64", - "osx-arm64", - ], "Unsupported platform: " + platform_id.value - - runtime_dependencies = d["runtimeDependencies"] - runtime_dependencies = [ - dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value - ] - assert len(runtime_dependencies) == 1 - # Select dependency matching the current platform - dependency = next((dep for dep in runtime_dependencies if dep["platformId"] == platform_id.value), None) - if dependency is None: - raise RuntimeError(f"No runtime dependency found for platform {platform_id.value}") - - clangd_ls_dir = os.path.join(os.path.dirname(__file__), "static", "clangd") - clangd_executable_path = os.path.join(clangd_ls_dir, "clangd_19.1.2", "bin", dependency["binaryName"]) - if not os.path.exists(clangd_executable_path): - clangd_url = dependency["url"] - logger.log(f"Clangd executable not found at {clangd_executable_path}. Downloading from {clangd_url}", logging.INFO) - os.makedirs(clangd_ls_dir, exist_ok=True) - if dependency["archiveType"] == "zip": - FileUtils.download_and_extract_archive( - logger, clangd_url, clangd_ls_dir, dependency["archiveType"] - ) - else: - raise RuntimeError(f"Unsupported archive type: {dependency['archiveType']}") - if not os.path.exists(clangd_executable_path): - raise FileNotFoundError( - f"Clangd executable not found at {clangd_executable_path}.\n" - "Make sure you have installed clangd. See https://clangd.llvm.org/installation" - ) - os.chmod(clangd_executable_path, stat.S_IEXEC) - - return clangd_executable_path - - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the clangd Language Server. - """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r") as f: - d = json.load(f) - - del d["_description"] - - d["processId"] = os.getpid() - assert d["rootPath"] == "$rootPath" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "$rootUri" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["uri"] == "$uri" - d["workspaceFolders"][0]["uri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["name"] == "$name" - d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) - - return d - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["ClangdLanguageServer"]: - """ - Starts the Clangd Language Server, waits for the server to be ready and yields the LanguageServer instance. - - Usage: - ``` - async with lsp.start_server(): - # LanguageServer has been initialized and ready to serve requests - await lsp.request_definition(...) - await lsp.request_references(...) - # Shutdown the LanguageServer on exit from scope - # LanguageServer has been shutdown - """ - async def register_capability_handler(params): - assert "registrations" in params - for registration in params["registrations"]: - if registration["method"] == "workspace/executeCommand": - self.initialize_searcher_command_available.set() - self.resolve_main_method_available.set() - return - - async def lang_status_handler(params): - # TODO: Should we wait for - # server -> client: {'jsonrpc': '2.0', 'method': 'language/status', 'params': {'type': 'ProjectStatus', 'message': 'OK'}} - # Before proceeding? - if params["type"] == "ServiceReady" and params["message"] == "ServiceReady": - self.service_ready_event.set() - - async def execute_client_command_handler(params): - return [] - - async def do_nothing(params): - return - - async def check_experimental_status(params): - if params["quiescent"] == True: - self.server_ready.set() - - async def window_log_message(msg): - self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - self.server.on_request("client/registerCapability", register_capability_handler) - self.server.on_notification("language/status", lang_status_handler) - self.server.on_notification("window/logMessage", window_log_message) - self.server.on_request("workspace/executeClientCommand", execute_client_command_handler) - self.server.on_notification("$/progress", do_nothing) - self.server.on_notification("textDocument/publishDiagnostics", do_nothing) - self.server.on_notification("language/actionableNotification", do_nothing) - self.server.on_notification("experimental/serverStatus", check_experimental_status) - - async with super().start_server(): - self.logger.log("Starting Clangd server process", logging.INFO) - await 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, - ) - init_response = await self.server.send.initialize(initialize_params) - assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 - assert "completionProvider" in init_response["capabilities"] - assert init_response["capabilities"]["completionProvider"] == { - "triggerCharacters": ['.', '<', '>', ':', '"', '/', '*'], - "resolveProvider": False, - } - - self.server.notify.initialized({}) - - self.completions_available.set() - # set ready flag - self.server_ready.set() - await self.server_ready.wait() - - yield self diff --git a/src/multilspy/language_servers/clangd_language_server/initialize_params.json b/src/multilspy/language_servers/clangd_language_server/initialize_params.json deleted file mode 100644 index 4330560e..00000000 --- a/src/multilspy/language_servers/clangd_language_server/initialize_params.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "_description": "The parameters sent by the client when initializing the language server with the \"initialize\" request. More details at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize", - "processId": "os.getpid()", - "locale": "en", - "rootPath": "$rootPath", - "rootUri": "$rootUri", - "capabilities": { - "textDocument": { - "synchronization": { - "didSave": true, - "dynamicRegistration": true - }, - "completion": { - "dynamicRegistration": true, - "completionItem": { - "snippetSupport": true - } - }, - "definition": { - "dynamicRegistration": true - } - }, - "workspace": { - "workspaceFolders": true, - "didChangeConfiguration": { - "dynamicRegistration": true - } - } - }, - "workspaceFolders": [ - { - "uri": "$uri", - "name": "$name" - } - ] -} \ No newline at end of file diff --git a/src/multilspy/language_servers/clangd_language_server/runtime_dependencies.json b/src/multilspy/language_servers/clangd_language_server/runtime_dependencies.json deleted file mode 100644 index 53f5b7f3..00000000 --- a/src/multilspy/language_servers/clangd_language_server/runtime_dependencies.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "_description": "Used to download the runtime dependencies for running Clangd.", - "runtimeDependencies": [ - { - "id": "Clangd", - "description": "Clangd for Linux (x64)", - "url": "https://github.com/clangd/clangd/releases/download/19.1.2/clangd-linux-19.1.2.zip", - "platformId": "linux-x64", - "archiveType": "zip", - "binaryName": "clangd" - }, - { - "id": "Clangd", - "description": "Clangd for Windows (x64)", - "url": "https://github.com/clangd/clangd/releases/download/19.1.2/clangd-windows-19.1.2.zip", - "platformId": "windows-x64", - "archiveType": "zip", - "binaryName": "clangd.exe" - }, - { - "id": "Clangd", - "description": "Clangd for macOS (Arm64)", - "url": "https://github.com/clangd/clangd/releases/download/19.1.2/clangd-mac-19.1.2.zip", - "platformId": "osx-arm64", - "archiveType": "zip", - "binaryName": "clangd" - } - ] -} diff --git a/src/multilspy/language_servers/dart_language_server/dart_language_server.py b/src/multilspy/language_servers/dart_language_server/dart_language_server.py deleted file mode 100644 index 8f9aaab0..00000000 --- a/src/multilspy/language_servers/dart_language_server/dart_language_server.py +++ /dev/null @@ -1,142 +0,0 @@ -from contextlib import asynccontextmanager -import logging -import os -import pathlib -import stat -from typing import AsyncIterator -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -import json -from solidlsp.multilspy_utils import FileUtils, PlatformUtils - - -class DartLanguageServer(LanguageServer): - """ - Provides Dart specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Dart. - """ - - def __init__(self, config, logger, repository_root_path): - """ - Creates a DartServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. - """ - - executable_path = self.setup_runtime_dependencies(logger) - super().__init__( - config, - logger, - repository_root_path, - ProcessLaunchInfo(cmd=executable_path, cwd=repository_root_path), - "dart", - ) - - def setup_runtime_dependencies(self, logger: "MultilspyLogger") -> str: - platform_id = PlatformUtils.get_platform_id() - - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r") as f: - d = json.load(f) - del d["_description"] - - runtime_dependencies = d["runtimeDependencies"] - runtime_dependencies = [ - dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value - ] - - assert len(runtime_dependencies) == 1 - dependency = runtime_dependencies[0] - - dart_ls_dir = os.path.join(os.path.dirname(__file__), "static", "dart-language-server") - dart_executable_path = os.path.join(dart_ls_dir, dependency["binaryName"]) - - if not os.path.exists(dart_ls_dir): - os.makedirs(dart_ls_dir) - FileUtils.download_and_extract_archive( - logger, dependency["url"], dart_ls_dir, dependency["archiveType"] - ) - - - assert os.path.exists(dart_executable_path) - os.chmod(dart_executable_path, stat.S_IEXEC) - - return f"{dart_executable_path} language-server --client-id multilspy.dart --client-version 1.2" - - - def _get_initialize_params(self, repository_absolute_path: str): - """ - Returns the initialize params for the Dart Language Server. - """ - with open( - os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r" - ) as f: - d = json.load(f) - - del d["_description"] - - d["processId"] = os.getpid() - assert d["rootPath"] == "$rootPath" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "$rootUri" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["uri"] == "$uri" - d["workspaceFolders"][0]["uri"] = pathlib.Path( - repository_absolute_path - ).as_uri() - - assert d["workspaceFolders"][0]["name"] == "$name" - d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) - - return d - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["DartLanguageServer"]: - """ - Start the language server and yield when the server is ready. - """ - - async def execute_client_command_handler(params): - return [] - - async def do_nothing(params): - return - - async def check_experimental_status(params): - pass - - async def window_log_message(msg): - self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - self.server.on_request("client/registerCapability", do_nothing) - self.server.on_notification("language/status", do_nothing) - self.server.on_notification("window/logMessage", window_log_message) - self.server.on_request( - "workspace/executeClientCommand", execute_client_command_handler - ) - self.server.on_notification("$/progress", do_nothing) - self.server.on_notification("textDocument/publishDiagnostics", do_nothing) - self.server.on_notification("language/actionableNotification", do_nothing) - self.server.on_notification( - "experimental/serverStatus", check_experimental_status - ) - - async with super().start_server(): - self.logger.log( - "Starting dart-language-server server process", logging.INFO - ) - await 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, - ) - init_response = await self.server.send_request( - "initialize", initialize_params - ) - self.logger.log( - f"Received initialize response from dart-language-server: {init_response}", - logging.INFO, - ) - - self.server.notify.initialized({}) - - yield self diff --git a/src/multilspy/language_servers/dart_language_server/initialize_params.json b/src/multilspy/language_servers/dart_language_server/initialize_params.json deleted file mode 100644 index e90e408a..00000000 --- a/src/multilspy/language_servers/dart_language_server/initialize_params.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_description": "This file contains the initialization parameters for the Dart Language Server.", - "processId": "$processId", - "rootPath": "$rootPath", - "rootUri": "$rootUri", - "capabilities": {}, - "initializationOptions": { - "onlyAnalyzeProjectsWithOpenFiles": false, - "suggestFromUnimportedLibraries": true, - "closingLabels": false, - "outline": false, - "flutterOutline": false, - "allowOpenUri": false - }, - "trace": "verbose", - "workspaceFolders": [ - { - "uri": "$uri", - "name": "$name" - } - ] - -} \ No newline at end of file diff --git a/src/multilspy/language_servers/dart_language_server/runtime_dependencies.json b/src/multilspy/language_servers/dart_language_server/runtime_dependencies.json deleted file mode 100644 index 32e49299..00000000 --- a/src/multilspy/language_servers/dart_language_server/runtime_dependencies.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "_description": "Used to download the runtime dependencies for running Dart Language Server, downloaded from https://dart.dev/get-dart/archive", - "runtimeDependencies": [ - { - "id": "DartLanguageServer", - "description": "Dart Language Server for Linux (x64)", - "url": "https://storage.googleapis.com/dart-archive/channels/stable/release/3.7.1/sdk/dartsdk-linux-x64-release.zip", - "platformId": "linux-x64", - "archiveType": "zip", - "binaryName": "dart-sdk/bin/dart" - }, - { - "id": "DartLanguageServer", - "description": "Dart Language Server for Windows (x64)", - "url": "https://storage.googleapis.com/dart-archive/channels/stable/release/3.7.1/sdk/dartsdk-windows-x64-release.zip", - "platformId": "win-x64", - "archiveType": "zip", - "binaryName": "dart-sdk/bin/dart.exe" - }, - { - "id": "DartLanguageServer", - "description": "Dart Language Server for Windows (arm64)", - "url": "https://storage.googleapis.com/dart-archive/channels/stable/release/3.7.1/sdk/dartsdk-windows-arm64-release.zip", - "platformId": "win-arm64", - "archiveType": "zip", - "binaryName": "dart-sdk/bin/dart.exe" - }, - { - "id": "DartLanguageServer", - "description": "Dart Language Server for macOS (x64)", - "url": "https://storage.googleapis.com/dart-archive/channels/stable/release/3.7.1/sdk/dartsdk-macos-x64-release.zip", - "platformId": "osx-x64", - "archiveType": "zip", - "binaryName": "dart-sdk/bin/dart" - }, - { - "id": "DartLanguageServer", - "description": "Dart Language Server for macOS (arm64)", - "url": "https://storage.googleapis.com/dart-archive/channels/stable/release/3.7.1/sdk/dartsdk-macos-arm64-release.zip", - "platformId": "osx-arm64", - "archiveType": "zip", - "binaryName": "dart-sdk/bin/dart" - } - ] -} \ No newline at end of file diff --git a/src/multilspy/language_servers/eclipse_jdtls/eclipse_jdtls.py b/src/multilspy/language_servers/eclipse_jdtls/eclipse_jdtls.py deleted file mode 100644 index 103c9760..00000000 --- a/src/multilspy/language_servers/eclipse_jdtls/eclipse_jdtls.py +++ /dev/null @@ -1,422 +0,0 @@ -""" -Provides Java specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Java. -""" - -import asyncio -import dataclasses -import json -import logging -import os -import pathlib -import shutil -import stat -import uuid -from contextlib import asynccontextmanager -from typing import AsyncIterator - -from overrides import override - -from solidlsp.multilspy_logger import MultilspyLogger -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_settings import MultilspySettings -from solidlsp.multilspy_utils import FileUtils -from solidlsp.multilspy_utils import PlatformUtils -from pathlib import PurePath - - -@dataclasses.dataclass -class RuntimeDependencyPaths: - """ - Stores the paths to the runtime dependencies of EclipseJDTLS - """ - - gradle_path: str - lombok_jar_path: str - jre_path: str - jre_home_path: str - jdtls_launcher_jar_path: str - jdtls_readonly_config_path: str - intellicode_jar_path: str - intellisense_members_path: str - - -class EclipseJDTLS(LanguageServer): - """ - The EclipseJDTLS class provides a Java specific implementation of the LanguageServer class - """ - - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): - """ - 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) - self.runtime_dependency_paths = runtime_dependency_paths - - # ws_dir is the workspace directory for the EclipseJDTLS server - ws_dir = str( - PurePath( - MultilspySettings.get_language_server_directory(), - "EclipseJDTLS", - "workspaces", - uuid.uuid4().hex, - ) - ) - - # shared_cache_location is the global cache used by Eclipse JDTLS across all workspaces - shared_cache_location = str( - PurePath(MultilspySettings.get_global_cache_directory(), "lsp", "EclipseJDTLS", "sharedIndex") - ) - - jre_path = self.runtime_dependency_paths.jre_path - lombok_jar_path = self.runtime_dependency_paths.lombok_jar_path - - jdtls_launcher_jar = self.runtime_dependency_paths.jdtls_launcher_jar_path - - os.makedirs(ws_dir, exist_ok=True) - - data_dir = str(PurePath(ws_dir, "data_dir")) - jdtls_config_path = str(PurePath(ws_dir, "config_path")) - - jdtls_readonly_config_path = self.runtime_dependency_paths.jdtls_readonly_config_path - - if not os.path.exists(jdtls_config_path): - shutil.copytree(jdtls_readonly_config_path, jdtls_config_path) - - for static_path in [ - jre_path, - lombok_jar_path, - jdtls_launcher_jar, - jdtls_config_path, - jdtls_readonly_config_path, - ]: - assert os.path.exists(static_path), static_path - - # TODO: Add "self.runtime_dependency_paths.jre_home_path"/bin to $PATH as well - proc_env = {"syntaxserver": "false", "JAVA_HOME": self.runtime_dependency_paths.jre_home_path} - proc_cwd = repository_root_path - cmd = " ".join( - [ - jre_path, - "--add-modules=ALL-SYSTEM", - "--add-opens", - "java.base/java.util=ALL-UNNAMED", - "--add-opens", - "java.base/java.lang=ALL-UNNAMED", - "--add-opens", - "java.base/sun.nio.fs=ALL-UNNAMED", - "-Declipse.application=org.eclipse.jdt.ls.core.id1", - "-Dosgi.bundles.defaultStartLevel=4", - "-Declipse.product=org.eclipse.jdt.ls.core.product", - "-Djava.import.generatesMetadataFilesAtProjectRoot=false", - "-Dfile.encoding=utf8", - "-noverify", - "-XX:+UseParallelGC", - "-XX:GCTimeRatio=4", - "-XX:AdaptiveSizePolicyWeight=90", - "-Dsun.zip.disableMemoryMapping=true", - "-Djava.lsp.joinOnCompletion=true", - "-Xmx3G", - "-Xms100m", - "-Xlog:disable", - "-Dlog.level=ALL", - f'"-javaagent:{lombok_jar_path}"', - f'"-Djdt.core.sharedIndexLocation={shared_cache_location}"', - "-jar", - f'"{jdtls_launcher_jar}"', - "-configuration", - f'"{jdtls_config_path}"', - "-data", - f'"{data_dir}"', - ] - ) - - self.service_ready_event = asyncio.Event() - self.intellicode_enable_command_available = asyncio.Event() - self.initialize_searcher_command_available = asyncio.Event() - - super().__init__(config, logger, repository_root_path, ProcessLaunchInfo(cmd, proc_env, proc_cwd), "java") - - @override - def is_ignored_dirname(self, dirname: str) -> bool: - # Ignore common Java build directories from different build tools: - # - Maven: target - # - Gradle: build, .gradle - # - Eclipse: bin, .settings - # - IntelliJ IDEA: out, .idea - # - General: classes, dist, lib - return super().is_ignored_dirname(dirname) or dirname in [ - "target", # Maven - "build", # Gradle - "bin", # Eclipse - "out", # IntelliJ IDEA - "classes", # General - "dist", # General - "lib" # General - ] - - def setupRuntimeDependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> RuntimeDependencyPaths: - """ - Setup runtime dependencies for EclipseJDTLS. - """ - platformId = PlatformUtils.get_platform_id() - - with open(str(PurePath(os.path.dirname(__file__), "runtime_dependencies.json")), "r", encoding="utf-8") as f: - runtimeDependencies = json.load(f) - del runtimeDependencies["_description"] - - os.makedirs(str(PurePath(os.path.abspath(os.path.dirname(__file__)), "static")), exist_ok=True) - - # assert platformId.value in [ - # "linux-x64", - # "win-x64", - # ], "Only linux-x64 platform is supported for in multilspy at the moment" - - gradle_path = str( - PurePath( - os.path.abspath(os.path.dirname(__file__)), - "static/gradle-7.3.3", - ) - ) - - if not os.path.exists(gradle_path): - FileUtils.download_and_extract_archive( - logger, - runtimeDependencies["gradle"]["platform-agnostic"]["url"], - str(PurePath(gradle_path).parent), - runtimeDependencies["gradle"]["platform-agnostic"]["archiveType"], - ) - - assert os.path.exists(gradle_path) - - dependency = runtimeDependencies["vscode-java"][platformId.value] - vscode_java_path = str( - PurePath(os.path.abspath(os.path.dirname(__file__)), "static", dependency["relative_extraction_path"]) - ) - os.makedirs(vscode_java_path, exist_ok=True) - jre_home_path = str(PurePath(vscode_java_path, dependency["jre_home_path"])) - jre_path = str(PurePath(vscode_java_path, dependency["jre_path"])) - lombok_jar_path = str(PurePath(vscode_java_path, dependency["lombok_jar_path"])) - jdtls_launcher_jar_path = str(PurePath(vscode_java_path, dependency["jdtls_launcher_jar_path"])) - jdtls_readonly_config_path = str(PurePath(vscode_java_path, dependency["jdtls_readonly_config_path"])) - if not all( - [ - os.path.exists(vscode_java_path), - os.path.exists(jre_home_path), - os.path.exists(jre_path), - os.path.exists(lombok_jar_path), - os.path.exists(jdtls_launcher_jar_path), - os.path.exists(jdtls_readonly_config_path), - ] - ): - FileUtils.download_and_extract_archive( - logger, dependency["url"], vscode_java_path, dependency["archiveType"] - ) - - os.chmod(jre_path, stat.S_IEXEC) - - assert os.path.exists(vscode_java_path) - assert os.path.exists(jre_home_path) - assert os.path.exists(jre_path) - assert os.path.exists(lombok_jar_path) - assert os.path.exists(jdtls_launcher_jar_path) - assert os.path.exists(jdtls_readonly_config_path) - - dependency = runtimeDependencies["intellicode"]["platform-agnostic"] - intellicode_directory_path = str( - PurePath(os.path.abspath(os.path.dirname(__file__)), "static", dependency["relative_extraction_path"]) - ) - os.makedirs(intellicode_directory_path, exist_ok=True) - intellicode_jar_path = str(PurePath(intellicode_directory_path, dependency["intellicode_jar_path"])) - intellisense_members_path = str(PurePath(intellicode_directory_path, dependency["intellisense_members_path"])) - if not all( - [ - os.path.exists(intellicode_directory_path), - os.path.exists(intellicode_jar_path), - os.path.exists(intellisense_members_path), - ] - ): - FileUtils.download_and_extract_archive( - logger, dependency["url"], intellicode_directory_path, dependency["archiveType"] - ) - - assert os.path.exists(intellicode_directory_path) - assert os.path.exists(intellicode_jar_path) - assert os.path.exists(intellisense_members_path) - - return RuntimeDependencyPaths( - gradle_path=gradle_path, - lombok_jar_path=lombok_jar_path, - jre_path=jre_path, - jre_home_path=jre_home_path, - jdtls_launcher_jar_path=jdtls_launcher_jar_path, - jdtls_readonly_config_path=jdtls_readonly_config_path, - intellicode_jar_path=intellicode_jar_path, - intellisense_members_path=intellisense_members_path, - ) - - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize parameters for the EclipseJDTLS server. - """ - # Look into https://github.com/eclipse/eclipse.jdt.ls/blob/master/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/preferences/Preferences.java to understand all the options available - with open(str(PurePath(os.path.dirname(__file__), "initialize_params.json")), "r", encoding="utf-8") as f: - d: InitializeParams = json.load(f) - - del d["_description"] - - if not os.path.isabs(repository_absolute_path): - repository_absolute_path = os.path.abspath(repository_absolute_path) - - assert d["processId"] == "os.getpid()" - d["processId"] = os.getpid() - - assert d["rootPath"] == "repository_absolute_path" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "pathlib.Path(repository_absolute_path).as_uri()" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["initializationOptions"]["workspaceFolders"] == "[pathlib.Path(repository_absolute_path).as_uri()]" - d["initializationOptions"]["workspaceFolders"] = [pathlib.Path(repository_absolute_path).as_uri()] - - assert ( - d["workspaceFolders"] - == '[\n {\n "uri": pathlib.Path(repository_absolute_path).as_uri(),\n "name": os.path.basename(repository_absolute_path),\n }\n ]' - ) - d["workspaceFolders"] = [ - { - "uri": pathlib.Path(repository_absolute_path).as_uri(), - "name": os.path.basename(repository_absolute_path), - } - ] - - assert d["initializationOptions"]["bundles"] == ["intellicode-core.jar"] - bundles = [self.runtime_dependency_paths.intellicode_jar_path] - d["initializationOptions"]["bundles"] = bundles - - assert d["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"] == [ - {"name": "JavaSE-21", "path": "static/vscode-java/extension/jre/21.0.7-linux-x86_64", "default": True} - ] - d["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"] = [ - {"name": "JavaSE-21", "path": self.runtime_dependency_paths.jre_home_path, "default": True} - ] - - for runtime in d["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"]: - assert "name" in runtime - assert "path" in runtime - assert os.path.exists( - runtime["path"] - ), f"Runtime required for eclipse_jdtls at path {runtime['path']} does not exist" - - assert d["initializationOptions"]["settings"]["java"]["import"]["gradle"]["home"] == "abs(static/gradle-7.3.3)" - d["initializationOptions"]["settings"]["java"]["import"]["gradle"][ - "home" - ] = self.runtime_dependency_paths.gradle_path - - d["initializationOptions"]["settings"]["java"]["import"]["gradle"]["java"][ - "home" - ] = self.runtime_dependency_paths.jre_path - - return d - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["EclipseJDTLS"]: - """ - Starts the Eclipse JDTLS Language Server, waits for the server to be ready and yields the LanguageServer instance. - - Usage: - ``` - async with lsp.start_server(): - # LanguageServer has been initialized and ready to serve requests - await lsp.request_definition(...) - await lsp.request_references(...) - # Shutdown the LanguageServer on exit from scope - # LanguageServer has been shutdown - ``` - """ - - async def register_capability_handler(params): - assert "registrations" in params - for registration in params["registrations"]: - if registration["method"] == "textDocument/completion": - assert registration["registerOptions"]["resolveProvider"] == True - assert registration["registerOptions"]["triggerCharacters"] == [ - ".", - "@", - "#", - "*", - " ", - ] - self.completions_available.set() - if registration["method"] == "workspace/executeCommand": - if "java.intellicode.enable" in registration["registerOptions"]["commands"]: - self.intellicode_enable_command_available.set() - return - - async def lang_status_handler(params): - # TODO: Should we wait for - # server -> client: {'jsonrpc': '2.0', 'method': 'language/status', 'params': {'type': 'ProjectStatus', 'message': 'OK'}} - # Before proceeding? - if params["type"] == "ServiceReady" and params["message"] == "ServiceReady": - self.service_ready_event.set() - - async def execute_client_command_handler(params): - assert params["command"] == "_java.reloadBundles.command" - assert params["arguments"] == [] - return [] - - async def window_log_message(msg): - self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - async def do_nothing(params): - return - - self.server.on_request("client/registerCapability", register_capability_handler) - self.server.on_notification("language/status", lang_status_handler) - self.server.on_notification("window/logMessage", window_log_message) - self.server.on_request("workspace/executeClientCommand", execute_client_command_handler) - self.server.on_notification("$/progress", do_nothing) - self.server.on_notification("textDocument/publishDiagnostics", do_nothing) - self.server.on_notification("language/actionableNotification", do_nothing) - - async with super().start_server(): - self.logger.log("Starting EclipseJDTLS server process", logging.INFO) - await 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, - ) - init_response = await self.server.send.initialize(initialize_params) - assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 - assert "completionProvider" not in init_response["capabilities"] - assert "executeCommandProvider" not in init_response["capabilities"] - - self.server.notify.initialized({}) - - self.server.notify.workspace_did_change_configuration( - {"settings": initialize_params["initializationOptions"]["settings"]} - ) - - await self.intellicode_enable_command_available.wait() - - java_intellisense_members_path = self.runtime_dependency_paths.intellisense_members_path - assert os.path.exists(java_intellisense_members_path) - intellicode_enable_result = await self.server.send.execute_command( - { - "command": "java.intellicode.enable", - "arguments": [True, java_intellisense_members_path], - } - ) - assert intellicode_enable_result - - # TODO: Add comments about why we wait here, and how this can be optimized - await self.service_ready_event.wait() - - yield self diff --git a/src/multilspy/language_servers/eclipse_jdtls/initialize_params.json b/src/multilspy/language_servers/eclipse_jdtls/initialize_params.json deleted file mode 100644 index b65b7b62..00000000 --- a/src/multilspy/language_servers/eclipse_jdtls/initialize_params.json +++ /dev/null @@ -1,849 +0,0 @@ -{ - "_description": "The parameters sent by the client when initializing the language server with the \"initialize\" request. More details at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize", - "processId": "os.getpid()", - "clientInfo": { - "name": "Visual Studio Code - Insiders", - "version": "1.77.0-insider" - }, - "locale": "en", - "rootPath": "repository_absolute_path", - "rootUri": "pathlib.Path(repository_absolute_path).as_uri()", - "capabilities": { - "workspace": { - "applyEdit": true, - "workspaceEdit": { - "documentChanges": true, - "resourceOperations": [ - "create", - "rename", - "delete" - ], - "failureHandling": "textOnlyTransactional", - "normalizesLineEndings": true, - "changeAnnotationSupport": { - "groupsOnLabel": true - } - }, - "didChangeConfiguration": { - "dynamicRegistration": true - }, - "didChangeWatchedFiles": { - "dynamicRegistration": true, - "relativePatternSupport": true - }, - "symbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "resolveSupport": { - "properties": [ - "location.range" - ] - } - }, - "codeLens": { - "refreshSupport": true - }, - "executeCommand": { - "dynamicRegistration": true - }, - "configuration": true, - "workspaceFolders": true, - "semanticTokens": { - "refreshSupport": true - }, - "fileOperations": { - "dynamicRegistration": true, - "didCreate": true, - "didRename": true, - "didDelete": true, - "willCreate": true, - "willRename": true, - "willDelete": true - }, - "inlineValue": { - "refreshSupport": true - }, - "inlayHint": { - "refreshSupport": true - }, - "diagnostics": { - "refreshSupport": true - } - }, - "textDocument": { - "publishDiagnostics": { - "relatedInformation": true, - "versionSupport": false, - "tagSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "codeDescriptionSupport": true, - "dataSupport": true - }, - "synchronization": { - "dynamicRegistration": true, - "willSave": true, - "willSaveWaitUntil": true, - "didSave": true - }, - "completion": { - "dynamicRegistration": true, - "contextSupport": true, - "completionItem": { - "snippetSupport": false, - "commitCharactersSupport": true, - "documentationFormat": [ - "markdown", - "plaintext" - ], - "deprecatedSupport": true, - "preselectSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "insertReplaceSupport": false, - "resolveSupport": { - "properties": [ - "documentation", - "detail", - "additionalTextEdits" - ] - }, - "insertTextModeSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "labelDetailsSupport": true - }, - "insertTextMode": 2, - "completionItemKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25 - ] - }, - "completionList": { - "itemDefaults": [ - "commitCharacters", - "editRange", - "insertTextFormat", - "insertTextMode" - ] - } - }, - "hover": { - "dynamicRegistration": true, - "contentFormat": [ - "markdown", - "plaintext" - ] - }, - "signatureHelp": { - "dynamicRegistration": true, - "signatureInformation": { - "documentationFormat": [ - "markdown", - "plaintext" - ], - "parameterInformation": { - "labelOffsetSupport": true - }, - "activeParameterSupport": true - }, - "contextSupport": true - }, - "definition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "references": { - "dynamicRegistration": true - }, - "documentHighlight": { - "dynamicRegistration": true - }, - "documentSymbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "hierarchicalDocumentSymbolSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "labelSupport": true - }, - "codeAction": { - "dynamicRegistration": true, - "isPreferredSupport": true, - "disabledSupport": true, - "dataSupport": true, - "resolveSupport": { - "properties": [ - "edit" - ] - }, - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "", - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite", - "source", - "source.organizeImports" - ] - } - }, - "honorsChangeAnnotations": false - }, - "codeLens": { - "dynamicRegistration": true - }, - "formatting": { - "dynamicRegistration": true - }, - "rangeFormatting": { - "dynamicRegistration": true - }, - "onTypeFormatting": { - "dynamicRegistration": true - }, - "rename": { - "dynamicRegistration": true, - "prepareSupport": true, - "prepareSupportDefaultBehavior": 1, - "honorsChangeAnnotations": true - }, - "documentLink": { - "dynamicRegistration": true, - "tooltipSupport": true - }, - "typeDefinition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "implementation": { - "dynamicRegistration": true, - "linkSupport": true - }, - "colorProvider": { - "dynamicRegistration": true - }, - "foldingRange": { - "dynamicRegistration": true, - "rangeLimit": 5000, - "lineFoldingOnly": true, - "foldingRangeKind": { - "valueSet": [ - "comment", - "imports", - "region" - ] - }, - "foldingRange": { - "collapsedText": false - } - }, - "declaration": { - "dynamicRegistration": true, - "linkSupport": true - }, - "selectionRange": { - "dynamicRegistration": true - }, - "callHierarchy": { - "dynamicRegistration": true - }, - "semanticTokens": { - "dynamicRegistration": true, - "tokenTypes": [ - "namespace", - "type", - "class", - "enum", - "interface", - "struct", - "typeParameter", - "parameter", - "variable", - "property", - "enumMember", - "event", - "function", - "method", - "macro", - "keyword", - "modifier", - "comment", - "string", - "number", - "regexp", - "operator", - "decorator" - ], - "tokenModifiers": [ - "declaration", - "definition", - "readonly", - "static", - "deprecated", - "abstract", - "async", - "modification", - "documentation", - "defaultLibrary" - ], - "formats": [ - "relative" - ], - "requests": { - "range": true, - "full": { - "delta": true - } - }, - "multilineTokenSupport": false, - "overlappingTokenSupport": false, - "serverCancelSupport": true, - "augmentsSyntaxTokens": true - }, - "linkedEditingRange": { - "dynamicRegistration": true - }, - "typeHierarchy": { - "dynamicRegistration": true - }, - "inlineValue": { - "dynamicRegistration": true - }, - "inlayHint": { - "dynamicRegistration": true, - "resolveSupport": { - "properties": [ - "tooltip", - "textEdits", - "label.tooltip", - "label.location", - "label.command" - ] - } - }, - "diagnostic": { - "dynamicRegistration": true, - "relatedDocumentSupport": false - } - }, - "window": { - "showMessage": { - "messageActionItem": { - "additionalPropertiesSupport": true - } - }, - "showDocument": { - "support": true - }, - "workDoneProgress": true - }, - "general": { - "staleRequestSupport": { - "cancel": true, - "retryOnContentModified": [ - "textDocument/semanticTokens/full", - "textDocument/semanticTokens/range", - "textDocument/semanticTokens/full/delta" - ] - }, - "regularExpressions": { - "engine": "ECMAScript", - "version": "ES2020" - }, - "markdown": { - "parser": "marked", - "version": "1.1.0" - }, - "positionEncodings": [ - "utf-16" - ] - }, - "notebookDocument": { - "synchronization": { - "dynamicRegistration": true, - "executionSummarySupport": true - } - } - }, - "initializationOptions": { - "bundles": [ - "intellicode-core.jar" - ], - "workspaceFolders": "[pathlib.Path(repository_absolute_path).as_uri()]", - "settings": { - "java": { - "home": null, - "jdt": { - "ls": { - "java": { - "home": null - }, - "vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx1G -Xms100m -Xlog:disable", - "lombokSupport": { - "enabled": true - }, - "protobufSupport": { - "enabled": true - }, - "androidSupport": { - "enabled": true - } - } - }, - "errors": { - "incompleteClasspath": { - "severity": "error" - } - }, - "configuration": { - "checkProjectSettingsExclusions": false, - "updateBuildConfiguration": "interactive", - "maven": { - "userSettings": null, - "globalSettings": null, - "notCoveredPluginExecutionSeverity": "warning", - "defaultMojoExecutionAction": "ignore" - }, - "workspaceCacheLimit": 90, - "runtimes": [ - { - "name": "JavaSE-21", - "path": "static/vscode-java/extension/jre/21.0.7-linux-x86_64", - "default": true - } - ] - }, - "trace": { - "server": "verbose" - }, - "import": { - "maven": { - "enabled": true, - "offline": { - "enabled": false - }, - "disableTestClasspathFlag": false - }, - "gradle": { - "enabled": true, - "wrapper": { - "enabled": true - }, - "version": null, - "home": "abs(static/gradle-7.3.3)", - "java": { - "home": "abs(static/launch_jres/21.0.7-linux-x86_64)" - }, - "offline": { - "enabled": false - }, - "arguments": null, - "jvmArguments": null, - "user": { - "home": null - }, - "annotationProcessing": { - "enabled": true - } - }, - "exclusions": [ - "**/node_modules/**", - "**/.metadata/**", - "**/archetype-resources/**", - "**/META-INF/maven/**" - ], - "generatesMetadataFilesAtProjectRoot": false - }, - "maven": { - "downloadSources": true, - "updateSnapshots": true - }, - "eclipse": { - "downloadSources": true - }, - "referencesCodeLens": { - "enabled": true - }, - "signatureHelp": { - "enabled": true, - "description": { - "enabled": true - } - }, - "implementationsCodeLens": { - "enabled": true - }, - "format": { - "enabled": true, - "settings": { - "url": null, - "profile": null - }, - "comments": { - "enabled": true - }, - "onType": { - "enabled": true - }, - "insertSpaces": true, - "tabSize": 4 - }, - "saveActions": { - "organizeImports": false - }, - "project": { - "referencedLibraries": [ - "lib/**/*.jar" - ], - "importOnFirstTimeStartup": "automatic", - "importHint": true, - "resourceFilters": [ - "node_modules", - "\\.git" - ], - "encoding": "ignore", - "exportJar": { - "targetPath": "${workspaceFolder}/${workspaceFolderBasename}.jar" - } - }, - "contentProvider": { - "preferred": null - }, - "autobuild": { - "enabled": true - }, - "maxConcurrentBuilds": 1, - "recommendations": { - "dependency": { - "analytics": { - "show": true - } - } - }, - "completion": { - "maxResults": 0, - "enabled": true, - "guessMethodArguments": true, - "favoriteStaticMembers": [ - "org.junit.Assert.*", - "org.junit.Assume.*", - "org.junit.jupiter.api.Assertions.*", - "org.junit.jupiter.api.Assumptions.*", - "org.junit.jupiter.api.DynamicContainer.*", - "org.junit.jupiter.api.DynamicTest.*", - "org.mockito.Mockito.*", - "org.mockito.ArgumentMatchers.*", - "org.mockito.Answers.*" - ], - "filteredTypes": [ - "java.awt.*", - "com.sun.*", - "sun.*", - "jdk.*", - "org.graalvm.*", - "io.micrometer.shaded.*" - ], - "importOrder": [ - "#", - "java", - "javax", - "org", - "com", - "" - ], - "postfix": { - "enabled": false - }, - "matchCase": "off" - }, - "foldingRange": { - "enabled": true - }, - "progressReports": { - "enabled": false - }, - "codeGeneration": { - "hashCodeEquals": { - "useJava7Objects": false, - "useInstanceof": false - }, - "useBlocks": false, - "generateComments": false, - "toString": { - "template": "${object.className} [${member.name()}=${member.value}, ${otherMembers}]", - "codeStyle": "STRING_CONCATENATION", - "skipNullValues": false, - "listArrayContents": true, - "limitElements": 0 - }, - "insertionLocation": "afterCursor" - }, - "selectionRange": { - "enabled": true - }, - "showBuildStatusOnStart": { - "enabled": "notification" - }, - "server": { - "launchMode": "Standard" - }, - "sources": { - "organizeImports": { - "starThreshold": 99, - "staticStarThreshold": 99 - } - }, - "imports": { - "gradle": { - "wrapper": { - "checksums": [] - } - } - }, - "templates": { - "fileHeader": [], - "typeComment": [] - }, - "references": { - "includeAccessors": true, - "includeDecompiledSources": true - }, - "typeHierarchy": { - "lazyLoad": false - }, - "settings": { - "url": null - }, - "symbols": { - "includeSourceMethodDeclarations": false - }, - "quickfix": { - "showAt": "line" - }, - "inlayHints": { - "parameterNames": { - "enabled": "literals", - "exclusions": [] - } - }, - "codeAction": { - "sortMembers": { - "avoidVolatileChanges": true - } - }, - "compile": { - "nullAnalysis": { - "nonnull": [ - "javax.annotation.Nonnull", - "org.eclipse.jdt.annotation.NonNull", - "org.springframework.lang.NonNull" - ], - "nullable": [ - "javax.annotation.Nullable", - "org.eclipse.jdt.annotation.Nullable", - "org.springframework.lang.Nullable" - ], - "mode": "automatic" - } - }, - "cleanup": { - "actionsOnSave": [] - }, - "sharedIndexes": { - "enabled": "auto", - "location": "" - }, - "refactoring": { - "extract": { - "interface": { - "replace": true - } - } - }, - "debug": { - "logLevel": "verbose", - "settings": { - "showHex": false, - "showStaticVariables": false, - "showQualifiedNames": false, - "showLogicalStructure": true, - "showToString": true, - "maxStringLength": 0, - "numericPrecision": 0, - "hotCodeReplace": "manual", - "enableRunDebugCodeLens": true, - "forceBuildBeforeLaunch": true, - "onBuildFailureProceed": false, - "console": "integratedTerminal", - "exceptionBreakpoint": { - "skipClasses": [] - }, - "stepping": { - "skipClasses": [], - "skipSynthetics": false, - "skipStaticInitializers": false, - "skipConstructors": false - }, - "jdwp": { - "limitOfVariablesPerJdwpRequest": 100, - "requestTimeout": 3000, - "async": "auto" - }, - "vmArgs": "" - } - }, - "silentNotification": false, - "dependency": { - "showMembers": false, - "syncWithFolderExplorer": true, - "autoRefresh": true, - "refreshDelay": 2000, - "packagePresentation": "flat" - }, - "help": { - "firstView": "auto", - "showReleaseNotes": true, - "collectErrorLog": false - }, - "test": { - "defaultConfig": "", - "config": {} - } - } - }, - "extendedClientCapabilities": { - "progressReportProvider": false, - "classFileContentsSupport": true, - "overrideMethodsPromptSupport": true, - "hashCodeEqualsPromptSupport": true, - "advancedOrganizeImportsSupport": true, - "generateToStringPromptSupport": true, - "advancedGenerateAccessorsSupport": true, - "generateConstructorsPromptSupport": true, - "generateDelegateMethodsPromptSupport": true, - "advancedExtractRefactoringSupport": true, - "inferSelectionSupport": [ - "extractMethod", - "extractVariable", - "extractField" - ], - "moveRefactoringSupport": true, - "clientHoverProvider": true, - "clientDocumentSymbolProvider": true, - "gradleChecksumWrapperPromptSupport": true, - "resolveAdditionalTextEditsSupport": true, - "advancedIntroduceParameterRefactoringSupport": true, - "actionableRuntimeNotificationSupport": true, - "shouldLanguageServerExitOnShutdown": true, - "onCompletionItemSelectedCommand": "editor.action.triggerParameterHints", - "extractInterfaceSupport": true, - "advancedUpgradeGradleSupport": true - }, - "triggerFiles": [] - }, - "trace": "verbose", - "workspaceFolders": "[\n {\n \"uri\": pathlib.Path(repository_absolute_path).as_uri(),\n \"name\": os.path.basename(repository_absolute_path),\n }\n ]" -} \ No newline at end of file diff --git a/src/multilspy/language_servers/eclipse_jdtls/runtime_dependencies.json b/src/multilspy/language_servers/eclipse_jdtls/runtime_dependencies.json deleted file mode 100644 index 1b000405..00000000 --- a/src/multilspy/language_servers/eclipse_jdtls/runtime_dependencies.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "_description": "This file lists the runtime dependencies for the Java Language Server", - "gradle": { - "platform-agnostic": { - "url": "https://services.gradle.org/distributions/gradle-7.3.3-bin.zip", - "archiveType": "zip", - "relative_extraction_path": "." - } - }, - "vscode-java": { - "darwin-arm64": { - "url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-darwin-arm64-1.42.0-561.vsix", - "archiveType": "zip", - "relative_extraction_path": "vscode-java" - }, - "osx-arm64": { - "url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-darwin-arm64-1.42.0-561.vsix", - "archiveType": "zip", - "relative_extraction_path": "vscode-java", - "jre_home_path": "extension/jre/21.0.7-macosx-aarch64", - "jre_path": "extension/jre/21.0.7-macosx-aarch64/bin/java", - "lombok_jar_path": "extension/lombok/lombok-1.18.36.jar", - "jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar", - "jdtls_readonly_config_path": "extension/server/config_mac_arm" - }, - "osx-x64": { - "url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-darwin-x64-1.42.0-561.vsix", - "archiveType": "zip", - "relative_extraction_path": "vscode-java", - "jre_home_path": "extension/jre/21.0.7-macosx-x86_64", - "jre_path": "extension/jre/21.0.7-macosx-x86_64/bin/java", - "lombok_jar_path": "extension/lombok/lombok-1.18.36.jar", - "jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar", - "jdtls_readonly_config_path": "extension/server/config_mac" - }, - "linux-arm64": { - "url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-linux-arm64-1.42.0-561.vsix", - "archiveType": "zip", - "relative_extraction_path": "vscode-java" - }, - "linux-x64": { - "url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-linux-x64-1.42.0-561.vsix", - "archiveType": "zip", - "relative_extraction_path": "vscode-java", - "jre_home_path": "extension/jre/21.0.7-linux-x86_64", - "jre_path": "extension/jre/21.0.7-linux-x86_64/bin/java", - "lombok_jar_path": "extension/lombok/lombok-1.18.36.jar", - "jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar", - "jdtls_readonly_config_path": "extension/server/config_linux" - }, - "win-x64": { - "url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-win32-x64-1.42.0-561.vsix", - "archiveType": "zip", - "relative_extraction_path": "vscode-java", - "jre_home_path": "extension/jre/21.0.7-win32-x86_64", - "jre_path": "extension/jre/21.0.7-win32-x86_64/bin/java.exe", - "lombok_jar_path": "extension/lombok/lombok-1.18.36.jar", - "jdtls_launcher_jar_path": "extension/server/plugins/org.eclipse.equinox.launcher_1.7.0.v20250424-1814.jar", - "jdtls_readonly_config_path": "extension/server/config_win" - } - }, - "intellicode": { - "platform-agnostic": { - "url": "https://VisualStudioExptTeam.gallery.vsassets.io/_apis/public/gallery/publisher/VisualStudioExptTeam/extension/vscodeintellicode/1.2.30/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage", - "alternate_url": "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/VisualStudioExptTeam/vsextensions/vscodeintellicode/1.2.30/vspackage", - "archiveType": "zip", - "relative_extraction_path": "intellicode", - "intellicode_jar_path": "extension/dist/com.microsoft.jdtls.intellicode.core-0.7.0.jar", - "intellisense_members_path": "extension/dist/bundledModels/java_intellisense-members" - } - } -} diff --git a/src/multilspy/language_servers/gopls/gopls.py b/src/multilspy/language_servers/gopls/gopls.py deleted file mode 100644 index 117f5c25..00000000 --- a/src/multilspy/language_servers/gopls/gopls.py +++ /dev/null @@ -1,151 +0,0 @@ -import asyncio -import json -import logging -import os -import pathlib -import subprocess -from contextlib import asynccontextmanager -from typing import AsyncIterator - -from overrides import override - -from solidlsp.multilspy_logger import MultilspyLogger -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams -from solidlsp.multilspy_config import MultilspyConfig - - -class Gopls(LanguageServer): - """ - Provides Go specific instantiation of the LanguageServer class using gopls. - """ - - @override - def is_ignored_dirname(self, dirname: str) -> bool: - # For Go projects, we should ignore: - # - vendor: third-party dependencies vendored into the project - # - node_modules: if the project has JavaScript components - # - dist/build: common output directories - return super().is_ignored_dirname(dirname) or dirname in ["vendor", "node_modules", "dist", "build"] - - @staticmethod - def _get_go_version(): - """Get the installed Go version or None if not found.""" - try: - result = subprocess.run(['go', 'version'], capture_output=True, text=True) - if result.returncode == 0: - return result.stdout.strip() - except FileNotFoundError: - return None - return None - - @staticmethod - def _get_gopls_version(): - """Get the installed gopls version or None if not found.""" - try: - result = subprocess.run(['gopls', 'version'], capture_output=True, text=True) - if result.returncode == 0: - return result.stdout.strip() - except FileNotFoundError: - return None - return None - - @classmethod - def setup_runtime_dependency(cls): - """ - Check if required Go runtime dependencies are available. - Raises RuntimeError with helpful message if dependencies are missing. - """ - go_version = cls._get_go_version() - if not go_version: - raise RuntimeError("Go is not installed. Please install Go from https://golang.org/doc/install and make sure it is added to your PATH.") - - gopls_version = cls._get_gopls_version() - if not gopls_version: - raise RuntimeError( - "Found a Go version but gopls is not installed.\n" - "Please install gopls as described in https://pkg.go.dev/golang.org/x/tools/gopls#section-readme\n\n" - "After installation, make sure it is added to your PATH (it might be installed in a different location than Go)." - ) - - return True - - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): - self.setup_runtime_dependency() - - super().__init__( - config, - logger, - repository_root_path, - ProcessLaunchInfo(cmd="gopls", cwd=repository_root_path), - "go", - ) - self.server_ready = asyncio.Event() - self.request_id = 0 - - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the TypeScript Language Server. - """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: - d = json.load(f) - - del d["_description"] - - d["processId"] = os.getpid() - assert d["rootPath"] == "$rootPath" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "$rootUri" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["uri"] == "$uri" - d["workspaceFolders"][0]["uri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["name"] == "$name" - d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) - - return d - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["Gopls"]: - """Start gopls server process""" - async def register_capability_handler(params): - return - - async def window_log_message(msg): - self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - async def do_nothing(params): - return - - 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) - - async with super().start_server(): - self.logger.log("Starting gopls server process", logging.INFO) - await 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, - ) - init_response = await self.server.send.initialize(initialize_params) - - # Verify server capabilities - assert "textDocumentSync" in init_response["capabilities"] - assert "completionProvider" in init_response["capabilities"] - assert "definitionProvider" in init_response["capabilities"] - - self.server.notify.initialized({}) - self.completions_available.set() - - # gopls server is typically ready immediately after initialization - self.server_ready.set() - await self.server_ready.wait() - - yield self diff --git a/src/multilspy/language_servers/gopls/initialize_params.json b/src/multilspy/language_servers/gopls/initialize_params.json deleted file mode 100644 index 40592e1a..00000000 --- a/src/multilspy/language_servers/gopls/initialize_params.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "_description": "The parameters sent by the client when initializing the language server with the \"initialize\" request. More details at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize", - "processId": "os.getpid()", - "locale": "en", - "rootPath": "$rootPath", - "rootUri": "$rootUri", - "capabilities": { - "textDocument": { - "synchronization": { - "didSave": true, - "dynamicRegistration": true - }, - "completion": { - "dynamicRegistration": true, - "completionItem": { - "snippetSupport": true - } - }, - "definition": { - "dynamicRegistration": true - }, - "documentSymbol": { - "dynamicRegistration": true, - "hierarchicalDocumentSymbolSupport": true, - "symbolKind": { - "valueSet": [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 - ] - } - } - }, - "workspace": { - "workspaceFolders": true, - "didChangeConfiguration": { - "dynamicRegistration": true - } - } - }, - "workspaceFolders": [ - { - "uri": "$uri", - "name": "$name" - } - ] -} \ No newline at end of file diff --git a/src/multilspy/language_servers/intelephense/initialize_params.json b/src/multilspy/language_servers/intelephense/initialize_params.json deleted file mode 100644 index 4330560e..00000000 --- a/src/multilspy/language_servers/intelephense/initialize_params.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "_description": "The parameters sent by the client when initializing the language server with the \"initialize\" request. More details at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize", - "processId": "os.getpid()", - "locale": "en", - "rootPath": "$rootPath", - "rootUri": "$rootUri", - "capabilities": { - "textDocument": { - "synchronization": { - "didSave": true, - "dynamicRegistration": true - }, - "completion": { - "dynamicRegistration": true, - "completionItem": { - "snippetSupport": true - } - }, - "definition": { - "dynamicRegistration": true - } - }, - "workspace": { - "workspaceFolders": true, - "didChangeConfiguration": { - "dynamicRegistration": true - } - } - }, - "workspaceFolders": [ - { - "uri": "$uri", - "name": "$name" - } - ] -} \ No newline at end of file diff --git a/src/multilspy/language_servers/intelephense/intelephense.py b/src/multilspy/language_servers/intelephense/intelephense.py deleted file mode 100644 index 5a023fcf..00000000 --- a/src/multilspy/language_servers/intelephense/intelephense.py +++ /dev/null @@ -1,200 +0,0 @@ -""" -Provides PHP specific instantiation of the LanguageServer class using Intelephense. -""" - -import asyncio -import json -import shutil -import logging -import os -import subprocess -import pathlib -from contextlib import asynccontextmanager -from time import sleep -from typing import AsyncIterator - -from overrides import override - -from solidlsp.multilspy_logger import MultilspyLogger -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.lsp_protocol_handler.lsp_types import DefinitionParams, InitializeParams -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_utils import PlatformUtils, PlatformId - -class Intelephense(LanguageServer): - """ - Provides PHP specific instantiation of the LanguageServer class using Intelephense. - """ - - @override - def is_ignored_dirname(self, dirname: str) -> bool: - # For PHP projects, we should ignore: - # - vendor: third-party dependencies managed by Composer - # - node_modules: if the project has JavaScript components - # - cache: commonly used for caching - return super().is_ignored_dirname(dirname) or dirname in ["node_modules", "vendor", "cache"] - - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str: - """ - Setup runtime dependencies for Intelephense. - """ - platform_id = PlatformUtils.get_platform_id() - - valid_platforms = [ - PlatformId.LINUX_x64, - PlatformId.LINUX_arm64, - PlatformId.OSX, - PlatformId.OSX_x64, - PlatformId.OSX_arm64, - PlatformId.WIN_x64, - PlatformId.WIN_arm64, - ] - assert platform_id in valid_platforms, f"Platform {platform_id} is not supported for multilspy PHP at the moment" - - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f: - d = json.load(f) - del d["_description"] - - runtime_dependencies = d.get("runtimeDependencies", []) - intelephense_ls_dir = os.path.join(os.path.dirname(__file__), "static", "php-lsp") - - # Verify both node and npm are installed - is_node_installed = shutil.which('node') is not None - assert is_node_installed, "node is not installed or isn't in PATH. Please install NodeJS and try again." - is_npm_installed = shutil.which('npm') is not None - assert is_npm_installed, "npm is not installed or isn't in PATH. Please install npm and try again." - - # Install intelephense if not already installed - if not os.path.exists(intelephense_ls_dir): - os.makedirs(intelephense_ls_dir, exist_ok=True) - for dependency in runtime_dependencies: - # Windows doesn't support the 'user' parameter and doesn't have pwd module - if PlatformUtils.get_platform_id().value.startswith("win"): - subprocess.run( - dependency["command"], - shell=True, - check=True, - cwd=intelephense_ls_dir, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL - ) - else: - # On Unix-like systems, run as non-root user - import pwd - user = pwd.getpwuid(os.getuid()).pw_name - subprocess.run( - dependency["command"], - shell=True, - check=True, - user=user, - cwd=intelephense_ls_dir, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL - ) - - intelephense_executable_path = os.path.join(intelephense_ls_dir, "node_modules", ".bin", "intelephense") - assert os.path.exists(intelephense_executable_path), "intelephense executable not found. Please install intelephense and try again." - - return f"{intelephense_executable_path} --stdio" - - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): - # Setup runtime dependencies before initializing - intelephense_cmd = self.setup_runtime_dependencies(logger, config) - - super().__init__( - config, - logger, - repository_root_path, - ProcessLaunchInfo(cmd=intelephense_cmd, cwd=repository_root_path), - "php" - ) - self.server_ready = asyncio.Event() - self.request_id = 0 - - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the TypeScript Language Server. - """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: - d = json.load(f) - - del d["_description"] - - d["processId"] = os.getpid() - assert d["rootPath"] == "$rootPath" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "$rootUri" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["uri"] == "$uri" - d["workspaceFolders"][0]["uri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["name"] == "$name" - d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) - - return d - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["Intelephense"]: - """Start Intelephense server process""" - async def register_capability_handler(params): - return - - async def window_log_message(msg): - self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - async def do_nothing(params): - return - - 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) - - async with super().start_server(): - self.logger.log("Starting Intelephense server process", logging.INFO) - await 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, - ) - init_response = await self.server.send.initialize(initialize_params) - self.logger.log( - "After sent initialize params", - logging.INFO, - ) - - # Verify server capabilities - assert "textDocumentSync" in init_response["capabilities"] - assert "completionProvider" in init_response["capabilities"] - assert "definitionProvider" in init_response["capabilities"] - - self.server.notify.initialized({}) - self.completions_available.set() - - # Intelephense server is typically ready immediately after initialization - self.server_ready.set() - await self.server_ready.wait() - - yield self - - @override - # For some reason, the LS may need longer to process this, so we just retry - async def _send_references_request(self, relative_file_path: str, line: int, column: int): - # TODO: The LS doesn't return references contained in other files if it doesn't sleep. This is - # despite the LS having processed requests already. I don't know what causes this, but sleeping - # one second helps. It may be that sleeping only once is enough but that's hard to reliably test. - # May be related to the time it takes to read the files or something like that. - # The sleeping doesn't seem to be needed on all systems - sleep(1) - return await super()._send_references_request(relative_file_path, line, column) - - @override - async def _send_definition_request(self, definition_params: DefinitionParams): - # TODO: same as above, also only a problem if the definition is in another file - sleep(1) - return await super()._send_definition_request(definition_params) diff --git a/src/multilspy/language_servers/intelephense/runtime_dependencies.json b/src/multilspy/language_servers/intelephense/runtime_dependencies.json deleted file mode 100644 index e3a6d7e1..00000000 --- a/src/multilspy/language_servers/intelephense/runtime_dependencies.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "_description": "Used to download the runtime dependencies for running intelephense. Obtained from https://www.npmjs.com/package/intelephense", - "runtimeDependencies": [ - { - "id": "intelephense", - "description": "Intelephense package for Linux, OSX, and Windows. Both x64 and arm64 are supported.", - "command": "npm install --prefix ./ intelephense@1.14.4" - } - ] -} diff --git a/src/multilspy/language_servers/jedi_language_server/initialize_params.json b/src/multilspy/language_servers/jedi_language_server/initialize_params.json deleted file mode 100644 index 5a04872c..00000000 --- a/src/multilspy/language_servers/jedi_language_server/initialize_params.json +++ /dev/null @@ -1,905 +0,0 @@ -{ - "_description": "The parameters sent by the client when initializing the language server with the \"initialize\" request. More details at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize", - "processId": "os.getpid()", - "clientInfo": { - "name": "Visual Studio Code - Insiders", - "version": "1.81.0-insider" - }, - "locale": "en", - "rootPath": "$rootPath", - "rootUri": "$rootUri", - "capabilities": { - "workspace": { - "applyEdit": true, - "workspaceEdit": { - "documentChanges": true, - "resourceOperations": [ - "create", - "rename", - "delete" - ], - "failureHandling": "textOnlyTransactional", - "normalizesLineEndings": true, - "changeAnnotationSupport": { - "groupsOnLabel": true - } - }, - "configuration": true, - "didChangeWatchedFiles": { - "dynamicRegistration": true, - "relativePatternSupport": true - }, - "symbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "resolveSupport": { - "properties": [ - "location.range" - ] - } - }, - "codeLens": { - "refreshSupport": true - }, - "executeCommand": { - "dynamicRegistration": true - }, - "didChangeConfiguration": { - "dynamicRegistration": true - }, - "workspaceFolders": true, - "semanticTokens": { - "refreshSupport": true - }, - "fileOperations": { - "dynamicRegistration": true, - "didCreate": true, - "didRename": true, - "didDelete": true, - "willCreate": true, - "willRename": true, - "willDelete": true - }, - "inlineValue": { - "refreshSupport": true - }, - "inlayHint": { - "refreshSupport": true - }, - "diagnostics": { - "refreshSupport": true - } - }, - "textDocument": { - "publishDiagnostics": { - "relatedInformation": true, - "versionSupport": false, - "tagSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "codeDescriptionSupport": true, - "dataSupport": true - }, - "synchronization": { - "dynamicRegistration": true, - "willSave": true, - "willSaveWaitUntil": true, - "didSave": true - }, - "completion": { - "dynamicRegistration": true, - "contextSupport": true, - "completionItem": { - "snippetSupport": true, - "commitCharactersSupport": true, - "documentationFormat": [ - "markdown", - "plaintext" - ], - "deprecatedSupport": true, - "preselectSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "insertReplaceSupport": true, - "resolveSupport": { - "properties": [ - "documentation", - "detail", - "additionalTextEdits" - ] - }, - "insertTextModeSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "labelDetailsSupport": true - }, - "insertTextMode": 2, - "completionItemKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25 - ] - }, - "completionList": { - "itemDefaults": [ - "commitCharacters", - "editRange", - "insertTextFormat", - "insertTextMode" - ] - } - }, - "hover": { - "dynamicRegistration": true, - "contentFormat": [ - "markdown", - "plaintext" - ] - }, - "signatureHelp": { - "dynamicRegistration": true, - "signatureInformation": { - "documentationFormat": [ - "markdown", - "plaintext" - ], - "parameterInformation": { - "labelOffsetSupport": true - }, - "activeParameterSupport": true - }, - "contextSupport": true - }, - "definition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "references": { - "dynamicRegistration": true - }, - "documentHighlight": { - "dynamicRegistration": true - }, - "documentSymbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "hierarchicalDocumentSymbolSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "labelSupport": true - }, - "codeAction": { - "dynamicRegistration": true, - "isPreferredSupport": true, - "disabledSupport": true, - "dataSupport": true, - "resolveSupport": { - "properties": [ - "edit" - ] - }, - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "", - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite", - "source", - "source.organizeImports" - ] - } - }, - "honorsChangeAnnotations": false - }, - "codeLens": { - "dynamicRegistration": true - }, - "formatting": { - "dynamicRegistration": true - }, - "rangeFormatting": { - "dynamicRegistration": true - }, - "onTypeFormatting": { - "dynamicRegistration": true - }, - "rename": { - "dynamicRegistration": true, - "prepareSupport": true, - "prepareSupportDefaultBehavior": 1, - "honorsChangeAnnotations": true - }, - "documentLink": { - "dynamicRegistration": true, - "tooltipSupport": true - }, - "typeDefinition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "implementation": { - "dynamicRegistration": true, - "linkSupport": true - }, - "colorProvider": { - "dynamicRegistration": true - }, - "foldingRange": { - "dynamicRegistration": true, - "rangeLimit": 5000, - "lineFoldingOnly": true, - "foldingRangeKind": { - "valueSet": [ - "comment", - "imports", - "region" - ] - }, - "foldingRange": { - "collapsedText": false - } - }, - "declaration": { - "dynamicRegistration": true, - "linkSupport": true - }, - "selectionRange": { - "dynamicRegistration": true - }, - "callHierarchy": { - "dynamicRegistration": true - }, - "semanticTokens": { - "dynamicRegistration": true, - "tokenTypes": [ - "namespace", - "type", - "class", - "enum", - "interface", - "struct", - "typeParameter", - "parameter", - "variable", - "property", - "enumMember", - "event", - "function", - "method", - "macro", - "keyword", - "modifier", - "comment", - "string", - "number", - "regexp", - "operator", - "decorator" - ], - "tokenModifiers": [ - "declaration", - "definition", - "readonly", - "static", - "deprecated", - "abstract", - "async", - "modification", - "documentation", - "defaultLibrary" - ], - "formats": [ - "relative" - ], - "requests": { - "range": true, - "full": { - "delta": true - } - }, - "multilineTokenSupport": false, - "overlappingTokenSupport": false, - "serverCancelSupport": true, - "augmentsSyntaxTokens": false - }, - "linkedEditingRange": { - "dynamicRegistration": true - }, - "typeHierarchy": { - "dynamicRegistration": true - }, - "inlineValue": { - "dynamicRegistration": true - }, - "inlayHint": { - "dynamicRegistration": true, - "resolveSupport": { - "properties": [ - "tooltip", - "textEdits", - "label.tooltip", - "label.location", - "label.command" - ] - } - }, - "diagnostic": { - "dynamicRegistration": true, - "relatedDocumentSupport": false - } - }, - "window": { - "showMessage": { - "messageActionItem": { - "additionalPropertiesSupport": true - } - }, - "showDocument": { - "support": true - }, - "workDoneProgress": true - }, - "general": { - "staleRequestSupport": { - "cancel": true, - "retryOnContentModified": [ - "textDocument/semanticTokens/full", - "textDocument/semanticTokens/range", - "textDocument/semanticTokens/full/delta" - ] - }, - "regularExpressions": { - "engine": "ECMAScript", - "version": "ES2020" - }, - "markdown": { - "parser": "marked", - "version": "1.1.0", - "allowedTags": [ - "ul", - "li", - "p", - "code", - "blockquote", - "ol", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "hr", - "em", - "pre", - "table", - "thead", - "tbody", - "tr", - "th", - "td", - "div", - "del", - "a", - "strong", - "br", - "img", - "span" - ] - }, - "positionEncodings": [ - "utf-16" - ] - }, - "notebookDocument": { - "synchronization": { - "dynamicRegistration": true, - "executionSummarySupport": true - } - }, - "experimental": { - "snippetTextEdit": true, - "codeActionGroup": true, - "hoverActions": true, - "serverStatusNotification": true, - "colorDiagnosticOutput": true, - "openServerLogs": true, - "commands": { - "commands": [ - "rust-analyzer.runSingle", - "rust-analyzer.debugSingle", - "rust-analyzer.showReferences", - "rust-analyzer.gotoLocation", - "editor.action.triggerParameterHints" - ] - } - } - }, - "initializationOptions": { - "cargoRunner": null, - "runnables": { - "extraEnv": null, - "problemMatcher": [ - "$rustc" - ], - "command": null, - "extraArgs": [] - }, - "server": { - "path": null, - "extraEnv": null - }, - "trace": { - "server": "verbose", - "extension": true - }, - "debug": { - "engine": "auto", - "sourceFileMap": { - "/rustc/": "${env:USERPROFILE}/.rustup/toolchains//lib/rustlib/src/rust" - }, - "openDebugPane": false, - "engineSettings": {} - }, - "restartServerOnConfigChange": false, - "typing": { - "continueCommentsOnNewline": true, - "autoClosingAngleBrackets": { - "enable": false - } - }, - "diagnostics": { - "previewRustcOutput": false, - "useRustcErrorCode": false, - "disabled": [], - "enable": true, - "experimental": { - "enable": false - }, - "remapPrefix": {}, - "warningsAsHint": [], - "warningsAsInfo": [] - }, - "discoverProjectCommand": null, - "showUnlinkedFileNotification": true, - "showDependenciesExplorer": true, - "assist": { - "emitMustUse": false, - "expressionFillDefault": "todo" - }, - "cachePriming": { - "enable": true, - "numThreads": 0 - }, - "cargo": { - "autoreload": true, - "buildScripts": { - "enable": true, - "invocationLocation": "workspace", - "invocationStrategy": "per_workspace", - "overrideCommand": null, - "useRustcWrapper": true - }, - "cfgs": {}, - "extraArgs": [], - "extraEnv": {}, - "features": [], - "noDefaultFeatures": false, - "sysroot": "discover", - "sysrootSrc": null, - "target": null, - "unsetTest": [ - "core" - ] - }, - "checkOnSave": true, - "check": { - "allTargets": true, - "command": "check", - "extraArgs": [], - "extraEnv": {}, - "features": null, - "invocationLocation": "workspace", - "invocationStrategy": "per_workspace", - "noDefaultFeatures": null, - "overrideCommand": null, - "targets": null - }, - "completion": { - "autoimport": { - "enable": true - }, - "autoself": { - "enable": true - }, - "callable": { - "snippets": "fill_arguments" - }, - "limit": null, - "postfix": { - "enable": true - }, - "privateEditable": { - "enable": false - }, - "snippets": { - "custom": { - "Arc::new": { - "postfix": "arc", - "body": "Arc::new(${receiver})", - "requires": "std::sync::Arc", - "description": "Put the expression into an `Arc`", - "scope": "expr" - }, - "Rc::new": { - "postfix": "rc", - "body": "Rc::new(${receiver})", - "requires": "std::rc::Rc", - "description": "Put the expression into an `Rc`", - "scope": "expr" - }, - "Box::pin": { - "postfix": "pinbox", - "body": "Box::pin(${receiver})", - "requires": "std::boxed::Box", - "description": "Put the expression into a pinned `Box`", - "scope": "expr" - }, - "Ok": { - "postfix": "ok", - "body": "Ok(${receiver})", - "description": "Wrap the expression in a `Result::Ok`", - "scope": "expr" - }, - "Err": { - "postfix": "err", - "body": "Err(${receiver})", - "description": "Wrap the expression in a `Result::Err`", - "scope": "expr" - }, - "Some": { - "postfix": "some", - "body": "Some(${receiver})", - "description": "Wrap the expression in an `Option::Some`", - "scope": "expr" - } - } - } - }, - "files": { - "excludeDirs": [], - "watcher": "client" - }, - "highlightRelated": { - "breakPoints": { - "enable": true - }, - "closureCaptures": { - "enable": true - }, - "exitPoints": { - "enable": true - }, - "references": { - "enable": true - }, - "yieldPoints": { - "enable": true - } - }, - "hover": { - "actions": { - "debug": { - "enable": true - }, - "enable": true, - "gotoTypeDef": { - "enable": true - }, - "implementations": { - "enable": true - }, - "references": { - "enable": false - }, - "run": { - "enable": true - } - }, - "documentation": { - "enable": true, - "keywords": { - "enable": true - } - }, - "links": { - "enable": true - }, - "memoryLayout": { - "alignment": "hexadecimal", - "enable": true, - "niches": false, - "offset": "hexadecimal", - "size": "both" - } - }, - "imports": { - "granularity": { - "enforce": false, - "group": "crate" - }, - "group": { - "enable": true - }, - "merge": { - "glob": true - }, - "prefer": { - "no": { - "std": false - } - }, - "prefix": "plain" - }, - "inlayHints": { - "bindingModeHints": { - "enable": false - }, - "chainingHints": { - "enable": true - }, - "closingBraceHints": { - "enable": true, - "minLines": 25 - }, - "closureCaptureHints": { - "enable": false - }, - "closureReturnTypeHints": { - "enable": "never" - }, - "closureStyle": "impl_fn", - "discriminantHints": { - "enable": "never" - }, - "expressionAdjustmentHints": { - "enable": "never", - "hideOutsideUnsafe": false, - "mode": "prefix" - }, - "lifetimeElisionHints": { - "enable": "never", - "useParameterNames": false - }, - "maxLength": 25, - "parameterHints": { - "enable": true - }, - "reborrowHints": { - "enable": "never" - }, - "renderColons": true, - "typeHints": { - "enable": true, - "hideClosureInitialization": false, - "hideNamedConstructor": false - } - }, - "interpret": { - "tests": false - }, - "joinLines": { - "joinAssignments": true, - "joinElseIf": true, - "removeTrailingComma": true, - "unwrapTrivialBlock": true - }, - "lens": { - "debug": { - "enable": true - }, - "enable": true, - "forceCustomCommands": true, - "implementations": { - "enable": true - }, - "location": "above_name", - "references": { - "adt": { - "enable": false - }, - "enumVariant": { - "enable": false - }, - "method": { - "enable": false - }, - "trait": { - "enable": false - } - }, - "run": { - "enable": true - } - }, - "linkedProjects": [], - "lru": { - "capacity": null, - "query": { - "capacities": {} - } - }, - "notifications": { - "cargoTomlNotFound": true - }, - "numThreads": null, - "procMacro": { - "attributes": { - "enable": true - }, - "enable": true, - "ignored": {}, - "server": null - }, - "references": { - "excludeImports": false - }, - "rustc": { - "source": null - }, - "rustfmt": { - "extraArgs": [], - "overrideCommand": null, - "rangeFormatting": { - "enable": false - } - }, - "semanticHighlighting": { - "doc": { - "comment": { - "inject": { - "enable": true - } - } - }, - "nonStandardTokens": true, - "operator": { - "enable": true, - "specialization": { - "enable": false - } - }, - "punctuation": { - "enable": false, - "separate": { - "macro": { - "bang": false - } - }, - "specialization": { - "enable": false - } - }, - "strings": { - "enable": true - } - }, - "signatureInfo": { - "detail": "full", - "documentation": { - "enable": true - } - }, - "workspace": { - "symbols": { - "maxSymbols": 0 - } - } - }, - "trace": "verbose", - "workspaceFolders": [ - { - "uri": "$uri", - "name": "$name" - } - ] -} \ No newline at end of file diff --git a/src/multilspy/language_servers/jedi_language_server/jedi_server.py b/src/multilspy/language_servers/jedi_language_server/jedi_server.py deleted file mode 100644 index 6df83653..00000000 --- a/src/multilspy/language_servers/jedi_language_server/jedi_server.py +++ /dev/null @@ -1,123 +0,0 @@ -""" -Provides Python specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Python. -""" - -import json -import logging -import os -import pathlib -from contextlib import asynccontextmanager -from typing import AsyncIterator - -from overrides import override - -from solidlsp.multilspy_logger import MultilspyLogger -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams -from solidlsp.multilspy_config import MultilspyConfig - - -class JediServer(LanguageServer): - """ - Provides Python specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Python. - """ - - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): - """ - 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", - ) - - @override - def is_ignored_dirname(self, dirname: str) -> bool: - return super().is_ignored_dirname(dirname) or dirname in ["venv", "__pycache__"] - - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the Jedi Language Server. - """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: - d = json.load(f) - - del d["_description"] - - d["processId"] = os.getpid() - assert d["rootPath"] == "$rootPath" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "$rootUri" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["uri"] == "$uri" - d["workspaceFolders"][0]["uri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["name"] == "$name" - d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) - - return d - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["JediServer"]: - """ - Starts the JEDI Language Server, waits for the server to be ready and yields the LanguageServer instance. - - Usage: - ``` - async with lsp.start_server(): - # LanguageServer has been initialized and ready to serve requests - await lsp.request_definition(...) - await lsp.request_references(...) - # Shutdown the LanguageServer on exit from scope - # LanguageServer has been shutdown - ``` - """ - - async def execute_client_command_handler(params): - return [] - - async def do_nothing(params): - return - - async def check_experimental_status(params): - if params["quiescent"] == True: - self.completions_available.set() - - async def window_log_message(msg): - self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - self.server.on_request("client/registerCapability", do_nothing) - self.server.on_notification("language/status", do_nothing) - self.server.on_notification("window/logMessage", window_log_message) - self.server.on_request("workspace/executeClientCommand", execute_client_command_handler) - self.server.on_notification("$/progress", do_nothing) - self.server.on_notification("textDocument/publishDiagnostics", do_nothing) - self.server.on_notification("language/actionableNotification", do_nothing) - self.server.on_notification("experimental/serverStatus", check_experimental_status) - - async with super().start_server(): - self.logger.log("Starting jedi-language-server server process", logging.INFO) - await 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, - ) - init_response = await self.server.send.initialize(initialize_params) - assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 - assert "completionProvider" in init_response["capabilities"] - assert init_response["capabilities"]["completionProvider"] == { - "triggerCharacters": [".", "'", '"'], - "resolveProvider": True, - } - - self.server.notify.initialized({}) - - yield self diff --git a/src/multilspy/language_servers/kotlin_language_server/initialize_params.json b/src/multilspy/language_servers/kotlin_language_server/initialize_params.json deleted file mode 100644 index a7b2ec0a..00000000 --- a/src/multilspy/language_servers/kotlin_language_server/initialize_params.json +++ /dev/null @@ -1,521 +0,0 @@ -{ - "_description": "The parameters sent by the client when initializing the language server with the \"initialize\" request. More details at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize", - "processId": "os.getpid()", - "clientInfo": { - "name": "Multilspy Kotlin Client", - "version": "1.0.0" - }, - "locale": "en", - "rootPath": "repository_absolute_path", - "rootUri": "pathlib.Path(repository_absolute_path).as_uri()", - "capabilities": { - "workspace": { - "applyEdit": true, - "workspaceEdit": { - "documentChanges": true, - "resourceOperations": [ - "create", - "rename", - "delete" - ], - "failureHandling": "textOnlyTransactional", - "normalizesLineEndings": true, - "changeAnnotationSupport": { - "groupsOnLabel": true - } - }, - "didChangeConfiguration": { - "dynamicRegistration": true - }, - "didChangeWatchedFiles": { - "dynamicRegistration": true, - "relativePatternSupport": true - }, - "symbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "resolveSupport": { - "properties": [ - "location.range" - ] - } - }, - "codeLens": { - "refreshSupport": true - }, - "executeCommand": { - "dynamicRegistration": true - }, - "configuration": true, - "workspaceFolders": true, - "semanticTokens": { - "refreshSupport": true - }, - "fileOperations": { - "dynamicRegistration": true, - "didCreate": true, - "didRename": true, - "didDelete": true, - "willCreate": true, - "willRename": true, - "willDelete": true - }, - "inlineValue": { - "refreshSupport": true - }, - "inlayHint": { - "refreshSupport": true - }, - "diagnostics": { - "refreshSupport": true - } - }, - "textDocument": { - "publishDiagnostics": { - "relatedInformation": true, - "versionSupport": false, - "tagSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "codeDescriptionSupport": true, - "dataSupport": true - }, - "synchronization": { - "dynamicRegistration": true, - "willSave": true, - "willSaveWaitUntil": true, - "didSave": true - }, - "completion": { - "dynamicRegistration": true, - "contextSupport": true, - "completionItem": { - "snippetSupport": false, - "commitCharactersSupport": true, - "documentationFormat": [ - "markdown", - "plaintext" - ], - "deprecatedSupport": true, - "preselectSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "insertReplaceSupport": false, - "resolveSupport": { - "properties": [ - "documentation", - "detail", - "additionalTextEdits" - ] - }, - "insertTextModeSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "labelDetailsSupport": true - }, - "insertTextMode": 2, - "completionItemKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25 - ] - }, - "completionList": { - "itemDefaults": [ - "commitCharacters", - "editRange", - "insertTextFormat", - "insertTextMode" - ] - } - }, - "hover": { - "dynamicRegistration": true, - "contentFormat": [ - "markdown", - "plaintext" - ] - }, - "signatureHelp": { - "dynamicRegistration": true, - "signatureInformation": { - "documentationFormat": [ - "markdown", - "plaintext" - ], - "parameterInformation": { - "labelOffsetSupport": true - }, - "activeParameterSupport": true - }, - "contextSupport": true - }, - "definition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "references": { - "dynamicRegistration": true - }, - "documentHighlight": { - "dynamicRegistration": true - }, - "documentSymbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "hierarchicalDocumentSymbolSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "labelSupport": true - }, - "codeAction": { - "dynamicRegistration": true, - "isPreferredSupport": true, - "disabledSupport": true, - "dataSupport": true, - "resolveSupport": { - "properties": [ - "edit" - ] - }, - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "", - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite", - "source", - "source.organizeImports" - ] - } - }, - "honorsChangeAnnotations": false - }, - "codeLens": { - "dynamicRegistration": true - }, - "formatting": { - "dynamicRegistration": true - }, - "rangeFormatting": { - "dynamicRegistration": true - }, - "onTypeFormatting": { - "dynamicRegistration": true - }, - "rename": { - "dynamicRegistration": true, - "prepareSupport": true, - "prepareSupportDefaultBehavior": 1, - "honorsChangeAnnotations": true - }, - "documentLink": { - "dynamicRegistration": true, - "tooltipSupport": true - }, - "typeDefinition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "implementation": { - "dynamicRegistration": true, - "linkSupport": true - }, - "colorProvider": { - "dynamicRegistration": true - }, - "foldingRange": { - "dynamicRegistration": true, - "rangeLimit": 5000, - "lineFoldingOnly": true, - "foldingRangeKind": { - "valueSet": [ - "comment", - "imports", - "region" - ] - }, - "foldingRange": { - "collapsedText": false - } - }, - "declaration": { - "dynamicRegistration": true, - "linkSupport": true - }, - "selectionRange": { - "dynamicRegistration": true - }, - "callHierarchy": { - "dynamicRegistration": true - }, - "semanticTokens": { - "dynamicRegistration": true, - "tokenTypes": [ - "namespace", - "type", - "class", - "enum", - "interface", - "struct", - "typeParameter", - "parameter", - "variable", - "property", - "enumMember", - "event", - "function", - "method", - "macro", - "keyword", - "modifier", - "comment", - "string", - "number", - "regexp", - "operator", - "decorator" - ], - "tokenModifiers": [ - "declaration", - "definition", - "readonly", - "static", - "deprecated", - "abstract", - "async", - "modification", - "documentation", - "defaultLibrary" - ], - "formats": [ - "relative" - ], - "requests": { - "range": true, - "full": { - "delta": true - } - }, - "multilineTokenSupport": false, - "overlappingTokenSupport": false, - "serverCancelSupport": true, - "augmentsSyntaxTokens": true - }, - "linkedEditingRange": { - "dynamicRegistration": true - }, - "typeHierarchy": { - "dynamicRegistration": true - }, - "inlineValue": { - "dynamicRegistration": true - }, - "inlayHint": { - "dynamicRegistration": true, - "resolveSupport": { - "properties": [ - "tooltip", - "textEdits", - "label.tooltip", - "label.location", - "label.command" - ] - } - }, - "diagnostic": { - "dynamicRegistration": true, - "relatedDocumentSupport": false - } - }, - "window": { - "showMessage": { - "messageActionItem": { - "additionalPropertiesSupport": true - } - }, - "showDocument": { - "support": true - }, - "workDoneProgress": true - }, - "general": { - "staleRequestSupport": { - "cancel": true, - "retryOnContentModified": [ - "textDocument/semanticTokens/full", - "textDocument/semanticTokens/range", - "textDocument/semanticTokens/full/delta" - ] - }, - "regularExpressions": { - "engine": "ECMAScript", - "version": "ES2020" - }, - "markdown": { - "parser": "marked", - "version": "1.1.0" - }, - "positionEncodings": [ - "utf-16" - ] - }, - "notebookDocument": { - "synchronization": { - "dynamicRegistration": true, - "executionSummarySupport": true - } - } - }, - "initializationOptions": { - "workspaceFolders": "[pathlib.Path(repository_absolute_path).as_uri()]", - "storagePath": null, - "codegen": { - "enabled": false - }, - "compiler": { - "jvm": { - "target": "default" - } - }, - "completion": { - "snippets": { - "enabled": true - } - }, - "diagnostics": { - "enabled": true, - "level": 4, - "debounceTime": 250 - }, - "scripts": { - "enabled": true, - "buildScriptsEnabled": true - }, - "indexing": { - "enabled": true - }, - "externalSources": { - "useKlsScheme": false, - "autoConvertToKotlin": false - }, - "inlayHints": { - "typeHints": false, - "parameterHints": false, - "chainedHints": false - }, - "formatting": { - "formatter": "ktfmt", - "ktfmt": { - "style": "google", - "indent": 4, - "maxWidth": 100, - "continuationIndent": 8, - "removeUnusedImports": true - } - } - }, - "trace": "verbose", - "workspaceFolders": "[\n {\n \"uri\": pathlib.Path(repository_absolute_path).as_uri(),\n \"name\": os.path.basename(repository_absolute_path),\n }\n ]" -} diff --git a/src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py b/src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py deleted file mode 100644 index 0b2906ca..00000000 --- a/src/multilspy/language_servers/kotlin_language_server/kotlin_language_server.py +++ /dev/null @@ -1,226 +0,0 @@ -""" -Provides Kotlin specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Kotlin. -""" - -import dataclasses -import json -import logging -import os -import stat -import pathlib -from contextlib import asynccontextmanager -from typing import AsyncIterator - -from solidlsp.multilspy_logger import MultilspyLogger -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_utils import FileUtils -from solidlsp.multilspy_utils import PlatformUtils - - -@dataclasses.dataclass -class KotlinRuntimeDependencyPaths: - """ - Stores the paths to the runtime dependencies of Kotlin Language Server - """ - java_path: str - java_home_path: str - kotlin_executable_path: str - - -class KotlinLanguageServer(LanguageServer): - """ - Provides Kotlin specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Kotlin. - """ - - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): - """ - 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) - self.runtime_dependency_paths = runtime_dependency_paths - - # Create command to execute the Kotlin Language Server script - cmd = f'"{self.runtime_dependency_paths.kotlin_executable_path}"' - - # Set environment variables including JAVA_HOME - 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", - ) - - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> KotlinRuntimeDependencyPaths: - """ - Setup runtime dependencies for Kotlin Language Server. - """ - platform_id = PlatformUtils.get_platform_id() - - # Verify platform support - assert platform_id.value.startswith("win-") or platform_id.value.startswith("linux-") or platform_id.value.startswith("osx-"), "Only Windows, Linux and macOS platforms are supported for Kotlin in multilspy at the moment" - - # Load dependency information - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f: - d = json.load(f) - del d["_description"] - - kotlin_dependency = d["runtimeDependency"] - java_dependency = d["java"][platform_id.value] - - # Setup paths for dependencies - static_dir = os.path.join(os.path.dirname(__file__), "static") - os.makedirs(static_dir, exist_ok=True) - - # Setup Java paths - java_dir = os.path.join(static_dir, "java") - os.makedirs(java_dir, exist_ok=True) - - java_home_path = os.path.join(java_dir, java_dependency["java_home_path"]) - java_path = os.path.join(java_dir, java_dependency["java_path"]) - - # 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"] - ) - # Make Java executable - if not platform_id.value.startswith("win-"): - os.chmod(java_path, 0o755) - - assert os.path.exists(java_path), f"Java executable not found at {java_path}" - - # Setup Kotlin Language Server paths - kotlin_ls_dir = os.path.join(static_dir, "server") - - # Get platform-specific executable script path - if platform_id.value.startswith("win-"): - kotlin_script = os.path.join(kotlin_ls_dir, "bin", "kotlin-language-server.bat") - else: - kotlin_script = os.path.join(kotlin_ls_dir, "bin", "kotlin-language-server") - - # 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"] - ) - - # Make script executable on Unix platforms - if os.path.exists(kotlin_script) and not platform_id.value.startswith("win-"): - os.chmod(kotlin_script, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) - - # 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) - else: - raise FileNotFoundError(f"Kotlin Language Server script not found at {kotlin_script}") - - return KotlinRuntimeDependencyPaths( - java_path=java_path, - java_home_path=java_home_path, - kotlin_executable_path=kotlin_executable_path - ) - - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the Kotlin Language Server. - """ - with open(str(pathlib.PurePath(os.path.dirname(__file__), "initialize_params.json")), "r", encoding="utf-8") as f: - d: InitializeParams = json.load(f) - - del d["_description"] - - if not os.path.isabs(repository_absolute_path): - repository_absolute_path = os.path.abspath(repository_absolute_path) - - assert d["processId"] == "os.getpid()" - d["processId"] = os.getpid() - - assert d["rootPath"] == "repository_absolute_path" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "pathlib.Path(repository_absolute_path).as_uri()" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["initializationOptions"]["workspaceFolders"] == "[pathlib.Path(repository_absolute_path).as_uri()]" - d["initializationOptions"]["workspaceFolders"] = [pathlib.Path(repository_absolute_path).as_uri()] - - assert ( - d["workspaceFolders"] - == '[\n {\n "uri": pathlib.Path(repository_absolute_path).as_uri(),\n "name": os.path.basename(repository_absolute_path),\n }\n ]' - ) - d["workspaceFolders"] = [ - { - "uri": pathlib.Path(repository_absolute_path).as_uri(), - "name": os.path.basename(repository_absolute_path), - } - ] - - return d - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["KotlinLanguageServer"]: - """ - Starts the Kotlin Language Server, waits for the server to be ready and yields the LanguageServer instance. - - Usage: - ``` - async with lsp.start_server(): - # LanguageServer has been initialized and ready to serve requests - await lsp.request_definition(...) - await lsp.request_references(...) - # Shutdown the LanguageServer on exit from scope - # LanguageServer has been shutdown - ``` - """ - async def execute_client_command_handler(params): - return [] - - async def do_nothing(params): - return - - async def window_log_message(msg): - self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - self.server.on_request("client/registerCapability", do_nothing) - self.server.on_notification("language/status", do_nothing) - self.server.on_notification("window/logMessage", window_log_message) - self.server.on_request("workspace/executeClientCommand", execute_client_command_handler) - self.server.on_notification("$/progress", do_nothing) - self.server.on_notification("textDocument/publishDiagnostics", do_nothing) - self.server.on_notification("language/actionableNotification", do_nothing) - - async with super().start_server(): - self.logger.log("Starting Kotlin server process", logging.INFO) - await 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, - ) - init_response = await self.server.send.initialize(initialize_params) - - capabilities = init_response["capabilities"] - assert "textDocumentSync" in capabilities, "Server must support textDocumentSync" - assert "hoverProvider" in capabilities, "Server must support hover" - assert "completionProvider" in capabilities, "Server must support code completion" - assert "signatureHelpProvider" in capabilities, "Server must support signature help" - assert "definitionProvider" in capabilities, "Server must support go to definition" - assert "referencesProvider" in capabilities, "Server must support find references" - assert "documentSymbolProvider" in capabilities, "Server must support document symbols" - assert "workspaceSymbolProvider" in capabilities, "Server must support workspace symbols" - assert "semanticTokensProvider" in capabilities, "Server must support semantic tokens" - - self.server.notify.initialized({}) - self.completions_available.set() - - yield self diff --git a/src/multilspy/language_servers/kotlin_language_server/runtime_dependencies.json b/src/multilspy/language_servers/kotlin_language_server/runtime_dependencies.json deleted file mode 100644 index 3568c9d4..00000000 --- a/src/multilspy/language_servers/kotlin_language_server/runtime_dependencies.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "_description": "Used to download the runtime dependencies for Kotlin Language Server from https://github.com/fwcd/kotlin-language-server", - "runtimeDependency": { - "id": "KotlinLsp", - "description": "Kotlin Language Server", - "url": "https://github.com/fwcd/kotlin-language-server/releases/download/1.3.13/server.zip", - "archiveType": "zip" - }, - "java": { - "win-x64": { - "url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-win32-x64-1.42.0-561.vsix", - "archiveType": "zip", - "java_home_path": "extension/jre/21.0.7-win32-x86_64", - "java_path": "extension/jre/21.0.7-win32-x86_64/bin/java.exe" - }, - "linux-x64": { - "url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-linux-x64-1.42.0-561.vsix", - "archiveType": "zip", - "java_home_path": "extension/jre/21.0.7-linux-x86_64", - "java_path": "extension/jre/21.0.7-linux-x86_64/bin/java" - }, - "linux-arm64": { - "url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-linux-arm64-1.42.0-561.vsix", - "archiveType": "zip", - "java_home_path": "extension/jre/21.0.7-linux-aarch64", - "java_path": "extension/jre/21.0.7-linux-aarch64/bin/java" - }, - "osx-x64": { - "url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-darwin-x64-1.42.0-561.vsix", - "archiveType": "zip", - "java_home_path": "extension/jre/21.0.7-macosx-x86_64", - "java_path": "extension/jre/21.0.7-macosx-x86_64/bin/java" - }, - "osx-arm64": { - "url": "https://github.com/redhat-developer/vscode-java/releases/download/v1.42.0/java-darwin-arm64-1.42.0-561.vsix", - "archiveType": "zip", - "java_home_path": "extension/jre/21.0.7-macosx-aarch64", - "java_path": "extension/jre/21.0.7-macosx-aarch64/bin/java" - } - } -} diff --git a/src/multilspy/language_servers/omnisharp/initialize_params.json b/src/multilspy/language_servers/omnisharp/initialize_params.json deleted file mode 100644 index f9ee86c3..00000000 --- a/src/multilspy/language_servers/omnisharp/initialize_params.json +++ /dev/null @@ -1,631 +0,0 @@ -{ - "_description": "The parameters sent by the client when initializing the language server with the \"initialize\" request. More details at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize", - "processId": "os.getpid()", - "clientInfo": { - "name": "Visual Studio Code - Insiders", - "version": "1.82.0-insider" - }, - "locale": "en", - "rootPath": "$rootPath", - "rootUri": "$rootUri", - "capabilities": { - "workspace": { - "applyEdit": true, - "workspaceEdit": { - "documentChanges": true, - "resourceOperations": [ - "create", - "rename", - "delete" - ], - "failureHandling": "textOnlyTransactional", - "normalizesLineEndings": true, - "changeAnnotationSupport": { - "groupsOnLabel": true - } - }, - "configuration": false, - "didChangeWatchedFiles": { - "dynamicRegistration": true, - "relativePatternSupport": true - }, - "symbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "resolveSupport": { - "properties": [ - "location.range" - ] - } - }, - "codeLens": { - "refreshSupport": true - }, - "executeCommand": { - "dynamicRegistration": true - }, - "didChangeConfiguration": { - "dynamicRegistration": true - }, - "workspaceFolders": true, - "semanticTokens": { - "refreshSupport": true - }, - "fileOperations": { - "dynamicRegistration": true, - "didCreate": true, - "didRename": true, - "didDelete": true, - "willCreate": true, - "willRename": true, - "willDelete": true - }, - "inlineValue": { - "refreshSupport": true - }, - "inlayHint": { - "refreshSupport": true - }, - "diagnostics": { - "refreshSupport": true - } - }, - "textDocument": { - "publishDiagnostics": { - "relatedInformation": true, - "versionSupport": false, - "tagSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "codeDescriptionSupport": true, - "dataSupport": true - }, - "synchronization": { - "dynamicRegistration": true, - "willSave": true, - "willSaveWaitUntil": true, - "didSave": true - }, - "completion": { - "dynamicRegistration": true, - "contextSupport": true, - "completionItem": { - "snippetSupport": true, - "commitCharactersSupport": true, - "documentationFormat": [ - "markdown", - "plaintext" - ], - "deprecatedSupport": true, - "preselectSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "insertReplaceSupport": true, - "resolveSupport": { - "properties": [ - "documentation", - "detail", - "additionalTextEdits" - ] - }, - "insertTextModeSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "labelDetailsSupport": true - }, - "insertTextMode": 2, - "completionItemKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25 - ] - }, - "completionList": { - "itemDefaults": [ - "commitCharacters", - "editRange", - "insertTextFormat", - "insertTextMode" - ] - } - }, - "hover": { - "dynamicRegistration": true, - "contentFormat": [ - "markdown", - "plaintext" - ] - }, - "signatureHelp": { - "dynamicRegistration": true, - "signatureInformation": { - "documentationFormat": [ - "markdown", - "plaintext" - ], - "parameterInformation": { - "labelOffsetSupport": true - }, - "activeParameterSupport": true - }, - "contextSupport": true - }, - "definition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "references": { - "dynamicRegistration": true - }, - "documentHighlight": { - "dynamicRegistration": true - }, - "documentSymbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "hierarchicalDocumentSymbolSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "labelSupport": true - }, - "codeAction": { - "dynamicRegistration": true, - "isPreferredSupport": true, - "disabledSupport": true, - "dataSupport": true, - "resolveSupport": { - "properties": [ - "edit" - ] - }, - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "", - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite", - "source", - "source.organizeImports" - ] - } - }, - "honorsChangeAnnotations": false - }, - "codeLens": { - "dynamicRegistration": true - }, - "formatting": { - "dynamicRegistration": true - }, - "rangeFormatting": { - "dynamicRegistration": true - }, - "onTypeFormatting": { - "dynamicRegistration": true - }, - "rename": { - "dynamicRegistration": true, - "prepareSupport": true, - "prepareSupportDefaultBehavior": 1, - "honorsChangeAnnotations": true - }, - "documentLink": { - "dynamicRegistration": true, - "tooltipSupport": true - }, - "typeDefinition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "implementation": { - "dynamicRegistration": true, - "linkSupport": true - }, - "colorProvider": { - "dynamicRegistration": true - }, - "foldingRange": { - "dynamicRegistration": true, - "rangeLimit": 5000, - "lineFoldingOnly": true, - "foldingRangeKind": { - "valueSet": [ - "comment", - "imports", - "region" - ] - }, - "foldingRange": { - "collapsedText": false - } - }, - "declaration": { - "dynamicRegistration": true, - "linkSupport": true - }, - "selectionRange": { - "dynamicRegistration": true - }, - "callHierarchy": { - "dynamicRegistration": true - }, - "semanticTokens": { - "dynamicRegistration": true, - "tokenTypes": [ - "namespace", - "type", - "class", - "enum", - "interface", - "struct", - "typeParameter", - "parameter", - "variable", - "property", - "enumMember", - "event", - "function", - "method", - "macro", - "keyword", - "modifier", - "comment", - "string", - "number", - "regexp", - "operator", - "decorator" - ], - "tokenModifiers": [ - "declaration", - "definition", - "readonly", - "static", - "deprecated", - "abstract", - "async", - "modification", - "documentation", - "defaultLibrary" - ], - "formats": [ - "relative" - ], - "requests": { - "range": true, - "full": { - "delta": true - } - }, - "multilineTokenSupport": false, - "overlappingTokenSupport": false, - "serverCancelSupport": true, - "augmentsSyntaxTokens": false - }, - "linkedEditingRange": { - "dynamicRegistration": true - }, - "typeHierarchy": { - "dynamicRegistration": true - }, - "inlineValue": { - "dynamicRegistration": true - }, - "inlayHint": { - "dynamicRegistration": true, - "resolveSupport": { - "properties": [ - "tooltip", - "textEdits", - "label.tooltip", - "label.location", - "label.command" - ] - } - }, - "diagnostic": { - "dynamicRegistration": true, - "relatedDocumentSupport": false - } - }, - "window": { - "showMessage": { - "messageActionItem": { - "additionalPropertiesSupport": true - } - }, - "showDocument": { - "support": true - }, - "workDoneProgress": true - }, - "general": { - "staleRequestSupport": { - "cancel": true, - "retryOnContentModified": [ - "textDocument/semanticTokens/full", - "textDocument/semanticTokens/range", - "textDocument/semanticTokens/full/delta" - ] - }, - "regularExpressions": { - "engine": "ECMAScript", - "version": "ES2020" - }, - "markdown": { - "parser": "marked", - "version": "1.1.0", - "allowedTags": [ - "ul", - "li", - "p", - "code", - "blockquote", - "ol", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "hr", - "em", - "pre", - "table", - "thead", - "tbody", - "tr", - "th", - "td", - "div", - "del", - "a", - "strong", - "br", - "img", - "span" - ] - }, - "positionEncodings": [ - "utf-16" - ] - }, - "notebookDocument": { - "synchronization": { - "dynamicRegistration": true, - "executionSummarySupport": true - } - }, - "experimental": { - "snippetTextEdit": true, - "codeActionGroup": true, - "hoverActions": true, - "serverStatusNotification": true, - "colorDiagnosticOutput": true, - "openServerLogs": true, - "commands": { - "commands": [ - "editor.action.triggerParameterHints" - ] - } - } - }, - "initializationOptions": { - "RoslynExtensionsOptions": { - "EnableDecompilationSupport": false, - "EnableAnalyzersSupport": true, - "EnableImportCompletion": true, - "EnableAsyncCompletion": false, - "DocumentAnalysisTimeoutMs": 30000, - "DiagnosticWorkersThreadCount": 18, - "AnalyzeOpenDocumentsOnly": true, - "InlayHintsOptions": { - "EnableForParameters": false, - "ForLiteralParameters": false, - "ForIndexerParameters": false, - "ForObjectCreationParameters": false, - "ForOtherParameters": false, - "SuppressForParametersThatDifferOnlyBySuffix": false, - "SuppressForParametersThatMatchMethodIntent": false, - "SuppressForParametersThatMatchArgumentName": false, - "EnableForTypes": false, - "ForImplicitVariableTypes": false, - "ForLambdaParameterTypes": false, - "ForImplicitObjectCreation": false - }, - "LocationPaths": null - }, - "FormattingOptions": { - "OrganizeImports": false, - "EnableEditorConfigSupport": true, - "NewLine": "\n", - "UseTabs": false, - "TabSize": 4, - "IndentationSize": 4, - "SpacingAfterMethodDeclarationName": false, - "SeparateImportDirectiveGroups": false, - "SpaceWithinMethodDeclarationParenthesis": false, - "SpaceBetweenEmptyMethodDeclarationParentheses": false, - "SpaceAfterMethodCallName": false, - "SpaceWithinMethodCallParentheses": false, - "SpaceBetweenEmptyMethodCallParentheses": false, - "SpaceAfterControlFlowStatementKeyword": true, - "SpaceWithinExpressionParentheses": false, - "SpaceWithinCastParentheses": false, - "SpaceWithinOtherParentheses": false, - "SpaceAfterCast": false, - "SpaceBeforeOpenSquareBracket": false, - "SpaceBetweenEmptySquareBrackets": false, - "SpaceWithinSquareBrackets": false, - "SpaceAfterColonInBaseTypeDeclaration": true, - "SpaceAfterComma": true, - "SpaceAfterDot": false, - "SpaceAfterSemicolonsInForStatement": true, - "SpaceBeforeColonInBaseTypeDeclaration": true, - "SpaceBeforeComma": false, - "SpaceBeforeDot": false, - "SpaceBeforeSemicolonsInForStatement": false, - "SpacingAroundBinaryOperator": "single", - "IndentBraces": false, - "IndentBlock": true, - "IndentSwitchSection": true, - "IndentSwitchCaseSection": true, - "IndentSwitchCaseSectionWhenBlock": true, - "LabelPositioning": "oneLess", - "WrappingPreserveSingleLine": true, - "WrappingKeepStatementsOnSingleLine": true, - "NewLinesForBracesInTypes": true, - "NewLinesForBracesInMethods": true, - "NewLinesForBracesInProperties": true, - "NewLinesForBracesInAccessors": true, - "NewLinesForBracesInAnonymousMethods": true, - "NewLinesForBracesInControlBlocks": true, - "NewLinesForBracesInAnonymousTypes": true, - "NewLinesForBracesInObjectCollectionArrayInitializers": true, - "NewLinesForBracesInLambdaExpressionBody": true, - "NewLineForElse": true, - "NewLineForCatch": true, - "NewLineForFinally": true, - "NewLineForMembersInObjectInit": true, - "NewLineForMembersInAnonymousTypes": true, - "NewLineForClausesInQuery": true - }, - "FileOptions": { - "SystemExcludeSearchPatterns": [ - "**/node_modules/**/*", - "**/bin/**/*", - "**/obj/**/*", - "**/.git/**/*", - "**/.git", - "**/.svn", - "**/.hg", - "**/CVS", - "**/.DS_Store", - "**/Thumbs.db" - ], - "ExcludeSearchPatterns": [] - }, - "RenameOptions": { - "RenameOverloads": false, - "RenameInStrings": false, - "RenameInComments": false - }, - "ImplementTypeOptions": { - "InsertionBehavior": 0, - "PropertyGenerationBehavior": 0 - }, - "DotNetCliOptions": { - "LocationPaths": null - }, - "Plugins": { - "LocationPaths": null - } - }, - "trace": "verbose", - "workspaceFolders": [ - { - "uri": "$uri", - "name": "$name" - } - ] -} \ No newline at end of file diff --git a/src/multilspy/language_servers/omnisharp/omnisharp.py b/src/multilspy/language_servers/omnisharp/omnisharp.py deleted file mode 100644 index b376de7d..00000000 --- a/src/multilspy/language_servers/omnisharp/omnisharp.py +++ /dev/null @@ -1,406 +0,0 @@ -""" -Provides C# specific instantiation of the LanguageServer class. Contains various configurations and settings specific to C#. -""" - -import asyncio -import json -import logging -import os -import pathlib -import stat -from contextlib import asynccontextmanager -from typing import AsyncIterator, Iterable - -from overrides import override - -from solidlsp.multilspy_logger import MultilspyLogger -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_exceptions import MultilspyException -from solidlsp.multilspy_utils import FileUtils, PlatformUtils, PlatformId, DotnetVersion - - -def breadth_first_file_scan(root) -> Iterable[str]: - """ - This function was obtained from https://stackoverflow.com/questions/49654234/is-there-a-breadth-first-search-option-available-in-os-walk-or-equivalent-py - It traverses the directory tree in breadth first order. - """ - dirs = [root] - # while we has dirs to scan - while len(dirs): - next_dirs = [] - for parent in dirs: - # scan each dir - for f in os.listdir(parent): - # if there is a dir, then save for next ittr - # if it is a file then yield it (we'll return later) - ff = os.path.join(parent, f) - if os.path.isdir(ff): - next_dirs.append(ff) - else: - yield ff - - # once we've done all the current dirs then - # we set up the next itter as the child dirs - # from the current itter. - dirs = next_dirs - - -def find_least_depth_sln_file(root_dir) -> str | None: - for filename in breadth_first_file_scan(root_dir): - if filename.endswith(".sln"): - return filename - return None - - -class OmniSharp(LanguageServer): - """ - Provides C# specific instantiation of the LanguageServer class. Contains various configurations and settings specific to C#. - """ - - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): - """ - Creates an OmniSharp instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. - """ - omnisharp_executable_path, dll_path = self.setupRuntimeDependencies(logger, config) - - slnfilename = find_least_depth_sln_file(repository_root_path) - if slnfilename is None: - logger.log("No *.sln file found in repository", logging.ERROR) - raise MultilspyException("No SLN file found in repository") - - cmd = " ".join( - [ - omnisharp_executable_path, - "-lsp", - "--encoding", - "ascii", - "-z", - "-s", - f'"{slnfilename}"', - "--hostPID", - str(os.getpid()), - "DotNet:enablePackageRestore=false", - "--loglevel", - "trace", - "--plugin", - dll_path, - "FileOptions:SystemExcludeSearchPatterns:0=**/.git", - "FileOptions:SystemExcludeSearchPatterns:1=**/.svn", - "FileOptions:SystemExcludeSearchPatterns:2=**/.hg", - "FileOptions:SystemExcludeSearchPatterns:3=**/CVS", - "FileOptions:SystemExcludeSearchPatterns:4=**/.DS_Store", - "FileOptions:SystemExcludeSearchPatterns:5=**/Thumbs.db", - "RoslynExtensionsOptions:EnableAnalyzersSupport=true", - "FormattingOptions:EnableEditorConfigSupport=true", - "RoslynExtensionsOptions:EnableImportCompletion=true", - "Sdk:IncludePrereleases=true", - "RoslynExtensionsOptions:AnalyzeOpenDocumentsOnly=true", - "formattingOptions:useTabs=false", - "formattingOptions:tabSize=4", - "formattingOptions:indentationSize=4", - ] - ) - super().__init__( - config, logger, repository_root_path, ProcessLaunchInfo(cmd=cmd, cwd=repository_root_path), "csharp" - ) - - self.definition_available = asyncio.Event() - self.references_available = asyncio.Event() - - @override - def is_ignored_dirname(self, dirname: str) -> bool: - return super().is_ignored_dirname(dirname) or dirname in ["bin", "obj"] - - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the Omnisharp Language Server. - """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: - d = json.load(f) - - del d["_description"] - - d["processId"] = os.getpid() - assert d["rootPath"] == "$rootPath" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "$rootUri" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["uri"] == "$uri" - d["workspaceFolders"][0]["uri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["name"] == "$name" - d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) - - return d - - def setupRuntimeDependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> tuple[str, str]: - """ - Setup runtime dependencies for OmniSharp. - """ - platform_id = PlatformUtils.get_platform_id() - dotnet_version = PlatformUtils.get_dotnet_version() - - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f: - d = json.load(f) - del d["_description"] - - assert platform_id in [ - PlatformId.LINUX_x64, - PlatformId.WIN_x64, - ], "Only linux-x64 and win-x64 platform is supported for in multilspy at the moment" - assert dotnet_version in [ - DotnetVersion.V6, - DotnetVersion.V7, - DotnetVersion.V8 - ], "Only dotnet version 6 and 7 are supported in multilspy at the moment" - - # TODO: Do away with this assumption - # Currently, runtime binaries are not available for .Net 7 and .Net 8. Hence, we assume .Net 6 runtime binaries to be compatible with .Net 7, .Net 8 - if dotnet_version in [DotnetVersion.V7, DotnetVersion.V8]: - dotnet_version = DotnetVersion.V6 - - runtime_dependencies = d["runtimeDependencies"] - runtime_dependencies = [ - dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value - ] - runtime_dependencies = [ - dependency - for dependency in runtime_dependencies - if not ("dotnet_version" in dependency) or dependency["dotnet_version"] == dotnet_version.value - ] - assert len(runtime_dependencies) == 2 - runtime_dependencies = { - runtime_dependencies[0]["id"]: runtime_dependencies[0], - runtime_dependencies[1]["id"]: runtime_dependencies[1], - } - - assert "OmniSharp" in runtime_dependencies - assert "RazorOmnisharp" in runtime_dependencies - - omnisharp_ls_dir = os.path.join(os.path.dirname(__file__), "static", "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" - ) - 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, stat.S_IEXEC) - - razor_omnisharp_ls_dir = os.path.join(os.path.dirname(__file__), "static", "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" - ) - razor_omnisharp_dll_path = os.path.join( - razor_omnisharp_ls_dir, runtime_dependencies["RazorOmnisharp"]["dll_path"] - ) - assert os.path.exists(razor_omnisharp_dll_path) - - return omnisharp_executable_path, razor_omnisharp_dll_path - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["OmniSharp"]: - """ - Starts the Omnisharp Language Server, waits for the server to be ready and yields the LanguageServer instance. - - Usage: - ``` - async with lsp.start_server(): - # LanguageServer has been initialized and ready to serve requests - await lsp.request_definition(...) - await lsp.request_references(...) - # Shutdown the LanguageServer on exit from scope - # LanguageServer has been shutdown - """ - - async def register_capability_handler(params): - assert "registrations" in params - for registration in params["registrations"]: - if registration["method"] == "textDocument/definition": - self.definition_available.set() - if registration["method"] == "textDocument/references": - self.references_available.set() - if registration["method"] == "textDocument/completion": - self.completions_available.set() - - async def lang_status_handler(params): - # TODO: Should we wait for - # server -> client: {'jsonrpc': '2.0', 'method': 'language/status', 'params': {'type': 'ProjectStatus', 'message': 'OK'}} - # Before proceeding? - # if params["type"] == "ServiceReady" and params["message"] == "ServiceReady": - # self.service_ready_event.set() - pass - - async def execute_client_command_handler(params): - return [] - - async def do_nothing(params): - return - - async def check_experimental_status(params): - if params["quiescent"] == True: - self.server_ready.set() - - async def window_log_message(msg): - self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - async def workspace_configuration_handler(params): - # TODO: We do not know the appropriate way to handle this request. Should ideally contact the OmniSharp dev team - return [ - { - "RoslynExtensionsOptions": { - "EnableDecompilationSupport": False, - "EnableAnalyzersSupport": True, - "EnableImportCompletion": True, - "EnableAsyncCompletion": False, - "DocumentAnalysisTimeoutMs": 30000, - "DiagnosticWorkersThreadCount": 18, - "AnalyzeOpenDocumentsOnly": True, - "InlayHintsOptions": { - "EnableForParameters": False, - "ForLiteralParameters": False, - "ForIndexerParameters": False, - "ForObjectCreationParameters": False, - "ForOtherParameters": False, - "SuppressForParametersThatDifferOnlyBySuffix": False, - "SuppressForParametersThatMatchMethodIntent": False, - "SuppressForParametersThatMatchArgumentName": False, - "EnableForTypes": False, - "ForImplicitVariableTypes": False, - "ForLambdaParameterTypes": False, - "ForImplicitObjectCreation": False, - }, - "LocationPaths": None, - }, - "FormattingOptions": { - "OrganizeImports": False, - "EnableEditorConfigSupport": True, - "NewLine": "\n", - "UseTabs": False, - "TabSize": 4, - "IndentationSize": 4, - "SpacingAfterMethodDeclarationName": False, - "SeparateImportDirectiveGroups": False, - "SpaceWithinMethodDeclarationParenthesis": False, - "SpaceBetweenEmptyMethodDeclarationParentheses": False, - "SpaceAfterMethodCallName": False, - "SpaceWithinMethodCallParentheses": False, - "SpaceBetweenEmptyMethodCallParentheses": False, - "SpaceAfterControlFlowStatementKeyword": True, - "SpaceWithinExpressionParentheses": False, - "SpaceWithinCastParentheses": False, - "SpaceWithinOtherParentheses": False, - "SpaceAfterCast": False, - "SpaceBeforeOpenSquareBracket": False, - "SpaceBetweenEmptySquareBrackets": False, - "SpaceWithinSquareBrackets": False, - "SpaceAfterColonInBaseTypeDeclaration": True, - "SpaceAfterComma": True, - "SpaceAfterDot": False, - "SpaceAfterSemicolonsInForStatement": True, - "SpaceBeforeColonInBaseTypeDeclaration": True, - "SpaceBeforeComma": False, - "SpaceBeforeDot": False, - "SpaceBeforeSemicolonsInForStatement": False, - "SpacingAroundBinaryOperator": "single", - "IndentBraces": False, - "IndentBlock": True, - "IndentSwitchSection": True, - "IndentSwitchCaseSection": True, - "IndentSwitchCaseSectionWhenBlock": True, - "LabelPositioning": "oneLess", - "WrappingPreserveSingleLine": True, - "WrappingKeepStatementsOnSingleLine": True, - "NewLinesForBracesInTypes": True, - "NewLinesForBracesInMethods": True, - "NewLinesForBracesInProperties": True, - "NewLinesForBracesInAccessors": True, - "NewLinesForBracesInAnonymousMethods": True, - "NewLinesForBracesInControlBlocks": True, - "NewLinesForBracesInAnonymousTypes": True, - "NewLinesForBracesInObjectCollectionArrayInitializers": True, - "NewLinesForBracesInLambdaExpressionBody": True, - "NewLineForElse": True, - "NewLineForCatch": True, - "NewLineForFinally": True, - "NewLineForMembersInObjectInit": True, - "NewLineForMembersInAnonymousTypes": True, - "NewLineForClausesInQuery": True, - }, - "FileOptions": { - "SystemExcludeSearchPatterns": [ - "**/node_modules/**/*", - "**/bin/**/*", - "**/obj/**/*", - "**/.git/**/*", - "**/.git", - "**/.svn", - "**/.hg", - "**/CVS", - "**/.DS_Store", - "**/Thumbs.db", - ], - "ExcludeSearchPatterns": [], - }, - "RenameOptions": { - "RenameOverloads": False, - "RenameInStrings": False, - "RenameInComments": False, - }, - "ImplementTypeOptions": { - "InsertionBehavior": 0, - "PropertyGenerationBehavior": 0, - }, - "DotNetCliOptions": {"LocationPaths": None}, - "Plugins": {"LocationPaths": None}, - } - ] - - self.server.on_request("client/registerCapability", register_capability_handler) - self.server.on_notification("language/status", lang_status_handler) - self.server.on_notification("window/logMessage", window_log_message) - self.server.on_request("workspace/executeClientCommand", execute_client_command_handler) - self.server.on_notification("$/progress", do_nothing) - self.server.on_notification("textDocument/publishDiagnostics", do_nothing) - self.server.on_notification("language/actionableNotification", do_nothing) - self.server.on_notification("experimental/serverStatus", check_experimental_status) - self.server.on_request("workspace/configuration", workspace_configuration_handler) - - async with super().start_server(): - self.logger.log("Starting OmniSharp server process", logging.INFO) - await 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, - ) - init_response = await self.server.send.initialize(initialize_params) - self.server.notify.initialized({}) - with open(os.path.join(os.path.dirname(__file__), "workspace_did_change_configuration.json"), "r", encoding="utf-8") as f: - self.server.notify.workspace_did_change_configuration({ - "settings": json.load(f) - }) - assert "capabilities" in init_response - if ( - "definitionProvider" in init_response["capabilities"] - and init_response["capabilities"]["definitionProvider"] - ): - self.definition_available.set() - if ( - "referencesProvider" in init_response["capabilities"] - and init_response["capabilities"]["referencesProvider"] - ): - self.references_available.set() - - await self.definition_available.wait() - await self.references_available.wait() - - yield self diff --git a/src/multilspy/language_servers/omnisharp/runtime_dependencies.json b/src/multilspy/language_servers/omnisharp/runtime_dependencies.json deleted file mode 100644 index 7dfa1fd4..00000000 --- a/src/multilspy/language_servers/omnisharp/runtime_dependencies.json +++ /dev/null @@ -1,441 +0,0 @@ -{ - "_description": "Used to download the runtime dependencies for running OmniSharp. Obtained from https://github.com/dotnet/vscode-csharp/blob/main/package.json", - "runtimeDependencies": [ - { - "id": "OmniSharp", - "description": "OmniSharp for Windows (.NET 4 / x86)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-x86-1.39.10.zip", - "installPath": ".omnisharp/1.39.10", - "platforms": [ - "win32" - ], - "architectures": [ - "x86" - ], - "installTestPath": "./.omnisharp/1.39.10/OmniSharp.exe", - "platformId": "win-x86", - "isFramework": true, - "integrity": "C81CE2099AD494EF63F9D88FAA70D55A68CF175810F944526FF94AAC7A5109F9", - "dotnet_version": "4", - "binaryName": "OmniSharp.exe" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Windows (.NET 6 / x86)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-x86-net6.0-1.39.10.zip", - "installPath": ".omnisharp/1.39.10-net6.0", - "platforms": [ - "win32" - ], - "architectures": [ - "x86" - ], - "installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll", - "platformId": "win-x86", - "isFramework": false, - "integrity": "B7E62415CFC3DAC2154AC636C5BF0FB4B2C9BBF11B5A1FBF72381DDDED59791E", - "dotnet_version": "6", - "binaryName": "OmniSharp.exe" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Windows (.NET 4 / x64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-x64-1.39.10.zip", - "installPath": ".omnisharp/1.39.10", - "platforms": [ - "win32" - ], - "architectures": [ - "x86_64" - ], - "installTestPath": "./.omnisharp/1.39.10/OmniSharp.exe", - "platformId": "win-x64", - "isFramework": true, - "integrity": "BE0ED10AACEA17E14B78BD0D887DE5935D4ECA3712192A701F3F2100CA3C8B6E", - "dotnet_version": "4", - "binaryName": "OmniSharp.exe" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Windows (.NET 6 / x64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-x64-net6.0-1.39.10.zip", - "installPath": ".omnisharp/1.39.10-net6.0", - "platforms": [ - "win32" - ], - "architectures": [ - "x86_64" - ], - "installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll", - "platformId": "win-x64", - "isFramework": false, - "integrity": "A73327395E7EF92C1D8E307055463DA412662C03F077ECC743462FD2760BB537", - "dotnet_version": "6", - "binaryName": "OmniSharp.exe" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Windows (.NET 4 / arm64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-arm64-1.39.10.zip", - "installPath": ".omnisharp/1.39.10", - "platforms": [ - "win32" - ], - "architectures": [ - "arm64" - ], - "installTestPath": "./.omnisharp/1.39.10/OmniSharp.exe", - "platformId": "win-arm64", - "isFramework": true, - "integrity": "32FA0067B0639F87760CD1A769B16E6A53588C137C4D31661836CA4FB28D3DD6", - "dotnet_version": "4", - "binaryName": "OmniSharp.exe" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Windows (.NET 6 / arm64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-win-arm64-net6.0-1.39.10.zip", - "installPath": ".omnisharp/1.39.10-net6.0", - "platforms": [ - "win32" - ], - "architectures": [ - "arm64" - ], - "installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll", - "platformId": "win-arm64", - "isFramework": false, - "integrity": "433F9B360CAA7B4DDD85C604D5C5542C1A718BCF2E71B2BCFC7526E6D41F4E8F", - "dotnet_version": "6", - "binaryName": "OmniSharp.exe" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for OSX (Mono / x64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-osx-1.39.10.zip", - "installPath": ".omnisharp/1.39.10", - "platforms": [ - "darwin" - ], - "architectures": [ - "x86_64", - "arm64" - ], - "binaries": [ - "./mono.osx", - "./run" - ], - "installTestPath": "./.omnisharp/1.39.10/run", - "platformId": "osx", - "isFramework": true, - "integrity": "2CC42F0EC7C30CFA8858501D12ECB6FB685A1FCFB8ECB35698A4B12406551968", - "dotnet_version": "mono" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for OSX (.NET 6 / x64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-osx-x64-net6.0-1.39.10.zip", - "installPath": ".omnisharp/1.39.10-net6.0", - "platforms": [ - "darwin" - ], - "architectures": [ - "x86_64" - ], - "installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll", - "platformId": "osx-x64", - "isFramework": false, - "integrity": "C9D6E9F2C839A66A7283AE6A9EC545EE049B48EB230D33E91A6322CB67FF9D97", - "dotnet_version": "6" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for OSX (.NET 6 / arm64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-osx-arm64-net6.0-1.39.10.zip", - "installPath": ".omnisharp/1.39.10-net6.0", - "platforms": [ - "darwin" - ], - "architectures": [ - "arm64" - ], - "installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll", - "platformId": "osx-arm64", - "isFramework": false, - "integrity": "851350F52F83E3BAD5A92D113E4B9882FCD1DEB16AA84FF94B6F2CEE3C70051E", - "dotnet_version": "6" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Linux (Mono / x86)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-x86-1.39.10.zip", - "installPath": ".omnisharp/1.39.10", - "platforms": [ - "linux" - ], - "architectures": [ - "x86", - "i686" - ], - "binaries": [ - "./mono.linux-x86", - "./run" - ], - "installTestPath": "./.omnisharp/1.39.10/run", - "platformId": "linux-x86", - "isFramework": true, - "integrity": "474B1CDBAE64CFEC655FB6B0659BCE481023C48274441C72991E67B6E13E56A1", - "dotnet_version": "mono" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Linux (Mono / x64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-x64-1.39.10.zip", - "installPath": ".omnisharp/1.39.10", - "platforms": [ - "linux" - ], - "architectures": [ - "x86_64" - ], - "binaries": [ - "./mono.linux-x86_64", - "./run" - ], - "installTestPath": "./.omnisharp/1.39.10/run", - "platformId": "linux-x64", - "isFramework": true, - "integrity": "FB4CAA47343265100349375D79DBCCE1868950CED675CB07FCBE8462EDBCDD37", - "dotnet_version": "mono" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Linux (.NET 6 / x64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-x64-net6.0-1.39.10.zip", - "installPath": ".omnisharp/1.39.10-net6.0", - "platforms": [ - "linux" - ], - "architectures": [ - "x86_64" - ], - "installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll", - "platformId": "linux-x64", - "isFramework": false, - "integrity": "0926D3BEA060BF4373356B2FC0A68C10D0DE1B1150100B551BA5932814CE51E2", - "dotnet_version": "6", - "binaryName": "OmniSharp" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Linux (Mono / arm64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-arm64-1.39.10.zip", - "installPath": ".omnisharp/1.39.10", - "platforms": [ - "linux" - ], - "architectures": [ - "arm64" - ], - "binaries": [ - "./mono.linux-arm64", - "./run" - ], - "installTestPath": "./.omnisharp/1.39.10/run", - "platformId": "linux-arm64", - "isFramework": true, - "integrity": "478F3594DFD0167E9A56E36F0364A86C73F8132A3E7EA916CA1419EFE141D2CC", - "dotnet_version": "mono" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Linux (.NET 6 / arm64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-arm64-net6.0-1.39.10.zip", - "installPath": ".omnisharp/1.39.10-net6.0", - "platforms": [ - "linux" - ], - "architectures": [ - "arm64" - ], - "installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll", - "platformId": "linux-arm64", - "isFramework": false, - "integrity": "6FB6A572043A74220A92F6C19C7BB0C3743321C7563A815FD2702EF4FA7D688E", - "dotnet_version": "6" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Linux musl (.NET 6 / x64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-musl-x64-net6.0-1.39.10.zip", - "installPath": ".omnisharp/1.39.10-net6.0", - "platforms": [ - "linux-musl" - ], - "architectures": [ - "x86_64" - ], - "installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll", - "platformId": "linux-musl-x64", - "isFramework": false, - "integrity": "6BFDA3AD11DBB0C6514B86ECC3E1597CC41C6E309B7575F7C599E07D9E2AE610", - "dotnet_version": "6" - }, - { - "id": "OmniSharp", - "description": "OmniSharp for Linux musl (.NET 6 / arm64)", - "url": "https://roslynomnisharp.blob.core.windows.net/releases/1.39.10/omnisharp-linux-musl-arm64-net6.0-1.39.10.zip", - "installPath": ".omnisharp/1.39.10-net6.0", - "platforms": [ - "linux-musl" - ], - "architectures": [ - "arm64" - ], - "installTestPath": "./.omnisharp/1.39.10-net6.0/OmniSharp.dll", - "platformId": "linux-musl-arm64", - "isFramework": false, - "integrity": "DA63619EA024EB9BBF6DB5A85C6150CAB5C0BD554544A3596ED1B17F926D6875", - "dotnet_version": "6" - }, - { - "id": "RazorOmnisharp", - "description": "Razor Language Server for OmniSharp (Windows / x64)", - "url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/8d42e62ea4051381c219b3e31bc4eced/razorlanguageserver-win-x64-7.0.0-preview.23363.1.zip", - "installPath": ".razoromnisharp", - "platforms": [ - "win32" - ], - "architectures": [ - "x86_64" - ], - "platformId": "win-x64", - "dll_path": "OmniSharpPlugin/Microsoft.AspNetCore.Razor.OmniSharpPlugin.dll" - }, - { - "id": "RazorOmnisharp", - "description": "Razor Language Server for OmniSharp (Windows / x86)", - "url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/e440c4f3a4a96334fe177513935fa010/razorlanguageserver-win-x86-7.0.0-preview.23363.1.zip", - "installPath": ".razoromnisharp", - "platforms": [ - "win32" - ], - "architectures": [ - "x86" - ], - "platformId": "win-x86", - "dll_path": "OmniSharpPlugin/Microsoft.AspNetCore.Razor.OmniSharpPlugin.dll" - }, - { - "id": "RazorOmnisharp", - "description": "Razor Language Server for OmniSharp (Windows / ARM64)", - "url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/4ef26e45cf32fe8d51c0e7dd21f1fef6/razorlanguageserver-win-arm64-7.0.0-preview.23363.1.zip", - "installPath": ".razoromnisharp", - "platforms": [ - "win32" - ], - "architectures": [ - "arm64" - ], - "platformId": "win-arm64", - "dll_path": "OmniSharpPlugin/Microsoft.AspNetCore.Razor.OmniSharpPlugin.dll" - }, - { - "id": "RazorOmnisharp", - "description": "Razor Language Server for OmniSharp (Linux / x64)", - "url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/6d4e23a3c7cf0465743950a39515a716/razorlanguageserver-linux-x64-7.0.0-preview.23363.1.zip", - "installPath": ".razoromnisharp", - "platforms": [ - "linux" - ], - "architectures": [ - "x86_64" - ], - "binaries": [ - "./rzls" - ], - "platformId": "linux-x64", - "dll_path": "OmniSharpPlugin/Microsoft.AspNetCore.Razor.OmniSharpPlugin.dll" - }, - { - "id": "RazorOmnisharp", - "description": "Razor Language Server for OmniSharp (Linux ARM64)", - "url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/85deebd44647ebf65724cc291d722283/razorlanguageserver-linux-arm64-7.0.0-preview.23363.1.zip", - "installPath": ".razoromnisharp", - "platforms": [ - "linux" - ], - "architectures": [ - "arm64" - ], - "binaries": [ - "./rzls" - ], - "platformId": "linux-arm64" - }, - { - "id": "RazorOmnisharp", - "description": "Razor Language Server for OmniSharp (Linux musl / x64)", - "url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/4f0caa94ae182785655efb15eafcef23/razorlanguageserver-linux-musl-x64-7.0.0-preview.23363.1.zip", - "installPath": ".razoromnisharp", - "platforms": [ - "linux-musl" - ], - "architectures": [ - "x86_64" - ], - "binaries": [ - "./rzls" - ], - "platformId": "linux-musl-x64" - }, - { - "id": "RazorOmnisharp", - "description": "Razor Language Server for OmniSharp (Linux musl ARM64)", - "url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/0a24828206a6f3b4bc743d058ef88ce7/razorlanguageserver-linux-musl-arm64-7.0.0-preview.23363.1.zip", - "installPath": ".razoromnisharp", - "platforms": [ - "linux-musl" - ], - "architectures": [ - "arm64" - ], - "binaries": [ - "./rzls" - ], - "platformId": "linux-musl-arm64" - }, - { - "id": "RazorOmnisharp", - "description": "Razor Language Server for OmniSharp (macOS / x64)", - "url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/2afcafaf41082989efcc10405abb9314/razorlanguageserver-osx-x64-7.0.0-preview.23363.1.zip", - "installPath": ".razoromnisharp", - "platforms": [ - "darwin" - ], - "architectures": [ - "x86_64" - ], - "binaries": [ - "./rzls" - ], - "platformId": "osx-x64" - }, - { - "id": "RazorOmnisharp", - "description": "Razor Language Server for OmniSharp (macOS ARM64)", - "url": "https://download.visualstudio.microsoft.com/download/pr/aee63398-023f-48db-bba2-30162c68f0c4/8bf2ed2f00d481a5987e3eb5165afddd/razorlanguageserver-osx-arm64-7.0.0-preview.23363.1.zip", - "installPath": ".razoromnisharp", - "platforms": [ - "darwin" - ], - "architectures": [ - "arm64" - ], - "binaries": [ - "./rzls" - ], - "platformId": "osx-arm64" - } - ] -} \ No newline at end of file diff --git a/src/multilspy/language_servers/omnisharp/workspace_did_change_configuration.json b/src/multilspy/language_servers/omnisharp/workspace_did_change_configuration.json deleted file mode 100644 index 148841c3..00000000 --- a/src/multilspy/language_servers/omnisharp/workspace_did_change_configuration.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "RoslynExtensionsOptions": { - "EnableDecompilationSupport": false, - "EnableAnalyzersSupport": true, - "EnableImportCompletion": true, - "EnableAsyncCompletion": false, - "DocumentAnalysisTimeoutMs": 30000, - "DiagnosticWorkersThreadCount": 18, - "AnalyzeOpenDocumentsOnly": true, - "InlayHintsOptions": { - "EnableForParameters": false, - "ForLiteralParameters": false, - "ForIndexerParameters": false, - "ForObjectCreationParameters": false, - "ForOtherParameters": false, - "SuppressForParametersThatDifferOnlyBySuffix": false, - "SuppressForParametersThatMatchMethodIntent": false, - "SuppressForParametersThatMatchArgumentName": false, - "EnableForTypes": false, - "ForImplicitVariableTypes": false, - "ForLambdaParameterTypes": false, - "ForImplicitObjectCreation": false - }, - "LocationPaths": null - }, - "FormattingOptions": { - "OrganizeImports": false, - "EnableEditorConfigSupport": true, - "NewLine": "\n", - "UseTabs": false, - "TabSize": 4, - "IndentationSize": 4, - "SpacingAfterMethodDeclarationName": false, - "SeparateImportDirectiveGroups": false, - "SpaceWithinMethodDeclarationParenthesis": false, - "SpaceBetweenEmptyMethodDeclarationParentheses": false, - "SpaceAfterMethodCallName": false, - "SpaceWithinMethodCallParentheses": false, - "SpaceBetweenEmptyMethodCallParentheses": false, - "SpaceAfterControlFlowStatementKeyword": true, - "SpaceWithinExpressionParentheses": false, - "SpaceWithinCastParentheses": false, - "SpaceWithinOtherParentheses": false, - "SpaceAfterCast": false, - "SpaceBeforeOpenSquareBracket": false, - "SpaceBetweenEmptySquareBrackets": false, - "SpaceWithinSquareBrackets": false, - "SpaceAfterColonInBaseTypeDeclaration": true, - "SpaceAfterComma": true, - "SpaceAfterDot": false, - "SpaceAfterSemicolonsInForStatement": true, - "SpaceBeforeColonInBaseTypeDeclaration": true, - "SpaceBeforeComma": false, - "SpaceBeforeDot": false, - "SpaceBeforeSemicolonsInForStatement": false, - "SpacingAroundBinaryOperator": "single", - "IndentBraces": false, - "IndentBlock": true, - "IndentSwitchSection": true, - "IndentSwitchCaseSection": true, - "IndentSwitchCaseSectionWhenBlock": true, - "LabelPositioning": "oneLess", - "WrappingPreserveSingleLine": true, - "WrappingKeepStatementsOnSingleLine": true, - "NewLinesForBracesInTypes": true, - "NewLinesForBracesInMethods": true, - "NewLinesForBracesInProperties": true, - "NewLinesForBracesInAccessors": true, - "NewLinesForBracesInAnonymousMethods": true, - "NewLinesForBracesInControlBlocks": true, - "NewLinesForBracesInAnonymousTypes": true, - "NewLinesForBracesInObjectCollectionArrayInitializers": true, - "NewLinesForBracesInLambdaExpressionBody": true, - "NewLineForElse": true, - "NewLineForCatch": true, - "NewLineForFinally": true, - "NewLineForMembersInObjectInit": true, - "NewLineForMembersInAnonymousTypes": true, - "NewLineForClausesInQuery": true - }, - "FileOptions": { - "SystemExcludeSearchPatterns": [ - "**/node_modules/**/*", - "**/bin/**/*", - "**/obj/**/*", - "**/.git/**/*", - "**/.git", - "**/.svn", - "**/.hg", - "**/CVS", - "**/.DS_Store", - "**/Thumbs.db" - ], - "ExcludeSearchPatterns": [] - }, - "RenameOptions": { - "RenameOverloads": false, - "RenameInStrings": false, - "RenameInComments": false - }, - "ImplementTypeOptions": { - "InsertionBehavior": 0, - "PropertyGenerationBehavior": 0 - }, - "DotNetCliOptions": { - "LocationPaths": null - }, - "Plugins": { - "LocationPaths": null - } -} \ No newline at end of file diff --git a/src/multilspy/language_servers/pyright_language_server/pyright_server.py b/src/multilspy/language_servers/pyright_language_server/pyright_server.py deleted file mode 100644 index 0cb9533a..00000000 --- a/src/multilspy/language_servers/pyright_language_server/pyright_server.py +++ /dev/null @@ -1,245 +0,0 @@ -""" -Provides Python specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Python. -""" - -import asyncio -import logging -import os -import pathlib -import re -from contextlib import asynccontextmanager -from typing import AsyncIterator - -from overrides import override - -from solidlsp.multilspy_logger import MultilspyLogger -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams -from solidlsp.multilspy_config import MultilspyConfig - - -class PyrightServer(LanguageServer): - """ - Provides Python specific instantiation of the LanguageServer class using Pyright. - Contains various configurations and settings specific to Python. - """ - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): - """ - 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", - ) - - # Event to signal when initial workspace analysis is complete - self.analysis_complete = asyncio.Event() - self.found_source_files = False - - @override - def is_ignored_dirname(self, dirname: str) -> bool: - return super().is_ignored_dirname(dirname) or dirname in ["venv", "__pycache__"] - - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the Pyright Language Server. - """ - # Create basic initialization parameters - initialize_params: InitializeParams = { # type: ignore - "processId": os.getpid(), - "rootPath": repository_absolute_path, - "rootUri": pathlib.Path(repository_absolute_path).as_uri(), - "initializationOptions": { - "exclude": [ - "**/__pycache__", - "**/.venv", - "**/.env", - "**/build", - "**/dist", - "**/.pixi", - ], - "reportMissingImports": "error", - }, - "capabilities": { - "workspace": { - "applyEdit": True, - "workspaceEdit": {"documentChanges": True}, - "didChangeConfiguration": {"dynamicRegistration": True}, - "didChangeWatchedFiles": {"dynamicRegistration": True}, - "symbol": { - "dynamicRegistration": True, - "symbolKind": { - "valueSet": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] - }, - }, - "executeCommand": {"dynamicRegistration": True}, - }, - "textDocument": { - "synchronization": {"dynamicRegistration": True, "willSave": True, "willSaveWaitUntil": True, "didSave": True}, - "completion": { - "dynamicRegistration": True, - "contextSupport": True, - "completionItem": { - "snippetSupport": True, - "commitCharactersSupport": True, - "documentationFormat": ["markdown", "plaintext"], - "deprecatedSupport": True, - "preselectSupport": True, - }, - "completionItemKind": { - "valueSet": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] - }, - }, - "hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]}, - "signatureHelp": { - "dynamicRegistration": True, - "signatureInformation": { - "documentationFormat": ["markdown", "plaintext"], - "parameterInformation": {"labelOffsetSupport": True}, - }, - }, - "definition": {"dynamicRegistration": True}, - "references": {"dynamicRegistration": True}, - "documentHighlight": {"dynamicRegistration": True}, - "documentSymbol": { - "dynamicRegistration": True, - "symbolKind": { - "valueSet": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] - }, - "hierarchicalDocumentSymbolSupport": True, - }, - "codeAction": { - "dynamicRegistration": True, - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "", - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite", - "source", - "source.organizeImports", - ] - } - }, - }, - "codeLens": {"dynamicRegistration": True}, - "formatting": {"dynamicRegistration": True}, - "rangeFormatting": {"dynamicRegistration": True}, - "onTypeFormatting": {"dynamicRegistration": True}, - "rename": {"dynamicRegistration": True}, - "publishDiagnostics": {"relatedInformation": True}, - }, - }, - "workspaceFolders": [ - {"uri": pathlib.Path(repository_absolute_path).as_uri(), "name": os.path.basename(repository_absolute_path)} - ], - } - - return initialize_params - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["PyrightServer"]: - """ - Starts the Pyright Language Server and waits for initial workspace analysis to complete. - - This prevents zombie processes by ensuring Pyright has finished its initial background - tasks before we consider the server ready. - - Usage: - ``` - async with lsp.start_server(): - # LanguageServer has been initialized and workspace analysis is complete - await lsp.request_definition(...) - await lsp.request_references(...) - # Shutdown the LanguageServer on exit from scope - # LanguageServer has been shutdown cleanly - ``` - """ - - async def execute_client_command_handler(params): - return [] - - async def do_nothing(params): - return - - async def window_log_message(msg): - """ - Monitor Pyright's log messages to detect when initial analysis is complete. - 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) - - # 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) - self.found_source_files = True - self.analysis_complete.set() - self.completions_available.set() - - async def check_experimental_status(params): - """ - 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) - if not self.found_source_files: - self.analysis_complete.set() - self.completions_available.set() - - # Set up notification handlers - self.server.on_request("client/registerCapability", do_nothing) - self.server.on_notification("language/status", do_nothing) - self.server.on_notification("window/logMessage", window_log_message) - self.server.on_request("workspace/executeClientCommand", execute_client_command_handler) - self.server.on_notification("$/progress", do_nothing) - self.server.on_notification("textDocument/publishDiagnostics", do_nothing) - self.server.on_notification("language/actionableNotification", do_nothing) - self.server.on_notification("experimental/serverStatus", check_experimental_status) - - async with super().start_server(): - self.logger.log("Starting pyright-langserver server process", logging.INFO) - await 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, - ) - init_response = await self.server.send.initialize(initialize_params) - self.logger.log(f"Received initialize response from pyright server: {init_response}", logging.INFO) - - # Verify that the server supports our required features - assert "textDocumentSync" in init_response["capabilities"] - assert "completionProvider" in init_response["capabilities"] - assert "definitionProvider" in init_response["capabilities"] - - # Complete the initialization handshake - self.server.notify.initialized({}) - - # 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) - try: - await asyncio.wait_for(self.analysis_complete.wait(), timeout=1.0) - self.logger.log("Pyright initial analysis complete, server ready", logging.INFO) - except asyncio.TimeoutError: - self.logger.log("Timeout waiting for Pyright analysis completion, proceeding anyway", logging.WARNING) - # Fallback: assume analysis is complete after timeout - self.analysis_complete.set() - self.completions_available.set() - - yield self \ No newline at end of file diff --git a/src/multilspy/language_servers/rust_analyzer/initialize_params.json b/src/multilspy/language_servers/rust_analyzer/initialize_params.json deleted file mode 100644 index 00368eed..00000000 --- a/src/multilspy/language_servers/rust_analyzer/initialize_params.json +++ /dev/null @@ -1,917 +0,0 @@ -{ - "_description": "The parameters sent by the client when initializing the language server with the \"initialize\" request. More details at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize", - "processId": "os.getpid()", - "clientInfo": { - "name": "Visual Studio Code - Insiders", - "version": "1.82.0-insider" - }, - "locale": "en", - "rootPath": "$rootPath", - "rootUri": "$rootUri", - "capabilities": { - "workspace": { - "applyEdit": true, - "workspaceEdit": { - "documentChanges": true, - "resourceOperations": [ - "create", - "rename", - "delete" - ], - "failureHandling": "textOnlyTransactional", - "normalizesLineEndings": true, - "changeAnnotationSupport": { - "groupsOnLabel": true - } - }, - "configuration": true, - "didChangeWatchedFiles": { - "dynamicRegistration": true, - "relativePatternSupport": true - }, - "symbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "resolveSupport": { - "properties": [ - "location.range" - ] - } - }, - "codeLens": { - "refreshSupport": true - }, - "executeCommand": { - "dynamicRegistration": true - }, - "didChangeConfiguration": { - "dynamicRegistration": true - }, - "workspaceFolders": true, - "semanticTokens": { - "refreshSupport": true - }, - "fileOperations": { - "dynamicRegistration": true, - "didCreate": true, - "didRename": true, - "didDelete": true, - "willCreate": true, - "willRename": true, - "willDelete": true - }, - "inlineValue": { - "refreshSupport": true - }, - "inlayHint": { - "refreshSupport": true - }, - "diagnostics": { - "refreshSupport": true - } - }, - "textDocument": { - "publishDiagnostics": { - "relatedInformation": true, - "versionSupport": false, - "tagSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "codeDescriptionSupport": true, - "dataSupport": true - }, - "synchronization": { - "dynamicRegistration": true, - "willSave": true, - "willSaveWaitUntil": true, - "didSave": true - }, - "completion": { - "dynamicRegistration": true, - "contextSupport": true, - "completionItem": { - "snippetSupport": true, - "commitCharactersSupport": true, - "documentationFormat": [ - "markdown", - "plaintext" - ], - "deprecatedSupport": true, - "preselectSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "insertReplaceSupport": true, - "resolveSupport": { - "properties": [ - "documentation", - "detail", - "additionalTextEdits" - ] - }, - "insertTextModeSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "labelDetailsSupport": true - }, - "insertTextMode": 2, - "completionItemKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25 - ] - }, - "completionList": { - "itemDefaults": [ - "commitCharacters", - "editRange", - "insertTextFormat", - "insertTextMode" - ] - } - }, - "hover": { - "dynamicRegistration": true, - "contentFormat": [ - "markdown", - "plaintext" - ] - }, - "signatureHelp": { - "dynamicRegistration": true, - "signatureInformation": { - "documentationFormat": [ - "markdown", - "plaintext" - ], - "parameterInformation": { - "labelOffsetSupport": true - }, - "activeParameterSupport": true - }, - "contextSupport": true - }, - "definition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "references": { - "dynamicRegistration": true - }, - "documentHighlight": { - "dynamicRegistration": true - }, - "documentSymbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "hierarchicalDocumentSymbolSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "labelSupport": true - }, - "codeAction": { - "dynamicRegistration": true, - "isPreferredSupport": true, - "disabledSupport": true, - "dataSupport": true, - "resolveSupport": { - "properties": [ - "edit" - ] - }, - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "", - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite", - "source", - "source.organizeImports" - ] - } - }, - "honorsChangeAnnotations": false - }, - "codeLens": { - "dynamicRegistration": true - }, - "formatting": { - "dynamicRegistration": true - }, - "rangeFormatting": { - "dynamicRegistration": true - }, - "onTypeFormatting": { - "dynamicRegistration": true - }, - "rename": { - "dynamicRegistration": true, - "prepareSupport": true, - "prepareSupportDefaultBehavior": 1, - "honorsChangeAnnotations": true - }, - "documentLink": { - "dynamicRegistration": true, - "tooltipSupport": true - }, - "typeDefinition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "implementation": { - "dynamicRegistration": true, - "linkSupport": true - }, - "colorProvider": { - "dynamicRegistration": true - }, - "foldingRange": { - "dynamicRegistration": true, - "rangeLimit": 5000, - "lineFoldingOnly": true, - "foldingRangeKind": { - "valueSet": [ - "comment", - "imports", - "region" - ] - }, - "foldingRange": { - "collapsedText": false - } - }, - "declaration": { - "dynamicRegistration": true, - "linkSupport": true - }, - "selectionRange": { - "dynamicRegistration": true - }, - "callHierarchy": { - "dynamicRegistration": true - }, - "semanticTokens": { - "dynamicRegistration": true, - "tokenTypes": [ - "namespace", - "type", - "class", - "enum", - "interface", - "struct", - "typeParameter", - "parameter", - "variable", - "property", - "enumMember", - "event", - "function", - "method", - "macro", - "keyword", - "modifier", - "comment", - "string", - "number", - "regexp", - "operator", - "decorator" - ], - "tokenModifiers": [ - "declaration", - "definition", - "readonly", - "static", - "deprecated", - "abstract", - "async", - "modification", - "documentation", - "defaultLibrary" - ], - "formats": [ - "relative" - ], - "requests": { - "range": true, - "full": { - "delta": true - } - }, - "multilineTokenSupport": false, - "overlappingTokenSupport": false, - "serverCancelSupport": true, - "augmentsSyntaxTokens": false - }, - "linkedEditingRange": { - "dynamicRegistration": true - }, - "typeHierarchy": { - "dynamicRegistration": true - }, - "inlineValue": { - "dynamicRegistration": true - }, - "inlayHint": { - "dynamicRegistration": true, - "resolveSupport": { - "properties": [ - "tooltip", - "textEdits", - "label.tooltip", - "label.location", - "label.command" - ] - } - }, - "diagnostic": { - "dynamicRegistration": true, - "relatedDocumentSupport": false - } - }, - "window": { - "showMessage": { - "messageActionItem": { - "additionalPropertiesSupport": true - } - }, - "showDocument": { - "support": true - }, - "workDoneProgress": true - }, - "general": { - "staleRequestSupport": { - "cancel": true, - "retryOnContentModified": [ - "textDocument/semanticTokens/full", - "textDocument/semanticTokens/range", - "textDocument/semanticTokens/full/delta" - ] - }, - "regularExpressions": { - "engine": "ECMAScript", - "version": "ES2020" - }, - "markdown": { - "parser": "marked", - "version": "1.1.0", - "allowedTags": [ - "ul", - "li", - "p", - "code", - "blockquote", - "ol", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "hr", - "em", - "pre", - "table", - "thead", - "tbody", - "tr", - "th", - "td", - "div", - "del", - "a", - "strong", - "br", - "img", - "span" - ] - }, - "positionEncodings": [ - "utf-16" - ] - }, - "notebookDocument": { - "synchronization": { - "dynamicRegistration": true, - "executionSummarySupport": true - } - }, - "experimental": { - "snippetTextEdit": true, - "codeActionGroup": true, - "hoverActions": true, - "serverStatusNotification": true, - "colorDiagnosticOutput": true, - "openServerLogs": true, - "localDocs": true, - "commands": { - "commands": [ - "rust-analyzer.runSingle", - "rust-analyzer.debugSingle", - "rust-analyzer.showReferences", - "rust-analyzer.gotoLocation", - "editor.action.triggerParameterHints" - ] - } - } - }, - "initializationOptions": { - "cargoRunner": null, - "runnables": { - "extraEnv": null, - "problemMatcher": [ - "$rustc" - ], - "command": null, - "extraArgs": [] - }, - "statusBar": { - "clickAction": "openLogs" - }, - "server": { - "path": null, - "extraEnv": null - }, - "trace": { - "server": "verbose", - "extension": false - }, - "debug": { - "engine": "auto", - "sourceFileMap": { - "/rustc/": "${env:USERPROFILE}/.rustup/toolchains//lib/rustlib/src/rust" - }, - "openDebugPane": false, - "engineSettings": {} - }, - "restartServerOnConfigChange": false, - "typing": { - "continueCommentsOnNewline": true, - "autoClosingAngleBrackets": { - "enable": false - } - }, - "diagnostics": { - "previewRustcOutput": false, - "useRustcErrorCode": false, - "disabled": [], - "enable": true, - "experimental": { - "enable": false - }, - "remapPrefix": {}, - "warningsAsHint": [], - "warningsAsInfo": [] - }, - "discoverProjectRunner": null, - "showUnlinkedFileNotification": true, - "showDependenciesExplorer": true, - "assist": { - "emitMustUse": false, - "expressionFillDefault": "todo" - }, - "cachePriming": { - "enable": true, - "numThreads": 0 - }, - "cargo": { - "autoreload": true, - "buildScripts": { - "enable": true, - "invocationLocation": "workspace", - "invocationStrategy": "per_workspace", - "overrideCommand": null, - "useRustcWrapper": true - }, - "cfgs": {}, - "extraArgs": [], - "extraEnv": {}, - "features": [], - "noDefaultFeatures": false, - "sysroot": "discover", - "sysrootSrc": null, - "target": null, - "unsetTest": [ - "core" - ] - }, - "checkOnSave": true, - "check": { - "allTargets": true, - "command": "check", - "extraArgs": [], - "extraEnv": {}, - "features": null, - "ignore": [], - "invocationLocation": "workspace", - "invocationStrategy": "per_workspace", - "noDefaultFeatures": null, - "overrideCommand": null, - "targets": null - }, - "completion": { - "autoimport": { - "enable": true - }, - "autoself": { - "enable": true - }, - "callable": { - "snippets": "fill_arguments" - }, - "fullFunctionSignatures": { - "enable": false - }, - "limit": null, - "postfix": { - "enable": true - }, - "privateEditable": { - "enable": false - }, - "snippets": { - "custom": { - "Arc::new": { - "postfix": "arc", - "body": "Arc::new(${receiver})", - "requires": "std::sync::Arc", - "description": "Put the expression into an `Arc`", - "scope": "expr" - }, - "Rc::new": { - "postfix": "rc", - "body": "Rc::new(${receiver})", - "requires": "std::rc::Rc", - "description": "Put the expression into an `Rc`", - "scope": "expr" - }, - "Box::pin": { - "postfix": "pinbox", - "body": "Box::pin(${receiver})", - "requires": "std::boxed::Box", - "description": "Put the expression into a pinned `Box`", - "scope": "expr" - }, - "Ok": { - "postfix": "ok", - "body": "Ok(${receiver})", - "description": "Wrap the expression in a `Result::Ok`", - "scope": "expr" - }, - "Err": { - "postfix": "err", - "body": "Err(${receiver})", - "description": "Wrap the expression in a `Result::Err`", - "scope": "expr" - }, - "Some": { - "postfix": "some", - "body": "Some(${receiver})", - "description": "Wrap the expression in an `Option::Some`", - "scope": "expr" - } - } - } - }, - "files": { - "excludeDirs": [], - "watcher": "client" - }, - "highlightRelated": { - "breakPoints": { - "enable": true - }, - "closureCaptures": { - "enable": true - }, - "exitPoints": { - "enable": true - }, - "references": { - "enable": true - }, - "yieldPoints": { - "enable": true - } - }, - "hover": { - "actions": { - "debug": { - "enable": true - }, - "enable": true, - "gotoTypeDef": { - "enable": true - }, - "implementations": { - "enable": true - }, - "references": { - "enable": false - }, - "run": { - "enable": true - } - }, - "documentation": { - "enable": true, - "keywords": { - "enable": true - } - }, - "links": { - "enable": true - }, - "memoryLayout": { - "alignment": "hexadecimal", - "enable": true, - "niches": false, - "offset": "hexadecimal", - "size": "both" - } - }, - "imports": { - "granularity": { - "enforce": false, - "group": "crate" - }, - "group": { - "enable": true - }, - "merge": { - "glob": true - }, - "preferNoStd": false, - "preferPrelude": false, - "prefix": "plain" - }, - "inlayHints": { - "bindingModeHints": { - "enable": false - }, - "chainingHints": { - "enable": true - }, - "closingBraceHints": { - "enable": true, - "minLines": 25 - }, - "closureCaptureHints": { - "enable": false - }, - "closureReturnTypeHints": { - "enable": "never" - }, - "closureStyle": "impl_fn", - "discriminantHints": { - "enable": "never" - }, - "expressionAdjustmentHints": { - "enable": "never", - "hideOutsideUnsafe": false, - "mode": "prefix" - }, - "lifetimeElisionHints": { - "enable": "never", - "useParameterNames": false - }, - "maxLength": 25, - "parameterHints": { - "enable": true - }, - "reborrowHints": { - "enable": "never" - }, - "renderColons": true, - "typeHints": { - "enable": true, - "hideClosureInitialization": false, - "hideNamedConstructor": false - } - }, - "interpret": { - "tests": false - }, - "joinLines": { - "joinAssignments": true, - "joinElseIf": true, - "removeTrailingComma": true, - "unwrapTrivialBlock": true - }, - "lens": { - "debug": { - "enable": true - }, - "enable": true, - "forceCustomCommands": true, - "implementations": { - "enable": true - }, - "location": "above_name", - "references": { - "adt": { - "enable": false - }, - "enumVariant": { - "enable": false - }, - "method": { - "enable": false - }, - "trait": { - "enable": false - } - }, - "run": { - "enable": true - } - }, - "linkedProjects": [], - "lru": { - "capacity": null, - "query": { - "capacities": {} - } - }, - "notifications": { - "cargoTomlNotFound": true - }, - "numThreads": null, - "procMacro": { - "attributes": { - "enable": true - }, - "enable": true, - "ignored": {}, - "server": null - }, - "references": { - "excludeImports": false - }, - "rust": { - "analyzerTargetDir": null - }, - "rustc": { - "source": null - }, - "rustfmt": { - "extraArgs": [], - "overrideCommand": null, - "rangeFormatting": { - "enable": false - } - }, - "semanticHighlighting": { - "doc": { - "comment": { - "inject": { - "enable": true - } - } - }, - "nonStandardTokens": true, - "operator": { - "enable": true, - "specialization": { - "enable": false - } - }, - "punctuation": { - "enable": false, - "separate": { - "macro": { - "bang": false - } - }, - "specialization": { - "enable": false - } - }, - "strings": { - "enable": true - } - }, - "signatureInfo": { - "detail": "full", - "documentation": { - "enable": true - } - }, - "workspace": { - "symbol": { - "search": { - "kind": "only_types", - "limit": 128, - "scope": "workspace" - } - } - } - }, - "trace": "verbose", - "workspaceFolders": [ - { - "uri": "$uri", - "name": "$name" - } - ] -} \ No newline at end of file diff --git a/src/multilspy/language_servers/rust_analyzer/runtime_dependencies.json b/src/multilspy/language_servers/rust_analyzer/runtime_dependencies.json deleted file mode 100644 index 517d8b87..00000000 --- a/src/multilspy/language_servers/rust_analyzer/runtime_dependencies.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "_description": "Used to download the runtime dependencies for running RustAnalyzer. Obtained from https://github.com/rust-lang/rust-analyzer/releases", - "runtimeDependencies": [ - { - "id": "RustAnalyzer", - "description": "RustAnalyzer for Linux (x64)", - "url": "https://github.com/rust-lang/rust-analyzer/releases/download/2023-10-09/rust-analyzer-aarch64-apple-darwin.gz", - "platformId": "osx-arm64", - "archiveType": "gz", - "binaryName": "rust_analyzer" - }, - { - "id": "RustAnalyzer", - "description": "RustAnalyzer for Linux (x64)", - "url": "https://github.com/rust-lang/rust-analyzer/releases/download/2023-10-09/rust-analyzer-x86_64-unknown-linux-gnu.gz", - "platformId": "linux-x64", - "archiveType": "gz", - "binaryName": "rust_analyzer" - }, - { - "id": "RustAnalyzer", - "description": "RustAnalyzer for Windows (x64)", - "url": "https://github.com/rust-lang/rust-analyzer/releases/download/2023-10-09/rust-analyzer-x86_64-pc-windows-msvc.zip", - "platformId": "win-x64", - "archiveType": "zip", - "binaryName": "rust-analyzer.exe" - } - ] -} \ No newline at end of file diff --git a/src/multilspy/language_servers/rust_analyzer/rust_analyzer.py b/src/multilspy/language_servers/rust_analyzer/rust_analyzer.py deleted file mode 100644 index 60d71035..00000000 --- a/src/multilspy/language_servers/rust_analyzer/rust_analyzer.py +++ /dev/null @@ -1,185 +0,0 @@ -""" -Provides Rust specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Rust. -""" - -import asyncio -import json -import logging -import os -import stat -import pathlib -from contextlib import asynccontextmanager -from typing import AsyncIterator - -from overrides import override - -from solidlsp.multilspy_logger import MultilspyLogger -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_utils import FileUtils -from solidlsp.multilspy_utils import PlatformUtils - - -class RustAnalyzer(LanguageServer): - """ - Provides Rust specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Rust. - """ - - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): - """ - Creates a RustAnalyzer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. - """ - rustanalyzer_executable_path = self.setup_runtime_dependencies(logger, config) - super().__init__( - config, - logger, - repository_root_path, - ProcessLaunchInfo(cmd=rustanalyzer_executable_path, cwd=repository_root_path), - "rust", - ) - self.server_ready = asyncio.Event() - - @override - def is_ignored_dirname(self, dirname: str) -> bool: - return super().is_ignored_dirname(dirname) or dirname in ["target"] - - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str: - """ - Setup runtime dependencies for rust_analyzer. - """ - platform_id = PlatformUtils.get_platform_id() - - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f: - d = json.load(f) - del d["_description"] - - # assert platform_id.value in [ - # "linux-x64", - # "win-x64", - # ], "Only linux-x64 and win-x64 platform is supported for in multilspy at the moment" - - runtime_dependencies = d["runtimeDependencies"] - runtime_dependencies = [ - dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value - ] - assert len(runtime_dependencies) == 1 - dependency = runtime_dependencies[0] - - rustanalyzer_ls_dir = os.path.join(os.path.dirname(__file__), "static", "RustAnalyzer") - rustanalyzer_executable_path = os.path.join(rustanalyzer_ls_dir, dependency["binaryName"]) - if not os.path.exists(rustanalyzer_ls_dir): - os.makedirs(rustanalyzer_ls_dir) - if dependency["archiveType"] == "gz": - FileUtils.download_and_extract_archive( - logger, dependency["url"], rustanalyzer_executable_path, dependency["archiveType"] - ) - else: - FileUtils.download_and_extract_archive( - logger, dependency["url"], rustanalyzer_ls_dir, dependency["archiveType"] - ) - assert os.path.exists(rustanalyzer_executable_path) - os.chmod(rustanalyzer_executable_path, stat.S_IEXEC) - - return rustanalyzer_executable_path - - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the Rust Analyzer Language Server. - """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: - d = json.load(f) - - del d["_description"] - - d["processId"] = os.getpid() - assert d["rootPath"] == "$rootPath" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "$rootUri" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["uri"] == "$uri" - d["workspaceFolders"][0]["uri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["name"] == "$name" - d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) - - return d - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["RustAnalyzer"]: - """ - Starts the Rust Analyzer Language Server, waits for the server to be ready and yields the LanguageServer instance. - - Usage: - ``` - async with lsp.start_server(): - # LanguageServer has been initialized and ready to serve requests - await lsp.request_definition(...) - await lsp.request_references(...) - # Shutdown the LanguageServer on exit from scope - # LanguageServer has been shutdown - """ - - async def register_capability_handler(params): - assert "registrations" in params - for registration in params["registrations"]: - if registration["method"] == "workspace/executeCommand": - self.initialize_searcher_command_available.set() - self.resolve_main_method_available.set() - return - - async def lang_status_handler(params): - # TODO: Should we wait for - # server -> client: {'jsonrpc': '2.0', 'method': 'language/status', 'params': {'type': 'ProjectStatus', 'message': 'OK'}} - # Before proceeding? - if params["type"] == "ServiceReady" and params["message"] == "ServiceReady": - self.service_ready_event.set() - - async def execute_client_command_handler(params): - return [] - - async def do_nothing(params): - return - - async def check_experimental_status(params): - if params["quiescent"] == True: - self.server_ready.set() - - async def window_log_message(msg): - self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - self.server.on_request("client/registerCapability", register_capability_handler) - self.server.on_notification("language/status", lang_status_handler) - self.server.on_notification("window/logMessage", window_log_message) - self.server.on_request("workspace/executeClientCommand", execute_client_command_handler) - self.server.on_notification("$/progress", do_nothing) - self.server.on_notification("textDocument/publishDiagnostics", do_nothing) - self.server.on_notification("language/actionableNotification", do_nothing) - self.server.on_notification("experimental/serverStatus", check_experimental_status) - - async with super().start_server(): - self.logger.log("Starting RustAnalyzer server process", logging.INFO) - await 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, - ) - init_response = await self.server.send.initialize(initialize_params) - assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 - assert "completionProvider" in init_response["capabilities"] - assert init_response["capabilities"]["completionProvider"] == { - "resolveProvider": True, - "triggerCharacters": [":", ".", "'", "("], - "completionItem": {"labelDetailsSupport": True}, - } - self.server.notify.initialized({}) - self.completions_available.set() - - await self.server_ready.wait() - - yield self diff --git a/src/multilspy/language_servers/solargraph/initialize_params.json b/src/multilspy/language_servers/solargraph/initialize_params.json deleted file mode 100644 index 2cbb41f8..00000000 --- a/src/multilspy/language_servers/solargraph/initialize_params.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "_description": "This file contains the initialization parameters for the Solargraph Language Server.", - "processId": "$processId", - "rootPath": "$rootPath", - "rootUri": "$rootUri", - "capabilities": { - }, - "trace": "verbose", - "workspaceFolders": [ - { - "uri": "$uri", - "name": "$name" - } - ] -} diff --git a/src/multilspy/language_servers/solargraph/runtime_dependencies.json b/src/multilspy/language_servers/solargraph/runtime_dependencies.json deleted file mode 100644 index 38cc2372..00000000 --- a/src/multilspy/language_servers/solargraph/runtime_dependencies.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "_description": "This file contains URLs and other metadata required for downloading and installing the Solargraph language server.", - "runtimeDependencies": [ - { - "url": "https://rubygems.org/downloads/solargraph-0.51.1.gem", - "installCommand": "gem install solargraph -v 0.51.1", - "binaryName": "solargraph", - "archiveType": "gem" - } - ] -} diff --git a/src/multilspy/language_servers/solargraph/solargraph.py b/src/multilspy/language_servers/solargraph/solargraph.py deleted file mode 100644 index 5f3d135b..00000000 --- a/src/multilspy/language_servers/solargraph/solargraph.py +++ /dev/null @@ -1,187 +0,0 @@ -""" -Provides Ruby specific instantiation of the LanguageServer class using Solargraph. -Contains various configurations and settings specific to Ruby. -""" - -import asyncio -import json -import logging -import os -import stat -import subprocess -import pathlib -from contextlib import asynccontextmanager -from typing import AsyncIterator, override - -from solidlsp.multilspy_logger import MultilspyLogger -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams -from solidlsp.multilspy_config import MultilspyConfig - - -class Solargraph(LanguageServer): - """ - Provides Ruby specific instantiation of the LanguageServer class using Solargraph. - Contains various configurations and settings specific to Ruby. - """ - - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): - """ - 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) - super().__init__( - config, - logger, - repository_root_path, - ProcessLaunchInfo(cmd=f"{solargraph_executable_path} stdio", cwd=repository_root_path), - "ruby", - ) - self.server_ready = asyncio.Event() - - @override - def is_ignored_dirname(self, dirname: str) -> bool: - return super().is_ignored_dirname(dirname) or dirname in ["vendor"] - - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig, repository_root_path: str) -> str: - """ - Setup runtime dependencies for Solargraph. - """ - - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), "r", encoding="utf-8") as f: - d = json.load(f) - del d["_description"] - - dependency = d["runtimeDependencies"][0] - - # Check if Ruby is installed - try: - result = subprocess.run(["ruby", "--version"], check=True, capture_output=True, cwd=repository_root_path) - ruby_version = result.stdout.strip() - logger.log(f"Ruby version: {ruby_version}", logging.INFO) - except subprocess.CalledProcessError as e: - raise RuntimeError(f"Error checking for Ruby installation: {e.stderr}") - except FileNotFoundError: - raise RuntimeError("Ruby is not installed. Please install Ruby before continuing.") - - # Check if solargraph is installed - try: - result = subprocess.run(["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) - subprocess.run(dependency["installCommand"].split(), check=True, capture_output=True, cwd=repository_root_path) - - # Get the gem executable path directly - result = subprocess.run(["gem", "which", "solargraph"], check=True, capture_output=True, text=True, cwd=repository_root_path) - gem_path = result.stdout.strip() - bin_dir = os.path.join(os.path.dirname(os.path.dirname(gem_path)), "bin") - executable_path = os.path.join(bin_dir, "solargraph") - - if not os.path.exists(executable_path): - raise RuntimeError(f"Solargraph executable not found at {executable_path}") - - # Ensure the executable has the right permissions - os.chmod(executable_path, os.stat(executable_path).st_mode | stat.S_IEXEC) - - return executable_path - except subprocess.CalledProcessError as e: - raise RuntimeError(f"Failed to check or install Solargraph. {e.stderr}") - - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the Solargraph Language Server. - """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), "r", encoding="utf-8") as f: - d = json.load(f) - - del d["_description"] - - d["processId"] = os.getpid() - assert d["rootPath"] == "$rootPath" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "$rootUri" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["uri"] == "$uri" - d["workspaceFolders"][0]["uri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["name"] == "$name" - d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) - - return d - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["Solargraph"]: - """ - Starts the Solargraph Language Server for Ruby, waits for the server to be ready and yields the LanguageServer instance. - - Usage: - ``` - async with lsp.start_server(): - # LanguageServer has been initialized and ready to serve requests - await lsp.request_definition(...) - await lsp.request_references(...) - # Shutdown the LanguageServer on exit from scope - # LanguageServer has been shutdown - """ - - async def register_capability_handler(params): - assert "registrations" in params - for registration in params["registrations"]: - if registration["method"] == "workspace/executeCommand": - self.initialize_searcher_command_available.set() - self.resolve_main_method_available.set() - return - - async def lang_status_handler(params): - # TODO: Should we wait for - # server -> client: {'jsonrpc': '2.0', 'method': 'language/status', 'params': {'type': 'ProjectStatus', 'message': 'OK'}} - # Before proceeding? - if params["type"] == "ServiceReady" and params["message"] == "ServiceReady": - self.service_ready_event.set() - - async def execute_client_command_handler(params): - return [] - - async def do_nothing(params): - return - - async def window_log_message(msg): - self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - self.server.on_request("client/registerCapability", register_capability_handler) - self.server.on_notification("language/status", lang_status_handler) - self.server.on_notification("window/logMessage", window_log_message) - self.server.on_request("workspace/executeClientCommand", execute_client_command_handler) - self.server.on_notification("$/progress", do_nothing) - self.server.on_notification("textDocument/publishDiagnostics", do_nothing) - self.server.on_notification("language/actionableNotification", do_nothing) - - async with super().start_server(): - self.logger.log("Starting solargraph server process", logging.INFO) - await 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) - init_response = await self.server.send.initialize(initialize_params) - self.logger.log(f"Received init response: {init_response}", logging.INFO) - assert init_response["capabilities"]["textDocumentSync"] == 2 - assert "completionProvider" in init_response["capabilities"] - assert init_response["capabilities"]["completionProvider"] == { - "resolveProvider": True, - "triggerCharacters": [".", ":", "@"], - } - self.server.notify.initialized({}) - self.completions_available.set() - - self.server_ready.set() - await self.server_ready.wait() - - yield self diff --git a/src/multilspy/language_servers/typescript_language_server/initialize_params.json b/src/multilspy/language_servers/typescript_language_server/initialize_params.json deleted file mode 100644 index 6209ba47..00000000 --- a/src/multilspy/language_servers/typescript_language_server/initialize_params.json +++ /dev/null @@ -1,906 +0,0 @@ -{ - "_description": "The parameters sent by the client when initializing the language server with the \"initialize\" request. More details at https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize", - "processId": "os.getpid()", - "clientInfo": { - "name": "Visual Studio Code - Insiders", - "version": "1.82.0-insider" - }, - "locale": "en", - "rootPath": "$rootPath", - "rootUri": "$rootUri", - "capabilities": { - "workspace": { - "applyEdit": true, - "workspaceEdit": { - "documentChanges": true, - "resourceOperations": [ - "create", - "rename", - "delete" - ], - "failureHandling": "textOnlyTransactional", - "normalizesLineEndings": true, - "changeAnnotationSupport": { - "groupsOnLabel": true - } - }, - "configuration": true, - "didChangeWatchedFiles": { - "dynamicRegistration": true, - "relativePatternSupport": true - }, - "symbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "resolveSupport": { - "properties": [ - "location.range" - ] - } - }, - "codeLens": { - "refreshSupport": true - }, - "executeCommand": { - "dynamicRegistration": true - }, - "didChangeConfiguration": { - "dynamicRegistration": true - }, - "workspaceFolders": true, - "semanticTokens": { - "refreshSupport": true - }, - "fileOperations": { - "dynamicRegistration": true, - "didCreate": true, - "didRename": true, - "didDelete": true, - "willCreate": true, - "willRename": true, - "willDelete": true - }, - "inlineValue": { - "refreshSupport": true - }, - "inlayHint": { - "refreshSupport": true - }, - "diagnostics": { - "refreshSupport": true - } - }, - "textDocument": { - "publishDiagnostics": { - "relatedInformation": true, - "versionSupport": false, - "tagSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "codeDescriptionSupport": true, - "dataSupport": true - }, - "synchronization": { - "dynamicRegistration": true, - "willSave": true, - "willSaveWaitUntil": true, - "didSave": true - }, - "completion": { - "dynamicRegistration": true, - "contextSupport": true, - "completionItem": { - "snippetSupport": true, - "commitCharactersSupport": true, - "documentationFormat": [ - "markdown", - "plaintext" - ], - "deprecatedSupport": true, - "preselectSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "insertReplaceSupport": true, - "resolveSupport": { - "properties": [ - "documentation", - "detail", - "additionalTextEdits" - ] - }, - "insertTextModeSupport": { - "valueSet": [ - 1, - 2 - ] - }, - "labelDetailsSupport": true - }, - "insertTextMode": 2, - "completionItemKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25 - ] - }, - "completionList": { - "itemDefaults": [ - "commitCharacters", - "editRange", - "insertTextFormat", - "insertTextMode" - ] - } - }, - "hover": { - "dynamicRegistration": true, - "contentFormat": [ - "markdown", - "plaintext" - ] - }, - "signatureHelp": { - "dynamicRegistration": true, - "signatureInformation": { - "documentationFormat": [ - "markdown", - "plaintext" - ], - "parameterInformation": { - "labelOffsetSupport": true - }, - "activeParameterSupport": true - }, - "contextSupport": true - }, - "definition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "references": { - "dynamicRegistration": true - }, - "documentHighlight": { - "dynamicRegistration": true - }, - "documentSymbol": { - "dynamicRegistration": true, - "symbolKind": { - "valueSet": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26 - ] - }, - "hierarchicalDocumentSymbolSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "labelSupport": true - }, - "codeAction": { - "dynamicRegistration": true, - "isPreferredSupport": true, - "disabledSupport": true, - "dataSupport": true, - "resolveSupport": { - "properties": [ - "edit" - ] - }, - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "", - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite", - "source", - "source.organizeImports" - ] - } - }, - "honorsChangeAnnotations": false - }, - "codeLens": { - "dynamicRegistration": true - }, - "formatting": { - "dynamicRegistration": true - }, - "rangeFormatting": { - "dynamicRegistration": true - }, - "onTypeFormatting": { - "dynamicRegistration": true - }, - "rename": { - "dynamicRegistration": true, - "prepareSupport": true, - "prepareSupportDefaultBehavior": 1, - "honorsChangeAnnotations": true - }, - "documentLink": { - "dynamicRegistration": true, - "tooltipSupport": true - }, - "typeDefinition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "implementation": { - "dynamicRegistration": true, - "linkSupport": true - }, - "colorProvider": { - "dynamicRegistration": true - }, - "foldingRange": { - "dynamicRegistration": true, - "rangeLimit": 5000, - "lineFoldingOnly": true, - "foldingRangeKind": { - "valueSet": [ - "comment", - "imports", - "region" - ] - }, - "foldingRange": { - "collapsedText": false - } - }, - "declaration": { - "dynamicRegistration": true, - "linkSupport": true - }, - "selectionRange": { - "dynamicRegistration": true - }, - "callHierarchy": { - "dynamicRegistration": true - }, - "semanticTokens": { - "dynamicRegistration": true, - "tokenTypes": [ - "namespace", - "type", - "class", - "enum", - "interface", - "struct", - "typeParameter", - "parameter", - "variable", - "property", - "enumMember", - "event", - "function", - "method", - "macro", - "keyword", - "modifier", - "comment", - "string", - "number", - "regexp", - "operator", - "decorator" - ], - "tokenModifiers": [ - "declaration", - "definition", - "readonly", - "static", - "deprecated", - "abstract", - "async", - "modification", - "documentation", - "defaultLibrary" - ], - "formats": [ - "relative" - ], - "requests": { - "range": true, - "full": { - "delta": true - } - }, - "multilineTokenSupport": false, - "overlappingTokenSupport": false, - "serverCancelSupport": true, - "augmentsSyntaxTokens": false - }, - "linkedEditingRange": { - "dynamicRegistration": true - }, - "typeHierarchy": { - "dynamicRegistration": true - }, - "inlineValue": { - "dynamicRegistration": true - }, - "inlayHint": { - "dynamicRegistration": true, - "resolveSupport": { - "properties": [ - "tooltip", - "textEdits", - "label.tooltip", - "label.location", - "label.command" - ] - } - }, - "diagnostic": { - "dynamicRegistration": true, - "relatedDocumentSupport": false - } - }, - "general": { - "staleRequestSupport": { - "cancel": true, - "retryOnContentModified": [ - "textDocument/semanticTokens/full", - "textDocument/semanticTokens/range", - "textDocument/semanticTokens/full/delta" - ] - }, - "regularExpressions": { - "engine": "ECMAScript", - "version": "ES2020" - }, - "markdown": { - "parser": "marked", - "version": "1.1.0", - "allowedTags": [ - "ul", - "li", - "p", - "code", - "blockquote", - "ol", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "hr", - "em", - "pre", - "table", - "thead", - "tbody", - "tr", - "th", - "td", - "div", - "del", - "a", - "strong", - "br", - "img", - "span" - ] - }, - "positionEncodings": [ - "utf-16" - ] - }, - "notebookDocument": { - "synchronization": { - "dynamicRegistration": true, - "executionSummarySupport": true - } - }, - "experimental": { - "snippetTextEdit": true, - "codeActionGroup": true, - "hoverActions": true, - "serverStatusNotification": true, - "colorDiagnosticOutput": true, - "openServerLogs": true, - "localDocs": true, - "commands": { - "commands": [ - "rust-analyzer.runSingle", - "rust-analyzer.debugSingle", - "rust-analyzer.showReferences", - "rust-analyzer.gotoLocation", - "editor.action.triggerParameterHints" - ] - } - } - }, - "initializationOptions": { - "cargoRunner": null, - "runnables": { - "extraEnv": null, - "problemMatcher": [ - "$rustc" - ], - "command": null, - "extraArgs": [] - }, - "statusBar": { - "clickAction": "openLogs" - }, - "server": { - "path": null, - "extraEnv": null - }, - "trace": { - "server": "verbose", - "extension": false - }, - "debug": { - "engine": "auto", - "sourceFileMap": { - "/rustc/": "${env:USERPROFILE}/.rustup/toolchains//lib/rustlib/src/rust" - }, - "openDebugPane": false, - "engineSettings": {} - }, - "restartServerOnConfigChange": false, - "typing": { - "continueCommentsOnNewline": true, - "autoClosingAngleBrackets": { - "enable": false - } - }, - "diagnostics": { - "previewRustcOutput": false, - "useRustcErrorCode": false, - "disabled": [], - "enable": true, - "experimental": { - "enable": false - }, - "remapPrefix": {}, - "warningsAsHint": [], - "warningsAsInfo": [] - }, - "discoverProjectRunner": null, - "showUnlinkedFileNotification": true, - "showDependenciesExplorer": true, - "assist": { - "emitMustUse": false, - "expressionFillDefault": "todo" - }, - "cachePriming": { - "enable": true, - "numThreads": 0 - }, - "cargo": { - "autoreload": true, - "buildScripts": { - "enable": true, - "invocationLocation": "workspace", - "invocationStrategy": "per_workspace", - "overrideCommand": null, - "useRustcWrapper": true - }, - "cfgs": {}, - "extraArgs": [], - "extraEnv": {}, - "features": [], - "noDefaultFeatures": false, - "sysroot": "discover", - "sysrootSrc": null, - "target": null, - "unsetTest": [ - "core" - ] - }, - "checkOnSave": true, - "check": { - "allTargets": true, - "command": "check", - "extraArgs": [], - "extraEnv": {}, - "features": null, - "ignore": [], - "invocationLocation": "workspace", - "invocationStrategy": "per_workspace", - "noDefaultFeatures": null, - "overrideCommand": null, - "targets": null - }, - "completion": { - "autoimport": { - "enable": true - }, - "autoself": { - "enable": true - }, - "callable": { - "snippets": "fill_arguments" - }, - "fullFunctionSignatures": { - "enable": false - }, - "limit": null, - "postfix": { - "enable": true - }, - "privateEditable": { - "enable": false - }, - "snippets": { - "custom": { - "Arc::new": { - "postfix": "arc", - "body": "Arc::new(${receiver})", - "requires": "std::sync::Arc", - "description": "Put the expression into an `Arc`", - "scope": "expr" - }, - "Rc::new": { - "postfix": "rc", - "body": "Rc::new(${receiver})", - "requires": "std::rc::Rc", - "description": "Put the expression into an `Rc`", - "scope": "expr" - }, - "Box::pin": { - "postfix": "pinbox", - "body": "Box::pin(${receiver})", - "requires": "std::boxed::Box", - "description": "Put the expression into a pinned `Box`", - "scope": "expr" - }, - "Ok": { - "postfix": "ok", - "body": "Ok(${receiver})", - "description": "Wrap the expression in a `Result::Ok`", - "scope": "expr" - }, - "Err": { - "postfix": "err", - "body": "Err(${receiver})", - "description": "Wrap the expression in a `Result::Err`", - "scope": "expr" - }, - "Some": { - "postfix": "some", - "body": "Some(${receiver})", - "description": "Wrap the expression in an `Option::Some`", - "scope": "expr" - } - } - } - }, - "files": { - "excludeDirs": [], - "watcher": "client" - }, - "highlightRelated": { - "breakPoints": { - "enable": true - }, - "closureCaptures": { - "enable": true - }, - "exitPoints": { - "enable": true - }, - "references": { - "enable": true - }, - "yieldPoints": { - "enable": true - } - }, - "hover": { - "actions": { - "debug": { - "enable": true - }, - "enable": true, - "gotoTypeDef": { - "enable": true - }, - "implementations": { - "enable": true - }, - "references": { - "enable": false - }, - "run": { - "enable": true - } - }, - "documentation": { - "enable": true, - "keywords": { - "enable": true - } - }, - "links": { - "enable": true - }, - "memoryLayout": { - "alignment": "hexadecimal", - "enable": true, - "niches": false, - "offset": "hexadecimal", - "size": "both" - } - }, - "imports": { - "granularity": { - "enforce": false, - "group": "crate" - }, - "group": { - "enable": true - }, - "merge": { - "glob": true - }, - "preferNoStd": false, - "preferPrelude": false, - "prefix": "plain" - }, - "inlayHints": { - "bindingModeHints": { - "enable": false - }, - "chainingHints": { - "enable": true - }, - "closingBraceHints": { - "enable": true, - "minLines": 25 - }, - "closureCaptureHints": { - "enable": false - }, - "closureReturnTypeHints": { - "enable": "never" - }, - "closureStyle": "impl_fn", - "discriminantHints": { - "enable": "never" - }, - "expressionAdjustmentHints": { - "enable": "never", - "hideOutsideUnsafe": false, - "mode": "prefix" - }, - "lifetimeElisionHints": { - "enable": "never", - "useParameterNames": false - }, - "maxLength": 25, - "parameterHints": { - "enable": true - }, - "reborrowHints": { - "enable": "never" - }, - "renderColons": true, - "typeHints": { - "enable": true, - "hideClosureInitialization": false, - "hideNamedConstructor": false - } - }, - "interpret": { - "tests": false - }, - "joinLines": { - "joinAssignments": true, - "joinElseIf": true, - "removeTrailingComma": true, - "unwrapTrivialBlock": true - }, - "lens": { - "debug": { - "enable": true - }, - "enable": true, - "forceCustomCommands": true, - "implementations": { - "enable": true - }, - "location": "above_name", - "references": { - "adt": { - "enable": false - }, - "enumVariant": { - "enable": false - }, - "method": { - "enable": false - }, - "trait": { - "enable": false - } - }, - "run": { - "enable": true - } - }, - "linkedProjects": [], - "lru": { - "capacity": null, - "query": { - "capacities": {} - } - }, - "notifications": { - "cargoTomlNotFound": true - }, - "numThreads": null, - "procMacro": { - "attributes": { - "enable": true - }, - "enable": true, - "ignored": {}, - "server": null - }, - "references": { - "excludeImports": false - }, - "rust": { - "analyzerTargetDir": null - }, - "rustc": { - "source": null - }, - "rustfmt": { - "extraArgs": [], - "overrideCommand": null, - "rangeFormatting": { - "enable": false - } - }, - "semanticHighlighting": { - "doc": { - "comment": { - "inject": { - "enable": true - } - } - }, - "nonStandardTokens": true, - "operator": { - "enable": true, - "specialization": { - "enable": false - } - }, - "punctuation": { - "enable": false, - "separate": { - "macro": { - "bang": false - } - }, - "specialization": { - "enable": false - } - }, - "strings": { - "enable": true - } - }, - "signatureInfo": { - "detail": "full", - "documentation": { - "enable": true - } - }, - "workspace": { - "symbol": { - "search": { - "kind": "only_types", - "limit": 128, - "scope": "workspace" - } - } - } - }, - "trace": "verbose", - "workspaceFolders": [ - { - "uri": "$uri", - "name": "$name" - } - ] -} \ No newline at end of file diff --git a/src/multilspy/language_servers/typescript_language_server/runtime_dependencies.json b/src/multilspy/language_servers/typescript_language_server/runtime_dependencies.json deleted file mode 100644 index 90824a7c..00000000 --- a/src/multilspy/language_servers/typescript_language_server/runtime_dependencies.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "_description": "Used to download the runtime dependencies for running typescript-language-server. Obtained from https://github.com/typescript-language-server/typescript-language-server/releases", - "runtimeDependencies": [ - { - "id": "typescript", - "description": "typescript package for Linux, OSX, and Windows. Both x64 and arm64 are supported.", - "command": "npm install --prefix ./ typescript@5.5.4" - }, - { - "id": "typescript-language-server", - "description": "typescript-language-server package for Linux, OSX, and Windows. Both x64 and arm64 are supported.", - "command": "npm install --prefix ./ typescript-language-server@4.3.3" - } - ] -} - diff --git a/src/multilspy/language_servers/typescript_language_server/typescript_language_server.py b/src/multilspy/language_servers/typescript_language_server/typescript_language_server.py deleted file mode 100644 index edd3b306..00000000 --- a/src/multilspy/language_servers/typescript_language_server/typescript_language_server.py +++ /dev/null @@ -1,245 +0,0 @@ -""" -Provides TypeScript specific instantiation of the LanguageServer class. Contains various configurations and settings specific to TypeScript. -""" - -import asyncio -import json -import logging -import os -import pathlib -import shutil -import subprocess -from collections.abc import AsyncIterator -from contextlib import asynccontextmanager -from time import sleep - -from overrides import override - -from multilspy.language_server import LanguageServer -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_utils import PlatformId, PlatformUtils - -# Platform-specific imports -if os.name != 'nt': # Unix-like systems - import pwd -else: - # Dummy pwd module for Windows - class pwd: - @staticmethod - def getpwuid(uid): - return type('obj', (), {'pw_name': os.environ.get('USERNAME', 'unknown')})() - - -# Conditionally import pwd module (Unix-only) -if not PlatformUtils.get_platform_id().value.startswith("win"): - import pwd - - -class TypeScriptLanguageServer(LanguageServer): - """ - Provides TypeScript specific instantiation of the LanguageServer class. Contains various configurations and settings specific to TypeScript. - """ - - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): - """ - 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) - super().__init__( - config, - logger, - repository_root_path, - ProcessLaunchInfo(cmd=ts_lsp_executable_path, cwd=repository_root_path), - "typescript", - ) - self.server_ready = asyncio.Event() - - @override - def is_ignored_dirname(self, dirname: str) -> bool: - return super().is_ignored_dirname(dirname) or dirname in [ - "node_modules", - "dist", - "build", - "coverage", - ] - - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str: - """ - Setup runtime dependencies for TypeScript Language Server. - """ - platform_id = PlatformUtils.get_platform_id() - - valid_platforms = [ - PlatformId.LINUX_x64, - PlatformId.LINUX_arm64, - PlatformId.OSX, - PlatformId.OSX_x64, - PlatformId.OSX_arm64, - PlatformId.WIN_x64, - PlatformId.WIN_arm64, - ] - assert platform_id in valid_platforms, f"Platform {platform_id} is not supported for multilspy javascript/typescript at the moment" - - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json")) as f: - d = json.load(f) - del d["_description"] - - runtime_dependencies = d.get("runtimeDependencies", []) - tsserver_ls_dir = os.path.join(os.path.dirname(__file__), "static", "ts-lsp") - tsserver_executable_path = os.path.join(tsserver_ls_dir, "typescript-language-server") - - # Verify both node and npm are installed - is_node_installed = shutil.which('node') is not None - assert is_node_installed, "node is not installed or isn't in PATH. Please install NodeJS and try again." - is_npm_installed = shutil.which('npm') is not None - assert is_npm_installed, "npm is not installed or isn't in PATH. Please install npm and try again." - - # Install typescript and typescript-language-server if not already installed - if not os.path.exists(tsserver_ls_dir): - os.makedirs(tsserver_ls_dir, exist_ok=True) - for dependency in runtime_dependencies: - # Windows doesn't support the 'user' parameter and doesn't have pwd module - if PlatformUtils.get_platform_id().value.startswith("win"): - subprocess.run( - dependency["command"], - shell=True, - check=True, - cwd=tsserver_ls_dir, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL - ) - else: - # On Unix-like systems, run as non-root user - user = pwd.getpwuid(os.getuid()).pw_name - subprocess.run( - dependency["command"], - shell=True, - check=True, - user=user, - cwd=tsserver_ls_dir, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL - ) - - tsserver_executable_path = os.path.join(tsserver_ls_dir, "node_modules", ".bin", "typescript-language-server") - - assert os.path.exists(tsserver_executable_path), "typescript-language-server executable not found. Please install typescript-language-server and try again." - return f"{tsserver_executable_path} --stdio" - - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: - """ - Returns the initialize params for the TypeScript Language Server. - """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json")) as f: - d = json.load(f) - - del d["_description"] - - d["processId"] = os.getpid() - assert d["rootPath"] == "$rootPath" - d["rootPath"] = repository_absolute_path - - assert d["rootUri"] == "$rootUri" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["uri"] == "$uri" - d["workspaceFolders"][0]["uri"] = pathlib.Path(repository_absolute_path).as_uri() - - assert d["workspaceFolders"][0]["name"] == "$name" - d["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) - - return d - - @asynccontextmanager - async def start_server(self) -> AsyncIterator["TypeScriptLanguageServer"]: - """ - Starts the TypeScript Language Server, waits for the server to be ready and yields the LanguageServer instance. - - Usage: - ``` - async with lsp.start_server(): - # LanguageServer has been initialized and ready to serve requests - await lsp.request_definition(...) - await lsp.request_references(...) - # Shutdown the LanguageServer on exit from scope - # LanguageServer has been shutdown - """ - - async def register_capability_handler(params): - assert "registrations" in params - for registration in params["registrations"]: - if registration["method"] == "workspace/executeCommand": - self.initialize_searcher_command_available.set() - # TypeScript doesn't have a direct equivalent to resolve_main_method - # You might want to set a different flag or remove this line - # self.resolve_main_method_available.set() - return - - async def execute_client_command_handler(params): - return [] - - async def do_nothing(params): - return - - async def window_log_message(msg): - self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) - - - async def check_experimental_status(params): - """ - Also listen for experimental/serverStatus as a backup signal - """ - if params.get("quiescent") == True: - self.server_ready.set() - self.completions_available.set() - - self.server.on_request("client/registerCapability", register_capability_handler) - self.server.on_notification("window/logMessage", window_log_message) - self.server.on_request("workspace/executeClientCommand", execute_client_command_handler) - self.server.on_notification("$/progress", do_nothing) - self.server.on_notification("textDocument/publishDiagnostics", do_nothing) - self.server.on_notification("experimental/serverStatus", check_experimental_status) - - async with super().start_server(): - self.logger.log("Starting TypeScript server process", logging.INFO) - await 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, - ) - init_response = await self.server.send.initialize(initialize_params) - - # TypeScript-specific capability checks - assert init_response["capabilities"]["textDocumentSync"] == 2 - assert "completionProvider" in init_response["capabilities"] - assert init_response["capabilities"]["completionProvider"] == { - "triggerCharacters": ['.', '"', "'", '/', '@', '<'], - "resolveProvider": True - } - - self.server.notify.initialized({}) - try: - await asyncio.wait_for(self.server_ready.wait(), timeout=1.0) - except asyncio.TimeoutError: - self.logger.log("Timeout waiting for TypeScript server to become ready, proceeding anyway", logging.INFO) - # Fallback: assume server is ready after timeout - self.server_ready.set() - self.completions_available.set() - - yield self - - @override - # For some reason, the LS may need longer to process this, so we just retry - async def _send_references_request(self, relative_file_path: str, line: int, column: int): - # TODO: The LS doesn't return references contained in other files if it doesn't sleep. This is - # despite the LS having processed requests already. I don't know what causes this, but sleeping - # one second helps. It may be that sleeping only once is enough but that's hard to reliably test. - # It may be that even this 1sec is not enough in larger TS projects, at some point we should find what - # causes this and solve it. - sleep(1) - return await super()._send_references_request(relative_file_path, line, column) From e60d42211afeafa0f5a1ff16e8e577e3ed25929d Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Mon, 23 Jun 2025 17:01:36 +0200 Subject: [PATCH 08/11] Update language server instantiation to always use SolidLanguageServer --- src/serena/agent.py | 24 +++++++----------------- src/serena/constants.py | 6 +----- src/serena/mcp.py | 5 ++--- test/conftest.py | 7 +------ 4 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/serena/agent.py b/src/serena/agent.py index 1ac3ffb6..db96625c 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -33,7 +33,6 @@ from sensai.util import logging from sensai.util.logging import LOG_DEFAULT_FORMAT, FallbackHandler from sensai.util.string import ToStringMixin, dict_string -from solidlsp import SolidLanguageServer as SyncLanguageServer from serena import serena_version from serena.config import SerenaAgentContext, SerenaAgentMode from serena.constants import ( @@ -42,7 +41,6 @@ from serena.constants import ( REPO_ROOT, SELENA_CONFIG_TEMPLATE_FILE, SERENA_MANAGED_DIR_NAME, - USE_SOLID_LSP, ) from serena.dashboard import MemoryLogHandler, SerenaDashboardAPI from serena.prompt_factory import PromptFactory, SerenaPromptFactory @@ -53,6 +51,7 @@ from serena.util.general import load_yaml, save_yaml from serena.util.inspection import determine_programming_language_composition, iter_subclasses from serena.util.shell import execute_shell_command from serena.util.thread import ExecutionResult, execute_with_timeout +from solidlsp import SolidLanguageServer as SyncLanguageServer from solidlsp.ls import SolidLanguageServer from solidlsp.multilspy_config import Language, MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger @@ -678,21 +677,12 @@ def create_ls_for_project( ) ls_logger = MultilspyLogger(log_level=log_level) log.info(f"Creating language server instance for {project_instance.project_root}.") - if USE_SOLID_LSP: - ls = SolidLanguageServer.create( - multilspy_config, - ls_logger, - project_instance.project_root, - timeout=ls_timeout, - ) - return cast(SyncLanguageServer, ls) # TODO: Fix type - else: - return SyncLanguageServer.create( - multilspy_config, - ls_logger, - project_instance.project_root, - timeout=ls_timeout, - ) + return SolidLanguageServer.create( + multilspy_config, + ls_logger, + project_instance.project_root, + timeout=ls_timeout, + ) @click.command() diff --git a/src/serena/constants.py b/src/serena/constants.py index 6a277598..30a9518b 100644 --- a/src/serena/constants.py +++ b/src/serena/constants.py @@ -19,8 +19,4 @@ DEFAULT_MODES = ("interactive", "editing") PROJECT_TEMPLATE_FILE = str(_serena_pkg_path / "resources" / "project.template.yml") SELENA_CONFIG_TEMPLATE_FILE = str(_serena_pkg_path / "resources" / "serena_config.template.yml") -USE_SOLID_LSP = True -"""Whether to use Solid LSP instead of multilspy for language server operations. -Multilspy uses asyncio, which creates many problems like deadlocks and requires full process -isolation (and even then some issues remain). -""" +USE_PROCESS_ISOLATION = False diff --git a/src/serena/mcp.py b/src/serena/mcp.py index e2a0bc95..83a2cbf0 100644 --- a/src/serena/mcp.py +++ b/src/serena/mcp.py @@ -36,7 +36,7 @@ from serena.agent import ( show_fatal_exception_safe, ) from serena.config import RegisteredContext, SerenaAgentContext, SerenaAgentMode -from serena.constants import DEFAULT_CONTEXT, DEFAULT_MODES, USE_SOLID_LSP +from serena.constants import DEFAULT_CONTEXT, DEFAULT_MODES, USE_SOLID_LSP, USE_PROCESS_ISOLATION from serena.process_isolated_agent import ( ProcessIsolatedDashboard, ProcessIsolatedSerenaAgent, @@ -587,10 +587,9 @@ def start_mcp_server( project_file = project_file_arg if project_file_arg is not None else project mcp_factory: SerenaMCPFactory - if USE_SOLID_LSP: + if not USE_PROCESS_ISOLATION: mcp_factory = SerenaMCPFactorySingleProcess(context=context, project=project_file) else: - # using multilspy requires process isolation to prevent asyncio contamination mcp_factory = SerenaMCPFactoryWithProcessIsolation(context=context, project=project_file) # Use process isolation by default to prevent asyncio event loop contamination diff --git a/test/conftest.py b/test/conftest.py index 8e086cf7..e41a53d4 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -45,12 +45,7 @@ def create_ls( ignored_paths.extend(spec.patterns) config = MultilspyConfig(code_language=language, ignored_paths=ignored_paths, trace_lsp_communication=trace_lsp_communication) logger = MultilspyLogger(log_level=log_level) - - if USE_SOLID_LSP: - ls = SolidLanguageServer.create(config, logger, repo_path) - return cast(SyncLanguageServer, ls) # TODO: Fix type - else: - return SyncLanguageServer.create(config, logger, repo_path) + return SolidLanguageServer.create(config, logger, repo_path) def create_default_ls(language: Language) -> SyncLanguageServer: From 05a7d438bc66748fd44c8b7100b3cccc7d0287b5 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Mon, 23 Jun 2025 17:11:22 +0200 Subject: [PATCH 09/11] Update language server imports to no longer use renames --- src/serena/agent.py | 9 ++-- src/serena/mcp.py | 2 +- src/serena/symbol.py | 6 +-- src/solidlsp/__init__.py | 1 + test/conftest.py | 7 +-- test/multilspy/go/test_go_basic.py | 6 +-- test/multilspy/java/test_java_basic.py | 8 ++-- test/multilspy/php/test_php_basic.py | 14 +++--- test/multilspy/python/test_python_basic.py | 14 +++--- .../test_retrieval_with_ignored_dirs.py | 8 ++-- .../multilspy/python/test_symbol_retrieval.py | 44 +++++++++---------- test/multilspy/rust/test_rust_basic.py | 10 ++--- .../typescript/test_typescript_basic.py | 6 +-- 13 files changed, 66 insertions(+), 69 deletions(-) diff --git a/src/serena/agent.py b/src/serena/agent.py index db96625c..965dbdfa 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -51,8 +51,7 @@ from serena.util.general import load_yaml, save_yaml from serena.util.inspection import determine_programming_language_composition, iter_subclasses from serena.util.shell import execute_shell_command from serena.util.thread import ExecutionResult, execute_with_timeout -from solidlsp import SolidLanguageServer as SyncLanguageServer -from solidlsp.ls import SolidLanguageServer +from solidlsp import SolidLanguageServer from solidlsp.multilspy_config import Language, MultilspyConfig from solidlsp.multilspy_logger import MultilspyLogger from solidlsp.multilspy_types import SymbolKind @@ -640,7 +639,7 @@ def create_ls_for_project( log_level: int = logging.INFO, ls_timeout: float | None = DEFAULT_TOOL_TIMEOUT - 5, trace_lsp_communication: bool = False, -) -> SyncLanguageServer: +) -> SolidLanguageServer: """ Create a language server for a project. Note that you will have to start it before performing any LS operations. @@ -789,7 +788,7 @@ class SerenaAgent: # project-specific instances, which will be initialized upon project activation self._active_project: Project | None = None self._active_project_root: str | None = None - self.language_server: SyncLanguageServer | None = None + self.language_server: SolidLanguageServer | None = None self.symbol_manager: SymbolManager | None = None self.memories_manager: MemoriesManager | None = None self.lines_read: LinesRead | None = None @@ -1135,7 +1134,7 @@ class Component(ABC): self.agent = agent @property - def language_server(self) -> SyncLanguageServer: + def language_server(self) -> SolidLanguageServer: assert self.agent.language_server is not None return self.agent.language_server diff --git a/src/serena/mcp.py b/src/serena/mcp.py index 83a2cbf0..8666ef00 100644 --- a/src/serena/mcp.py +++ b/src/serena/mcp.py @@ -36,7 +36,7 @@ from serena.agent import ( show_fatal_exception_safe, ) from serena.config import RegisteredContext, SerenaAgentContext, SerenaAgentMode -from serena.constants import DEFAULT_CONTEXT, DEFAULT_MODES, USE_SOLID_LSP, USE_PROCESS_ISOLATION +from serena.constants import DEFAULT_CONTEXT, DEFAULT_MODES, USE_PROCESS_ISOLATION from serena.process_isolated_agent import ( ProcessIsolatedDashboard, ProcessIsolatedSerenaAgent, diff --git a/src/serena/symbol.py b/src/serena/symbol.py index ec94db29..e4da2743 100644 --- a/src/serena/symbol.py +++ b/src/serena/symbol.py @@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Any, Literal, NamedTuple, Self, Union from sensai.util.string import ToStringMixin -from solidlsp import SolidLanguageServer as SyncLanguageServer +from solidlsp import SolidLanguageServer from solidlsp.ls import ReferenceInSymbol as LSPReferenceInSymbol from solidlsp.multilspy_types import Position, SymbolKind, UnifiedSymbolInformation @@ -488,7 +488,7 @@ class ReferenceInSymbol(ToStringMixin): class SymbolManager: - def __init__(self, lang_server: SyncLanguageServer, agent: Union["SerenaAgent", None] = None) -> None: + def __init__(self, lang_server: SolidLanguageServer, agent: Union["SerenaAgent", None] = None) -> None: """ :param lang_server: the language server to use for symbol retrieval as well as editing operations. :param agent: the agent to use (only needed for marking files as modified). You can pass None if you don't @@ -497,7 +497,7 @@ class SymbolManager: self._lang_server = lang_server self.agent = agent - def set_language_server(self, lang_server: SyncLanguageServer) -> None: + def set_language_server(self, lang_server: SolidLanguageServer) -> None: """ Set the language server to use for symbol retrieval and editing operations. This is useful if you want to change the language server after initializing the SymbolManager. diff --git a/src/solidlsp/__init__.py b/src/solidlsp/__init__.py index fbd65b10..0378e2d6 100644 --- a/src/solidlsp/__init__.py +++ b/src/solidlsp/__init__.py @@ -1 +1,2 @@ +# ruff: noqa from .ls import SolidLanguageServer diff --git a/test/conftest.py b/test/conftest.py index e41a53d4..1cb81ea0 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,12 +1,9 @@ import logging from pathlib import Path -from typing import cast import pytest from sensai.util.logging import configure -from solidlsp import SolidLanguageServer as SyncLanguageServer -from serena.constants import USE_SOLID_LSP from serena.util.file_system import GitignoreParser from solidlsp.ls import SolidLanguageServer from solidlsp.multilspy_config import Language, MultilspyConfig @@ -36,7 +33,7 @@ def create_ls( ignored_paths: list[str] | None = None, trace_lsp_communication: bool = False, log_level: int = logging.INFO, -) -> SyncLanguageServer: +) -> SolidLanguageServer: ignored_paths = ignored_paths or [] if repo_path is None: repo_path = str(get_repo_path(language)) @@ -48,7 +45,7 @@ def create_ls( return SolidLanguageServer.create(config, logger, repo_path) -def create_default_ls(language: Language) -> SyncLanguageServer: +def create_default_ls(language: Language) -> SolidLanguageServer: repo_path = str(get_repo_path(language)) return create_ls(language, repo_path) diff --git a/test/multilspy/go/test_go_basic.py b/test/multilspy/go/test_go_basic.py index e5d82de5..82b6960d 100644 --- a/test/multilspy/go/test_go_basic.py +++ b/test/multilspy/go/test_go_basic.py @@ -2,7 +2,7 @@ import os import pytest -from solidlsp import SolidLanguageServer as SyncLanguageServer +from solidlsp import SolidLanguageServer from solidlsp.multilspy_config import Language from solidlsp.multilspy_utils import SymbolUtils @@ -10,14 +10,14 @@ from solidlsp.multilspy_utils import SymbolUtils @pytest.mark.go class TestGoLanguageServer: @pytest.mark.parametrize("language_server", [Language.GO], indirect=True) - def test_find_symbol(self, language_server: SyncLanguageServer) -> None: + def test_find_symbol(self, language_server: SolidLanguageServer) -> None: symbols = language_server.request_full_symbol_tree() assert SymbolUtils.symbol_tree_contains_name(symbols, "main"), "main function not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "Helper"), "Helper function not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "DemoStruct"), "DemoStruct not found in symbol tree" @pytest.mark.parametrize("language_server", [Language.GO], indirect=True) - def test_find_referencing_symbols(self, language_server: SyncLanguageServer) -> None: + def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None: file_path = os.path.join("main.go") symbols = language_server.request_document_symbols(file_path) helper_symbol = None diff --git a/test/multilspy/java/test_java_basic.py b/test/multilspy/java/test_java_basic.py index 679a7a8e..f5923736 100644 --- a/test/multilspy/java/test_java_basic.py +++ b/test/multilspy/java/test_java_basic.py @@ -2,7 +2,7 @@ import os import pytest -from solidlsp import SolidLanguageServer as SyncLanguageServer +from solidlsp import SolidLanguageServer from solidlsp.multilspy_config import Language from solidlsp.multilspy_utils import SymbolUtils @@ -10,14 +10,14 @@ from solidlsp.multilspy_utils import SymbolUtils @pytest.mark.java class TestJavaLanguageServer: @pytest.mark.parametrize("language_server", [Language.JAVA], indirect=True) - def test_find_symbol(self, language_server: SyncLanguageServer) -> None: + def test_find_symbol(self, language_server: SolidLanguageServer) -> None: symbols = language_server.request_full_symbol_tree() assert SymbolUtils.symbol_tree_contains_name(symbols, "Main"), "Main class not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "Utils"), "Utils class not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "Model"), "Model class not found in symbol tree" @pytest.mark.parametrize("language_server", [Language.JAVA], indirect=True) - def test_find_referencing_symbols(self, language_server: SyncLanguageServer) -> None: + def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None: # Use correct Maven/Java file paths file_path = os.path.join("src", "main", "java", "test_repo", "Utils.java") refs = language_server.request_references(file_path, 4, 20) @@ -43,7 +43,7 @@ class TestJavaLanguageServer: ), "Main should reference Model (tried all positions in selectionRange)" @pytest.mark.parametrize("language_server", [Language.JAVA], indirect=True) - def test_overview_methods(self, language_server: SyncLanguageServer) -> None: + def test_overview_methods(self, language_server: SolidLanguageServer) -> None: symbols = language_server.request_full_symbol_tree() assert SymbolUtils.symbol_tree_contains_name(symbols, "Main"), "Main missing from overview" assert SymbolUtils.symbol_tree_contains_name(symbols, "Utils"), "Utils missing from overview" diff --git a/test/multilspy/php/test_php_basic.py b/test/multilspy/php/test_php_basic.py index ff4bf324..3ae2797e 100644 --- a/test/multilspy/php/test_php_basic.py +++ b/test/multilspy/php/test_php_basic.py @@ -2,7 +2,7 @@ from pathlib import Path import pytest -from solidlsp import SolidLanguageServer as SyncLanguageServer +from solidlsp import SolidLanguageServer from solidlsp.multilspy_config import Language @@ -10,7 +10,7 @@ from solidlsp.multilspy_config import Language class TestPhpLanguageServer: @pytest.mark.parametrize("language_server", [Language.PHP], indirect=True) @pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True) - def test_ls_is_running(self, language_server: SyncLanguageServer, repo_path: Path) -> None: + def test_ls_is_running(self, language_server: SolidLanguageServer, repo_path: Path) -> None: """Test that the language server starts and stops successfully.""" # The fixture already handles start and stop assert language_server.is_running() @@ -18,7 +18,7 @@ class TestPhpLanguageServer: @pytest.mark.parametrize("language_server", [Language.PHP], indirect=True) @pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True) - def test_find_definition_within_file(self, language_server: SyncLanguageServer, repo_path: Path) -> None: + def test_find_definition_within_file(self, language_server: SolidLanguageServer, repo_path: Path) -> None: # In index.php: # Line 9 (1-indexed): $greeting = greet($userName); @@ -41,7 +41,7 @@ class TestPhpLanguageServer: @pytest.mark.parametrize("language_server", [Language.PHP], indirect=True) @pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True) - def test_find_definition_across_files(self, language_server: SyncLanguageServer, repo_path: Path) -> None: + def test_find_definition_across_files(self, language_server: SolidLanguageServer, repo_path: Path) -> None: definition_location_list = language_server.request_definition(str(repo_path / "index.php"), 12, 5) # helperFunction assert definition_location_list, f"Expected non-empty definition_location_list but got {definition_location_list=}" @@ -53,7 +53,7 @@ class TestPhpLanguageServer: @pytest.mark.parametrize("language_server", [Language.PHP], indirect=True) @pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True) - def test_find_definition_simple_variable(self, language_server: SyncLanguageServer, repo_path: Path) -> None: + def test_find_definition_simple_variable(self, language_server: SolidLanguageServer, repo_path: Path) -> None: file_path = str(repo_path / "simple_var.php") # In simple_var.php: @@ -74,7 +74,7 @@ class TestPhpLanguageServer: @pytest.mark.parametrize("language_server", [Language.PHP], indirect=True) @pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True) - def test_find_references_within_file(self, language_server: SyncLanguageServer, repo_path: Path) -> None: + def test_find_references_within_file(self, language_server: SolidLanguageServer, repo_path: Path) -> None: index_php_path = str(repo_path / "index.php") # In index.php (0-indexed lines): @@ -108,7 +108,7 @@ class TestPhpLanguageServer: @pytest.mark.parametrize("language_server", [Language.PHP], indirect=True) @pytest.mark.parametrize("repo_path", [Language.PHP], indirect=True) - def test_find_references_across_files(self, language_server: SyncLanguageServer, repo_path: Path) -> None: + def test_find_references_across_files(self, language_server: SolidLanguageServer, repo_path: Path) -> None: helper_php_path = str(repo_path / "helper.php") # In index.php (0-indexed lines): # Line 13: helperFunction(); // Usage of helperFunction diff --git a/test/multilspy/python/test_python_basic.py b/test/multilspy/python/test_python_basic.py index c5522e5d..541f7f50 100644 --- a/test/multilspy/python/test_python_basic.py +++ b/test/multilspy/python/test_python_basic.py @@ -9,8 +9,8 @@ import os import pytest -from solidlsp import SolidLanguageServer as SyncLanguageServer from serena.text_utils import LineType +from solidlsp import SolidLanguageServer from solidlsp.multilspy_config import Language @@ -19,7 +19,7 @@ class TestLanguageServerBasics: """Test basic functionality of the language server.""" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_references_user_class(self, language_server: SyncLanguageServer) -> None: + def test_request_references_user_class(self, language_server: SolidLanguageServer) -> None: """Test request_references on the User class.""" # Get references to the User class in models.py file_path = os.path.join("test_repo", "models.py") @@ -34,7 +34,7 @@ class TestLanguageServerBasics: assert len(references) > 1, "User class should be referenced in multiple files (using selectionRange if present)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_references_item_class(self, language_server: SyncLanguageServer) -> None: + def test_request_references_item_class(self, language_server: SolidLanguageServer) -> None: """Test request_references on the Item class.""" # Get references to the Item class in models.py file_path = os.path.join("test_repo", "models.py") @@ -50,7 +50,7 @@ class TestLanguageServerBasics: assert len(services_references) > 0, "At least one reference should be in services.py (using selectionRange if present)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_references_function_parameter(self, language_server: SyncLanguageServer) -> None: + def test_request_references_function_parameter(self, language_server: SolidLanguageServer) -> None: """Test request_references on a function parameter.""" # Get references to the id parameter in get_user method file_path = os.path.join("test_repo", "services.py") @@ -65,7 +65,7 @@ class TestLanguageServerBasics: assert len(references) > 0, "id parameter should be referenced within the method (using selectionRange if present)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_references_create_user_method(self, language_server: SyncLanguageServer) -> None: + def test_request_references_create_user_method(self, language_server: SolidLanguageServer) -> None: # Get references to the create_user method in UserService file_path = os.path.join("test_repo", "services.py") # Line 15 contains the create_user method definition @@ -79,7 +79,7 @@ class TestLanguageServerBasics: assert len(references) > 1, "Should get valid references for create_user (using selectionRange if present)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_retrieve_content_around_line(self, language_server: SyncLanguageServer) -> None: + def test_retrieve_content_around_line(self, language_server: SolidLanguageServer) -> None: """Test retrieve_content_around_line functionality with various scenarios.""" file_path = os.path.join("test_repo", "models.py") @@ -186,7 +186,7 @@ class TestLanguageServerBasics: assert line.match_type == LineType.AFTER_MATCH @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_search_files_for_pattern(self, language_server: SyncLanguageServer) -> None: + def test_search_files_for_pattern(self, language_server: SolidLanguageServer) -> None: """Test search_files_for_pattern with various patterns and glob filters.""" # Test 1: Search for class definitions across all files class_pattern = r"class\s+\w+\s*(?:\([^{]*\)|:)" diff --git a/test/multilspy/python/test_retrieval_with_ignored_dirs.py b/test/multilspy/python/test_retrieval_with_ignored_dirs.py index d0446f9a..f100d412 100644 --- a/test/multilspy/python/test_retrieval_with_ignored_dirs.py +++ b/test/multilspy/python/test_retrieval_with_ignored_dirs.py @@ -3,7 +3,7 @@ from pathlib import Path import pytest -from solidlsp import SolidLanguageServer as SyncLanguageServer +from solidlsp import SolidLanguageServer from solidlsp.multilspy_config import Language from test.conftest import create_ls @@ -12,7 +12,7 @@ pytestmark = pytest.mark.python @pytest.fixture(scope="module") -def ls_with_ignored_dirs() -> Generator[SyncLanguageServer, None, None]: +def ls_with_ignored_dirs() -> Generator[SolidLanguageServer, None, None]: """Fixture to set up an LS for the python test repo with the 'scripts' directory ignored.""" ignored_paths = ["scripts", "custom_test"] ls = create_ls(ignored_paths=ignored_paths, language=Language.PYTHON) @@ -24,7 +24,7 @@ def ls_with_ignored_dirs() -> Generator[SyncLanguageServer, None, None]: @pytest.mark.parametrize("ls_with_ignored_dirs", [Language.PYTHON], indirect=True) -def test_symbol_tree_ignores_dir(ls_with_ignored_dirs: SyncLanguageServer): +def test_symbol_tree_ignores_dir(ls_with_ignored_dirs: SolidLanguageServer): """Tests that request_full_symbol_tree ignores the configured directory.""" root = ls_with_ignored_dirs.request_full_symbol_tree()[0] root_children = root["children"] @@ -33,7 +33,7 @@ def test_symbol_tree_ignores_dir(ls_with_ignored_dirs: SyncLanguageServer): @pytest.mark.parametrize("ls_with_ignored_dirs", [Language.PYTHON], indirect=True) -def test_find_references_ignores_dir(ls_with_ignored_dirs: SyncLanguageServer): +def test_find_references_ignores_dir(ls_with_ignored_dirs: SolidLanguageServer): """Tests that find_references ignores the configured directory.""" # Location of Item, which is referenced in scripts definition_file = "test_repo/models.py" diff --git a/test/multilspy/python/test_symbol_retrieval.py b/test/multilspy/python/test_symbol_retrieval.py index 5bca8f64..97f5d448 100644 --- a/test/multilspy/python/test_symbol_retrieval.py +++ b/test/multilspy/python/test_symbol_retrieval.py @@ -10,7 +10,7 @@ import os import pytest -from solidlsp import SolidLanguageServer as SyncLanguageServer +from solidlsp import SolidLanguageServer from solidlsp.multilspy_config import Language from solidlsp.multilspy_types import SymbolKind @@ -21,7 +21,7 @@ class TestLanguageServerSymbols: """Test the language server's symbol-related functionality.""" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_containing_symbol_function(self, language_server: SyncLanguageServer) -> None: + def test_request_containing_symbol_function(self, language_server: SolidLanguageServer) -> None: """Test request_containing_symbol for a function.""" # Test for a position inside the create_user method file_path = os.path.join("test_repo", "services.py") @@ -36,7 +36,7 @@ class TestLanguageServerSymbols: assert containing_symbol["body"].strip().startswith("def create_user(self") @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_references_to_variables(self, language_server: SyncLanguageServer) -> None: + def test_references_to_variables(self, language_server: SolidLanguageServer) -> None: """Test request_referencing_symbols for a variable.""" file_path = os.path.join("test_repo", "variables.py") # Line 75 contains the field status that is later modified @@ -51,7 +51,7 @@ class TestLanguageServerSymbols: assert "second_dataclass" in ref_names @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_containing_symbol_class(self, language_server: SyncLanguageServer) -> None: + def test_request_containing_symbol_class(self, language_server: SolidLanguageServer) -> None: """Test request_containing_symbol for a class.""" # Test for a position inside the UserService class but outside any method file_path = os.path.join("test_repo", "services.py") @@ -64,7 +64,7 @@ class TestLanguageServerSymbols: assert containing_symbol["kind"] == SymbolKind.Class @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_containing_symbol_nested(self, language_server: SyncLanguageServer) -> None: + def test_request_containing_symbol_nested(self, language_server: SolidLanguageServer) -> None: """Test request_containing_symbol with nested scopes.""" # Test for a position inside a method which is inside a class file_path = os.path.join("test_repo", "services.py") @@ -90,7 +90,7 @@ class TestLanguageServerSymbols: assert parent_symbol["kind"] == SymbolKind.Class @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_containing_symbol_none(self, language_server: SyncLanguageServer) -> None: + def test_request_containing_symbol_none(self, language_server: SolidLanguageServer) -> None: """Test request_containing_symbol for a position with no containing symbol.""" # Test for a position outside any function/class (e.g., in imports) file_path = os.path.join("test_repo", "services.py") @@ -101,7 +101,7 @@ class TestLanguageServerSymbols: assert containing_symbol is None or containing_symbol == {} @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_referencing_symbols_function(self, language_server: SyncLanguageServer) -> None: + def test_request_referencing_symbols_function(self, language_server: SolidLanguageServer) -> None: """Test request_referencing_symbols for a function.""" # Test referencing symbols for create_user function file_path = os.path.join("test_repo", "services.py") @@ -125,7 +125,7 @@ class TestLanguageServerSymbols: assert "end" in symbol["location"]["range"] @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_referencing_symbols_class(self, language_server: SyncLanguageServer) -> None: + def test_request_referencing_symbols_class(self, language_server: SolidLanguageServer) -> None: """Test request_referencing_symbols for a class.""" # Test referencing symbols for User class file_path = os.path.join("test_repo", "models.py") @@ -146,7 +146,7 @@ class TestLanguageServerSymbols: assert len(services_references) > 0, "No referencing symbols from services.py for User (selectionRange)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_referencing_symbols_parameter(self, language_server: SyncLanguageServer) -> None: + def test_request_referencing_symbols_parameter(self, language_server: SolidLanguageServer) -> None: """Test request_referencing_symbols for a function parameter.""" # Test referencing symbols for id parameter in get_user file_path = os.path.join("test_repo", "services.py") @@ -167,7 +167,7 @@ class TestLanguageServerSymbols: assert len(method_refs) > 0, "No referencing symbols within method body for get_user (selectionRange)" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_referencing_symbols_none(self, language_server: SyncLanguageServer) -> None: + def test_request_referencing_symbols_none(self, language_server: SolidLanguageServer) -> None: """Test request_referencing_symbols for a position with no symbol.""" # For positions with no symbol, the method might throw an error or return None/empty list # We'll modify our test to handle this by using a try-except block @@ -185,7 +185,7 @@ class TestLanguageServerSymbols: # Tests for request_defining_symbol @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_defining_symbol_variable(self, language_server: SyncLanguageServer) -> None: + def test_request_defining_symbol_variable(self, language_server: SolidLanguageServer) -> None: """Test request_defining_symbol for a variable usage.""" # Test finding the definition of a symbol in the create_user method file_path = os.path.join("test_repo", "services.py") @@ -204,7 +204,7 @@ class TestLanguageServerSymbols: assert "services.py" in defining_symbol["location"]["uri"] @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_defining_symbol_imported_class(self, language_server: SyncLanguageServer) -> None: + def test_request_defining_symbol_imported_class(self, language_server: SolidLanguageServer) -> None: """Test request_defining_symbol for an imported class.""" # Test finding the definition of the 'User' class used in the UserService.create_user method file_path = os.path.join("test_repo", "services.py") @@ -216,7 +216,7 @@ class TestLanguageServerSymbols: assert defining_symbol.get("name") == "User" @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_defining_symbol_method_call(self, language_server: SyncLanguageServer) -> None: + def test_request_defining_symbol_method_call(self, language_server: SolidLanguageServer) -> None: """Test request_defining_symbol for a method call.""" # Create an example file path for a file that calls UserService.create_user examples_file_path = os.path.join("examples", "user_management.py") @@ -241,7 +241,7 @@ class TestLanguageServerSymbols: warnings.warn("Could not verify method call definition - file structure may differ from expected") @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_defining_symbol_none(self, language_server: SyncLanguageServer) -> None: + def test_request_defining_symbol_none(self, language_server: SolidLanguageServer) -> None: """Test request_defining_symbol for a position with no symbol.""" # Test for a position with no symbol (e.g., whitespace or comment) file_path = os.path.join("test_repo", "services.py") @@ -252,7 +252,7 @@ class TestLanguageServerSymbols: assert defining_symbol is None @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_containing_symbol_variable(self, language_server: SyncLanguageServer) -> None: + def test_request_containing_symbol_variable(self, language_server: SolidLanguageServer) -> None: """Test request_containing_symbol where the symbol is a variable.""" # Test for a position inside a variable definition file_path = os.path.join("test_repo", "services.py") @@ -265,7 +265,7 @@ class TestLanguageServerSymbols: assert containing_symbol["kind"] == SymbolKind.Variable @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_defining_symbol_nested_function(self, language_server: SyncLanguageServer) -> None: + def test_request_defining_symbol_nested_function(self, language_server: SolidLanguageServer) -> None: """Test request_defining_symbol for a nested function or closure.""" # Use the existing nested.py file which contains nested classes and methods file_path = os.path.join("test_repo", "nested.py") @@ -316,7 +316,7 @@ class TestLanguageServerSymbols: assert defining_symbol.get("kind") == SymbolKind.Function.value @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_symbol_methods_integration(self, language_server: SyncLanguageServer) -> None: + def test_symbol_methods_integration(self, language_server: SolidLanguageServer) -> None: """Test the integration between different symbol-related methods.""" # This test demonstrates using the various symbol methods together # by finding a symbol and then checking its definition @@ -365,7 +365,7 @@ class TestLanguageServerSymbols: warnings.warn("Could not verify container hierarchy - implementation detail") @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_symbol_tree_structure(self, language_server: SyncLanguageServer) -> None: + def test_symbol_tree_structure(self, language_server: SolidLanguageServer) -> None: """Test that the symbol tree structure is correctly built.""" # Get all symbols in the test file repo_structure = language_server.request_full_symbol_tree() @@ -393,7 +393,7 @@ class TestLanguageServerSymbols: assert user_management_roots == user_management_node["children"] @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_symbol_tree_structure_subdir(self, language_server: SyncLanguageServer) -> None: + def test_symbol_tree_structure_subdir(self, language_server: SolidLanguageServer) -> None: """Test that the symbol tree structure is correctly built.""" # Get all symbols in the test file examples_package_roots = language_server.request_full_symbol_tree(within_relative_path="examples") @@ -414,7 +414,7 @@ class TestLanguageServerSymbols: assert user_management_roots == user_management_node["children"] @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_dir_overview(self, language_server: SyncLanguageServer) -> None: + def test_request_dir_overview(self, language_server: SolidLanguageServer) -> None: """Test that request_dir_overview returns correct symbol information for files in a directory.""" # Get overview of the examples directory overview = language_server.request_dir_overview("test_repo") @@ -439,7 +439,7 @@ class TestLanguageServerSymbols: assert symbol in services_symbols @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_request_document_overview(self, language_server: SyncLanguageServer) -> None: + def test_request_document_overview(self, language_server: SolidLanguageServer) -> None: """Test that request_document_overview returns correct symbol information for a file.""" # Get overview of the user_management.py file overview = language_server.request_document_overview(os.path.join("examples", "user_management.py")) @@ -449,7 +449,7 @@ class TestLanguageServerSymbols: assert {"UserStats", "UserManager", "process_user_data", "main"}.issubset(symbol_names) @pytest.mark.parametrize("language_server", [Language.PYTHON], indirect=True) - def test_containing_symbol_of_var_is_file(self, language_server: SyncLanguageServer) -> None: + def test_containing_symbol_of_var_is_file(self, language_server: SolidLanguageServer) -> None: """Test that the containing symbol of a variable is the file itself.""" # Get the containing symbol of a variable in a file file_path = os.path.join("test_repo", "services.py") diff --git a/test/multilspy/rust/test_rust_basic.py b/test/multilspy/rust/test_rust_basic.py index ffe1245c..e6cd6f4c 100644 --- a/test/multilspy/rust/test_rust_basic.py +++ b/test/multilspy/rust/test_rust_basic.py @@ -2,7 +2,7 @@ import os import pytest -from solidlsp import SolidLanguageServer as SyncLanguageServer +from solidlsp import SolidLanguageServer from solidlsp.multilspy_config import Language from solidlsp.multilspy_utils import SymbolUtils @@ -10,7 +10,7 @@ from solidlsp.multilspy_utils import SymbolUtils @pytest.mark.rust class TestRustLanguageServer: @pytest.mark.parametrize("language_server", [Language.RUST], indirect=True) - def test_find_references_raw(self, language_server: SyncLanguageServer) -> None: + def test_find_references_raw(self, language_server: SolidLanguageServer) -> None: # Directly test the request_references method for the add function file_path = os.path.join("src", "lib.rs") symbols = language_server.request_document_symbols(file_path) @@ -27,14 +27,14 @@ class TestRustLanguageServer: ), "main.rs should reference add (raw, tried all positions in selectionRange)" @pytest.mark.parametrize("language_server", [Language.RUST], indirect=True) - def test_find_symbol(self, language_server: SyncLanguageServer) -> None: + def test_find_symbol(self, language_server: SolidLanguageServer) -> None: symbols = language_server.request_full_symbol_tree() assert SymbolUtils.symbol_tree_contains_name(symbols, "main"), "main function not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "add"), "add function not found in symbol tree" # Add more as needed based on test_repo @pytest.mark.parametrize("language_server", [Language.RUST], indirect=True) - def test_find_referencing_symbols(self, language_server: SyncLanguageServer) -> None: + def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None: # Find references to 'add' defined in lib.rs, should be referenced from main.rs file_path = os.path.join("src", "lib.rs") symbols = language_server.request_document_symbols(file_path) @@ -51,7 +51,7 @@ class TestRustLanguageServer: ), "main.rs should reference add (tried all positions in selectionRange)" @pytest.mark.parametrize("language_server", [Language.RUST], indirect=True) - def test_overview_methods(self, language_server: SyncLanguageServer) -> None: + def test_overview_methods(self, language_server: SolidLanguageServer) -> None: symbols = language_server.request_full_symbol_tree() assert SymbolUtils.symbol_tree_contains_name(symbols, "main"), "main missing from overview" assert SymbolUtils.symbol_tree_contains_name(symbols, "add"), "add missing from overview" diff --git a/test/multilspy/typescript/test_typescript_basic.py b/test/multilspy/typescript/test_typescript_basic.py index e135f88d..1c6cb5fc 100644 --- a/test/multilspy/typescript/test_typescript_basic.py +++ b/test/multilspy/typescript/test_typescript_basic.py @@ -2,7 +2,7 @@ import os import pytest -from solidlsp import SolidLanguageServer as SyncLanguageServer +from solidlsp import SolidLanguageServer from solidlsp.multilspy_config import Language from solidlsp.multilspy_utils import SymbolUtils @@ -10,14 +10,14 @@ from solidlsp.multilspy_utils import SymbolUtils @pytest.mark.typescript class TestTypescriptLanguageServer: @pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True) - def test_find_symbol(self, language_server: SyncLanguageServer) -> None: + def test_find_symbol(self, language_server: SolidLanguageServer) -> None: symbols = language_server.request_full_symbol_tree() assert SymbolUtils.symbol_tree_contains_name(symbols, "DemoClass"), "DemoClass not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "helperFunction"), "helperFunction not found in symbol tree" assert SymbolUtils.symbol_tree_contains_name(symbols, "printValue"), "printValue method not found in symbol tree" @pytest.mark.parametrize("language_server", [Language.TYPESCRIPT], indirect=True) - def test_find_referencing_symbols(self, language_server: SyncLanguageServer) -> None: + def test_find_referencing_symbols(self, language_server: SolidLanguageServer) -> None: file_path = os.path.join("index.ts") symbols = language_server.request_document_symbols(file_path) helper_symbol = None From f182db59e3479f84c17273815a89ea12ec94d65b Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Mon, 23 Jun 2025 17:40:29 +0200 Subject: [PATCH 10/11] Rename language server modules and classes --- src/serena/agent.py | 10 +- src/serena/symbol.py | 2 +- src/serena/util/inspection.py | 2 +- .../clangd_language_server.py | 10 +- .../dart_language_server.py | 6 +- .../eclipse_jdtls/eclipse_jdtls.py | 16 +- src/solidlsp/language_servers/gopls/gopls.py | 6 +- .../intelephense/intelephense.py | 10 +- .../jedi_language_server/jedi_server.py | 6 +- .../kotlin_language_server.py | 10 +- .../language_servers/omnisharp/omnisharp.py | 14 +- .../pyright_language_server/pyright_server.py | 6 +- .../rust_analyzer/rust_analyzer.py | 10 +- .../language_servers/solargraph/solargraph.py | 8 +- .../typescript_language_server.py | 10 +- src/solidlsp/ls.py | 172 +++--- .../{multilspy_config.py => ls_config.py} | 2 +- ...ltilspy_exceptions.py => ls_exceptions.py} | 2 +- src/solidlsp/ls_handler.py | 8 +- .../{multilspy_logger.py => ls_logger.py} | 2 +- .../{lsp_request.py => ls_request.py} | 2 +- .../{multilspy_types.py => ls_types.py} | 0 .../{multilspy_utils.py => ls_utils.py} | 32 +- src/solidlsp/lsp_protocol_handler/server.py | 517 +----------------- .../{multilspy_settings.py => settings.py} | 2 +- src/solidlsp/type_helpers.py | 28 - test/conftest.py | 8 +- test/multilspy/go/test_go_basic.py | 4 +- test/multilspy/java/test_java_basic.py | 4 +- test/multilspy/php/test_php_basic.py | 2 +- test/multilspy/python/test_python_basic.py | 2 +- .../test_retrieval_with_ignored_dirs.py | 2 +- .../multilspy/python/test_symbol_retrieval.py | 4 +- test/multilspy/rust/test_rust_basic.py | 4 +- .../typescript/test_typescript_basic.py | 4 +- test/serena/test_serena_agent.py | 2 +- test/serena/test_symbol_editing.py | 2 +- 37 files changed, 194 insertions(+), 737 deletions(-) rename src/solidlsp/{multilspy_config.py => ls_config.py} (98%) rename src/solidlsp/{multilspy_exceptions.py => ls_exceptions.py} (87%) rename src/solidlsp/{multilspy_logger.py => ls_logger.py} (98%) rename src/solidlsp/{lsp_request.py => ls_request.py} (99%) rename src/solidlsp/{multilspy_types.py => ls_types.py} (100%) rename src/solidlsp/{multilspy_utils.py => ls_utils.py} (91%) rename src/solidlsp/{multilspy_settings.py => settings.py} (96%) delete mode 100644 src/solidlsp/type_helpers.py diff --git a/src/serena/agent.py b/src/serena/agent.py index 965dbdfa..9fea8ec9 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -52,9 +52,9 @@ from serena.util.inspection import determine_programming_language_composition, i from serena.util.shell import execute_shell_command from serena.util.thread import ExecutionResult, execute_with_timeout from solidlsp import SolidLanguageServer -from solidlsp.multilspy_config import Language, MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_types import SymbolKind +from solidlsp.ls_config import Language, LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_types import SymbolKind if TYPE_CHECKING: from serena.gui_log_viewer import GuiLogViewerHandler @@ -669,12 +669,12 @@ def create_ls_for_project( log.debug(f"Adding {len(spec.patterns)} patterns from {spec.file_path} to the ignored paths.") ignored_paths.extend(spec.patterns) log.debug(f"Using {len(ignored_paths)} ignored paths in total.") - multilspy_config = MultilspyConfig( + multilspy_config = LanguageServerConfig( code_language=project_instance.language, ignored_paths=ignored_paths, trace_lsp_communication=trace_lsp_communication, ) - ls_logger = MultilspyLogger(log_level=log_level) + ls_logger = LanguageServerLogger(log_level=log_level) log.info(f"Creating language server instance for {project_instance.project_root}.") return SolidLanguageServer.create( multilspy_config, diff --git a/src/serena/symbol.py b/src/serena/symbol.py index e4da2743..e039440c 100644 --- a/src/serena/symbol.py +++ b/src/serena/symbol.py @@ -11,7 +11,7 @@ from sensai.util.string import ToStringMixin from solidlsp import SolidLanguageServer from solidlsp.ls import ReferenceInSymbol as LSPReferenceInSymbol -from solidlsp.multilspy_types import Position, SymbolKind, UnifiedSymbolInformation +from solidlsp.ls_types import Position, SymbolKind, UnifiedSymbolInformation if TYPE_CHECKING: from .agent import SerenaAgent diff --git a/src/serena/util/inspection.py b/src/serena/util/inspection.py index 4847fd19..24c21719 100644 --- a/src/serena/util/inspection.py +++ b/src/serena/util/inspection.py @@ -4,7 +4,7 @@ from collections.abc import Generator from typing import TypeVar from serena.util.file_system import find_all_non_ignored_files -from solidlsp.multilspy_config import Language +from solidlsp.ls_config import Language T = TypeVar("T") diff --git a/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py index a61ba13f..4572de90 100644 --- a/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py @@ -12,9 +12,9 @@ import threading from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_utils import FileUtils, PlatformUtils +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_utils import FileUtils, PlatformUtils class ClangdLanguageServer(SolidLanguageServer): @@ -24,7 +24,7 @@ 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: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): """ Creates a ClangdLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ @@ -41,7 +41,7 @@ class ClangdLanguageServer(SolidLanguageServer): self.initialize_searcher_command_available = threading.Event() self.resolve_main_method_available = threading.Event() - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str: + def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ Setup runtime dependencies for ClangdLanguageServer. """ diff --git a/src/solidlsp/language_servers/dart_language_server/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server/dart_language_server.py index bf406995..59575581 100644 --- a/src/solidlsp/language_servers/dart_language_server/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server/dart_language_server.py @@ -6,8 +6,8 @@ import stat from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_utils import FileUtils, PlatformUtils +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_utils import FileUtils, PlatformUtils class DartLanguageServer(SolidLanguageServer): @@ -28,7 +28,7 @@ class DartLanguageServer(SolidLanguageServer): "dart", ) - def setup_runtime_dependencies(self, logger: "MultilspyLogger") -> str: + def setup_runtime_dependencies(self, logger: "LanguageServerLogger") -> str: platform_id = PlatformUtils.get_platform_id() with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json")) as f: diff --git a/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py index bd635945..a2453fae 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py @@ -18,10 +18,10 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_settings import MultilspySettings -from solidlsp.multilspy_utils import FileUtils, PlatformUtils +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.settings import SolidLSPSettings +from solidlsp.ls_utils import FileUtils, PlatformUtils @dataclasses.dataclass @@ -45,7 +45,7 @@ class EclipseJDTLS(SolidLanguageServer): The EclipseJDTLS class provides a Java specific implementation of the LanguageServer class """ - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): """ Creates a new EclipseJDTLS instance initializing the language server settings appropriately. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. @@ -56,7 +56,7 @@ class EclipseJDTLS(SolidLanguageServer): # ws_dir is the workspace directory for the EclipseJDTLS server ws_dir = str( PurePath( - MultilspySettings.get_language_server_directory(), + SolidLSPSettings.get_language_server_directory(), "EclipseJDTLS", "workspaces", uuid.uuid4().hex, @@ -64,7 +64,7 @@ class EclipseJDTLS(SolidLanguageServer): ) # shared_cache_location is the global cache used by Eclipse JDTLS across all workspaces - shared_cache_location = str(PurePath(MultilspySettings.get_global_cache_directory(), "lsp", "EclipseJDTLS", "sharedIndex")) + shared_cache_location = str(PurePath(SolidLSPSettings.get_global_cache_directory(), "lsp", "EclipseJDTLS", "sharedIndex")) jre_path = self.runtime_dependency_paths.jre_path lombok_jar_path = self.runtime_dependency_paths.lombok_jar_path @@ -153,7 +153,7 @@ class EclipseJDTLS(SolidLanguageServer): "lib", # General ] - def setupRuntimeDependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> RuntimeDependencyPaths: + def setupRuntimeDependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> RuntimeDependencyPaths: """ Setup runtime dependencies for EclipseJDTLS. """ diff --git a/src/solidlsp/language_servers/gopls/gopls.py b/src/solidlsp/language_servers/gopls/gopls.py index cd7f6e0b..32ae5e5c 100644 --- a/src/solidlsp/language_servers/gopls/gopls.py +++ b/src/solidlsp/language_servers/gopls/gopls.py @@ -10,8 +10,8 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger class Gopls(SolidLanguageServer): @@ -71,7 +71,7 @@ class Gopls(SolidLanguageServer): return True - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): self.setup_runtime_dependency() super().__init__( diff --git a/src/solidlsp/language_servers/intelephense/intelephense.py b/src/solidlsp/language_servers/intelephense/intelephense.py index 7e7e8db3..a02f9d18 100644 --- a/src/solidlsp/language_servers/intelephense/intelephense.py +++ b/src/solidlsp/language_servers/intelephense/intelephense.py @@ -15,9 +15,9 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import DefinitionParams, InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_utils import PlatformId, PlatformUtils +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_utils import PlatformId, PlatformUtils class Intelephense(SolidLanguageServer): @@ -33,7 +33,7 @@ class Intelephense(SolidLanguageServer): # - cache: commonly used for caching return super().is_ignored_dirname(dirname) or dirname in ["node_modules", "vendor", "cache"] - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str: + def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ Setup runtime dependencies for Intelephense. """ @@ -97,7 +97,7 @@ class Intelephense(SolidLanguageServer): return f"{intelephense_executable_path} --stdio" - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): # Setup runtime dependencies before initializing intelephense_cmd = self.setup_runtime_dependencies(logger, config) diff --git a/src/solidlsp/language_servers/jedi_language_server/jedi_server.py b/src/solidlsp/language_servers/jedi_language_server/jedi_server.py index 9ec7554c..a0ea2030 100644 --- a/src/solidlsp/language_servers/jedi_language_server/jedi_server.py +++ b/src/solidlsp/language_servers/jedi_language_server/jedi_server.py @@ -12,8 +12,8 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger class JediServer(SolidLanguageServer): @@ -21,7 +21,7 @@ class JediServer(SolidLanguageServer): Provides Python specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Python. """ - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): """ Creates a JediServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ diff --git a/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py b/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py index cf4022c2..30315357 100644 --- a/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py +++ b/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py @@ -12,9 +12,9 @@ import stat from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_utils import FileUtils, PlatformUtils +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_utils import FileUtils, PlatformUtils @dataclasses.dataclass @@ -33,7 +33,7 @@ class KotlinLanguageServer(SolidLanguageServer): Provides Kotlin specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Kotlin. """ - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): """ Creates a Kotlin Language Server instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ @@ -54,7 +54,7 @@ class KotlinLanguageServer(SolidLanguageServer): "kotlin", ) - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> KotlinRuntimeDependencyPaths: + def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> KotlinRuntimeDependencyPaths: """ Setup runtime dependencies for Kotlin Language Server. """ diff --git a/src/solidlsp/language_servers/omnisharp/omnisharp.py b/src/solidlsp/language_servers/omnisharp/omnisharp.py index b8b33eab..0a0216cd 100644 --- a/src/solidlsp/language_servers/omnisharp/omnisharp.py +++ b/src/solidlsp/language_servers/omnisharp/omnisharp.py @@ -15,10 +15,10 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_exceptions import MultilspyException -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_utils import DotnetVersion, FileUtils, PlatformId, PlatformUtils +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_exceptions import LanguageServerException +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_utils import DotnetVersion, FileUtils, PlatformId, PlatformUtils def breadth_first_file_scan(root) -> Iterable[str]: @@ -59,7 +59,7 @@ class OmniSharp(SolidLanguageServer): Provides C# specific instantiation of the LanguageServer class. Contains various configurations and settings specific to C#. """ - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): """ Creates an OmniSharp instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ @@ -68,7 +68,7 @@ class OmniSharp(SolidLanguageServer): slnfilename = find_least_depth_sln_file(repository_root_path) if slnfilename is None: logger.log("No *.sln file found in repository", logging.ERROR) - raise MultilspyException("No SLN file found in repository") + raise LanguageServerException("No SLN file found in repository") cmd = " ".join( [ @@ -136,7 +136,7 @@ class OmniSharp(SolidLanguageServer): return d - def setupRuntimeDependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> tuple[str, str]: + def setupRuntimeDependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> tuple[str, str]: """ Setup runtime dependencies for OmniSharp. """ diff --git a/src/solidlsp/language_servers/pyright_language_server/pyright_server.py b/src/solidlsp/language_servers/pyright_language_server/pyright_server.py index b7ddd075..64dbd574 100644 --- a/src/solidlsp/language_servers/pyright_language_server/pyright_server.py +++ b/src/solidlsp/language_servers/pyright_language_server/pyright_server.py @@ -13,8 +13,8 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger class PyrightServer(SolidLanguageServer): @@ -23,7 +23,7 @@ class PyrightServer(SolidLanguageServer): Contains various configurations and settings specific to Python. """ - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): """ Creates a PyrightServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. diff --git a/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py index d88cc598..a21768ef 100644 --- a/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py @@ -14,9 +14,9 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_utils import FileUtils, PlatformUtils +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_utils import FileUtils, PlatformUtils class RustAnalyzer(SolidLanguageServer): @@ -24,7 +24,7 @@ class RustAnalyzer(SolidLanguageServer): Provides Rust specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Rust. """ - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): """ Creates a RustAnalyzer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ @@ -45,7 +45,7 @@ class RustAnalyzer(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["target"] - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str: + def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ Setup runtime dependencies for rust_analyzer. """ diff --git a/src/solidlsp/language_servers/solargraph/solargraph.py b/src/solidlsp/language_servers/solargraph/solargraph.py index f733e306..07cc1b38 100644 --- a/src/solidlsp/language_servers/solargraph/solargraph.py +++ b/src/solidlsp/language_servers/solargraph/solargraph.py @@ -15,8 +15,8 @@ from typing import override from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger class Solargraph(SolidLanguageServer): @@ -25,7 +25,7 @@ class Solargraph(SolidLanguageServer): Contains various configurations and settings specific to Ruby. """ - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): """ Creates a Solargraph instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. @@ -47,7 +47,7 @@ class Solargraph(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["vendor"] - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig, repository_root_path: str) -> str: + def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig, repository_root_path: str) -> str: """ Setup runtime dependencies for Solargraph. """ diff --git a/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py index 3fe2d86e..dd1d230a 100644 --- a/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py @@ -16,9 +16,9 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -from solidlsp.multilspy_config import MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_utils import PlatformId, PlatformUtils +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_utils import PlatformId, PlatformUtils # Platform-specific imports if os.name != "nt": # Unix-like systems @@ -41,7 +41,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): Provides TypeScript specific instantiation of the LanguageServer class. Contains various configurations and settings specific to TypeScript. """ - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): """ Creates a TypeScriptLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ @@ -65,7 +65,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): "coverage", ] - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str: + def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ Setup runtime dependencies for TypeScript Language Server. """ diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index 610c60e4..426d471e 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -19,7 +19,7 @@ import pathspec import tqdm from serena.text_utils import MatchedConsecutiveLines, search_files -from solidlsp import multilspy_types +from solidlsp import ls_types from solidlsp.ls_handler import SolidLanguageServerHandler from solidlsp.lsp_protocol_handler import lsp_types from solidlsp.lsp_protocol_handler import lsp_types as LSPTypes @@ -30,19 +30,19 @@ from solidlsp.lsp_protocol_handler.server import ( ProcessLaunchInfo, StringDict, ) -from solidlsp.multilspy_config import Language, MultilspyConfig -from solidlsp.multilspy_exceptions import MultilspyException -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_utils import FileUtils, PathUtils, TextUtils +from solidlsp.ls_config import Language, LanguageServerConfig +from solidlsp.ls_exceptions import LanguageServerException +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_utils import FileUtils, PathUtils, TextUtils -GenericDocumentSymbol = Union[LSPTypes.DocumentSymbol, LSPTypes.SymbolInformation, multilspy_types.UnifiedSymbolInformation] +GenericDocumentSymbol = Union[LSPTypes.DocumentSymbol, LSPTypes.SymbolInformation, ls_types.UnifiedSymbolInformation] @dataclasses.dataclass(kw_only=True) class ReferenceInSymbol: """A symbol retrieved when requesting reference to a symbol, together with the location of the reference""" - symbol: multilspy_types.UnifiedSymbolInformation + symbol: ls_types.UnifiedSymbolInformation line: int character: int @@ -90,7 +90,7 @@ class SolidLanguageServer(ABC): @classmethod def create( - cls, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str, timeout: float | None = None + cls, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, timeout: float | None = None ) -> "SolidLanguageServer": """ Creates a language specific LanguageServer instance based on the given configuration, and appropriate settings for the programming language. @@ -178,12 +178,12 @@ class SolidLanguageServer(ABC): else: logger.log(f"Language {config.code_language} is not supported", logging.ERROR) - raise MultilspyException(f"Language {config.code_language} is not supported") + raise LanguageServerException(f"Language {config.code_language} is not supported") def __init__( self, - config: MultilspyConfig, - logger: MultilspyLogger, + config: LanguageServerConfig, + logger: LanguageServerLogger, repository_root_path: str, process_launch_info: ProcessLaunchInfo, language_id: str, @@ -214,7 +214,7 @@ class SolidLanguageServer(ABC): # load cache first to prevent any racing conditions due to asyncio stuff self._document_symbols_cache: dict[ - str, tuple[str, tuple[list[multilspy_types.UnifiedSymbolInformation], list[multilspy_types.UnifiedSymbolInformation]]] + str, tuple[str, tuple[list[ls_types.UnifiedSymbolInformation], list[ls_types.UnifiedSymbolInformation]]] ] = {} """Maps file paths to a tuple of (file_content_hash, result_of_request_document_symbols)""" self._cache_lock = threading.Lock() @@ -368,7 +368,7 @@ class SolidLanguageServer(ABC): "open_file called before Language Server started", logging.ERROR, ) - raise MultilspyException("Language Server not started") + raise LanguageServerException("Language Server not started") absolute_file_path = str(PurePath(self.repository_root_path, relative_file_path)) uri = pathlib.Path(absolute_file_path).as_uri() @@ -411,7 +411,7 @@ class SolidLanguageServer(ABC): def insert_text_at_position( self, relative_file_path: str, line: int, column: int, text_to_be_inserted: str - ) -> multilspy_types.Position: + ) -> ls_types.Position: """ Insert text at the given line and column in the given file and return the updated cursor position after inserting the text. @@ -426,7 +426,7 @@ class SolidLanguageServer(ABC): "insert_text_at_position called before Language Server started", logging.ERROR, ) - raise MultilspyException("Language Server not started") + raise LanguageServerException("Language Server not started") absolute_file_path = str(PurePath(self.repository_root_path, relative_file_path)) uri = pathlib.Path(absolute_file_path).as_uri() @@ -456,13 +456,13 @@ class SolidLanguageServer(ABC): ], } ) - return multilspy_types.Position(line=new_l, character=new_c) + return ls_types.Position(line=new_l, character=new_c) def delete_text_between_positions( self, relative_file_path: str, - start: multilspy_types.Position, - end: multilspy_types.Position, + start: ls_types.Position, + end: ls_types.Position, ) -> str: """ Delete text between the given start and end positions in the given file and return the deleted text. @@ -472,7 +472,7 @@ class SolidLanguageServer(ABC): "insert_text_at_position called before Language Server started", logging.ERROR, ) - raise MultilspyException("Language Server not started") + raise LanguageServerException("Language Server not started") absolute_file_path = str(PurePath(self.repository_root_path, relative_file_path)) uri = pathlib.Path(absolute_file_path).as_uri() @@ -500,7 +500,7 @@ class SolidLanguageServer(ABC): def _send_definition_request(self, definition_params: DefinitionParams) -> Definition | list[LocationLink] | None: return self.server.send.definition(definition_params) - def request_definition(self, relative_file_path: str, line: int, column: int) -> list[multilspy_types.Location]: + def request_definition(self, relative_file_path: str, line: int, column: int) -> list[ls_types.Location]: """ Raise a [textDocument/definition](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition) request to the Language Server for the symbol at the given line and column in the given file. Wait for the response and return the result. @@ -516,7 +516,7 @@ class SolidLanguageServer(ABC): "find_function_definition called before Language Server started", logging.ERROR, ) - raise MultilspyException("Language Server not started") + raise LanguageServerException("Language Server not started") with self.open_file(relative_file_path): # sending request to the language server and waiting for response @@ -534,29 +534,29 @@ class SolidLanguageServer(ABC): ) response = self._send_definition_request(definition_params) - ret: list[multilspy_types.Location] = [] + ret: list[ls_types.Location] = [] if isinstance(response, list): # response is either of type Location[] or LocationLink[] for item in response: assert isinstance(item, dict) if LSPConstants.URI in item and LSPConstants.RANGE in item: - new_item: multilspy_types.Location = {} + new_item: ls_types.Location = {} new_item.update(item) new_item["absolutePath"] = PathUtils.uri_to_path(new_item["uri"]) new_item["relativePath"] = PathUtils.get_relative_path(new_item["absolutePath"], self.repository_root_path) - ret.append(multilspy_types.Location(new_item)) + ret.append(ls_types.Location(new_item)) elif ( LSPConstants.ORIGIN_SELECTION_RANGE in item and LSPConstants.TARGET_URI in item and LSPConstants.TARGET_RANGE in item and LSPConstants.TARGET_SELECTION_RANGE in item ): - new_item: multilspy_types.Location = {} + new_item: ls_types.Location = {} new_item["uri"] = item[LSPConstants.TARGET_URI] new_item["absolutePath"] = PathUtils.uri_to_path(new_item["uri"]) new_item["relativePath"] = PathUtils.get_relative_path(new_item["absolutePath"], self.repository_root_path) new_item["range"] = item[LSPConstants.TARGET_SELECTION_RANGE] - ret.append(multilspy_types.Location(**new_item)) + ret.append(ls_types.Location(**new_item)) else: assert False, f"Unexpected response from Language Server: {item}" elif isinstance(response, dict): @@ -564,11 +564,11 @@ class SolidLanguageServer(ABC): assert LSPConstants.URI in response assert LSPConstants.RANGE in response - new_item: multilspy_types.Location = {} + new_item: ls_types.Location = {} new_item.update(response) new_item["absolutePath"] = PathUtils.uri_to_path(new_item["uri"]) new_item["relativePath"] = PathUtils.get_relative_path(new_item["absolutePath"], self.repository_root_path) - ret.append(multilspy_types.Location(**new_item)) + ret.append(ls_types.Location(**new_item)) 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 @@ -591,7 +591,7 @@ class SolidLanguageServer(ABC): } ) - def request_references(self, relative_file_path: str, line: int, column: int) -> list[multilspy_types.Location]: + def request_references(self, relative_file_path: str, line: int, column: int) -> list[ls_types.Location]: """ Raise a [textDocument/references](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references) request to the Language Server to find references to the symbol at the given line and column in the given file. Wait for the response and return the result. @@ -608,7 +608,7 @@ class SolidLanguageServer(ABC): "request_references called before Language Server started", logging.ERROR, ) - raise MultilspyException("Language Server not started") + raise LanguageServerException("Language Server not started") with self.open_file(relative_file_path): try: @@ -624,7 +624,7 @@ class SolidLanguageServer(ABC): if response is None: return [] - ret: list[multilspy_types.Location] = [] + ret: list[ls_types.Location] = [] assert isinstance(response, list), f"Unexpected response from Language Server (expected list, got {type(response)}): {response}" for item in response: assert isinstance(item, dict), f"Unexpected response from Language Server (expected dict, got {type(item)}): {item}" @@ -637,11 +637,11 @@ class SolidLanguageServer(ABC): self.logger.log(f"Ignoring reference in {rel_path} since it should be ignored", logging.DEBUG) continue - new_item: multilspy_types.Location = {} + new_item: ls_types.Location = {} new_item.update(item) new_item["absolutePath"] = str(abs_path) new_item["relativePath"] = str(rel_path) - ret.append(multilspy_types.Location(**new_item)) + ret.append(ls_types.Location(**new_item)) return ret @@ -697,7 +697,7 @@ class SolidLanguageServer(ABC): def request_completions( self, relative_file_path: str, line: int, column: int, allow_incomplete: bool = False - ) -> list[multilspy_types.CompletionItem]: + ) -> list[ls_types.CompletionItem]: """ Raise a [textDocument/completion](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion) request to the Language Server to find completions at the given line and column in the given file. Wait for the response and return the result. @@ -737,7 +737,7 @@ class SolidLanguageServer(ABC): # TODO: Handle the case when the completion is a keyword items = [item for item in response if item["kind"] != LSPTypes.CompletionItemKind.Keyword] - completions_list: list[multilspy_types.CompletionItem] = [] + completions_list: list[ls_types.CompletionItem] = [] for item in items: assert "insertText" in item or "textEdit" in item @@ -776,14 +776,14 @@ class SolidLanguageServer(ABC): else: assert False - completion_item = multilspy_types.CompletionItem(**completion_item) + completion_item = ls_types.CompletionItem(**completion_item) completions_list.append(completion_item) return [json.loads(json_repr) for json_repr in set(json.dumps(item, sort_keys=True) for item in completions_list)] def request_document_symbols( self, relative_file_path: str, include_body: bool = False - ) -> tuple[list[multilspy_types.UnifiedSymbolInformation], list[multilspy_types.UnifiedSymbolInformation]]: + ) -> tuple[list[ls_types.UnifiedSymbolInformation], list[ls_types.UnifiedSymbolInformation]]: """ Raise a [textDocument/documentSymbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol) request to the Language Server to find symbols in the given file. Wait for the response and return the result. @@ -823,14 +823,14 @@ class SolidLanguageServer(ABC): ) def turn_item_into_symbol_with_children(item: GenericDocumentSymbol): - item = cast(multilspy_types.UnifiedSymbolInformation, item) + item = cast(ls_types.UnifiedSymbolInformation, item) absolute_path = os.path.join(self.repository_root_path, relative_file_path) # handle missing entries in location if "location" not in item: uri = pathlib.Path(absolute_path).as_uri() assert "range" in item - tree_location = multilspy_types.Location( + tree_location = ls_types.Location( uri=uri, range=item["range"], absolutePath=absolute_path, @@ -855,9 +855,9 @@ class SolidLanguageServer(ABC): child["parent"] = item item[LSPConstants.CHILDREN] = children - flat_all_symbol_list: list[multilspy_types.UnifiedSymbolInformation] = [] + flat_all_symbol_list: list[ls_types.UnifiedSymbolInformation] = [] assert isinstance(response, list), f"Unexpected response from Language Server: {response}" - root_nodes: list[multilspy_types.UnifiedSymbolInformation] = [] + root_nodes: list[ls_types.UnifiedSymbolInformation] = [] for root_item in response: if "range" not in root_item and "location" not in root_item: if root_item["kind"] in [SymbolKind.File, SymbolKind.Module]: @@ -867,7 +867,7 @@ class SolidLanguageServer(ABC): # so we cast and rename the var after the mutating call to turn_item_into_symbol_with_children # which turned and item into a "symbol" turn_item_into_symbol_with_children(root_item) - root_symbol = cast(multilspy_types.UnifiedSymbolInformation, root_item) + root_symbol = cast(ls_types.UnifiedSymbolInformation, root_item) root_symbol["parent"] = None root_nodes.append(root_symbol) @@ -878,9 +878,9 @@ class SolidLanguageServer(ABC): if LSPConstants.CHILDREN in root_symbol: # TODO: l_tree should be a list of TreeRepr. Define the following function to return TreeRepr as well - def visit_tree_nodes_and_build_tree_repr(node: GenericDocumentSymbol) -> list[multilspy_types.UnifiedSymbolInformation]: - node = cast(multilspy_types.UnifiedSymbolInformation, node) - l: list[multilspy_types.UnifiedSymbolInformation] = [] + def visit_tree_nodes_and_build_tree_repr(node: GenericDocumentSymbol) -> list[ls_types.UnifiedSymbolInformation]: + node = cast(ls_types.UnifiedSymbolInformation, node) + l: list[ls_types.UnifiedSymbolInformation] = [] turn_item_into_symbol_with_children(node) assert LSPConstants.CHILDREN in node children = node[LSPConstants.CHILDREN] @@ -891,7 +891,7 @@ class SolidLanguageServer(ABC): flat_all_symbol_list.extend(visit_tree_nodes_and_build_tree_repr(root_symbol)) else: - flat_all_symbol_list.append(multilspy_types.UnifiedSymbolInformation(**root_symbol)) + flat_all_symbol_list.append(ls_types.UnifiedSymbolInformation(**root_symbol)) result = flat_all_symbol_list, root_nodes self.logger.log(f"Caching document symbols for {relative_file_path}", logging.DEBUG) @@ -902,7 +902,7 @@ class SolidLanguageServer(ABC): def request_full_symbol_tree( self, within_relative_path: str | None = None, include_body: bool = False - ) -> list[multilspy_types.UnifiedSymbolInformation]: + ) -> list[ls_types.UnifiedSymbolInformation]: """ Will go through all files in the project or within a relative path and build a tree of symbols. Note: this may be slow the first time it is called, especially if `within_relative_path` is not used to restrict the search. @@ -937,7 +937,7 @@ class SolidLanguageServer(ABC): return root_nodes # Helper function to recursively process directories - def process_directory(rel_dir_path: str) -> list[multilspy_types.UnifiedSymbolInformation]: + def process_directory(rel_dir_path: str) -> list[ls_types.UnifiedSymbolInformation]: abs_dir_path = self.repository_root_path if rel_dir_path == "." else os.path.join(self.repository_root_path, rel_dir_path) abs_dir_path = os.path.realpath(abs_dir_path) @@ -952,10 +952,10 @@ class SolidLanguageServer(ABC): return [] # Create package symbol for directory - package_symbol = multilspy_types.UnifiedSymbolInformation( # type: ignore + package_symbol = ls_types.UnifiedSymbolInformation( # type: ignore name=os.path.basename(abs_dir_path), - kind=multilspy_types.SymbolKind.Package, - location=multilspy_types.Location( + kind=ls_types.SymbolKind.Package, + location=ls_types.Location( uri=str(pathlib.Path(abs_dir_path).as_uri()), range={"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 0}}, absolutePath=str(abs_dir_path), @@ -985,12 +985,12 @@ class SolidLanguageServer(ABC): file_rel_path = str(Path(contained_dir_or_file_abs_path).resolve().relative_to(self.repository_root_path)) with self.open_file(file_rel_path) as file_data: fileRange = self._get_range_from_file_content(file_data.contents) - file_symbol = multilspy_types.UnifiedSymbolInformation( # type: ignore + file_symbol = ls_types.UnifiedSymbolInformation( # type: ignore name=os.path.splitext(contained_dir_or_file_name)[0], - kind=multilspy_types.SymbolKind.File, + kind=ls_types.SymbolKind.File, range=fileRange, selectionRange=fileRange, - location=multilspy_types.Location( + location=ls_types.Location( uri=str(pathlib.Path(contained_dir_or_file_abs_path).as_uri()), range=fileRange, absolutePath=str(contained_dir_or_file_abs_path), @@ -1006,7 +1006,7 @@ class SolidLanguageServer(ABC): package_symbol["children"].append(file_symbol) # TODO: Not sure if this is actually still needed given recent changes to relative path handling - def fix_relative_path(nodes: list[multilspy_types.UnifiedSymbolInformation]): + def fix_relative_path(nodes: list[ls_types.UnifiedSymbolInformation]): for node in nodes: if "location" in node and "relativePath" in node["location"]: path = Path(node["location"]["relativePath"]) @@ -1028,18 +1028,18 @@ class SolidLanguageServer(ABC): return process_directory(start_rel_path) @staticmethod - def _get_range_from_file_content(file_content: str) -> multilspy_types.Range: + def _get_range_from_file_content(file_content: str) -> ls_types.Range: """ Get the range for the given file. """ lines = file_content.split("\n") end_line = len(lines) end_column = len(lines[-1]) - return multilspy_types.Range( - start=multilspy_types.Position(line=0, character=0), end=multilspy_types.Position(line=end_line, character=end_column) + return ls_types.Range( + start=ls_types.Position(line=0, character=0), end=ls_types.Position(line=end_line, character=end_column) ) - def request_dir_overview(self, relative_dir_path: str) -> dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]]: + def request_dir_overview(self, relative_dir_path: str) -> dict[str, list[tuple[str, ls_types.SymbolKind, int, int]]]: """ An overview of the given directory. @@ -1048,11 +1048,11 @@ class SolidLanguageServer(ABC): """ symbol_tree = self.request_full_symbol_tree(relative_dir_path) # Initialize result dictionary - result: dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]] = defaultdict(list) + result: dict[str, list[tuple[str, ls_types.SymbolKind, int, int]]] = defaultdict(list) # Helper function to process a symbol and its children - def process_symbol(symbol: multilspy_types.UnifiedSymbolInformation): - if symbol["kind"] == multilspy_types.SymbolKind.File: + def process_symbol(symbol: ls_types.UnifiedSymbolInformation): + if symbol["kind"] == ls_types.SymbolKind.File: # For file symbols, process their children (top-level symbols) for child in symbol["children"]: assert "location" in child @@ -1075,7 +1075,7 @@ class SolidLanguageServer(ABC): process_symbol(root) return result - def request_document_overview(self, relative_file_path: str) -> list[tuple[str, multilspy_types.SymbolKind, int, int]]: + def request_document_overview(self, relative_file_path: str) -> list[tuple[str, ls_types.SymbolKind, int, int]]: """ An overview of the given file. Returns the list of tuples (name, kind, line, column) of all top-level symbols in the file. @@ -1091,7 +1091,7 @@ class SolidLanguageServer(ABC): raise KeyError(f"Could not process symbol of name {root.get('name', 'unknown')} in {relative_file_path=}") from e return result - def request_overview(self, within_relative_path: str) -> dict[str, list[tuple[str, multilspy_types.SymbolKind, int, int]]]: + def request_overview(self, within_relative_path: str) -> dict[str, list[tuple[str, ls_types.SymbolKind, int, int]]]: """ An overview of all symbols in the given file or directory. @@ -1108,7 +1108,7 @@ class SolidLanguageServer(ABC): else: return self.request_dir_overview(within_relative_path) - def request_hover(self, relative_file_path: str, line: int, column: int) -> multilspy_types.Hover | None: + def request_hover(self, relative_file_path: str, line: int, column: int) -> ls_types.Hover | None: """ Raise a [textDocument/hover](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover) request to the Language Server to find the hover information at the given line and column in the given file. Wait for the response and return the result. @@ -1135,12 +1135,12 @@ class SolidLanguageServer(ABC): assert isinstance(response, dict) - return multilspy_types.Hover(**response) + return ls_types.Hover(**response) # ----------------------------- FROM HERE ON MODIFICATIONS BY MISCHA -------------------- def retrieve_symbol_body( - self, symbol: multilspy_types.UnifiedSymbolInformation | LSPTypes.DocumentSymbol | LSPTypes.SymbolInformation + self, symbol: ls_types.UnifiedSymbolInformation | LSPTypes.DocumentSymbol | LSPTypes.SymbolInformation ) -> str: """ Load the body of the given symbol. If the body is already contained in the symbol, just return it. @@ -1169,7 +1169,7 @@ class SolidLanguageServer(ABC): "request_parsed_files called before Language Server started", logging.ERROR, ) - raise MultilspyException("Language Server not started") + raise LanguageServerException("Language Server not started") rel_file_paths = [] for root, dirs, files in os.walk(self.repository_root_path): dirs[:] = [d for d in dirs if not self.is_ignored_path(os.path.join(root, d))] @@ -1244,7 +1244,7 @@ class SolidLanguageServer(ABC): "request_referencing_symbols called before Language Server started", logging.ERROR, ) - raise MultilspyException("Language Server not started") + raise LanguageServerException("Language Server not started") # First, get all references to the symbol references = self.request_references(relative_file_path, line, column) @@ -1281,7 +1281,7 @@ class SolidLanguageServer(ABC): containing_symbol_name = ref_text.split(".")[0] all_symbols, _ = self.request_document_symbols(ref_path) for symbol in all_symbols: - if symbol["name"] == containing_symbol_name and symbol["kind"] == multilspy_types.SymbolKind.Variable: + if symbol["name"] == containing_symbol_name and symbol["kind"] == ls_types.SymbolKind.Variable: containing_symbol = copy(symbol) containing_symbol["location"] = ref containing_symbol["range"] = ref["range"] @@ -1294,7 +1294,7 @@ class SolidLanguageServer(ABC): logging.WARNING, ) fileRange = self._get_range_from_file_content(file_data.contents) - location = multilspy_types.Location( + location = ls_types.Location( uri=str(pathlib.Path(os.path.join(self.repository_root_path, ref_path)).as_uri()), range=fileRange, absolutePath=str(os.path.join(self.repository_root_path, ref_path)), @@ -1307,8 +1307,8 @@ class SolidLanguageServer(ABC): else: body = "" - containing_symbol = multilspy_types.UnifiedSymbolInformation( - kind=multilspy_types.SymbolKind.File, + containing_symbol = ls_types.UnifiedSymbolInformation( + kind=ls_types.SymbolKind.File, range=fileRange, selectionRange=fileRange, location=location, @@ -1316,7 +1316,7 @@ class SolidLanguageServer(ABC): children=[], body=body, ) - if containing_symbol is None or (not include_file_symbols and containing_symbol["kind"] == multilspy_types.SymbolKind.File): + if containing_symbol is None or (not include_file_symbols and containing_symbol["kind"] == ls_types.SymbolKind.File): continue assert "location" in containing_symbol @@ -1363,7 +1363,7 @@ class SolidLanguageServer(ABC): column: int | None = None, strict: bool = False, include_body: bool = False, - ) -> multilspy_types.UnifiedSymbolInformation | None: + ) -> ls_types.UnifiedSymbolInformation | None: """ Finds the first symbol containing the position for the given file. For Python, container symbols are considered to be those with kinds corresponding to @@ -1409,7 +1409,7 @@ class SolidLanguageServer(ABC): for symbol in symbols: if "location" not in symbol: range = symbol["range"] - location = multilspy_types.Location( + location = ls_types.Location( uri=f"file:/{absolute_file_path}", range=range, absolutePath=absolute_file_path, @@ -1424,9 +1424,9 @@ class SolidLanguageServer(ABC): location["uri"] = Path(absolute_file_path).as_uri() # Allowed container kinds, currently only for Python - container_symbol_kinds = {multilspy_types.SymbolKind.Method, multilspy_types.SymbolKind.Function, multilspy_types.SymbolKind.Class} + container_symbol_kinds = {ls_types.SymbolKind.Method, ls_types.SymbolKind.Function, ls_types.SymbolKind.Class} - def is_position_in_range(line: int, range_d: multilspy_types.Range) -> bool: + def is_position_in_range(line: int, range_d: ls_types.Range) -> bool: start = range_d["start"] end = range_d["end"] @@ -1447,7 +1447,7 @@ class SolidLanguageServer(ABC): for s in symbols if s["kind"] in container_symbol_kinds and s["location"]["range"]["start"]["line"] != s["location"]["range"]["end"]["line"] ] - var_containers = [s for s in symbols if s["kind"] == multilspy_types.SymbolKind.Variable] + var_containers = [s for s in symbols if s["kind"] == ls_types.SymbolKind.Variable] candidate_containers.extend(var_containers) if not candidate_containers: @@ -1471,8 +1471,8 @@ class SolidLanguageServer(ABC): return None def request_container_of_symbol( - self, symbol: multilspy_types.UnifiedSymbolInformation, include_body: bool = False - ) -> multilspy_types.UnifiedSymbolInformation | None: + self, symbol: ls_types.UnifiedSymbolInformation, include_body: bool = False + ) -> ls_types.UnifiedSymbolInformation | None: """ Finds the container of the given symbol if there is one. If the parent attribute is present, the parent is returned without further searching. @@ -1498,7 +1498,7 @@ class SolidLanguageServer(ABC): line: int, column: int, include_body: bool = False, - ) -> multilspy_types.UnifiedSymbolInformation | None: + ) -> ls_types.UnifiedSymbolInformation | None: """ Finds the symbol that defines the symbol at the given location. @@ -1516,7 +1516,7 @@ class SolidLanguageServer(ABC): "request_defining_symbol called before Language Server started", logging.ERROR, ) - raise MultilspyException("Language Server not started") + raise LanguageServerException("Language Server not started") # Get the definition location(s) definitions = self.request_definition(relative_file_path, line, column) @@ -1596,7 +1596,7 @@ class SolidLanguageServer(ABC): logging.ERROR, ) - def request_workspace_symbol(self, query: str) -> list[multilspy_types.UnifiedSymbolInformation] | None: + def request_workspace_symbol(self, query: str) -> list[ls_types.UnifiedSymbolInformation] | None: """ Raise a [workspace/symbol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol) request to the Language Server to find symbols across the whole workspace. Wait for the response and return the result. @@ -1611,7 +1611,7 @@ class SolidLanguageServer(ABC): assert isinstance(response, list) - ret: list[multilspy_types.UnifiedSymbolInformation] = [] + ret: list[ls_types.UnifiedSymbolInformation] = [] for item in response: assert isinstance(item, dict) @@ -1619,7 +1619,7 @@ class SolidLanguageServer(ABC): assert LSPConstants.KIND in item assert LSPConstants.LOCATION in item - ret.append(multilspy_types.UnifiedSymbolInformation(**item)) + ret.append(ls_types.UnifiedSymbolInformation(**item)) return ret diff --git a/src/solidlsp/multilspy_config.py b/src/solidlsp/ls_config.py similarity index 98% rename from src/solidlsp/multilspy_config.py rename to src/solidlsp/ls_config.py index f676e52a..27deaf15 100644 --- a/src/solidlsp/multilspy_config.py +++ b/src/solidlsp/ls_config.py @@ -73,7 +73,7 @@ class Language(str, Enum): @dataclass -class MultilspyConfig: +class LanguageServerConfig: """ Configuration parameters """ diff --git a/src/solidlsp/multilspy_exceptions.py b/src/solidlsp/ls_exceptions.py similarity index 87% rename from src/solidlsp/multilspy_exceptions.py rename to src/solidlsp/ls_exceptions.py index 1dc12ed4..0484da6b 100644 --- a/src/solidlsp/multilspy_exceptions.py +++ b/src/solidlsp/ls_exceptions.py @@ -3,7 +3,7 @@ This module contains the exceptions raised by the Multilspy framework. """ -class MultilspyException(Exception): +class LanguageServerException(Exception): """ Exceptions raised by the Multilspy framework. """ diff --git a/src/solidlsp/ls_handler.py b/src/solidlsp/ls_handler.py index e4c8b688..6de9ef7e 100644 --- a/src/solidlsp/ls_handler.py +++ b/src/solidlsp/ls_handler.py @@ -28,8 +28,8 @@ from solidlsp.lsp_protocol_handler.server import ( make_request, make_response, ) -from solidlsp.lsp_request import SolidLspRequest -from solidlsp.multilspy_exceptions import MultilspyException +from solidlsp.ls_request import LanguageServerRequest +from solidlsp.ls_exceptions import LanguageServerException log = logging.getLogger(__name__) @@ -109,7 +109,7 @@ class SolidLanguageServerHandler: logger: An optional function that takes two strings (source and destination) and a payload dictionary, and logs the communication between the client and the server. """ - self.send = SolidLspRequest(self.send_request) + self.send = LanguageServerRequest(self.send_request) self.notify = LspNotification(self.send_notification) self.process_launch_info = process_launch_info @@ -422,7 +422,7 @@ class SolidLanguageServerHandler: self._log("Processing result") if result.is_error(): - raise MultilspyException( + raise LanguageServerException( f"Could not process request {method} with params:\n{params}.\n Language server error: {result.error}" ) from result.error diff --git a/src/solidlsp/multilspy_logger.py b/src/solidlsp/ls_logger.py similarity index 98% rename from src/solidlsp/multilspy_logger.py rename to src/solidlsp/ls_logger.py index 993aa3b8..6bdf12cb 100644 --- a/src/solidlsp/multilspy_logger.py +++ b/src/solidlsp/ls_logger.py @@ -22,7 +22,7 @@ class LogLine(BaseModel): message: str -class MultilspyLogger: +class LanguageServerLogger: """ Logger class """ diff --git a/src/solidlsp/lsp_request.py b/src/solidlsp/ls_request.py similarity index 99% rename from src/solidlsp/lsp_request.py rename to src/solidlsp/ls_request.py index 11fc67b9..209a3f04 100644 --- a/src/solidlsp/lsp_request.py +++ b/src/solidlsp/ls_request.py @@ -3,7 +3,7 @@ from typing import Union from solidlsp.lsp_protocol_handler import lsp_types -class SolidLspRequest: +class LanguageServerRequest: def __init__(self, send_request): self.send_request = send_request diff --git a/src/solidlsp/multilspy_types.py b/src/solidlsp/ls_types.py similarity index 100% rename from src/solidlsp/multilspy_types.py rename to src/solidlsp/ls_types.py diff --git a/src/solidlsp/multilspy_utils.py b/src/solidlsp/ls_utils.py similarity index 91% rename from src/solidlsp/multilspy_utils.py rename to src/solidlsp/ls_utils.py index e1b71c2c..535d3429 100644 --- a/src/solidlsp/multilspy_utils.py +++ b/src/solidlsp/ls_utils.py @@ -14,9 +14,9 @@ from pathlib import Path, PurePath import requests -from solidlsp.multilspy_exceptions import MultilspyException -from solidlsp.multilspy_logger import MultilspyLogger -from solidlsp.multilspy_types import UnifiedSymbolInformation +from solidlsp.ls_exceptions import LanguageServerException +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_types import UnifiedSymbolInformation class TextUtils: @@ -147,22 +147,22 @@ class FileUtils: """ @staticmethod - def read_file(logger: MultilspyLogger, file_path: str) -> str: + def read_file(logger: LanguageServerLogger, file_path: str) -> str: """ Reads the file at the given path and returns the contents as a string. """ if not os.path.exists(file_path): logger.log(f"File read '{file_path}' failed: File does not exist.", logging.ERROR) - raise MultilspyException(f"File read '{file_path}' failed: File does not exist.") + raise LanguageServerException(f"File read '{file_path}' failed: File does not exist.") try: with open(file_path, encoding="utf-8") as inp_file: return inp_file.read() except Exception as exc: logger.log(f"File read '{file_path}' failed to read with encoding 'utf-8': {exc}", logging.ERROR) - raise MultilspyException("File read failed.") from None + raise LanguageServerException("File read failed.") from None @staticmethod - def download_file(logger: MultilspyLogger, url: str, target_path: str) -> None: + def download_file(logger: LanguageServerLogger, url: str, target_path: str) -> None: """ Downloads the file from the given URL to the given {target_path} """ @@ -170,15 +170,15 @@ class FileUtils: 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) - raise MultilspyException("Error downloading file.") + raise LanguageServerException("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) - raise MultilspyException("Error downloading file.") from None + raise LanguageServerException("Error downloading file.") from None @staticmethod - def download_and_extract_archive(logger: MultilspyLogger, url: str, target_path: str, archive_type: str) -> None: + def download_and_extract_archive(logger: LanguageServerLogger, 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} """ @@ -203,10 +203,10 @@ class FileUtils: shutil.copyfileobj(f_in, f_out) else: logger.log(f"Unknown archive type '{archive_type}' for extraction", logging.ERROR) - raise MultilspyException(f"Unknown archive type '{archive_type}'") + raise LanguageServerException(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) - raise MultilspyException("Error extracting archive.") from exc + raise LanguageServerException("Error extracting archive.") from exc finally: for tmp_file_name in tmp_files: if os.path.exists(tmp_file_name): @@ -268,7 +268,7 @@ class PlatformUtils: platform_id += "-" + libc return PlatformId(platform_id) else: - raise MultilspyException(f"Unknown platform: {system=}, {machine=}, {bitness=}") + raise LanguageServerException(f"Unknown platform: {system=}, {machine=}, {bitness=}") @staticmethod def _determine_windows_machine_type(): @@ -324,7 +324,7 @@ class PlatformUtils: available_version_cmd_output.append(version_cmd_output) if not available_version_cmd_output: - raise MultilspyException("dotnet not found on the system") + raise LanguageServerException("dotnet not found on the system") # Check for supported versions in order of preference (latest first) for version_cmd_output in available_version_cmd_output: @@ -341,7 +341,7 @@ class PlatformUtils: return DotnetVersion.V4 # If no supported version found, raise exception with all available versions - raise MultilspyException( + raise LanguageServerException( f"No supported dotnet version found. Available versions: {', '.join(available_version_cmd_output)}. Supported versions: 4, 6, 7, 8" ) except (FileNotFoundError, subprocess.CalledProcessError): @@ -349,7 +349,7 @@ class PlatformUtils: result = subprocess.run(["mono", "--version"], capture_output=True, check=True) return DotnetVersion.VMONO except (FileNotFoundError, subprocess.CalledProcessError): - raise MultilspyException("dotnet or mono not found on the system") + raise LanguageServerException("dotnet or mono not found on the system") class SymbolUtils: diff --git a/src/solidlsp/lsp_protocol_handler/server.py b/src/solidlsp/lsp_protocol_handler/server.py index c628d434..0a001f44 100644 --- a/src/solidlsp/lsp_protocol_handler/server.py +++ b/src/solidlsp/lsp_protocol_handler/server.py @@ -39,7 +39,7 @@ from typing import Any, Union import psutil -from ..multilspy_exceptions import MultilspyException +from ..ls_exceptions import LanguageServerException from .lsp_requests import LspNotification, LspRequest from .lsp_types import ErrorCodes @@ -118,23 +118,6 @@ class MessageType: log = 4 -class Request: - def __init__(self) -> None: - self.cv = asyncio.Condition() - self.result: PayloadLike | None = None - self.error: Error | None = None - - async def on_result(self, params: PayloadLike) -> None: - self.result = params - async with self.cv: - self.cv.notify() - - async def on_error(self, err: Error) -> None: - self.error = err - async with self.cv: - self.cv.notify() - - def content_length(line: bytes) -> int | None: if line.startswith(b"Content-Length: "): _, value = line.split(b"Content-Length: ") @@ -145,501 +128,3 @@ def content_length(line: bytes) -> int | None: raise ValueError(f"Invalid Content-Length header: {value}") return None - -class LanguageServerHandler: - """ - This class provides the implementation of Python client for the Language Server Protocol. - A class that launches the language server and communicates with it - using the Language Server Protocol (LSP). - - It provides methods for sending requests, responses, and notifications to the server - and for registering handlers for requests and notifications from the server. - - Uses JSON-RPC 2.0 for communication with the server over stdin/stdout. - - Attributes: - send: A LspRequest object that can be used to send requests to the server and - await for the responses. - notify: A LspNotification object that can be used to send notifications to the server. - cmd: A string that represents the command to launch the language server process. - process: A subprocess.Popen object that represents the language server process. - _received_shutdown: A boolean flag that indicates whether the client has received - a shutdown request from the server. - request_id: An integer that represents the next available request id for the client. - _response_handlers: A dictionary that maps request ids to Request objects that - store the results or errors of the requests. - on_request_handlers: A dictionary that maps method names to callback functions - that handle requests from the server. - on_notification_handlers: A dictionary that maps method names to callback functions - that handle notifications from the server. - logger: An optional function that takes two strings (source and destination) and - a payload dictionary, and logs the communication between the client and the server. - tasks: A dictionary that maps task ids to asyncio.Task objects that represent - the asynchronous tasks created by the handler. - task_counter: An integer that represents the next available task id for the handler. - loop: An asyncio.AbstractEventLoop object that represents the event loop used by the handler. - start_independent_lsp_process: An optional boolean flag that indicates whether to start the - language server process in an independent process group. Default is `True`. Setting it to - `False` means that the language server process will be in the same process group as the - the current process, and any SIGINT and SIGTERM signals will be sent to both processes. - - """ - - def __init__( - self, - process_launch_info: ProcessLaunchInfo, - logger: Callable[[str, str, StringDict | str], None] | None = None, - start_independent_lsp_process=True, - ) -> None: - """ - Params: - cmd: A string that represents the command to launch the language server process. - logger: An optional function that takes two strings (source and destination) and - a payload dictionary, and logs the communication between the client and the server. - """ - self.send = LspRequest(self.send_request) - self.notify = LspNotification(self.send_notification) - - self.process_launch_info = process_launch_info - self.process = None - self._received_shutdown = False - - self.request_id = 1 - self._response_handlers: dict[Any, Request] = {} - self.on_request_handlers = {} - self.on_notification_handlers = {} - self.logger = logger - self.tasks = {} - self.task_counter = 0 - self.loop = None - self.start_independent_lsp_process = start_independent_lsp_process - - # Add thread locks for shared resources to prevent race conditions - self._stdin_lock = threading.Lock() - self._request_id_lock = threading.Lock() - self._response_handlers_lock = threading.Lock() - self._tasks_lock = threading.Lock() - - def is_running(self) -> bool: - """ - Checks if the language server process is currently running. - """ - return self.process is not None and self.process.returncode is None - - async def start(self) -> None: - """ - Starts the language server process and creates a task to continuously read from its stdout to handle communications - from the server to the client - """ - child_proc_env = os.environ.copy() - child_proc_env.update(self.process_launch_info.env) - - log.info("Starting language server process via command: %s", self.process_launch_info.cmd) - self.process = await asyncio.create_subprocess_shell( - self.process_launch_info.cmd, - stdout=asyncio.subprocess.PIPE, - stdin=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - env=child_proc_env, - cwd=self.process_launch_info.cwd, - start_new_session=self.start_independent_lsp_process, - ) - - # Check if process terminated immediately - if self.process.returncode is not None: - log.error("Language server has already terminated/could not be started") - # Process has already terminated - stderr_data = await self.process.stderr.read() - error_message = stderr_data.decode("utf-8", errors="replace") - raise RuntimeError(f"Process terminated immediately with code {self.process.returncode}. Error: {error_message}") - - self.loop = asyncio.get_event_loop() - - # Use lock to prevent race conditions on tasks and task_counter during startup - with self._tasks_lock: - self.tasks[self.task_counter] = self.loop.create_task(self.run_forever()) - self.task_counter += 1 - self.tasks[self.task_counter] = self.loop.create_task(self.run_forever_stderr()) - self.task_counter += 1 - - async def stop(self) -> None: - """ - Sends the terminate signal to the language server process and waits for it to exit, with a timeout, killing it if necessary - """ - # First cancel all tasks - await self._cancel_pending_tasks() - - process = self.process - self.process = None - - if not process: - return - - # Clean up the process - await self._cleanup_process(process) - - async def _cancel_pending_tasks(self): - """Cancel all pending tasks and wait for them to complete or timeout.""" - pending_tasks = [] - - # Use lock to safely access tasks dictionary - with self._tasks_lock: - for task in self.tasks.values(): - if not task.done(): - task.cancel() - pending_tasks.append(task) - - if pending_tasks: - try: - await asyncio.wait_for(asyncio.gather(*pending_tasks, return_exceptions=True), timeout=5.0) - except (TimeoutError, Exception): - pass - - # Clear tasks dictionary under lock - with self._tasks_lock: - self.tasks = {} - - async def _cleanup_process(self, process): - """Clean up a process: close stdin, terminate/kill process, close stdout/stderr.""" - # Close stdin first to prevent deadlocks - # See: https://bugs.python.org/issue35539 - self._safely_close_pipe(process.stdin) - - # Terminate/kill the process if it's still running - if process.returncode is None: - await self._terminate_or_kill_process(process) - - # Close stdout and stderr pipes after process has exited - # This is essential to prevent "I/O operation on closed pipe" errors and - # "Event loop is closed" errors during garbage collection - # See: https://bugs.python.org/issue41320 and https://github.com/python/cpython/issues/88050 - self._safely_close_pipe(process.stdout) - self._safely_close_pipe(process.stderr) - - # Small delay to ensure OS has released file handles - await asyncio.sleep(0.5) - - def _safely_close_pipe(self, pipe): - """Safely close a pipe, ignoring any exceptions.""" - if pipe: - try: - pipe.close() - except Exception: - pass - - async def _terminate_or_kill_process(self, process): - """Try to terminate the process gracefully, then forcefully if necessary.""" - # First try to terminate the process tree gracefully - self._signal_process_tree(process, terminate=True) - - # Wait for the process to exit (with timeout) - try: - await asyncio.wait_for(process.wait(), timeout=10) - except (TimeoutError, Exception): - # If termination failed, forcefully kill the process tree - self._signal_process_tree(process, terminate=False) - try: - # Give it one more chance to exit - await asyncio.wait_for(process.wait(), timeout=2) - except Exception: - pass - - def _signal_process_tree(self, process, terminate=True): - """Send signal (terminate or kill) to the process and all its children.""" - signal_method = "terminate" if terminate else "kill" - - # Try to get the parent process - parent = None - try: - parent = psutil.Process(process.pid) - except (psutil.NoSuchProcess, psutil.AccessDenied, Exception): - pass - - # If we have the parent process and it's running, signal the entire tree - if parent and parent.is_running(): - # Signal children first - for child in parent.children(recursive=True): - try: - getattr(child, signal_method)() - except (psutil.NoSuchProcess, psutil.AccessDenied, Exception): - pass - - # Then signal the parent - try: - getattr(parent, signal_method)() - except (psutil.NoSuchProcess, psutil.AccessDenied, Exception): - pass - else: - # Fall back to direct process signaling - try: - getattr(process, signal_method)() - except Exception: - pass - - async def shutdown(self) -> None: - """ - Perform the shutdown sequence for the client, including sending the shutdown request to the server and notifying it of exit - """ - self._log("Sending shutdown request to server") - await self.send.shutdown() - self._log("Received shutdown response from server") - self._received_shutdown = True - self._log("Sending exit notification to server") - self.notify.exit() - self._log("Sent exit notification to server") - if self.process and self.process.stdout: - self.process.stdout.set_exception(StopLoopException()) - # This yields the control to the event loop to allow the exception to be handled - # in the run_forever and run_forever_stderr methods - await asyncio.sleep(0) - - def _log(self, message: str | StringDict) -> None: - """ - Create a log message - """ - if self.logger is not None: - self.logger("client", "logger", message) - - async def run_forever(self) -> bool: - """ - Continuously read from the language server process stdout and handle the messages - invoking the registered response and notification handlers - """ - try: - while self.process and self.process.stdout and not self.process.stdout.at_eof(): - line = await self.process.stdout.readline() - if not line: - continue - try: - num_bytes = content_length(line) - except ValueError: - continue - if num_bytes is None: - continue - while line and line.strip(): - line = await self.process.stdout.readline() - if not line: - continue - body = await self.process.stdout.readexactly(num_bytes) - - # Use lock to prevent race conditions on tasks and task_counter - with self._tasks_lock: - self.tasks[self.task_counter] = asyncio.get_event_loop().create_task(self._handle_body(body)) - self.task_counter += 1 - except (BrokenPipeError, ConnectionResetError, StopLoopException): - pass - return self._received_shutdown - - async def run_forever_stderr(self) -> None: - """ - Continuously read from the language server process stderr and log the messages - """ - try: - while self.process and self.process.stderr and not self.process.stderr.at_eof(): - line = await self.process.stderr.readline() - if not line: - continue - self._log("LSP stderr: " + line.decode(ENCODING, errors="replace")) - except (BrokenPipeError, ConnectionResetError, StopLoopException): - pass - - async def _handle_body(self, body: bytes) -> None: - """ - Parse the body text received from the language server process and invoke the appropriate handler - """ - try: - await self._receive_payload(json.loads(body)) - except OSError as ex: - self._log(f"malformed {ENCODING}: {ex}") - except UnicodeDecodeError as ex: - self._log(f"malformed {ENCODING}: {ex}") - except json.JSONDecodeError as ex: - self._log(f"malformed JSON: {ex}") - - async def _receive_payload(self, payload: StringDict) -> None: - """ - Determine if the payload received from server is for a request, response, or notification and invoke the appropriate handler - """ - if self.logger: - self.logger("server", "client", payload) - try: - if "method" in payload: - if "id" in payload: - await self._request_handler(payload) - else: - await self._notification_handler(payload) - elif "id" in payload: - await self._response_handler(payload) - else: - self._log(f"Unknown payload type: {payload}") - except Exception as err: - self._log(f"Error handling server payload: {err}") - - def send_notification(self, method: str, params: dict | None = None) -> None: - """ - Send notification pertaining to the given method to the server with the given parameters - """ - self._send_payload_sync(make_notification(method, params)) - - def send_response(self, request_id: Any, params: PayloadLike) -> None: - """ - Send response to the given request id to the server with the given parameters - """ - # Use lock to prevent race conditions on tasks and task_counter - with self._tasks_lock: - self.tasks[self.task_counter] = asyncio.get_event_loop().create_task(self._send_payload(make_response(request_id, params))) - self.task_counter += 1 - - def send_error_response(self, request_id: Any, err: Error) -> None: - """ - Send error response to the given request id to the server with the given error - """ - # Use lock to prevent race conditions on tasks and task_counter - with self._tasks_lock: - self.tasks[self.task_counter] = asyncio.get_event_loop().create_task(self._send_payload(make_error_response(request_id, err))) - self.task_counter += 1 - - async def send_request(self, method: str, params: dict | None = None) -> PayloadLike: - """ - Send request to the server, register the request id, and wait for the response - """ - request = Request() - - # Use lock to prevent race conditions on request_id and _response_handlers - with self._request_id_lock: - request_id = self.request_id - self.request_id += 1 - - with self._response_handlers_lock: - self._response_handlers[request_id] = request - - async with request.cv: - await self._send_payload(make_request(method, request_id, params)) - self._log(f"Waiting for asyncio condition for request {method} with params:\n{params}") - await request.cv.wait() - self._log("Finished waiting, processing result") - if isinstance(request.error, Error): - raise MultilspyException( - f"Could not process request {method} with params:\n{params}.\n Language server error: {request.error}" - ) from request.error - self._log(f"Returning non-error result, which is:\n{request.result}") - return request.result - - def _send_payload_sync(self, payload: StringDict) -> None: - """ - Send the payload to the server by writing to its stdin synchronously - """ - if not self.process or not self.process.stdin: - return - msg = create_message(payload) - if self.logger: - self.logger("client", "server", payload) - - # Use lock to prevent concurrent writes to stdin that cause buffer corruption - with self._stdin_lock: - try: - self.process.stdin.writelines(msg) - except (BrokenPipeError, ConnectionResetError, OSError) as e: - # Log the error but don't raise to prevent cascading failures - if self.logger: - self.logger("client", "logger", f"Failed to write to stdin: {e}") - return - - async def _send_payload(self, payload: StringDict) -> None: - """ - Send the payload to the server by writing to its stdin asynchronously. - """ - if not self.process or not self.process.stdin: - return - self._log(payload) - msg = create_message(payload) - - # Use lock to prevent concurrent writes to stdin that cause buffer corruption - with self._stdin_lock: - try: - self.process.stdin.writelines(msg) - await self.process.stdin.drain() - except (BrokenPipeError, ConnectionResetError, OSError) as e: - # Log the error but don't raise to prevent cascading failures - if self.logger: - self.logger("client", "logger", f"Failed to write to stdin: {e}") - return - - def on_request(self, method: str, cb) -> None: - """ - Register the callback function to handle requests from the server to the client for the given method - """ - self.on_request_handlers[method] = cb - - def on_notification(self, method: str, cb) -> None: - """ - Register the callback function to handle notifications from the server to the client for the given method - """ - self.on_notification_handlers[method] = cb - - async def _response_handler(self, response: StringDict) -> None: - """ - Handle the response received from the server for a request, using the id to determine the request - """ - with self._response_handlers_lock: - request = self._response_handlers.pop(response["id"]) - - if "result" in response and "error" not in response: - await request.on_result(response["result"]) - elif "result" not in response and "error" in response: - await request.on_error(Error.from_lsp(response["error"])) - else: - await request.on_error(Error(ErrorCodes.InvalidRequest, "")) - - async def _request_handler(self, response: StringDict) -> None: - """ - Handle the request received from the server: call the appropriate callback function and return the result - """ - method = response.get("method", "") - params = response.get("params") - request_id = response.get("id") - handler = self.on_request_handlers.get(method) - if not handler: - self.send_error_response( - request_id, - Error( - ErrorCodes.MethodNotFound, - f"method '{method}' not handled on client.", - ), - ) - return - try: - self.send_response(request_id, await handler(params)) - except Error as ex: - self.send_error_response(request_id, ex) - except Exception as ex: - self.send_error_response(request_id, Error(ErrorCodes.InternalError, str(ex))) - - async def _notification_handler(self, response: StringDict) -> None: - """ - Handle the notification received from the server: call the appropriate callback function - """ - method = response.get("method", "") - params = response.get("params") - handler = self.on_notification_handlers.get(method) - if not handler: - self._log(f"unhandled {method}") - return - try: - await handler(params) - except asyncio.CancelledError: - return - except Exception as ex: - if (not self._received_shutdown) and self.logger: - self.logger( - "client", - "logger", - str( - { - "type": MessageType.error, - "message": str(ex), - "method": method, - "params": params, - } - ), - ) diff --git a/src/solidlsp/multilspy_settings.py b/src/solidlsp/settings.py similarity index 96% rename from src/solidlsp/multilspy_settings.py rename to src/solidlsp/settings.py index b4040301..166be83c 100644 --- a/src/solidlsp/multilspy_settings.py +++ b/src/solidlsp/settings.py @@ -6,7 +6,7 @@ import os import pathlib -class MultilspySettings: +class SolidLSPSettings: """ Provides the various settings for multilspy. """ diff --git a/src/solidlsp/type_helpers.py b/src/solidlsp/type_helpers.py deleted file mode 100644 index 53a10de2..00000000 --- a/src/solidlsp/type_helpers.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -This module provides type-helpers used across multilspy implementation -""" - -import inspect -from collections.abc import Callable -from typing import TypeVar - -R = TypeVar("R", bound=object) - - -def ensure_all_methods_implemented( - source_cls: type[object], -) -> Callable[[type[R]], type[R]]: - """ - A decorator to ensure that all methods of source_cls class are implemented in the decorated class. - """ - - def check_all_methods_implemented(target_cls: R) -> R: - for name, _ in inspect.getmembers(source_cls, inspect.isfunction): - if name.startswith("_"): - continue - if name not in target_cls.__dict__ or not callable(target_cls.__dict__[name]): - raise NotImplementedError(f"{name} is not implemented in {target_cls}") - - return target_cls - - return check_all_methods_implemented diff --git a/test/conftest.py b/test/conftest.py index 1cb81ea0..6632f3ac 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -6,8 +6,8 @@ from sensai.util.logging import configure from serena.util.file_system import GitignoreParser from solidlsp.ls import SolidLanguageServer -from solidlsp.multilspy_config import Language, MultilspyConfig -from solidlsp.multilspy_logger import MultilspyLogger +from solidlsp.ls_config import Language, LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger configure(level=logging.DEBUG) @@ -40,8 +40,8 @@ def create_ls( gitignore_parser = GitignoreParser(str(repo_path)) for spec in gitignore_parser.get_ignore_specs(): ignored_paths.extend(spec.patterns) - config = MultilspyConfig(code_language=language, ignored_paths=ignored_paths, trace_lsp_communication=trace_lsp_communication) - logger = MultilspyLogger(log_level=log_level) + 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) diff --git a/test/multilspy/go/test_go_basic.py b/test/multilspy/go/test_go_basic.py index 82b6960d..56323157 100644 --- a/test/multilspy/go/test_go_basic.py +++ b/test/multilspy/go/test_go_basic.py @@ -3,8 +3,8 @@ import os import pytest from solidlsp import SolidLanguageServer -from solidlsp.multilspy_config import Language -from solidlsp.multilspy_utils import SymbolUtils +from solidlsp.ls_config import Language +from solidlsp.ls_utils import SymbolUtils @pytest.mark.go diff --git a/test/multilspy/java/test_java_basic.py b/test/multilspy/java/test_java_basic.py index f5923736..d74a9fea 100644 --- a/test/multilspy/java/test_java_basic.py +++ b/test/multilspy/java/test_java_basic.py @@ -3,8 +3,8 @@ import os import pytest from solidlsp import SolidLanguageServer -from solidlsp.multilspy_config import Language -from solidlsp.multilspy_utils import SymbolUtils +from solidlsp.ls_config import Language +from solidlsp.ls_utils import SymbolUtils @pytest.mark.java diff --git a/test/multilspy/php/test_php_basic.py b/test/multilspy/php/test_php_basic.py index 3ae2797e..391c565b 100644 --- a/test/multilspy/php/test_php_basic.py +++ b/test/multilspy/php/test_php_basic.py @@ -3,7 +3,7 @@ from pathlib import Path import pytest from solidlsp import SolidLanguageServer -from solidlsp.multilspy_config import Language +from solidlsp.ls_config import Language @pytest.mark.php diff --git a/test/multilspy/python/test_python_basic.py b/test/multilspy/python/test_python_basic.py index 541f7f50..74737c47 100644 --- a/test/multilspy/python/test_python_basic.py +++ b/test/multilspy/python/test_python_basic.py @@ -11,7 +11,7 @@ import pytest from serena.text_utils import LineType from solidlsp import SolidLanguageServer -from solidlsp.multilspy_config import Language +from solidlsp.ls_config import Language @pytest.mark.python diff --git a/test/multilspy/python/test_retrieval_with_ignored_dirs.py b/test/multilspy/python/test_retrieval_with_ignored_dirs.py index f100d412..a3d7d6a2 100644 --- a/test/multilspy/python/test_retrieval_with_ignored_dirs.py +++ b/test/multilspy/python/test_retrieval_with_ignored_dirs.py @@ -4,7 +4,7 @@ from pathlib import Path import pytest from solidlsp import SolidLanguageServer -from solidlsp.multilspy_config import Language +from solidlsp.ls_config import Language from test.conftest import create_ls # This mark will be applied to all tests in this module diff --git a/test/multilspy/python/test_symbol_retrieval.py b/test/multilspy/python/test_symbol_retrieval.py index 97f5d448..c19bf8bb 100644 --- a/test/multilspy/python/test_symbol_retrieval.py +++ b/test/multilspy/python/test_symbol_retrieval.py @@ -11,8 +11,8 @@ import os import pytest from solidlsp import SolidLanguageServer -from solidlsp.multilspy_config import Language -from solidlsp.multilspy_types import SymbolKind +from solidlsp.ls_config import Language +from solidlsp.ls_types import SymbolKind pytestmark = pytest.mark.python diff --git a/test/multilspy/rust/test_rust_basic.py b/test/multilspy/rust/test_rust_basic.py index e6cd6f4c..d0576de2 100644 --- a/test/multilspy/rust/test_rust_basic.py +++ b/test/multilspy/rust/test_rust_basic.py @@ -3,8 +3,8 @@ import os import pytest from solidlsp import SolidLanguageServer -from solidlsp.multilspy_config import Language -from solidlsp.multilspy_utils import SymbolUtils +from solidlsp.ls_config import Language +from solidlsp.ls_utils import SymbolUtils @pytest.mark.rust diff --git a/test/multilspy/typescript/test_typescript_basic.py b/test/multilspy/typescript/test_typescript_basic.py index 1c6cb5fc..1410c5c4 100644 --- a/test/multilspy/typescript/test_typescript_basic.py +++ b/test/multilspy/typescript/test_typescript_basic.py @@ -3,8 +3,8 @@ import os import pytest from solidlsp import SolidLanguageServer -from solidlsp.multilspy_config import Language -from solidlsp.multilspy_utils import SymbolUtils +from solidlsp.ls_config import Language +from solidlsp.ls_utils import SymbolUtils @pytest.mark.typescript diff --git a/test/serena/test_serena_agent.py b/test/serena/test_serena_agent.py index a31ccd42..4e297853 100644 --- a/test/serena/test_serena_agent.py +++ b/test/serena/test_serena_agent.py @@ -7,7 +7,7 @@ import pytest from serena.agent import FindReferencingSymbolsTool, FindSymbolTool, Project, ProjectConfig, SerenaAgent, SerenaConfigBase from serena.process_isolated_agent import ProcessIsolatedSerenaAgent -from solidlsp.multilspy_config import Language +from solidlsp.ls_config import Language from test.conftest import get_repo_path diff --git a/test/serena/test_symbol_editing.py b/test/serena/test_symbol_editing.py index bf6e2a33..86fdc7c3 100644 --- a/test/serena/test_symbol_editing.py +++ b/test/serena/test_symbol_editing.py @@ -12,7 +12,7 @@ from typing import Literal import pytest from serena.symbol import CodeDiff -from solidlsp.multilspy_config import Language +from solidlsp.ls_config import Language from src.serena.symbol import SymbolManager from test.conftest import create_ls, get_repo_path From a96e5bb476e3a062e4c78193b32507c070dc58ee Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Mon, 23 Jun 2025 18:04:59 +0200 Subject: [PATCH 11/11] Update change log [skip ci] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51a8fc82..f2eabb38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ Status of the `main` branch. Changes prior to the next official version change will appear here. * **Reduce the use of asyncio to a minimum**, improving stability and reducing the need for workarounds - * Switch to newly developed fully synchronous LSP library `solidlsp` (derived from `multilspy`) + * Switch to newly developed fully synchronous LSP library `solidlsp` (derived from `multilspy`), + removing our fork of `multilspy` (src/multilspy) * Switch from fastapi (which uses asyncio) to Flask in the Serena dashboard * The MCP server is the only asynchronous component now, which resolves cross-component loop contamination, such that process isolation is no longer required.