From e8e417a18e7632ba8fa955ad4ad63c8a98ea4c93 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Wed, 2 Jul 2025 00:13:42 +0200 Subject: [PATCH 01/25] Refactoring, stage 1: move ls implementations one step up Paths to jsons were adjusted in the modules, minimal change, tests run through --- .gitignore | 1 + .../clangd_language_server.py | 4 ++-- .../{clojure_lsp => }/clojure_lsp.py | 0 .../csharp_language_server.py | 0 .../dart_language_server.py | 0 .../{eclipse_jdtls => }/eclipse_jdtls.py | 4 ++-- .../language_servers/{gopls => }/gopls.py | 2 +- .../{intelephense => }/intelephense.py | 4 ++-- .../kotlin_language_server.py | 8 ++++--- .../pyright_server.py | 0 .../{rust_analyzer => }/rust_analyzer.py | 4 ++-- .../{solargraph => }/solargraph.py | 4 ++-- .../typescript_language_server.py | 4 ++-- src/solidlsp/ls.py | 24 +++++++++---------- test/solidlsp/clojure/__init__.py | 2 +- test/solidlsp/csharp/test_csharp_basic.py | 10 ++++---- 16 files changed, 37 insertions(+), 34 deletions(-) rename src/solidlsp/language_servers/{clangd_language_server => }/clangd_language_server.py (97%) rename src/solidlsp/language_servers/{clojure_lsp => }/clojure_lsp.py (100%) rename src/solidlsp/language_servers/{csharp_language_server => }/csharp_language_server.py (100%) rename src/solidlsp/language_servers/{dart_language_server => }/dart_language_server.py (100%) rename src/solidlsp/language_servers/{eclipse_jdtls => }/eclipse_jdtls.py (98%) rename src/solidlsp/language_servers/{gopls => }/gopls.py (97%) rename src/solidlsp/language_servers/{intelephense => }/intelephense.py (96%) rename src/solidlsp/language_servers/{kotlin_language_server => }/kotlin_language_server.py (96%) rename src/solidlsp/language_servers/{pyright_language_server => }/pyright_server.py (100%) rename src/solidlsp/language_servers/{rust_analyzer => }/rust_analyzer.py (96%) rename src/solidlsp/language_servers/{solargraph => }/solargraph.py (96%) rename src/solidlsp/language_servers/{typescript_language_server => }/typescript_language_server.py (97%) diff --git a/.gitignore b/.gitignore index fabbe519..73796aca 100644 --- a/.gitignore +++ b/.gitignore @@ -204,6 +204,7 @@ pylint.html # dynamic LS installations /src/multilspy/language_servers/*/static /src/solidlsp/language_servers/*/static +/src/solidlsp/language_servers/static # clojure-lsp temporary files .calva/ diff --git a/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server.py similarity index 97% rename from src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py rename to src/solidlsp/language_servers/clangd_language_server.py index 893c5cc7..d37f5913 100644 --- a/src/solidlsp/language_servers/clangd_language_server/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server.py @@ -47,7 +47,7 @@ class ClangdLanguageServer(SolidLanguageServer): """ platform_id = PlatformUtils.get_platform_id() - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json")) as f: + with open(os.path.join(os.path.dirname(__file__), "clangd_language_server", "runtime_dependencies.json")) as f: d = json.load(f) del d["_description"] @@ -90,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")) as f: + with open(os.path.join(os.path.dirname(__file__), "clangd_language_server", "initialize_params.json")) as f: d = json.load(f) del d["_description"] diff --git a/src/solidlsp/language_servers/clojure_lsp/clojure_lsp.py b/src/solidlsp/language_servers/clojure_lsp.py similarity index 100% rename from src/solidlsp/language_servers/clojure_lsp/clojure_lsp.py rename to src/solidlsp/language_servers/clojure_lsp.py diff --git a/src/solidlsp/language_servers/csharp_language_server/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py similarity index 100% rename from src/solidlsp/language_servers/csharp_language_server/csharp_language_server.py rename to src/solidlsp/language_servers/csharp_language_server.py diff --git a/src/solidlsp/language_servers/dart_language_server/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server.py similarity index 100% rename from src/solidlsp/language_servers/dart_language_server/dart_language_server.py rename to src/solidlsp/language_servers/dart_language_server.py diff --git a/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls.py similarity index 98% rename from src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py rename to src/solidlsp/language_servers/eclipse_jdtls.py index 7453bf15..0a87990d 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls.py @@ -159,7 +159,7 @@ class EclipseJDTLS(SolidLanguageServer): """ platformId = PlatformUtils.get_platform_id() - with open(str(PurePath(os.path.dirname(__file__), "runtime_dependencies.json")), encoding="utf-8") as f: + with open(str(PurePath(os.path.dirname(__file__), "eclipse_jdtls", "runtime_dependencies.json")), encoding="utf-8") as f: runtimeDependencies = json.load(f) del runtimeDependencies["_description"] @@ -252,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")), encoding="utf-8") as f: + with open(str(PurePath(os.path.dirname(__file__), "eclipse_jdtls", "initialize_params.json")), encoding="utf-8") as f: d: InitializeParams = json.load(f) del d["_description"] diff --git a/src/solidlsp/language_servers/gopls/gopls.py b/src/solidlsp/language_servers/gopls.py similarity index 97% rename from src/solidlsp/language_servers/gopls/gopls.py rename to src/solidlsp/language_servers/gopls.py index c3fadd71..f391e0ea 100644 --- a/src/solidlsp/language_servers/gopls/gopls.py +++ b/src/solidlsp/language_servers/gopls.py @@ -88,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"), encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "gopls", "initialize_params.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] diff --git a/src/solidlsp/language_servers/intelephense/intelephense.py b/src/solidlsp/language_servers/intelephense.py similarity index 96% rename from src/solidlsp/language_servers/intelephense/intelephense.py rename to src/solidlsp/language_servers/intelephense.py index 19eaa8b1..a5fd47cc 100644 --- a/src/solidlsp/language_servers/intelephense/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -50,7 +50,7 @@ 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"), encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "intelephense", "runtime_dependencies.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] @@ -108,7 +108,7 @@ class Intelephense(SolidLanguageServer): """ Returns the initialize params for the TypeScript Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "intelephense", "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.py similarity index 96% rename from src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py rename to src/solidlsp/language_servers/kotlin_language_server.py index 6ddab2c7..57632fbe 100644 --- a/src/solidlsp/language_servers/kotlin_language_server/kotlin_language_server.py +++ b/src/solidlsp/language_servers/kotlin_language_server.py @@ -66,7 +66,7 @@ class KotlinLanguageServer(SolidLanguageServer): ), "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"), encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "kotlin_language_server", "runtime_dependencies.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] @@ -74,7 +74,7 @@ class KotlinLanguageServer(SolidLanguageServer): java_dependency = d["java"][platform_id.value] # Setup paths for dependencies - static_dir = os.path.join(os.path.dirname(__file__), "static") + static_dir = os.path.join(os.path.dirname(__file__), "static", "kotlin_language_server") os.makedirs(static_dir, exist_ok=True) # Setup Java paths @@ -129,7 +129,9 @@ class KotlinLanguageServer(SolidLanguageServer): """ Returns the initialize params for the Kotlin Language Server. """ - with open(str(pathlib.PurePath(os.path.dirname(__file__), "initialize_params.json")), encoding="utf-8") as f: + with open( + str(pathlib.PurePath(os.path.dirname(__file__), "kotlin_language_server", "initialize_params.json")), encoding="utf-8" + ) as f: d: InitializeParams = json.load(f) del d["_description"] diff --git a/src/solidlsp/language_servers/pyright_language_server/pyright_server.py b/src/solidlsp/language_servers/pyright_server.py similarity index 100% rename from src/solidlsp/language_servers/pyright_language_server/pyright_server.py rename to src/solidlsp/language_servers/pyright_server.py diff --git a/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py similarity index 96% rename from src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py rename to src/solidlsp/language_servers/rust_analyzer.py index 1f951e5c..987e2cfa 100644 --- a/src/solidlsp/language_servers/rust_analyzer/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -51,7 +51,7 @@ class RustAnalyzer(SolidLanguageServer): """ platform_id = PlatformUtils.get_platform_id() - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "rust_analyzer", "runtime_dependencies.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] @@ -82,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"), encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "rust_analyzer", "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.py similarity index 96% rename from src/solidlsp/language_servers/solargraph/solargraph.py rename to src/solidlsp/language_servers/solargraph.py index 171cc888..731be1ad 100644 --- a/src/solidlsp/language_servers/solargraph/solargraph.py +++ b/src/solidlsp/language_servers/solargraph.py @@ -52,7 +52,7 @@ class Solargraph(SolidLanguageServer): """ Setup runtime dependencies for Solargraph. """ - with open(os.path.join(os.path.dirname(__file__), "runtime_dependencies.json"), encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "solargraph", "runtime_dependencies.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] @@ -97,7 +97,7 @@ class Solargraph(SolidLanguageServer): """ Returns the initialize params for the Solargraph Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "solargraph", "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.py similarity index 97% rename from src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py rename to src/solidlsp/language_servers/typescript_language_server.py index f2204065..c7a39655 100644 --- a/src/solidlsp/language_servers/typescript_language_server/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -82,7 +82,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): ] 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: + with open(os.path.join(os.path.dirname(__file__), "typescript_language_server", "runtime_dependencies.json")) as f: d = json.load(f) del d["_description"] @@ -134,7 +134,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): """ Returns the initialize params for the TypeScript Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json")) as f: + with open(os.path.join(os.path.dirname(__file__), "typescript_language_server", "initialize_params.json")) as f: d = json.load(f) del d["_description"] diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index 4b8163cc..6c141f47 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -108,28 +108,28 @@ class SolidLanguageServer(ABC): ls: SolidLanguageServer if config.code_language == Language.PYTHON: - from solidlsp.language_servers.pyright_language_server.pyright_server import ( + from solidlsp.language_servers.pyright_server import ( PyrightServer, ) ls = PyrightServer(config, logger, repository_root_path) elif config.code_language == Language.JAVA: - from solidlsp.language_servers.eclipse_jdtls.eclipse_jdtls import ( + from solidlsp.language_servers.eclipse_jdtls import ( EclipseJDTLS, ) ls = EclipseJDTLS(config, logger, repository_root_path) elif config.code_language == Language.KOTLIN: - from solidlsp.language_servers.kotlin_language_server.kotlin_language_server import ( + from solidlsp.language_servers.kotlin_language_server import ( KotlinLanguageServer, ) ls = KotlinLanguageServer(config, logger, repository_root_path) elif config.code_language == Language.RUST: - from solidlsp.language_servers.rust_analyzer.rust_analyzer import ( + from solidlsp.language_servers.rust_analyzer import ( RustAnalyzer, ) @@ -141,44 +141,44 @@ class SolidLanguageServer(ABC): # from solidlsp.language_servers.omnisharp.omnisharp import OmniSharp # ls = OmniSharp(config, logger, repository_root_path) - from solidlsp.language_servers.csharp_language_server.csharp_language_server import CSharpLanguageServer + from solidlsp.language_servers.csharp_language_server import CSharpLanguageServer ls = CSharpLanguageServer(config, logger, repository_root_path) elif config.code_language == Language.TYPESCRIPT: - from solidlsp.language_servers.typescript_language_server.typescript_language_server import ( + from solidlsp.language_servers.typescript_language_server import ( TypeScriptLanguageServer, ) ls = TypeScriptLanguageServer(config, logger, repository_root_path) elif config.code_language == Language.GO: - from solidlsp.language_servers.gopls.gopls import Gopls + from solidlsp.language_servers.gopls import Gopls ls = Gopls(config, logger, repository_root_path) elif config.code_language == Language.RUBY: - from solidlsp.language_servers.solargraph.solargraph import Solargraph + from solidlsp.language_servers.solargraph import Solargraph ls = Solargraph(config, logger, repository_root_path) elif config.code_language == Language.DART: - from solidlsp.language_servers.dart_language_server.dart_language_server import DartLanguageServer + from solidlsp.language_servers.dart_language_server import DartLanguageServer ls = DartLanguageServer(config, logger, repository_root_path) elif config.code_language == Language.CPP: - from solidlsp.language_servers.clangd_language_server.clangd_language_server import ClangdLanguageServer + from solidlsp.language_servers.clangd_language_server import ClangdLanguageServer ls = ClangdLanguageServer(config, logger, repository_root_path) elif config.code_language == Language.PHP: - from solidlsp.language_servers.intelephense.intelephense import Intelephense + from solidlsp.language_servers.intelephense import Intelephense ls = Intelephense(config, logger, repository_root_path) elif config.code_language == Language.CLOJURE: - from solidlsp.language_servers.clojure_lsp.clojure_lsp import ClojureLSP + from solidlsp.language_servers.clojure_lsp import ClojureLSP ls = ClojureLSP(config, logger, repository_root_path) diff --git a/test/solidlsp/clojure/__init__.py b/test/solidlsp/clojure/__init__.py index c16da80d..be945a53 100644 --- a/test/solidlsp/clojure/__init__.py +++ b/test/solidlsp/clojure/__init__.py @@ -1,6 +1,6 @@ from pathlib import Path -from solidlsp.language_servers.clojure_lsp.clojure_lsp import verify_clojure_cli +from solidlsp.language_servers.clojure_lsp import verify_clojure_cli def _test_clojure_cli() -> bool: diff --git a/test/solidlsp/csharp/test_csharp_basic.py b/test/solidlsp/csharp/test_csharp_basic.py index 56db5cfa..cff8cc5c 100644 --- a/test/solidlsp/csharp/test_csharp_basic.py +++ b/test/solidlsp/csharp/test_csharp_basic.py @@ -6,7 +6,7 @@ from unittest.mock import Mock, patch import pytest from solidlsp import SolidLanguageServer -from solidlsp.language_servers.csharp_language_server.csharp_language_server import ( +from solidlsp.language_servers.csharp_language_server import ( CSharpLanguageServer, breadth_first_file_scan, find_solution_or_project_file, @@ -196,8 +196,8 @@ class TestCSharpSolutionProjectOpening: # Should still prefer .sln file even though it's deeper assert result == str(solution_file) - @patch("solidlsp.language_servers.csharp_language_server.csharp_language_server.CSharpLanguageServer._ensure_server_installed") - @patch("solidlsp.language_servers.csharp_language_server.csharp_language_server.CSharpLanguageServer._start_server") + @patch("solidlsp.language_servers.csharp_language_server.CSharpLanguageServer._ensure_server_installed") + @patch("solidlsp.language_servers.csharp_language_server.CSharpLanguageServer._start_server") def test_csharp_language_server_logs_solution_discovery(self, mock_start_server, mock_ensure_server_installed): """Test that CSharpLanguageServer logs solution/project discovery during initialization.""" with tempfile.TemporaryDirectory() as cache_dir: @@ -221,8 +221,8 @@ class TestCSharpSolutionProjectOpening: # Verify that logger was called with solution file discovery mock_logger.log.assert_any_call(f"Found solution/project file: {solution_file}", 20) # logging.INFO - @patch("solidlsp.language_servers.csharp_language_server.csharp_language_server.CSharpLanguageServer._ensure_server_installed") - @patch("solidlsp.language_servers.csharp_language_server.csharp_language_server.CSharpLanguageServer._start_server") + @patch("solidlsp.language_servers.csharp_language_server.CSharpLanguageServer._ensure_server_installed") + @patch("solidlsp.language_servers.csharp_language_server.CSharpLanguageServer._start_server") def test_csharp_language_server_logs_no_solution_warning(self, mock_start_server, mock_ensure_server_installed): """Test that CSharpLanguageServer logs warning when no solution/project files are found.""" with tempfile.TemporaryDirectory() as cache_dir: From 16a7ff5f56bdd12ef7c5a68db64ec7e49f1a9c43 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Wed, 2 Jul 2025 00:26:49 +0200 Subject: [PATCH 02/25] Refactoring, stage 2-ts: remove ts jsons in favor of dicts in python --- .../typescript_language_server.py | 500 +++++++++- .../initialize_params.json | 906 ------------------ .../runtime_dependencies.json | 16 - 3 files changed, 477 insertions(+), 945 deletions(-) delete mode 100644 src/solidlsp/language_servers/typescript_language_server/initialize_params.json delete mode 100644 src/solidlsp/language_servers/typescript_language_server/runtime_dependencies.json diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index c7a39655..a71313de 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -2,7 +2,6 @@ Provides TypeScript specific instantiation of the LanguageServer class. Contains various configurations and settings specific to TypeScript. """ -import json import logging import os import pathlib @@ -82,11 +81,18 @@ class TypeScriptLanguageServer(SolidLanguageServer): ] 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__), "typescript_language_server", "runtime_dependencies.json")) as f: - d = json.load(f) - del d["_description"] - - runtime_dependencies = d.get("runtimeDependencies", []) + runtime_dependencies = [ + { + "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", + }, + ] 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") @@ -134,25 +140,473 @@ class TypeScriptLanguageServer(SolidLanguageServer): """ Returns the initialize params for the TypeScript Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "typescript_language_server", "initialize_params.json")) as f: - d = json.load(f) + initialize_params = { + "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": None, + "runnables": {"extraEnv": None, "problemMatcher": ["$rustc"], "command": None, "extraArgs": []}, + "statusBar": {"clickAction": "openLogs"}, + "server": {"path": None, "extraEnv": None}, + "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": None, + "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": None, + "useRustcWrapper": True, + }, + "cfgs": {}, + "extraArgs": [], + "extraEnv": {}, + "features": [], + "noDefaultFeatures": False, + "sysroot": "discover", + "sysrootSrc": None, + "target": None, + "unsetTest": ["core"], + }, + "checkOnSave": True, + "check": { + "allTargets": True, + "command": "check", + "extraArgs": [], + "extraEnv": {}, + "features": None, + "ignore": [], + "invocationLocation": "workspace", + "invocationStrategy": "per_workspace", + "noDefaultFeatures": None, + "overrideCommand": None, + "targets": None, + }, + "completion": { + "autoimport": {"enable": True}, + "autoself": {"enable": True}, + "callable": {"snippets": "fill_arguments"}, + "fullFunctionSignatures": {"enable": False}, + "limit": None, + "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": None, "query": {"capacities": {}}}, + "notifications": {"cargoTomlNotFound": True}, + "numThreads": None, + "procMacro": {"attributes": {"enable": True}, "enable": True, "ignored": {}, "server": None}, + "references": {"excludeImports": False}, + "rust": {"analyzerTargetDir": None}, + "rustc": {"source": None}, + "rustfmt": {"extraArgs": [], "overrideCommand": None, "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"}], + } - del d["_description"] + root_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params["processId"] = os.getpid() + initialize_params["rootPath"] = repository_absolute_path + initialize_params["rootUri"] = root_uri + initialize_params["workspaceFolders"][0]["uri"] = root_uri + initialize_params["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) - 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 + return initialize_params def _start_server(self): """ diff --git a/src/solidlsp/language_servers/typescript_language_server/initialize_params.json b/src/solidlsp/language_servers/typescript_language_server/initialize_params.json deleted file mode 100644 index 6209ba47..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/typescript_language_server/runtime_dependencies.json b/src/solidlsp/language_servers/typescript_language_server/runtime_dependencies.json deleted file mode 100644 index 90824a7c..00000000 --- a/src/solidlsp/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" - } - ] -} - From 987277d153c14149f36aaae6c1c2ca8b6d814e54 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Tue, 1 Jul 2025 17:03:51 +0200 Subject: [PATCH 03/25] Limit exposed tools to the ones not excluded by the (fixed) agent context --- src/serena/agent.py | 29 +++++++++++++++++++++-------- src/serena/config.py | 6 +++++- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/serena/agent.py b/src/serena/agent.py index aa78640e..8f68119a 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -769,10 +769,20 @@ class SerenaAgent: ) Logger.root.addHandler(self._gui_log_handler) + # set the agent context + if context is None: + context = SerenaAgentContext.load_default() + self._context = context + # instantiate all tool classes self._all_tools: dict[type[Tool], Tool] = {tool_class: tool_class(self) for tool_class in ToolRegistry.get_all_tool_classes()} tool_names = [tool.get_name_from_cls() for tool in self._all_tools.values()] + # determine the set exposed tools (which e.g. the MCP shall see), limited by the context + # (which is fixed for the session) + excluded_tool_classes = set(self._context.get_excluded_tool_classes()) + self._exposed_tools = {tc: t for tc, t in self._all_tools.items() if tc not in excluded_tool_classes} + # If GUI log window is enabled, set the tool names for highlighting if self._gui_log_handler is not None: self._gui_log_handler.log_viewer.set_tool_names(tool_names) @@ -807,14 +817,14 @@ class SerenaAgent: self.ignore_spec: PathSpec # not set to None to avoid assert statements """Ignore spec, extracted from the project's gitignore files and the explicitly configured ignored paths.""" - # Apply context and mode tool configurations - if context is None: - context = SerenaAgentContext.load_default() + # set the active modes if modes is None: modes = SerenaAgentMode.load_default_modes() - self._context = context self._modes = modes + + # log tool information log.info(f"Loaded tools ({len(self._all_tools)}): {', '.join([tool.get_name_from_cls() for tool in self._all_tools.values()])}") + log.info(f"Number of exposed tools given {self._context}: {len(self._exposed_tools)}") self._active_tools: dict[type[Tool], Tool] = {} self._update_active_tools() @@ -880,11 +890,14 @@ class SerenaAgent: def get_exposed_tool_instances(self) -> list["Tool"]: """ - :return: all tool instances, including the non-active ones. For MCP clients, we need to expose them all since typical - clients don't react to changes in the set of tools. - An attempt to use a non-active tool will result in an error. + :return: the tool instances which are exposed (e.g. to the MCP client). + Note that the set of exposed tools is fixed for the session, as + clients don't react to changes in the set of tools, so this is the superset + of tools that can be offered during the session. + If a client should attempt to use a tool that is dynamically disabled + (e.g. because a project is activated that disables it), it will receive an error. """ - return list(self._all_tools.values()) + return list(self._exposed_tools.values()) def get_active_project(self) -> Project | None: """ diff --git a/src/serena/config.py b/src/serena/config.py index 2f2659dc..b8cfb9f8 100644 --- a/src/serena/config.py +++ b/src/serena/config.py @@ -11,6 +11,7 @@ from typing import TYPE_CHECKING, Self import yaml from sensai.util import logging +from sensai.util.string import ToStringMixin from serena.constants import CONTEXT_YAMLS_DIR, DEFAULT_CONTEXT, DEFAULT_MODES, MODE_YAMLS_DIR @@ -93,7 +94,7 @@ class SerenaAgentMode: @dataclass -class SerenaAgentContext: +class SerenaAgentContext(ToStringMixin): """Represents a context where the agent is operating (an IDE, a chat, etc.), typically read off a YAML file. An agent can only be in a single context at a time. The contexts cannot be changed after the agent is running. @@ -104,6 +105,9 @@ class SerenaAgentContext: description: str = "" excluded_tools: set[str] = field(default_factory=set) + def _tostring_includes(self) -> list[str]: + return ["name"] + def to_json_dict(self) -> dict[str, str | list[str]]: result = asdict(self) result["excluded_tools"] = list(result["excluded_tools"]) From 30f2f518a5330b2bbfd6982e5d26c334dc0d883b Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Wed, 2 Jul 2025 01:38:18 +0200 Subject: [PATCH 04/25] Refactoring, stage 2 continued: all jsons in favor of dicts in python --- .../clangd_language_server.py | 71 +- .../initialize_params.json | 36 - .../runtime_dependencies.json | 29 - .../language_servers/dart_language_server.py | 88 +- .../initialize_params.json | 23 - .../runtime_dependencies.json | 45 - .../language_servers/eclipse_jdtls.py | 565 ++++++++++- .../eclipse_jdtls/initialize_params.json | 849 ---------------- .../eclipse_jdtls/runtime_dependencies.json | 72 -- src/solidlsp/language_servers/gopls.py | 49 +- .../gopls/initialize_params.json | 46 - src/solidlsp/language_servers/intelephense.py | 52 +- .../intelephense/initialize_params.json | 36 - .../intelephense/runtime_dependencies.json | 10 - .../kotlin_language_server.py | 330 ++++++- .../initialize_params.json | 521 ---------- .../runtime_dependencies.json | 41 - .../language_servers/rust_analyzer.py | 522 +++++++++- .../rust_analyzer/initialize_params.json | 917 ------------------ .../rust_analyzer/runtime_dependencies.json | 29 - src/solidlsp/language_servers/solargraph.py | 46 +- .../solargraph/initialize_params.json | 15 - .../solargraph/runtime_dependencies.json | 11 - 23 files changed, 1508 insertions(+), 2895 deletions(-) delete mode 100644 src/solidlsp/language_servers/clangd_language_server/initialize_params.json delete mode 100644 src/solidlsp/language_servers/clangd_language_server/runtime_dependencies.json delete mode 100644 src/solidlsp/language_servers/dart_language_server/initialize_params.json delete mode 100644 src/solidlsp/language_servers/dart_language_server/runtime_dependencies.json delete mode 100644 src/solidlsp/language_servers/eclipse_jdtls/initialize_params.json delete mode 100644 src/solidlsp/language_servers/eclipse_jdtls/runtime_dependencies.json delete mode 100644 src/solidlsp/language_servers/gopls/initialize_params.json delete mode 100644 src/solidlsp/language_servers/intelephense/initialize_params.json delete mode 100644 src/solidlsp/language_servers/intelephense/runtime_dependencies.json delete mode 100644 src/solidlsp/language_servers/kotlin_language_server/initialize_params.json delete mode 100644 src/solidlsp/language_servers/kotlin_language_server/runtime_dependencies.json delete mode 100644 src/solidlsp/language_servers/rust_analyzer/initialize_params.json delete mode 100644 src/solidlsp/language_servers/rust_analyzer/runtime_dependencies.json delete mode 100644 src/solidlsp/language_servers/solargraph/initialize_params.json delete mode 100644 src/solidlsp/language_servers/solargraph/runtime_dependencies.json diff --git a/src/solidlsp/language_servers/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server.py index d37f5913..1ba9f6da 100644 --- a/src/solidlsp/language_servers/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server.py @@ -2,7 +2,6 @@ Provides C/C++ specific instantiation of the LanguageServer class. Contains various configurations and settings specific to C/C++. """ -import json import logging import os import pathlib @@ -47,9 +46,32 @@ class ClangdLanguageServer(SolidLanguageServer): """ platform_id = PlatformUtils.get_platform_id() - with open(os.path.join(os.path.dirname(__file__), "clangd_language_server", "runtime_dependencies.json")) as f: - d = json.load(f) - del d["_description"] + runtime_dependencies = [ + { + "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": "win-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", + }, + ] assert platform_id.value in [ "linux-x64", @@ -59,7 +81,6 @@ class ClangdLanguageServer(SolidLanguageServer): "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 @@ -90,25 +111,29 @@ class ClangdLanguageServer(SolidLanguageServer): """ Returns the initialize params for the clangd Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "clangd_language_server", "initialize_params.json")) as f: - d = json.load(f) + root_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params = { + "locale": "en", + "capabilities": { + "textDocument": { + "synchronization": {"didSave": True, "dynamicRegistration": True}, + "completion": {"dynamicRegistration": True, "completionItem": {"snippetSupport": True}}, + "definition": {"dynamicRegistration": True}, + }, + "workspace": {"workspaceFolders": True, "didChangeConfiguration": {"dynamicRegistration": True}}, + }, + "processId": os.getpid(), + "rootPath": repository_absolute_path, + "rootUri": root_uri, + "workspaceFolders": [ + { + "uri": root_uri, + "name": "$name", + } + ], + } - 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 + return initialize_params def _start_server(self): """ diff --git a/src/solidlsp/language_servers/clangd_language_server/initialize_params.json b/src/solidlsp/language_servers/clangd_language_server/initialize_params.json deleted file mode 100644 index 4330560e..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/clangd_language_server/runtime_dependencies.json b/src/solidlsp/language_servers/clangd_language_server/runtime_dependencies.json deleted file mode 100644 index f96239db..00000000 --- a/src/solidlsp/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": "win-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/solidlsp/language_servers/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server.py index 58d71014..595ce8a6 100644 --- a/src/solidlsp/language_servers/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server.py @@ -1,4 +1,3 @@ -import json import logging import os import pathlib @@ -31,11 +30,49 @@ class DartLanguageServer(SolidLanguageServer): 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: - d = json.load(f) - del d["_description"] + runtime_dependencies = [ + { + "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", + }, + ] - runtime_dependencies = d["runtimeDependencies"] runtime_dependencies = [dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value] assert len(runtime_dependencies) == 1 @@ -57,25 +94,30 @@ class DartLanguageServer(SolidLanguageServer): """ Returns the initialize params for the Dart Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json")) as f: - d = json.load(f) + root_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params = { + "capabilities": {}, + "initializationOptions": { + "onlyAnalyzeProjectsWithOpenFiles": False, + "suggestFromUnimportedLibraries": True, + "closingLabels": False, + "outline": False, + "flutterOutline": False, + "allowOpenUri": False, + }, + "trace": "verbose", + "processId": os.getpid(), + "rootPath": repository_absolute_path, + "rootUri": pathlib.Path(repository_absolute_path).as_uri(), + "workspaceFolders": [ + { + "uri": root_uri, + "name": os.path.basename(repository_absolute_path), + } + ], + } - 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 + return initialize_params def _start_server(self): """ diff --git a/src/solidlsp/language_servers/dart_language_server/initialize_params.json b/src/solidlsp/language_servers/dart_language_server/initialize_params.json deleted file mode 100644 index e90e408a..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/dart_language_server/runtime_dependencies.json b/src/solidlsp/language_servers/dart_language_server/runtime_dependencies.json deleted file mode 100644 index 32e49299..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls.py index 0a87990d..090d86f4 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls.py @@ -3,7 +3,6 @@ Provides Java specific instantiation of the LanguageServer class. Contains vario """ import dataclasses -import json import logging import os import pathlib @@ -159,9 +158,77 @@ class EclipseJDTLS(SolidLanguageServer): """ platformId = PlatformUtils.get_platform_id() - with open(str(PurePath(os.path.dirname(__file__), "eclipse_jdtls", "runtime_dependencies.json")), encoding="utf-8") as f: - runtimeDependencies = json.load(f) - del runtimeDependencies["_description"] + runtime_dependencies = { + "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", + } + }, + } os.makedirs(str(PurePath(os.path.abspath(os.path.dirname(__file__)), "static")), exist_ok=True) @@ -180,14 +247,14 @@ class EclipseJDTLS(SolidLanguageServer): if not os.path.exists(gradle_path): FileUtils.download_and_extract_archive( logger, - runtimeDependencies["gradle"]["platform-agnostic"]["url"], + runtime_dependencies["gradle"]["platform-agnostic"]["url"], str(PurePath(gradle_path).parent), - runtimeDependencies["gradle"]["platform-agnostic"]["archiveType"], + runtime_dependencies["gradle"]["platform-agnostic"]["archiveType"], ) assert os.path.exists(gradle_path) - dependency = runtimeDependencies["vscode-java"][platformId.value] + dependency = runtime_dependencies["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"])) @@ -216,7 +283,7 @@ class EclipseJDTLS(SolidLanguageServer): assert os.path.exists(jdtls_launcher_jar_path) assert os.path.exists(jdtls_readonly_config_path) - dependency = runtimeDependencies["intellicode"]["platform-agnostic"] + dependency = runtime_dependencies["intellicode"]["platform-agnostic"] intellicode_directory_path = str( PurePath(os.path.abspath(os.path.dirname(__file__)), "static", dependency["relative_extraction_path"]) ) @@ -252,59 +319,477 @@ 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__), "eclipse_jdtls", "initialize_params.json")), encoding="utf-8") as f: - d: InitializeParams = json.load(f) - - del d["_description"] + initialize_params = { + "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"], + "settings": { + "java": { + "home": None, + "jdt": { + "ls": { + "java": {"home": None}, + "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": None, + "globalSettings": None, + "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": None, + "home": "abs(static/gradle-7.3.3)", + "java": {"home": "abs(static/launch_jres/21.0.7-linux-x86_64)"}, + "offline": {"enabled": False}, + "arguments": None, + "jvmArguments": None, + "user": {"home": None}, + "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": None, "profile": None}, + "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": None}, + "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": None}, + "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", + } 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() + initialize_params["processId"] = os.getpid() - assert d["rootPath"] == "repository_absolute_path" - d["rootPath"] = repository_absolute_path + initialize_params["rootPath"] = repository_absolute_path - assert d["rootUri"] == "pathlib.Path(repository_absolute_path).as_uri()" - d["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() + initialize_params["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()] + repo_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params["initializationOptions"]["workspaceFolders"] = [repo_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"] = [ + initialize_params["workspaceFolders"] = [ { - "uri": pathlib.Path(repository_absolute_path).as_uri(), + "uri": repo_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"] = [ + initialize_params["initializationOptions"]["bundles"] = bundles + initialize_params["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"]: + for runtime in initialize_params["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 + gradle_settings = initialize_params["initializationOptions"]["settings"]["java"]["import"]["gradle"] + gradle_settings["home"] = self.runtime_dependency_paths.gradle_path + gradle_settings["java"]["home"] = self.runtime_dependency_paths.jre_path + return initialize_params def _start_server(self): """ diff --git a/src/solidlsp/language_servers/eclipse_jdtls/initialize_params.json b/src/solidlsp/language_servers/eclipse_jdtls/initialize_params.json deleted file mode 100644 index b65b7b62..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/eclipse_jdtls/runtime_dependencies.json b/src/solidlsp/language_servers/eclipse_jdtls/runtime_dependencies.json deleted file mode 100644 index 1b000405..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/gopls.py b/src/solidlsp/language_servers/gopls.py index f391e0ea..b3db43e2 100644 --- a/src/solidlsp/language_servers/gopls.py +++ b/src/solidlsp/language_servers/gopls.py @@ -1,4 +1,3 @@ -import json import logging import os import pathlib @@ -88,25 +87,35 @@ class Gopls(SolidLanguageServer): """ Returns the initialize params for the TypeScript Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "gopls", "initialize_params.json"), 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 + root_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params = { + "locale": "en", + "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}}, + }, + "processId": os.getpid(), + "rootPath": repository_absolute_path, + "rootUri": root_uri, + "workspaceFolders": [ + { + "uri": root_uri, + "name": os.path.basename(repository_absolute_path), + } + ], + } + return initialize_params def _start_server(self): """Start gopls server process""" diff --git a/src/solidlsp/language_servers/gopls/initialize_params.json b/src/solidlsp/language_servers/gopls/initialize_params.json deleted file mode 100644 index 40592e1a..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/intelephense.py b/src/solidlsp/language_servers/intelephense.py index a5fd47cc..165272de 100644 --- a/src/solidlsp/language_servers/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -2,7 +2,6 @@ Provides PHP specific instantiation of the LanguageServer class using Intelephense. """ -import json import logging import os import pathlib @@ -50,13 +49,12 @@ 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__), "intelephense", "runtime_dependencies.json"), encoding="utf-8") as f: - d = json.load(f) - del d["_description"] - - runtime_dependencies = d.get("runtimeDependencies", []) + runtime_dependencies = { + "id": "intelephense", + "description": "Intelephense package for Linux, OSX, and Windows. Both x64 and arm64 are supported.", + "command": "npm install --prefix ./ intelephense@1.14.4", + } 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." @@ -108,25 +106,29 @@ class Intelephense(SolidLanguageServer): """ Returns the initialize params for the TypeScript Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "intelephense", "initialize_params.json"), encoding="utf-8") as f: - d = json.load(f) + root_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params = { + "locale": "en", + "capabilities": { + "textDocument": { + "synchronization": {"didSave": True, "dynamicRegistration": True}, + "completion": {"dynamicRegistration": True, "completionItem": {"snippetSupport": True}}, + "definition": {"dynamicRegistration": True}, + }, + "workspace": {"workspaceFolders": True, "didChangeConfiguration": {"dynamicRegistration": True}}, + }, + "processId": os.getpid(), + "rootPath": repository_absolute_path, + "rootUri": root_uri, + "workspaceFolders": [ + { + "uri": root_uri, + "name": os.path.basename(repository_absolute_path), + } + ], + } - 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 + return initialize_params def _start_server(self): """Start Intelephense server process""" diff --git a/src/solidlsp/language_servers/intelephense/initialize_params.json b/src/solidlsp/language_servers/intelephense/initialize_params.json deleted file mode 100644 index 4330560e..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/intelephense/runtime_dependencies.json b/src/solidlsp/language_servers/intelephense/runtime_dependencies.json deleted file mode 100644 index e3a6d7e1..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/kotlin_language_server.py b/src/solidlsp/language_servers/kotlin_language_server.py index 57632fbe..13a24380 100644 --- a/src/solidlsp/language_servers/kotlin_language_server.py +++ b/src/solidlsp/language_servers/kotlin_language_server.py @@ -3,7 +3,6 @@ Provides Kotlin specific instantiation of the LanguageServer class. Contains var """ import dataclasses -import json import logging import os import pathlib @@ -65,13 +64,50 @@ class KotlinLanguageServer(SolidLanguageServer): 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__), "kotlin_language_server", "runtime_dependencies.json"), encoding="utf-8") as f: - d = json.load(f) - del d["_description"] + # Runtime dependency information + runtime_dependencies = { + "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", + }, + }, + } - kotlin_dependency = d["runtimeDependency"] - java_dependency = d["java"][platform_id.value] + kotlin_dependency = runtime_dependencies["runtimeDependency"] + java_dependency = runtime_dependencies["java"][platform_id.value] # Setup paths for dependencies static_dir = os.path.join(os.path.dirname(__file__), "static", "kotlin_language_server") @@ -129,40 +165,258 @@ class KotlinLanguageServer(SolidLanguageServer): """ Returns the initialize params for the Kotlin Language Server. """ - with open( - str(pathlib.PurePath(os.path.dirname(__file__), "kotlin_language_server", "initialize_params.json")), 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 + root_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params = { + "clientInfo": {"name": "Multilspy Kotlin Client", "version": "1.0.0"}, + "locale": "en", + "rootPath": repository_absolute_path, + "rootUri": root_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": [root_uri], + "storagePath": None, + "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", + "processId": os.getpid(), + "workspaceFolders": [ + { + "uri": root_uri, + "name": os.path.basename(repository_absolute_path), + } + ], + } + return initialize_params def _start_server(self): """ diff --git a/src/solidlsp/language_servers/kotlin_language_server/initialize_params.json b/src/solidlsp/language_servers/kotlin_language_server/initialize_params.json deleted file mode 100644 index a7b2ec0a..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/kotlin_language_server/runtime_dependencies.json b/src/solidlsp/language_servers/kotlin_language_server/runtime_dependencies.json deleted file mode 100644 index 3568c9d4..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index 987e2cfa..948b61df 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -2,7 +2,6 @@ Provides Rust specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Rust. """ -import json import logging import os import pathlib @@ -51,16 +50,38 @@ class RustAnalyzer(SolidLanguageServer): """ platform_id = PlatformUtils.get_platform_id() - with open(os.path.join(os.path.dirname(__file__), "rust_analyzer", "runtime_dependencies.json"), encoding="utf-8") as f: - d = json.load(f) - del d["_description"] + runtime_dependencies = [ + { + "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", + }, + ] # 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] @@ -82,25 +103,478 @@ class RustAnalyzer(SolidLanguageServer): """ Returns the initialize params for the Rust Analyzer Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "rust_analyzer", "initialize_params.json"), 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 + root_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params = { + "clientInfo": {"name": "Visual Studio Code - Insiders", "version": "1.82.0-insider"}, + "locale": "en", + "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": None, + "runnables": {"extraEnv": None, "problemMatcher": ["$rustc"], "command": None, "extraArgs": []}, + "statusBar": {"clickAction": "openLogs"}, + "server": {"path": None, "extraEnv": None}, + "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": None, + "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": None, + "useRustcWrapper": True, + }, + "cfgs": {}, + "extraArgs": [], + "extraEnv": {}, + "features": [], + "noDefaultFeatures": False, + "sysroot": "discover", + "sysrootSrc": None, + "target": None, + "unsetTest": ["core"], + }, + "checkOnSave": True, + "check": { + "allTargets": True, + "command": "check", + "extraArgs": [], + "extraEnv": {}, + "features": None, + "ignore": [], + "invocationLocation": "workspace", + "invocationStrategy": "per_workspace", + "noDefaultFeatures": None, + "overrideCommand": None, + "targets": None, + }, + "completion": { + "autoimport": {"enable": True}, + "autoself": {"enable": True}, + "callable": {"snippets": "fill_arguments"}, + "fullFunctionSignatures": {"enable": False}, + "limit": None, + "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": None, "query": {"capacities": {}}}, + "notifications": {"cargoTomlNotFound": True}, + "numThreads": None, + "procMacro": {"attributes": {"enable": True}, "enable": True, "ignored": {}, "server": None}, + "references": {"excludeImports": False}, + "rust": {"analyzerTargetDir": None}, + "rustc": {"source": None}, + "rustfmt": {"extraArgs": [], "overrideCommand": None, "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", + "processId": os.getpid(), + "rootPath": repository_absolute_path, + "rootUri": root_uri, + "workspaceFolders": [ + { + "uri": root_uri, + "name": os.path.basename(repository_absolute_path), + } + ], + } + return initialize_params def _start_server(self): """ diff --git a/src/solidlsp/language_servers/rust_analyzer/initialize_params.json b/src/solidlsp/language_servers/rust_analyzer/initialize_params.json deleted file mode 100644 index 00368eed..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/rust_analyzer/runtime_dependencies.json b/src/solidlsp/language_servers/rust_analyzer/runtime_dependencies.json deleted file mode 100644 index 517d8b87..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/solargraph.py b/src/solidlsp/language_servers/solargraph.py index 731be1ad..cf25c34f 100644 --- a/src/solidlsp/language_servers/solargraph.py +++ b/src/solidlsp/language_servers/solargraph.py @@ -52,11 +52,16 @@ class Solargraph(SolidLanguageServer): """ Setup runtime dependencies for Solargraph. """ - with open(os.path.join(os.path.dirname(__file__), "solargraph", "runtime_dependencies.json"), encoding="utf-8") as f: - d = json.load(f) - del d["_description"] + runtime_dependencies = [ + { + "url": "https://rubygems.org/downloads/solargraph-0.51.1.gem", + "installCommand": "gem install solargraph -v 0.51.1", + "binaryName": "solargraph", + "archiveType": "gem", + } + ] - dependency = d["runtimeDependencies"][0] + dependency = runtime_dependencies[0] # Check if Ruby is installed try: @@ -97,25 +102,22 @@ class Solargraph(SolidLanguageServer): """ Returns the initialize params for the Solargraph Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "solargraph", "initialize_params.json"), encoding="utf-8") as f: - d = json.load(f) + root_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params = { + "capabilities": {}, + "trace": "verbose", + "processId": os.getpid(), + "rootPath": repository_absolute_path, + "rootUri": pathlib.Path(repository_absolute_path).as_uri(), + "workspaceFolders": [ + { + "uri": root_uri, + "name": os.path.basename(repository_absolute_path), + } + ], + } - 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 + return initialize_params def _start_server(self): """ diff --git a/src/solidlsp/language_servers/solargraph/initialize_params.json b/src/solidlsp/language_servers/solargraph/initialize_params.json deleted file mode 100644 index 2cbb41f8..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/solargraph/runtime_dependencies.json b/src/solidlsp/language_servers/solargraph/runtime_dependencies.json deleted file mode 100644 index 38cc2372..00000000 --- a/src/solidlsp/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" - } - ] -} From 77fb2e18bbde7a3260f82f0257783087a1b7886e Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Wed, 2 Jul 2025 02:11:19 +0200 Subject: [PATCH 05/25] Simplified config of various LS, tests run through --- src/solidlsp/language_servers/clojure_lsp.py | 12 +- .../csharp_language_server.py | 47 +- .../language_servers/dart_language_server.py | 1 - .../language_servers/eclipse_jdtls.py | 34 +- src/solidlsp/language_servers/gopls.py | 5 +- src/solidlsp/language_servers/intelephense.py | 1 - .../kotlin_language_server.py | 8 +- .../language_servers/pyright_server.py | 46 +- .../language_servers/rust_analyzer.py | 8 +- src/solidlsp/language_servers/solargraph.py | 1 - .../typescript_language_server.py | 474 +----------------- 11 files changed, 46 insertions(+), 591 deletions(-) diff --git a/src/solidlsp/language_servers/clojure_lsp.py b/src/solidlsp/language_servers/clojure_lsp.py index e83b540d..34dd4c14 100644 --- a/src/solidlsp/language_servers/clojure_lsp.py +++ b/src/solidlsp/language_servers/clojure_lsp.py @@ -116,16 +116,11 @@ class ClojureLSP(SolidLanguageServer): "processId": os.getpid(), "rootPath": repository_absolute_path, "rootUri": root_uri, - "clientInfo": {"name": "solidlsp", "version": "0.1.0"}, "capabilities": { "workspace": { "applyEdit": True, "workspaceEdit": {"documentChanges": True}, - "symbol": { - "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] - } - }, + "symbol": {"symbolKind": {"valueSet": list(range(1, 27))}}, "workspaceFolders": True, }, "textDocument": { @@ -133,13 +128,10 @@ class ClojureLSP(SolidLanguageServer): "publishDiagnostics": {"relatedInformation": True, "tagSupport": {"valueSet": [1, 2]}}, "definition": {"linkSupport": True}, "references": {}, - "completion": {"completionItem": {"snippetSupport": True, "documentationFormat": ["markdown", "plaintext"]}}, "hover": {"contentFormat": ["markdown", "plaintext"]}, "documentSymbol": { "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] - }, + "symbolKind": {"valueSet": list(range(1, 27))}, # }, }, "general": {"positionEncodings": ["utf-16"]}, diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index fbf13779..b71a25f9 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -499,9 +499,7 @@ class CSharpLanguageServer(SolidLanguageServer): "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] - }, + "symbolKind": {"valueSet": list(range(1, 27))}, }, "executeCommand": {"dynamicRegistration": True}, "configuration": True, @@ -510,20 +508,6 @@ class CSharpLanguageServer(SolidLanguageServer): }, "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, @@ -534,38 +518,11 @@ class CSharpLanguageServer(SolidLanguageServer): }, "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] - }, + "symbolKind": {"valueSet": list(range(1, 27))}, "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}, - "foldingRange": {"dynamicRegistration": True, "rangeLimit": 5000, "lineFoldingOnly": True}, }, }, }, diff --git a/src/solidlsp/language_servers/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server.py index 595ce8a6..b4fcdff0 100644 --- a/src/solidlsp/language_servers/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server.py @@ -99,7 +99,6 @@ class DartLanguageServer(SolidLanguageServer): "capabilities": {}, "initializationOptions": { "onlyAnalyzeProjectsWithOpenFiles": False, - "suggestFromUnimportedLibraries": True, "closingLabels": False, "outline": False, "flutterOutline": False, diff --git a/src/solidlsp/language_servers/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls.py index 090d86f4..ad104a16 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls.py @@ -319,10 +319,15 @@ 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 + + if not os.path.isabs(repository_absolute_path): + repository_absolute_path = os.path.abspath(repository_absolute_path) + repo_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params = { "locale": "en", - "rootPath": "repository_absolute_path", - "rootUri": "pathlib.Path(repository_absolute_path).as_uri()", + "rootPath": repository_absolute_path, + "rootUri": pathlib.Path(repository_absolute_path).as_uri(), "capabilities": { "workspace": { "applyEdit": True, @@ -754,27 +759,16 @@ class EclipseJDTLS(SolidLanguageServer): "triggerFiles": [], }, "trace": "verbose", + "processId": os.getpid(), + "workspaceFolders": [ + { + "uri": repo_uri, + "name": os.path.basename(repository_absolute_path), + } + ], } - if not os.path.isabs(repository_absolute_path): - repository_absolute_path = os.path.abspath(repository_absolute_path) - - initialize_params["processId"] = os.getpid() - - initialize_params["rootPath"] = repository_absolute_path - - initialize_params["rootUri"] = pathlib.Path(repository_absolute_path).as_uri() - - repo_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params["initializationOptions"]["workspaceFolders"] = [repo_uri] - - initialize_params["workspaceFolders"] = [ - { - "uri": repo_uri, - "name": os.path.basename(repository_absolute_path), - } - ] - bundles = [self.runtime_dependency_paths.intellicode_jar_path] initialize_params["initializationOptions"]["bundles"] = bundles initialize_params["initializationOptions"]["settings"]["java"]["configuration"]["runtimes"] = [ diff --git a/src/solidlsp/language_servers/gopls.py b/src/solidlsp/language_servers/gopls.py index b3db43e2..d87340a0 100644 --- a/src/solidlsp/language_servers/gopls.py +++ b/src/solidlsp/language_servers/gopls.py @@ -93,14 +93,11 @@ class Gopls(SolidLanguageServer): "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] - }, + "symbolKind": {"valueSet": list(range(1, 27))}, }, }, "workspace": {"workspaceFolders": True, "didChangeConfiguration": {"dynamicRegistration": True}}, diff --git a/src/solidlsp/language_servers/intelephense.py b/src/solidlsp/language_servers/intelephense.py index 165272de..5d702d8c 100644 --- a/src/solidlsp/language_servers/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -112,7 +112,6 @@ class Intelephense(SolidLanguageServer): "capabilities": { "textDocument": { "synchronization": {"didSave": True, "dynamicRegistration": True}, - "completion": {"dynamicRegistration": True, "completionItem": {"snippetSupport": True}}, "definition": {"dynamicRegistration": True}, }, "workspace": {"workspaceFolders": True, "didChangeConfiguration": {"dynamicRegistration": True}}, diff --git a/src/solidlsp/language_servers/kotlin_language_server.py b/src/solidlsp/language_servers/kotlin_language_server.py index 13a24380..6a11deec 100644 --- a/src/solidlsp/language_servers/kotlin_language_server.py +++ b/src/solidlsp/language_servers/kotlin_language_server.py @@ -188,9 +188,7 @@ class KotlinLanguageServer(SolidLanguageServer): "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] - }, + "symbolKind": {"valueSet": list(range(1, 27))}, "tagSupport": {"valueSet": [1]}, "resolveSupport": {"properties": ["location.range"]}, }, @@ -257,9 +255,7 @@ class KotlinLanguageServer(SolidLanguageServer): "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] - }, + "symbolKind": {"valueSet": list(range(1, 27))}, "hierarchicalDocumentSymbolSupport": True, "tagSupport": {"valueSet": [1]}, "labelSupport": True, diff --git a/src/solidlsp/language_servers/pyright_server.py b/src/solidlsp/language_servers/pyright_server.py index 893db94e..23c0c39a 100644 --- a/src/solidlsp/language_servers/pyright_server.py +++ b/src/solidlsp/language_servers/pyright_server.py @@ -68,34 +68,17 @@ class PyrightServer(SolidLanguageServer): }, "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] - }, + "symbolKind": {"valueSet": list(range(1, 27))}, }, "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, @@ -106,36 +89,11 @@ class PyrightServer(SolidLanguageServer): }, "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] - }, + "symbolKind": {"valueSet": list(range(1, 27))}, "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}, }, }, diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index 948b61df..aafcea6d 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -121,9 +121,7 @@ class RustAnalyzer(SolidLanguageServer): "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] - }, + "symbolKind": {"valueSet": list(range(1, 27))}, "tagSupport": {"valueSet": [1]}, "resolveSupport": {"properties": ["location.range"]}, }, @@ -190,9 +188,7 @@ class RustAnalyzer(SolidLanguageServer): "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] - }, + "symbolKind": {"valueSet": list(range(1, 27))}, "hierarchicalDocumentSymbolSupport": True, "tagSupport": {"valueSet": [1]}, "labelSupport": True, diff --git a/src/solidlsp/language_servers/solargraph.py b/src/solidlsp/language_servers/solargraph.py index cf25c34f..8a4260f9 100644 --- a/src/solidlsp/language_servers/solargraph.py +++ b/src/solidlsp/language_servers/solargraph.py @@ -116,7 +116,6 @@ class Solargraph(SolidLanguageServer): } ], } - return initialize_params def _start_server(self): diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index a71313de..49cf8d60 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -140,472 +140,40 @@ class TypeScriptLanguageServer(SolidLanguageServer): """ Returns the initialize params for the TypeScript Language Server. """ + root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { "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}, + "synchronization": {"didSave": True, "dynamicRegistration": True}, + "completion": {"dynamicRegistration": True, "completionItem": {"snippetSupport": 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, - "tagSupport": {"valueSet": [1]}, - "labelSupport": True, + "symbolKind": {"valueSet": list(range(1, 27))}, }, - "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}, + "hover": {"dynamicRegistration": True, "contentFormat": ["markdown", "plaintext"]}, + "signatureHelp": {"dynamicRegistration": True}, + "codeAction": {"dynamicRegistration": 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", - ] - }, + "workspace": { + "workspaceFolders": True, + "didChangeConfiguration": {"dynamicRegistration": True}, + "symbol": {"dynamicRegistration": True}, }, }, - "initializationOptions": { - "cargoRunner": None, - "runnables": {"extraEnv": None, "problemMatcher": ["$rustc"], "command": None, "extraArgs": []}, - "statusBar": {"clickAction": "openLogs"}, - "server": {"path": None, "extraEnv": None}, - "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": None, - "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": None, - "useRustcWrapper": True, - }, - "cfgs": {}, - "extraArgs": [], - "extraEnv": {}, - "features": [], - "noDefaultFeatures": False, - "sysroot": "discover", - "sysrootSrc": None, - "target": None, - "unsetTest": ["core"], - }, - "checkOnSave": True, - "check": { - "allTargets": True, - "command": "check", - "extraArgs": [], - "extraEnv": {}, - "features": None, - "ignore": [], - "invocationLocation": "workspace", - "invocationStrategy": "per_workspace", - "noDefaultFeatures": None, - "overrideCommand": None, - "targets": None, - }, - "completion": { - "autoimport": {"enable": True}, - "autoself": {"enable": True}, - "callable": {"snippets": "fill_arguments"}, - "fullFunctionSignatures": {"enable": False}, - "limit": None, - "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": None, "query": {"capacities": {}}}, - "notifications": {"cargoTomlNotFound": True}, - "numThreads": None, - "procMacro": {"attributes": {"enable": True}, "enable": True, "ignored": {}, "server": None}, - "references": {"excludeImports": False}, - "rust": {"analyzerTargetDir": None}, - "rustc": {"source": None}, - "rustfmt": {"extraArgs": [], "overrideCommand": None, "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"}], + "processId": os.getpid(), + "rootPath": repository_absolute_path, + "rootUri": root_uri, + "workspaceFolders": [ + { + "uri": root_uri, + "name": os.path.basename(repository_absolute_path), + } + ], } - - root_uri = pathlib.Path(repository_absolute_path).as_uri() - initialize_params["processId"] = os.getpid() - initialize_params["rootPath"] = repository_absolute_path - initialize_params["rootUri"] = root_uri - initialize_params["workspaceFolders"][0]["uri"] = root_uri - initialize_params["workspaceFolders"][0]["name"] = os.path.basename(repository_absolute_path) - return initialize_params def _start_server(self): From 89b2ada09ba18dfdced33fb46d175bcb8aca3049 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Wed, 2 Jul 2025 02:19:32 +0200 Subject: [PATCH 06/25] Simplified eclipse config --- .../language_servers/eclipse_jdtls.py | 150 +----------------- 1 file changed, 4 insertions(+), 146 deletions(-) diff --git a/src/solidlsp/language_servers/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls.py index ad104a16..aa76b73a 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls.py @@ -343,7 +343,7 @@ class EclipseJDTLS(SolidLanguageServer): "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] + "valueSet": list(range(1, 27)) }, "tagSupport": {"valueSet": [1]}, "resolveSupport": {"properties": ["location.range"]}, @@ -375,6 +375,8 @@ class EclipseJDTLS(SolidLanguageServer): "dataSupport": True, }, "synchronization": {"dynamicRegistration": True, "willSave": True, "willSaveWaitUntil": True, "didSave": True}, + # TODO: we have an assert that completion provider is not included in the capabilities at server startup + # Removing this will cause the assert to fail. Investigate why this is the case, simplify config "completion": { "dynamicRegistration": True, "contextSupport": True, @@ -404,46 +406,18 @@ class EclipseJDTLS(SolidLanguageServer): "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] + "valueSet": list(range(1, 27)) # }, "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, @@ -454,13 +428,6 @@ class EclipseJDTLS(SolidLanguageServer): "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}, @@ -510,20 +477,10 @@ class EclipseJDTLS(SolidLanguageServer): "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, @@ -534,7 +491,6 @@ class EclipseJDTLS(SolidLanguageServer): ], }, "regularExpressions": {"engine": "ECMAScript", "version": "ES2020"}, - "markdown": {"parser": "marked", "version": "1.1.0"}, "positionEncodings": ["utf-16"], }, "notebookDocument": {"synchronization": {"dynamicRegistration": True, "executionSummarySupport": True}}, @@ -597,7 +553,6 @@ class EclipseJDTLS(SolidLanguageServer): }, "maven": {"downloadSources": True, "updateSnapshots": True}, "eclipse": {"downloadSources": True}, - "referencesCodeLens": {"enabled": True}, "signatureHelp": {"enabled": True, "description": {"enabled": True}}, "implementationsCodeLens": {"enabled": True}, "format": { @@ -620,49 +575,6 @@ class EclipseJDTLS(SolidLanguageServer): "contentProvider": {"preferred": None}, "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"}, @@ -673,7 +585,6 @@ class EclipseJDTLS(SolidLanguageServer): "typeHierarchy": {"lazyLoad": False}, "settings": {"url": None}, "symbols": {"includeSourceMethodDeclarations": False}, - "quickfix": {"showAt": "line"}, "inlayHints": {"parameterNames": {"enabled": "literals", "exclusions": []}}, "codeAction": {"sortMembers": {"avoidVolatileChanges": True}}, "compile": { @@ -691,35 +602,7 @@ class EclipseJDTLS(SolidLanguageServer): "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, @@ -732,31 +615,6 @@ class EclipseJDTLS(SolidLanguageServer): "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", "processId": os.getpid(), From 23577d42bd533601cc07bc8f87bbcabd45f268a4 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Wed, 2 Jul 2025 02:30:37 +0200 Subject: [PATCH 07/25] Minor fixes in intelephense install --- .../language_servers/eclipse_jdtls.py | 8 +-- src/solidlsp/language_servers/intelephense.py | 65 +++++++++---------- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/src/solidlsp/language_servers/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls.py index aa76b73a..429d7345 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls.py @@ -342,9 +342,7 @@ class EclipseJDTLS(SolidLanguageServer): "didChangeWatchedFiles": {"dynamicRegistration": True, "relativePatternSupport": True}, "symbol": { "dynamicRegistration": True, - "symbolKind": { - "valueSet": list(range(1, 27)) - }, + "symbolKind": {"valueSet": list(range(1, 27))}, "tagSupport": {"valueSet": [1]}, "resolveSupport": {"properties": ["location.range"]}, }, @@ -411,9 +409,7 @@ class EclipseJDTLS(SolidLanguageServer): "references": {"dynamicRegistration": True}, "documentSymbol": { "dynamicRegistration": True, - "symbolKind": { - "valueSet": list(range(1, 27)) # - }, + "symbolKind": {"valueSet": list(range(1, 27))}, "hierarchicalDocumentSymbolSupport": True, "tagSupport": {"valueSet": [1]}, "labelSupport": True, diff --git a/src/solidlsp/language_servers/intelephense.py b/src/solidlsp/language_servers/intelephense.py index 5d702d8c..ed840ebe 100644 --- a/src/solidlsp/language_servers/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -49,11 +49,6 @@ class Intelephense(SolidLanguageServer): ] assert platform_id in valid_platforms, f"Platform {platform_id} is not supported for multilspy PHP at the moment" - runtime_dependencies = { - "id": "intelephense", - "description": "Intelephense package for Linux, OSX, and Windows. Both x64 and arm64 are supported.", - "command": "npm install --prefix ./ intelephense@1.14.4", - } 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 @@ -62,36 +57,38 @@ class Intelephense(SolidLanguageServer): 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." + if not os.path.exists(intelephense_executable_path): + install_command = "npm install --prefix ./ intelephense@1.14.4" + is_windows = PlatformUtils.get_platform_id().value.startswith("win") + # Windows doesn't support the 'user' parameter and doesn't have pwd module + if is_windows: + subprocess.run( + install_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( + install_command, + shell=True, + check=True, + user=user, + cwd=intelephense_ls_dir, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + assert os.path.exists( + intelephense_executable_path + ), f"intelephense executable not found at {intelephense_executable_path}, something went wrong." return f"{intelephense_executable_path} --stdio" From 5f45c3cc743135585a319e00db7ddf99b4b7f933 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Wed, 2 Jul 2025 12:20:36 +0200 Subject: [PATCH 08/25] Missing makedirs --- src/solidlsp/language_servers/intelephense.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/solidlsp/language_servers/intelephense.py b/src/solidlsp/language_servers/intelephense.py index ed840ebe..8e5fbdd4 100644 --- a/src/solidlsp/language_servers/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -49,7 +49,6 @@ class Intelephense(SolidLanguageServer): ] assert platform_id in valid_platforms, f"Platform {platform_id} is not supported for multilspy PHP at the moment" - 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." @@ -57,6 +56,8 @@ class Intelephense(SolidLanguageServer): 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 + intelephense_ls_dir = os.path.join(os.path.dirname(__file__), "static", "php-lsp") + os.makedirs(intelephense_ls_dir, exist_ok=True) intelephense_executable_path = os.path.join(intelephense_ls_dir, "node_modules", ".bin", "intelephense") if not os.path.exists(intelephense_executable_path): install_command = "npm install --prefix ./ intelephense@1.14.4" From 1ce53993d557089e0bbb8c339d47a16a10b370a8 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Wed, 2 Jul 2025 12:35:37 +0200 Subject: [PATCH 09/25] Minor cleanup in LS implementations: methods not using state are static Also reduced public interface --- .../clangd_language_server.py | 10 +++-- src/solidlsp/language_servers/clojure_lsp.py | 14 ++++--- .../csharp_language_server.py | 40 +++++++++++-------- .../language_servers/dart_language_server.py | 8 ++-- .../language_servers/eclipse_jdtls.py | 7 ++-- src/solidlsp/language_servers/gopls.py | 15 +++---- src/solidlsp/language_servers/intelephense.py | 10 +++-- .../kotlin_language_server.py | 10 +++-- .../language_servers/pyright_server.py | 3 +- .../language_servers/rust_analyzer.py | 10 +++-- src/solidlsp/language_servers/solargraph.py | 10 +++-- .../typescript_language_server.py | 10 +++-- 12 files changed, 87 insertions(+), 60 deletions(-) diff --git a/src/solidlsp/language_servers/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server.py index 1ba9f6da..871d7c45 100644 --- a/src/solidlsp/language_servers/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server.py @@ -27,7 +27,7 @@ class ClangdLanguageServer(SolidLanguageServer): """ 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) + clangd_executable_path = self._setup_runtime_dependencies(logger, config) super().__init__( config, logger, @@ -40,9 +40,10 @@ class ClangdLanguageServer(SolidLanguageServer): self.initialize_searcher_command_available = threading.Event() self.resolve_main_method_available = threading.Event() - def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: + @staticmethod + def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ - Setup runtime dependencies for ClangdLanguageServer. + Setup runtime dependencies for ClangdLanguageServer and return the command to start the server. """ platform_id = PlatformUtils.get_platform_id() @@ -107,7 +108,8 @@ class ClangdLanguageServer(SolidLanguageServer): return clangd_executable_path - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + @staticmethod + def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the clangd Language Server. """ diff --git a/src/solidlsp/language_servers/clojure_lsp.py b/src/solidlsp/language_servers/clojure_lsp.py index 34dd4c14..0fe06e61 100644 --- a/src/solidlsp/language_servers/clojure_lsp.py +++ b/src/solidlsp/language_servers/clojure_lsp.py @@ -75,7 +75,7 @@ class ClojureLSP(SolidLanguageServer): """ Creates a ClojureLSP instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ - clojure_lsp_executable_path = self.setup_runtime_dependencies(logger, config) + clojure_lsp_executable_path = self._setup_runtime_dependencies(logger, config) super().__init__( config, logger, @@ -88,12 +88,15 @@ class ClojureLSP(SolidLanguageServer): self.resolve_main_method_available = threading.Event() self.service_ready_event = threading.Event() - def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: - """Setup runtime dependencies for clojure-lsp.""" + @staticmethod + def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> str: + """Setup runtime dependencies for clojure-lsp and return the command to start the server.""" verify_clojure_cli() platform_id = PlatformUtils.get_platform_id() - runtime_dependencies = [dependency for dependency in self.runtime_dependencies if dependency["platformId"] == platform_id.value] + runtime_dependencies = [ + dependency for dependency in ClojureLSP.runtime_dependencies if dependency["platformId"] == platform_id.value + ] if len(runtime_dependencies) != 1: raise RuntimeError(f"Could not find a suitable clojure-lsp runtime dependency for platform {platform_id.value}.") dependency = runtime_dependencies[0] @@ -109,7 +112,8 @@ class ClojureLSP(SolidLanguageServer): os.chmod(clojurelsp_executable_path, stat.S_IEXEC) return clojurelsp_executable_path - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + @staticmethod + def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: """Returns the init params for clojure-lsp.""" root_uri = pathlib.Path(repository_absolute_path).as_uri() return { # type: ignore diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index b71a25f9..153c6b6a 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -228,30 +228,32 @@ class CSharpLanguageServer(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["bin", "obj", "packages", ".vs"] - def _ensure_server_installed(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> tuple[str, str, Path]: + @staticmethod + def _ensure_server_installed(logger: LanguageServerLogger, config: LanguageServerConfig) -> tuple[str, str, Path]: """ Ensure .NET runtime and Microsoft.CodeAnalysis.LanguageServer are available. Returns a tuple of (dotnet_path, language_server_dll_path, cache_dir). """ # Determine platform runtime ID - runtime_id = self._get_runtime_id() + runtime_id = CSharpLanguageServer._get_runtime_id() # Set up cache directory cache_dir = Path.home() / ".cache" / "serena" / "language-servers" / "csharp" cache_dir.mkdir(parents=True, exist_ok=True) # Get runtime dependencies for this platform - lang_server_dep, dotnet_runtime_dep = self._get_runtime_dependencies(runtime_id) + lang_server_dep, dotnet_runtime_dep = CSharpLanguageServer._get_runtime_dependencies(runtime_id) # Ensure .NET runtime is available - dotnet_path = self._ensure_dotnet_runtime(logger, cache_dir, dotnet_runtime_dep) + dotnet_path = CSharpLanguageServer._ensure_dotnet_runtime(logger, cache_dir, dotnet_runtime_dep) # Ensure language server is available - server_dll_path = self._ensure_language_server(logger, cache_dir, lang_server_dep) + server_dll_path = CSharpLanguageServer._ensure_language_server(logger, cache_dir, lang_server_dep) return dotnet_path, server_dll_path, cache_dir - def _get_runtime_id(self) -> str: + @staticmethod + def _get_runtime_id() -> str: """Determine the runtime ID based on the platform.""" system = platform.system().lower() machine = platform.machine().lower() @@ -265,7 +267,8 @@ class CSharpLanguageServer(SolidLanguageServer): else: raise LanguageServerException(f"Unsupported platform: {system} {machine}") - def _get_runtime_dependencies(self, runtime_id: str) -> tuple[RuntimeDependency, RuntimeDependency]: + @staticmethod + def _get_runtime_dependencies(runtime_id: str) -> tuple[RuntimeDependency, RuntimeDependency]: """Get the language server and .NET runtime dependencies for the platform.""" lang_server_dep = None dotnet_runtime_dep = None @@ -283,7 +286,8 @@ class CSharpLanguageServer(SolidLanguageServer): return lang_server_dep, dotnet_runtime_dep - def _ensure_dotnet_runtime(self, logger: LanguageServerLogger, cache_dir: Path, runtime_dep: RuntimeDependency) -> str: + @staticmethod + def _ensure_dotnet_runtime(logger: LanguageServerLogger, cache_dir: Path, runtime_dep: RuntimeDependency) -> str: """Ensure .NET runtime is available and return the dotnet executable path.""" # Check if dotnet is already available in system system_dotnet = shutil.which("dotnet") @@ -300,9 +304,10 @@ class CSharpLanguageServer(SolidLanguageServer): pass # Download .NET 9 runtime using config - return self._ensure_dotnet_runtime_from_config(logger, cache_dir, runtime_dep) + return CSharpLanguageServer._ensure_dotnet_runtime_from_config(logger, cache_dir, runtime_dep) - def _ensure_language_server(self, logger: LanguageServerLogger, cache_dir: Path, lang_server_dep: RuntimeDependency) -> str: + @staticmethod + def _ensure_language_server(logger: LanguageServerLogger, cache_dir: Path, lang_server_dep: RuntimeDependency) -> str: """Ensure language server is available and return the DLL path.""" package_name = lang_server_dep.package_name package_version = lang_server_dep.package_version @@ -316,10 +321,10 @@ class CSharpLanguageServer(SolidLanguageServer): # Download and install the language server logger.log(f"Downloading {package_name} version {package_version}...", logging.INFO) - package_path = self._download_nuget_package_direct(logger, package_name, package_version, cache_dir) + package_path = CSharpLanguageServer._download_nuget_package_direct(logger, package_name, package_version, cache_dir) # Extract and install - self._extract_language_server(lang_server_dep, package_path, server_dir) + CSharpLanguageServer._extract_language_server(lang_server_dep, package_path, server_dir) if not server_dll.exists(): raise LanguageServerException("Microsoft.CodeAnalysis.LanguageServer.dll not found after extraction") @@ -331,7 +336,8 @@ class CSharpLanguageServer(SolidLanguageServer): logger.log(f"Successfully installed Microsoft.CodeAnalysis.LanguageServer to {server_dll}", logging.INFO) return str(server_dll) - def _extract_language_server(self, lang_server_dep: RuntimeDependency, package_path: Path, server_dir: Path) -> None: + @staticmethod + def _extract_language_server(lang_server_dep: RuntimeDependency, package_path: Path, server_dir: Path) -> None: """Extract language server files from downloaded package.""" extract_path = lang_server_dep.extract_path or "lib/net9.0" source_dir = package_path / extract_path @@ -353,9 +359,8 @@ class CSharpLanguageServer(SolidLanguageServer): server_dir.mkdir(parents=True, exist_ok=True) shutil.copytree(source_dir, server_dir, dirs_exist_ok=True) - def _download_nuget_package_direct( - self, logger: LanguageServerLogger, package_name: str, package_version: str, cache_dir: Path - ) -> Path: + @staticmethod + def _download_nuget_package_direct(logger: LanguageServerLogger, package_name: str, package_version: str, cache_dir: Path) -> Path: """ Download a NuGet package directly from the Azure NuGet feed. Returns the path to the extracted package directory. @@ -411,7 +416,8 @@ class CSharpLanguageServer(SolidLanguageServer): f"Failed to download package {package_name} version {package_version} from Azure NuGet feed: {e}" ) from e - def _ensure_dotnet_runtime_from_config(self, logger: LanguageServerLogger, cache_dir: Path, runtime_dep: RuntimeDependency) -> str: + @staticmethod + def _ensure_dotnet_runtime_from_config(logger: LanguageServerLogger, cache_dir: Path, runtime_dep: RuntimeDependency) -> str: """ Ensure .NET 9 runtime is available using runtime dependency configuration. Returns the path to the dotnet executable. diff --git a/src/solidlsp/language_servers/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server.py index b4fcdff0..702bc9a9 100644 --- a/src/solidlsp/language_servers/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server.py @@ -18,7 +18,7 @@ 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) + executable_path = self._setup_runtime_dependencies(logger) super().__init__( config, logger, @@ -27,7 +27,8 @@ class DartLanguageServer(SolidLanguageServer): "dart", ) - def setup_runtime_dependencies(self, logger: "LanguageServerLogger") -> str: + @staticmethod + def _setup_runtime_dependencies(logger: "LanguageServerLogger") -> str: platform_id = PlatformUtils.get_platform_id() runtime_dependencies = [ @@ -90,7 +91,8 @@ class DartLanguageServer(SolidLanguageServer): return f"{dart_executable_path} language-server --client-id multilspy.dart --client-version 1.2" - def _get_initialize_params(self, repository_absolute_path: str): + @staticmethod + def _get_initialize_params(repository_absolute_path: str): """ Returns the initialize params for the Dart Language Server. """ diff --git a/src/solidlsp/language_servers/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls.py index 429d7345..0dce4cfe 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls.py @@ -49,7 +49,7 @@ 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) + runtime_dependency_paths = self._setupRuntimeDependencies(logger, config) self.runtime_dependency_paths = runtime_dependency_paths # ws_dir is the workspace directory for the EclipseJDTLS server @@ -152,9 +152,10 @@ class EclipseJDTLS(SolidLanguageServer): "lib", # General ] - def setupRuntimeDependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> RuntimeDependencyPaths: + @staticmethod + def _setupRuntimeDependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> RuntimeDependencyPaths: """ - Setup runtime dependencies for EclipseJDTLS. + Setup runtime dependencies for EclipseJDTLS and return the paths. """ platformId = PlatformUtils.get_platform_id() diff --git a/src/solidlsp/language_servers/gopls.py b/src/solidlsp/language_servers/gopls.py index d87340a0..70eb15fa 100644 --- a/src/solidlsp/language_servers/gopls.py +++ b/src/solidlsp/language_servers/gopls.py @@ -48,19 +48,19 @@ class Gopls(SolidLanguageServer): return None return None - @classmethod - def setup_runtime_dependency(cls): + @staticmethod + def _setup_runtime_dependency(): """ Check if required Go runtime dependencies are available. Raises RuntimeError with helpful message if dependencies are missing. """ - go_version = cls._get_go_version() + go_version = Gopls._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() + gopls_version = Gopls._get_gopls_version() if not gopls_version: raise RuntimeError( "Found a Go version but gopls is not installed.\n" @@ -71,7 +71,7 @@ class Gopls(SolidLanguageServer): return True def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): - self.setup_runtime_dependency() + self._setup_runtime_dependency() super().__init__( config, @@ -83,9 +83,10 @@ class Gopls(SolidLanguageServer): self.server_ready = threading.Event() self.request_id = 0 - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + @staticmethod + def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: """ - Returns the initialize params for the TypeScript Language Server. + Returns the initialize params for the Go Language Server. """ root_uri = pathlib.Path(repository_absolute_path).as_uri() initialize_params = { diff --git a/src/solidlsp/language_servers/intelephense.py b/src/solidlsp/language_servers/intelephense.py index 8e5fbdd4..e971c7dd 100644 --- a/src/solidlsp/language_servers/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -32,9 +32,10 @@ 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: LanguageServerLogger, config: LanguageServerConfig) -> str: + @staticmethod + def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ - Setup runtime dependencies for Intelephense. + Setup runtime dependencies for Intelephense and return the command to start the server. """ platform_id = PlatformUtils.get_platform_id() @@ -95,12 +96,13 @@ class Intelephense(SolidLanguageServer): def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): # Setup runtime dependencies before initializing - intelephense_cmd = self.setup_runtime_dependencies(logger, config) + 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.request_id = 0 - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + @staticmethod + def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the TypeScript Language Server. """ diff --git a/src/solidlsp/language_servers/kotlin_language_server.py b/src/solidlsp/language_servers/kotlin_language_server.py index 6a11deec..e89e5c7e 100644 --- a/src/solidlsp/language_servers/kotlin_language_server.py +++ b/src/solidlsp/language_servers/kotlin_language_server.py @@ -36,7 +36,7 @@ class KotlinLanguageServer(SolidLanguageServer): """ 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) + 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 @@ -53,9 +53,10 @@ class KotlinLanguageServer(SolidLanguageServer): "kotlin", ) - def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> KotlinRuntimeDependencyPaths: + @staticmethod + def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> KotlinRuntimeDependencyPaths: """ - Setup runtime dependencies for Kotlin Language Server. + Setup runtime dependencies for Kotlin Language Server and return the paths. """ platform_id = PlatformUtils.get_platform_id() @@ -161,7 +162,8 @@ class KotlinLanguageServer(SolidLanguageServer): 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: + @staticmethod + def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the Kotlin Language Server. """ diff --git a/src/solidlsp/language_servers/pyright_server.py b/src/solidlsp/language_servers/pyright_server.py index 23c0c39a..905bb3d6 100644 --- a/src/solidlsp/language_servers/pyright_server.py +++ b/src/solidlsp/language_servers/pyright_server.py @@ -46,7 +46,8 @@ class PyrightServer(SolidLanguageServer): 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: + @staticmethod + def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the Pyright Language Server. """ diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index aafcea6d..7edab82c 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -27,7 +27,7 @@ class RustAnalyzer(SolidLanguageServer): """ 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) + rustanalyzer_executable_path = self._setup_runtime_dependencies(logger, config) super().__init__( config, logger, @@ -44,9 +44,10 @@ 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: LanguageServerLogger, config: LanguageServerConfig) -> str: + @staticmethod + def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ - Setup runtime dependencies for rust_analyzer. + Setup runtime dependencies for rust_analyzer and return the command to start the server. """ platform_id = PlatformUtils.get_platform_id() @@ -99,7 +100,8 @@ class RustAnalyzer(SolidLanguageServer): return rustanalyzer_executable_path - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + @staticmethod + def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the Rust Analyzer Language Server. """ diff --git a/src/solidlsp/language_servers/solargraph.py b/src/solidlsp/language_servers/solargraph.py index 8a4260f9..8034ad80 100644 --- a/src/solidlsp/language_servers/solargraph.py +++ b/src/solidlsp/language_servers/solargraph.py @@ -31,7 +31,7 @@ class Solargraph(SolidLanguageServer): Creates a Solargraph instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ - solargraph_executable_path = self.setup_runtime_dependencies(logger, config, repository_root_path) + solargraph_executable_path = self._setup_runtime_dependencies(logger, config, repository_root_path) super().__init__( config, logger, @@ -48,9 +48,10 @@ 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: LanguageServerLogger, config: LanguageServerConfig, repository_root_path: str) -> str: + @staticmethod + def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig, repository_root_path: str) -> str: """ - Setup runtime dependencies for Solargraph. + Setup runtime dependencies for Solargraph and return the command to start the server. """ runtime_dependencies = [ { @@ -98,7 +99,8 @@ class Solargraph(SolidLanguageServer): except subprocess.CalledProcessError as e: raise RuntimeError(f"Failed to check or install Solargraph. {e.stderr}") from e - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + @staticmethod + def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the Solargraph Language Server. """ diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index 49cf8d60..c0130d35 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -44,7 +44,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): """ 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) + ts_lsp_executable_path = self._setup_runtime_dependencies(logger, config) super().__init__( config, logger, @@ -64,9 +64,10 @@ class TypeScriptLanguageServer(SolidLanguageServer): "coverage", ] - def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: + @staticmethod + def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ - Setup runtime dependencies for TypeScript Language Server. + Setup runtime dependencies for TypeScript Language Server and return the command to start the server. """ platform_id = PlatformUtils.get_platform_id() @@ -136,7 +137,8 @@ class TypeScriptLanguageServer(SolidLanguageServer): ), "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: + @staticmethod + def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the TypeScript Language Server. """ From 25f52331cff5f8cd72026f6f1bf62752be9800fd Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sat, 5 Jul 2025 13:29:48 +0200 Subject: [PATCH 10/25] LS: in finding references, skip over files outside the repo --- src/solidlsp/ls.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index 6c141f47..0f18cb55 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -108,6 +108,10 @@ class SolidLanguageServer(ABC): ls: SolidLanguageServer if config.code_language == Language.PYTHON: + # We can also use jedi + # from solidlsp.language_servers.jedi_server import JediServer + # + # ls = JediServer(config, logger, repository_root_path) from solidlsp.language_servers.pyright_server import ( PyrightServer, ) @@ -664,6 +668,14 @@ class SolidLanguageServer(ABC): assert LSPConstants.RANGE in item abs_path = PathUtils.uri_to_path(item[LSPConstants.URI]) + if not Path(abs_path).is_relative_to(self.repository_root_path): + self.logger.log( + "Found a reference in a path outside the repository, probably the LS is parsing things in installed packages or in the standardlib! " + f"Path: {abs_path}. This is a bug but we currently simply skip these references.", + logging.WARNING, + ) + continue + rel_path = Path(abs_path).relative_to(self.repository_root_path) if self.is_ignored_path(str(rel_path)): self.logger.log(f"Ignoring reference in {rel_path} since it should be ignored", logging.DEBUG) From 751bf1561951dd5cb3c6ff6c2f9a104a733fa36d Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sat, 5 Jul 2025 13:30:33 +0200 Subject: [PATCH 11/25] Refactored jedi-server, removed a whole lot of unnused config --- .../initialize_params.json | 905 ------------------ .../jedi_language_server/jedi_server.py | 107 --- src/solidlsp/language_servers/jedi_server.py | 194 ++++ 3 files changed, 194 insertions(+), 1012 deletions(-) delete mode 100644 src/solidlsp/language_servers/jedi_language_server/initialize_params.json delete mode 100644 src/solidlsp/language_servers/jedi_language_server/jedi_server.py create mode 100644 src/solidlsp/language_servers/jedi_server.py diff --git a/src/solidlsp/language_servers/jedi_language_server/initialize_params.json b/src/solidlsp/language_servers/jedi_language_server/initialize_params.json deleted file mode 100644 index 5a04872c..00000000 --- a/src/solidlsp/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/solidlsp/language_servers/jedi_language_server/jedi_server.py b/src/solidlsp/language_servers/jedi_language_server/jedi_server.py deleted file mode 100644 index 467d3cab..00000000 --- a/src/solidlsp/language_servers/jedi_language_server/jedi_server.py +++ /dev/null @@ -1,107 +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 overrides import override - -from solidlsp.ls import SolidLanguageServer -from solidlsp.ls_config import LanguageServerConfig -from solidlsp.ls_logger import LanguageServerLogger -from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams -from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo - - -class JediServer(SolidLanguageServer): - """ - Provides Python specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Python. - """ - - def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): - """ - 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"), 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 _start_server(self): - """ - Starts the JEDI Language Server - """ - - def execute_client_command_handler(params): - return [] - - def do_nothing(params): - return - - def check_experimental_status(params): - if params["quiescent"] == True: - self.completions_available.set() - - 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) - - self.logger.log("Starting jedi-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 from LSP client to LSP server and awaiting response", - logging.INFO, - ) - init_response = 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({}) diff --git a/src/solidlsp/language_servers/jedi_server.py b/src/solidlsp/language_servers/jedi_server.py new file mode 100644 index 00000000..161ce754 --- /dev/null +++ b/src/solidlsp/language_servers/jedi_server.py @@ -0,0 +1,194 @@ +""" +Provides Python specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Python. +""" + +import logging +import os +import pathlib + +from overrides import override + +from solidlsp.ls import SolidLanguageServer +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo + + +class JediServer(SolidLanguageServer): + """ + Provides Python specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Python. + """ + + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): + """ + 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__"] + + @staticmethod + def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: + """ + Returns the initialize params for the Jedi Language Server. + """ + root_uri = pathlib.Path(repository_absolute_path).as_uri() + initialize_params = { + "processId": os.getpid(), + "clientInfo": {"name": "Serena", "version": "0.1.0"}, + "locale": "en", + "rootPath": repository_absolute_path, + "rootUri": root_uri, + # Note: this is not necessarily the minimal set of capabilities... + "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": list(range(1, 27))}, + "tagSupport": {"valueSet": [1]}, + "resolveSupport": {"properties": ["location.range"]}, + }, + "workspaceFolders": 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}, + "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": list(range(1, 27))}, + "hierarchicalDocumentSymbolSupport": True, + "tagSupport": {"valueSet": [1]}, + "labelSupport": True, + }, + "documentLink": {"dynamicRegistration": True, "tooltipSupport": True}, + "typeDefinition": {"dynamicRegistration": True, "linkSupport": True}, + "implementation": {"dynamicRegistration": True, "linkSupport": True}, + "declaration": {"dynamicRegistration": True, "linkSupport": True}, + "selectionRange": {"dynamicRegistration": True}, + "callHierarchy": {"dynamicRegistration": 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}, + }, + "notebookDocument": {"synchronization": {"dynamicRegistration": True, "executionSummarySupport": True}}, + "experimental": { + "serverStatusNotification": True, + "openServerLogs": True, + }, + }, + # See https://github.com/pappasam/jedi-language-server?tab=readme-ov-file + # We use the default options except for maxSymbols, where 0 means no limit + "initializationOptions": { + "workspace": { + "symbols": {"ignoreFolders": [".nox", ".tox", ".venv", "__pycache__", "venv"], "maxSymbols": 0}, + }, + }, + "trace": "verbose", + "workspaceFolders": [ + { + "uri": root_uri, + "name": os.path.basename(repository_absolute_path), + } + ], + } + return initialize_params + + def _start_server(self): + """ + Starts the JEDI Language Server + """ + + def execute_client_command_handler(params): + return [] + + def do_nothing(params): + return + + def check_experimental_status(params): + if params["quiescent"] == True: + self.completions_available.set() + + 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) + + self.logger.log("Starting jedi-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 from LSP client to LSP server and awaiting response", + logging.INFO, + ) + init_response = 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({}) From cf2a0c248bc227ac4c52d9c54e583bdec13b2d07 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sun, 6 Jul 2025 00:08:40 +0200 Subject: [PATCH 12/25] Moved omnisharp module up, minor tweaks In this state, C# tests with omnisharp as LS mostly pass, except those querying for references --- .../{omnisharp => }/omnisharp.py | 19 +++-- src/solidlsp/ls_utils.py | 6 +- .../repos/csharp/{ => test_repo}/serena.sln | 76 +++++++++---------- test/solidlsp/csharp/test_csharp_basic.py | 2 +- 4 files changed, 53 insertions(+), 50 deletions(-) rename src/solidlsp/language_servers/{omnisharp => }/omnisharp.py (95%) rename test/resources/repos/csharp/{ => test_repo}/serena.sln (98%) diff --git a/src/solidlsp/language_servers/omnisharp/omnisharp.py b/src/solidlsp/language_servers/omnisharp.py similarity index 95% rename from src/solidlsp/language_servers/omnisharp/omnisharp.py rename to src/solidlsp/language_servers/omnisharp.py index e66540ca..2f043b62 100644 --- a/src/solidlsp/language_servers/omnisharp/omnisharp.py +++ b/src/solidlsp/language_servers/omnisharp.py @@ -112,11 +112,12 @@ class OmniSharp(SolidLanguageServer): 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: + @staticmethod + def _get_initialize_params(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"), encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "omnisharp", "initialize_params.json"), encoding="utf-8") as f: d = json.load(f) del d["_description"] @@ -136,30 +137,32 @@ class OmniSharp(SolidLanguageServer): return d - def setupRuntimeDependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> tuple[str, str]: + @staticmethod + def setupRuntimeDependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> 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"), encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "omnisharp", "runtime_dependencies.json"), 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" + ], f"Only linux-x64 and win-x64 platform is supported at the moment but got {platform_id=}" assert dotnet_version in [ DotnetVersion.V6, DotnetVersion.V7, DotnetVersion.V8, - ], "Only dotnet version 6 and 7 are supported in multilspy at the moment" + DotnetVersion.V9, + ], f"Only dotnet version 6-9 are supported at the moment but got {dotnet_version=}" # 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]: + if dotnet_version in [DotnetVersion.V7, DotnetVersion.V8, DotnetVersion.V9]: dotnet_version = DotnetVersion.V6 runtime_dependencies = d["runtimeDependencies"] @@ -363,7 +366,7 @@ 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"), encoding="utf-8") as f: + with open(os.path.join(os.path.dirname(__file__), "omnisharp", "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"]: diff --git a/src/solidlsp/ls_utils.py b/src/solidlsp/ls_utils.py index 57875c04..32aab581 100644 --- a/src/solidlsp/ls_utils.py +++ b/src/solidlsp/ls_utils.py @@ -257,6 +257,7 @@ class DotnetVersion(str, Enum): V6 = "6" V7 = "7" V8 = "8" + V9 = "9" VMONO = "mono" @@ -345,15 +346,14 @@ class PlatformUtils: # Check for supported versions in order of preference (latest first) for version_cmd_output in available_version_cmd_output: + if version_cmd_output.startswith("9"): + return DotnetVersion.V9 if version_cmd_output.startswith("8"): return DotnetVersion.V8 - for version_cmd_output in available_version_cmd_output: if version_cmd_output.startswith("7"): return DotnetVersion.V7 - for version_cmd_output in available_version_cmd_output: if version_cmd_output.startswith("6"): return DotnetVersion.V6 - for version_cmd_output in available_version_cmd_output: if version_cmd_output.startswith("4"): return DotnetVersion.V4 diff --git a/test/resources/repos/csharp/serena.sln b/test/resources/repos/csharp/test_repo/serena.sln similarity index 98% rename from test/resources/repos/csharp/serena.sln rename to test/resources/repos/csharp/test_repo/serena.sln index 30e1b535..ef7a1315 100644 --- a/test/resources/repos/csharp/serena.sln +++ b/test/resources/repos/csharp/test_repo/serena.sln @@ -1,38 +1,38 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.2.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0C88DD14-F956-CE84-757C-A364CCF449FC}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "resources", "resources", "{EF7103B4-4C75-1E6D-A485-A154A88D107A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "repos", "repos", "{E2326EEF-E677-6A44-0935-7677816F09E7}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "csharp", "csharp", "{C21E6CE7-177A-86D9-040F-A317F18B6DBF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "test\resources\repos\csharp\test_repo\TestProject.csproj", "{A4D04E18-760A-73F9-3303-0542F6298C84}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A4D04E18-760A-73F9-3303-0542F6298C84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A4D04E18-760A-73F9-3303-0542F6298C84}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A4D04E18-760A-73F9-3303-0542F6298C84}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A4D04E18-760A-73F9-3303-0542F6298C84}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EF7103B4-4C75-1E6D-A485-A154A88D107A} = {0C88DD14-F956-CE84-757C-A364CCF449FC} - {E2326EEF-E677-6A44-0935-7677816F09E7} = {EF7103B4-4C75-1E6D-A485-A154A88D107A} - {C21E6CE7-177A-86D9-040F-A317F18B6DBF} = {E2326EEF-E677-6A44-0935-7677816F09E7} - {A4D04E18-760A-73F9-3303-0542F6298C84} = {C21E6CE7-177A-86D9-040F-A317F18B6DBF} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {BDCA748E-D888-4BAF-BF24-DAC683113BFC} - EndGlobalSection -EndGlobal +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0C88DD14-F956-CE84-757C-A364CCF449FC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "resources", "resources", "{EF7103B4-4C75-1E6D-A485-A154A88D107A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "repos", "repos", "{E2326EEF-E677-6A44-0935-7677816F09E7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "csharp", "csharp", "{C21E6CE7-177A-86D9-040F-A317F18B6DBF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "test\resources\repos\csharp\test_repo\TestProject.csproj", "{A4D04E18-760A-73F9-3303-0542F6298C84}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A4D04E18-760A-73F9-3303-0542F6298C84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4D04E18-760A-73F9-3303-0542F6298C84}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4D04E18-760A-73F9-3303-0542F6298C84}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4D04E18-760A-73F9-3303-0542F6298C84}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EF7103B4-4C75-1E6D-A485-A154A88D107A} = {0C88DD14-F956-CE84-757C-A364CCF449FC} + {E2326EEF-E677-6A44-0935-7677816F09E7} = {EF7103B4-4C75-1E6D-A485-A154A88D107A} + {C21E6CE7-177A-86D9-040F-A317F18B6DBF} = {E2326EEF-E677-6A44-0935-7677816F09E7} + {A4D04E18-760A-73F9-3303-0542F6298C84} = {C21E6CE7-177A-86D9-040F-A317F18B6DBF} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BDCA748E-D888-4BAF-BF24-DAC683113BFC} + EndGlobalSection +EndGlobal diff --git a/test/solidlsp/csharp/test_csharp_basic.py b/test/solidlsp/csharp/test_csharp_basic.py index cff8cc5c..bb74397a 100644 --- a/test/solidlsp/csharp/test_csharp_basic.py +++ b/test/solidlsp/csharp/test_csharp_basic.py @@ -57,7 +57,7 @@ class TestCSharpLanguageServer: break assert add_symbol is not None, "Could not find 'Add' method symbol in Program.cs" sel_start = add_symbol["selectionRange"]["start"] - refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) + refs = language_server.request_references(file_path, sel_start["line"], sel_start["character"] + 1) assert any( "Program.cs" in ref.get("relativePath", "") for ref in refs ), "Program.cs should reference Add method (tried all positions in selectionRange)" From 6107b179bf0c264d66d429eef571248497e156ea Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sun, 6 Jul 2025 14:06:16 +0200 Subject: [PATCH 13/25] Don't include relative_path in symbol's children dict representation --- src/serena/symbol.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/serena/symbol.py b/src/serena/symbol.py index 0f48bb54..8d684671 100644 --- a/src/serena/symbol.py +++ b/src/serena/symbol.py @@ -189,8 +189,11 @@ class SymbolLocation: if self.relative_path is not None: self.relative_path = self.relative_path.replace("/", os.path.sep) - def to_dict(self) -> dict[str, Any]: - return asdict(self) + def to_dict(self, include_relative_path: bool = True) -> dict[str, Any]: + result = asdict(self) + if not include_relative_path: + result.pop("relative_path", None) + return result def has_position_in_file(self) -> bool: return self.relative_path is not None and self.line is not None and self.column is not None @@ -426,7 +429,8 @@ class Symbol(ToStringMixin): return result def to_dict( - self, kind: bool = False, location: bool = False, depth: int = 0, include_body: bool = False, include_children_body: bool = False + self, kind: bool = False, location: bool = False, depth: int = 0, include_body: bool = False, include_children_body: bool = False, + include_relative_path=True, ) -> dict[str, Any]: """ Converts the symbol to a dictionary. @@ -439,6 +443,8 @@ class Symbol(ToStringMixin): Note that the body of the children is part of the body of the parent symbol, so there is usually no need to set this to True unless you want process the output and pass the children without passing the parent body to the LM. + :param include_relative_path: whether to include the relative path of the symbol in the location + entry. Relative paths of the symbol's children are always excluded. :return: a dictionary representation of the symbol """ result: dict[str, Any] = {"name": self.name, "name_path": self.get_name_path()} @@ -447,7 +453,7 @@ class Symbol(ToStringMixin): result["kind"] = self.kind if location: - result["location"] = self.location.to_dict() + result["location"] = self.location.to_dict(include_relative_path=include_relative_path) body_start_line, body_end_line = self.get_body_line_numbers() result["body_location"] = {"start_line": body_start_line, "end_line": body_end_line} @@ -466,6 +472,8 @@ class Symbol(ToStringMixin): depth=depth - 1, include_body=include_children_body, include_children_body=include_children_body, + # all children have the same relative path as the parent + include_relative_path=False, ) ) return children From 5eba8a749b62192380c58a4bf0cffa57470b2d43 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sun, 6 Jul 2025 14:06:22 +0200 Subject: [PATCH 14/25] Formatting, removed unused vars and imports --- src/serena/symbol.py | 7 +- .../language_servers/elixir_tools/__init__.py | 2 +- .../elixir_tools/elixir_tools.py | 60 +++++++------- test/solidlsp/elixir/__init__.py | 12 ++- test/solidlsp/elixir/conftest.py | 73 +++++++++-------- test/solidlsp/elixir/test_elixir_basic.py | 39 ++++------ .../elixir/test_elixir_ignored_dirs.py | 31 ++++---- .../elixir/test_elixir_integration.py | 41 +++++----- .../elixir/test_elixir_symbol_retrieval.py | 78 +++++++++---------- 9 files changed, 166 insertions(+), 177 deletions(-) diff --git a/src/serena/symbol.py b/src/serena/symbol.py index 8d684671..4fa90677 100644 --- a/src/serena/symbol.py +++ b/src/serena/symbol.py @@ -429,7 +429,12 @@ class Symbol(ToStringMixin): return result def to_dict( - self, kind: bool = False, location: bool = False, depth: int = 0, include_body: bool = False, include_children_body: bool = False, + self, + kind: bool = False, + location: bool = False, + depth: int = 0, + include_body: bool = False, + include_children_body: bool = False, include_relative_path=True, ) -> dict[str, Any]: """ diff --git a/src/solidlsp/language_servers/elixir_tools/__init__.py b/src/solidlsp/language_servers/elixir_tools/__init__.py index 0519ecba..8b137891 100644 --- a/src/solidlsp/language_servers/elixir_tools/__init__.py +++ b/src/solidlsp/language_servers/elixir_tools/__init__.py @@ -1 +1 @@ - \ No newline at end of file + diff --git a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py index 62d7df67..96674722 100644 --- a/src/solidlsp/language_servers/elixir_tools/elixir_tools.py +++ b/src/solidlsp/language_servers/elixir_tools/elixir_tools.py @@ -35,24 +35,27 @@ class ElixirTools(SolidLanguageServer): def _is_next_ls_internal_file(self, abs_path: str) -> bool: """Check if an absolute path is a Next LS internal file that should be ignored.""" - return any(pattern in abs_path for pattern in [ - ".burrito", # Next LS runtime directory - "next_ls_erts-", # Next LS Erlang runtime - "_next_ls_private_", # Next LS private files - "/priv/monkey/", # Next LS monkey patching directory - ]) + return any( + pattern in abs_path + for pattern in [ + ".burrito", # Next LS runtime directory + "next_ls_erts-", # Next LS Erlang runtime + "_next_ls_private_", # Next LS private files + "/priv/monkey/", # Next LS monkey patching directory + ] + ) @override def _send_references_request(self, relative_file_path: str, line: int, column: int): """Override to filter out Next LS internal files from references.""" from solidlsp.ls_utils import PathUtils - + # Get the raw response from the parent implementation raw_response = super()._send_references_request(relative_file_path, line, column) - + if raw_response is None: return None - + # Filter out Next LS internal files filtered_response = [] for item in raw_response: @@ -62,7 +65,7 @@ class ElixirTools(SolidLanguageServer): self.logger.log(f"Filtering out Next LS internal file: {abs_path}", logging.DEBUG) continue filtered_response.append(item) - + return filtered_response @staticmethod @@ -87,7 +90,7 @@ class ElixirTools(SolidLanguageServer): raise RuntimeError( "Elixir is not installed. Please install Elixir from https://elixir-lang.org/install.html and make sure it is added to your PATH." ) - + logger.log(f"Found Elixir: {elixir_version}", logging.INFO) platformId = PlatformUtils.get_platform_id() @@ -101,17 +104,17 @@ class ElixirTools(SolidLanguageServer): # Map platform IDs to runtime dependency keys platform_mapping = { "linux-x64": "linux-x64", - "osx-x64": "darwin-x64", + "osx-x64": "darwin-x64", "osx-arm64": "darwin-arm64", "darwin-x64": "darwin-x64", - "darwin-arm64": "darwin-arm64", - "win-x64": "win-x64" + "darwin-arm64": "darwin-arm64", + "win-x64": "win-x64", } platform_key = platform_mapping.get(platformId.value) if not platform_key: raise RuntimeError(f"Unsupported platform for Next LS: {platformId.value}") - + # Check for Windows and provide a helpful error message if platformId.value.startswith("win"): raise RuntimeError( @@ -122,18 +125,18 @@ class ElixirTools(SolidLanguageServer): dependency = runtimeDependencies["next_ls"][platform_key] next_ls_dir = str(PurePath(os.path.abspath(os.path.dirname(__file__)), "static", dependency["relative_extraction_path"])) os.makedirs(next_ls_dir, exist_ok=True) - + executable_path = str(PurePath(next_ls_dir, dependency["executable_name"])) binary_path = str(PurePath(next_ls_dir, dependency["binary_name"])) if not os.path.exists(executable_path): logger.log(f"Downloading Next LS binary from {dependency['url']}", logging.INFO) FileUtils.download_file(logger, dependency["url"], binary_path) - + # Make the binary executable on Unix-like systems if not platformId.value.startswith("win"): os.chmod(binary_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) - + # Create a symlink or copy with the expected name if binary_path != executable_path: if os.path.exists(executable_path): @@ -141,13 +144,14 @@ class ElixirTools(SolidLanguageServer): if platformId.value.startswith("win"): # On Windows, copy the file import shutil + shutil.copy2(binary_path, executable_path) else: # On Unix-like systems, create a symlink os.symlink(os.path.basename(binary_path), executable_path) assert os.path.exists(executable_path), f"Next LS executable not found at {executable_path}" - + logger.log(f"Next LS binary ready at: {executable_path}", logging.INFO) return executable_path @@ -163,7 +167,7 @@ class ElixirTools(SolidLanguageServer): ) self.server_ready = threading.Event() self.request_id = 0 - + # Set generous timeout for Next LS which can be slow to initialize and respond self.set_request_timeout(180.0) # 60 seconds for all environments @@ -201,7 +205,7 @@ class ElixirTools(SolidLanguageServer): """Handle window/logMessage notifications from Next LS""" message_text = msg.get("message", "") self.logger.log(f"LSP: window/logMessage: {message_text}", logging.INFO) - + # Check for the specific Next LS readiness signal # Based on Next LS source: "Runtime for folder #{name} is ready..." if "Runtime for folder" in message_text and "is ready..." in message_text: @@ -214,11 +218,11 @@ class ElixirTools(SolidLanguageServer): def check_server_ready(params): """ Handle $/progress notifications from Next LS. - Keep as fallback for error detection, but primary readiness detection + Keep as fallback for error detection, but primary readiness detection is now done via window/logMessage handler. """ value = params.get("value", {}) - + # Check for initialization completion progress (fallback signal) if value.get("kind") == "end": message = value.get("message", "") @@ -255,10 +259,10 @@ class ElixirTools(SolidLanguageServer): # Verify server capabilities - be more lenient with Next LS self.logger.log(f"Next LS capabilities: {list(init_response['capabilities'].keys())}", logging.INFO) - + # Next LS may not provide all capabilities immediately, so we check for basic ones assert "textDocumentSync" in init_response["capabilities"], f"Missing textDocumentSync in {init_response['capabilities']}" - + # Some capabilities might be optional or provided later if "completionProvider" not in init_response["capabilities"]: self.logger.log("Warning: completionProvider not available in initial capabilities", logging.WARNING) @@ -272,10 +276,10 @@ class ElixirTools(SolidLanguageServer): # This is the authoritative signal that Next LS is truly ready for requests ready_timeout = 180.0 self.logger.log(f"Waiting up to {ready_timeout} seconds for Next LS runtime readiness...", logging.INFO) - + if self.server_ready.wait(timeout=ready_timeout): self.logger.log("Next LS is ready and available for requests", logging.INFO) - + # Add a small settling period to ensure background indexing is complete # Next LS often continues compilation/indexing in background after ready signal settling_time = 120.0 @@ -285,4 +289,4 @@ class ElixirTools(SolidLanguageServer): else: error_msg = f"Next LS failed to initialize within {ready_timeout} seconds. This may indicate a problem with the Elixir installation, project compilation, or Next LS itself." self.logger.log(error_msg, logging.ERROR) - raise RuntimeError(error_msg) \ No newline at end of file + raise RuntimeError(error_msg) diff --git a/test/solidlsp/elixir/__init__.py b/test/solidlsp/elixir/__init__.py index 184fc8ed..4d7e8860 100644 --- a/test/solidlsp/elixir/__init__.py +++ b/test/solidlsp/elixir/__init__.py @@ -1,25 +1,23 @@ import platform -from pathlib import Path def _test_nextls_available() -> str: """Test if Next LS is available and return error reason if not.""" - # Check if we're on Windows (Next LS doesn't support Windows) if platform.system() == "Windows": return "Next LS does not support Windows" - + # Try to import and check Elixir availability try: from solidlsp.language_servers.elixir_tools.elixir_tools import ElixirTools - + # Check if Elixir is installed elixir_version = ElixirTools._get_elixir_version() if not elixir_version: return "Elixir is not installed or not in PATH" - + return "" # No error, Next LS should be available - + except ImportError as e: return f"Failed to import ElixirTools: {e}" except Exception as e: @@ -27,4 +25,4 @@ def _test_nextls_available() -> str: NEXTLS_UNAVAILABLE_REASON = _test_nextls_available() -NEXTLS_UNAVAILABLE = bool(NEXTLS_UNAVAILABLE_REASON) \ No newline at end of file +NEXTLS_UNAVAILABLE = bool(NEXTLS_UNAVAILABLE_REASON) diff --git a/test/solidlsp/elixir/conftest.py b/test/solidlsp/elixir/conftest.py index 6dc31cf5..34523765 100644 --- a/test/solidlsp/elixir/conftest.py +++ b/test/solidlsp/elixir/conftest.py @@ -1,137 +1,142 @@ """ Elixir-specific test configuration and fixtures. """ + import os import subprocess -import pytest import time from pathlib import Path +import pytest + def ensure_elixir_test_repo_compiled(repo_path: str) -> None: """Ensure the Elixir test repository dependencies are installed and project is compiled. - + Next LS requires the project to be fully compiled and indexed before providing complete references and symbol resolution. This function: 1. Installs dependencies via 'mix deps.get' 2. Compiles the project via 'mix compile' - + This is essential in CI environments where dependencies aren't pre-installed. - + Args: repo_path: Path to the Elixir project root directory + """ # Check if this looks like an Elixir project mix_file = os.path.join(repo_path, "mix.exs") if not os.path.exists(mix_file): return - + # Check if already compiled (optimization for repeated runs) build_path = os.path.join(repo_path, "_build") deps_path = os.path.join(repo_path, "deps") - + if os.path.exists(build_path) and os.path.exists(deps_path): print(f"Elixir test repository already compiled in {repo_path}") return - + try: - print(f"Installing dependencies and compiling Elixir test repository for optimal Next LS performance...") - + print("Installing dependencies and compiling Elixir test repository for optimal Next LS performance...") + # First, install dependencies with increased timeout for CI print("=" * 60) print("Step 1/2: Installing Elixir dependencies...") print("=" * 60) start_time = time.time() - + deps_result = subprocess.run( ["mix", "deps.get"], cwd=repo_path, capture_output=True, text=True, - timeout=180 # 3 minutes for dependency installation (CI can be slow) + timeout=180, + check=False, # 3 minutes for dependency installation (CI can be slow) ) - + deps_duration = time.time() - start_time print(f"Dependencies installation completed in {deps_duration:.2f} seconds") - + # Always log the output for transparency if deps_result.stdout.strip(): print("Dependencies stdout:") print("-" * 40) print(deps_result.stdout) print("-" * 40) - + if deps_result.stderr.strip(): print("Dependencies stderr:") print("-" * 40) print(deps_result.stderr) print("-" * 40) - + if deps_result.returncode != 0: print(f"⚠️ Warning: Dependencies installation failed with exit code {deps_result.returncode}") # Continue anyway - some projects might not have dependencies else: print("✓ Dependencies installed successfully") - + # Then compile the project with increased timeout for CI print("=" * 60) print("Step 2/2: Compiling Elixir project...") print("=" * 60) start_time = time.time() - + compile_result = subprocess.run( ["mix", "compile"], cwd=repo_path, capture_output=True, text=True, - timeout=300 # 5 minutes for compilation (Credo compilation can be slow in CI) + timeout=300, + check=False, # 5 minutes for compilation (Credo compilation can be slow in CI) ) - + compile_duration = time.time() - start_time print(f"Compilation completed in {compile_duration:.2f} seconds") - + # Always log the output for transparency if compile_result.stdout.strip(): print("Compilation stdout:") print("-" * 40) print(compile_result.stdout) print("-" * 40) - + if compile_result.stderr.strip(): print("Compilation stderr:") print("-" * 40) print(compile_result.stderr) print("-" * 40) - + if compile_result.returncode == 0: print(f"✓ Elixir test repository compiled successfully in {repo_path}") else: print(f"⚠️ Warning: Compilation completed with exit code {compile_result.returncode}") # Still continue - warnings are often non-fatal - + print("=" * 60) print(f"Total setup time: {time.time() - (start_time - compile_duration - deps_duration):.2f} seconds") print("=" * 60) - + except subprocess.TimeoutExpired as e: print("=" * 60) print(f"❌ TIMEOUT: Elixir setup timed out after {e.timeout} seconds") print(f"Command: {' '.join(e.cmd)}") print("This may indicate slow CI environment - Next LS may still work but with reduced functionality") - + # Try to get partial output if available - if hasattr(e, 'stdout') and e.stdout: + if hasattr(e, "stdout") and e.stdout: print("Partial stdout before timeout:") print("-" * 40) print(e.stdout) print("-" * 40) - if hasattr(e, 'stderr') and e.stderr: + if hasattr(e, "stderr") and e.stderr: print("Partial stderr before timeout:") print("-" * 40) print(e.stderr) print("-" * 40) print("=" * 60) - + except FileNotFoundError: print("❌ ERROR: 'mix' command not found - Elixir test repository may not be compiled") print("Please ensure Elixir is installed and available in PATH") @@ -142,14 +147,14 @@ def ensure_elixir_test_repo_compiled(repo_path: str) -> None: @pytest.fixture(scope="session", autouse=True) def setup_elixir_test_environment(): """Automatically prepare Elixir test environment for all Elixir tests. - + This fixture runs once per test session and automatically: 1. Installs dependencies via 'mix deps.get' 2. Compiles the Elixir test repository via 'mix compile' - - It uses autouse=True so it runs automatically without needing to be explicitly + + It uses autouse=True so it runs automatically without needing to be explicitly requested by tests. This ensures Next LS has a fully prepared project to work with. - + Uses generous timeouts (3-5 minutes) to accommodate slow CI environments. All output is logged for transparency and debugging. """ @@ -162,8 +167,8 @@ def setup_elixir_test_environment(): @pytest.fixture(scope="session") def elixir_test_repo_path(setup_elixir_test_environment): """Get the path to the prepared Elixir test repository. - + This fixture depends on setup_elixir_test_environment to ensure dependencies are installed and compilation has completed before returning the path. """ - return setup_elixir_test_environment \ No newline at end of file + return setup_elixir_test_environment diff --git a/test/solidlsp/elixir/test_elixir_basic.py b/test/solidlsp/elixir/test_elixir_basic.py index a95b41e5..10391406 100644 --- a/test/solidlsp/elixir/test_elixir_basic.py +++ b/test/solidlsp/elixir/test_elixir_basic.py @@ -6,19 +6,16 @@ like request_references using the test repository. """ import os + import pytest from solidlsp import SolidLanguageServer from solidlsp.ls_config import Language -from solidlsp.ls_utils import SymbolUtils from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON # These marks will be applied to all tests in this module -pytestmark = [ - pytest.mark.elixir, - pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}") -] +pytestmark = [pytest.mark.elixir, pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")] class TestElixirBasic: @@ -29,7 +26,7 @@ class TestElixirBasic: """Test finding references to a function definition.""" file_path = os.path.join("lib", "models.ex") symbols = language_server.request_document_symbols(file_path) - + # Find the User module's 'new' function user_new_symbol = None for symbol in symbols[0]: # Top level symbols @@ -39,22 +36,18 @@ class TestElixirBasic: user_new_symbol = child break break - + if not user_new_symbol or "selectionRange" not in user_new_symbol: pytest.skip("User.new function or its selectionRange not found") sel_start = user_new_symbol["selectionRange"]["start"] - references = language_server.request_references( - file_path, sel_start["line"], sel_start["character"] - ) + references = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) assert references is not None assert len(references) > 0 - + # Should find at least one reference (the definition itself) - found_definition = any( - ref["uri"].endswith("models.ex") for ref in references - ) + found_definition = any(ref["uri"].endswith("models.ex") for ref in references) assert found_definition, "Should find the function definition" @pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True) @@ -62,7 +55,7 @@ class TestElixirBasic: """Test finding references to create_user function.""" file_path = os.path.join("lib", "services.ex") symbols = language_server.request_document_symbols(file_path) - + # Find the UserService module's 'create_user' function create_user_symbol = None for symbol in symbols[0]: # Top level symbols @@ -72,14 +65,12 @@ class TestElixirBasic: create_user_symbol = child break break - + if not create_user_symbol or "selectionRange" not in create_user_symbol: pytest.skip("UserService.create_user function or its selectionRange not found") sel_start = create_user_symbol["selectionRange"]["start"] - references = language_server.request_references( - file_path, sel_start["line"], sel_start["character"] - ) + references = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) assert references is not None assert len(references) > 0 @@ -89,7 +80,7 @@ class TestElixirBasic: """Test finding symbols that reference a specific function.""" file_path = os.path.join("lib", "models.ex") symbols = language_server.request_document_symbols(file_path) - + # Find the User module's 'new' function user_new_symbol = None for symbol in symbols[0]: # Top level symbols @@ -99,14 +90,12 @@ class TestElixirBasic: user_new_symbol = child break break - + if not user_new_symbol or "selectionRange" not in user_new_symbol: pytest.skip("User.new function or its selectionRange not found") sel_start = user_new_symbol["selectionRange"]["start"] - referencing_symbols = language_server.request_referencing_symbols( - file_path, sel_start["line"], sel_start["character"] - ) + referencing_symbols = language_server.request_referencing_symbols(file_path, sel_start["line"], sel_start["character"]) assert referencing_symbols is not None @@ -120,4 +109,4 @@ class TestElixirBasic: # Test multiple symbol requests in succession for _ in range(3): symbols = language_server.request_document_symbols("lib/services.ex") - assert symbols is not None \ No newline at end of file + assert symbols is not None diff --git a/test/solidlsp/elixir/test_elixir_ignored_dirs.py b/test/solidlsp/elixir/test_elixir_ignored_dirs.py index 4e11f65f..95a6f5f2 100644 --- a/test/solidlsp/elixir/test_elixir_ignored_dirs.py +++ b/test/solidlsp/elixir/test_elixir_ignored_dirs.py @@ -10,10 +10,7 @@ from test.conftest import create_ls from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON # These marks will be applied to all tests in this module -pytestmark = [ - pytest.mark.elixir, - pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}") -] +pytestmark = [pytest.mark.elixir, pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")] @pytest.fixture(scope="module") @@ -34,7 +31,7 @@ def test_symbol_tree_ignores_dir(ls_with_ignored_dirs: SolidLanguageServer): root = ls_with_ignored_dirs.request_full_symbol_tree()[0] root_children = root["children"] children_names = {child["name"] for child in root_children} - + # Should have lib and test directories, but not scripts or ignored_dir expected_dirs = {"lib", "test"} assert expected_dirs.issubset(children_names), f"Expected {expected_dirs} to be in {children_names}" @@ -47,7 +44,7 @@ def test_find_references_ignores_dir(ls_with_ignored_dirs: SolidLanguageServer): """Tests that find_references ignores the configured directory.""" # Location of User struct, which is referenced in scripts and ignored_dir definition_file = "lib/models.ex" - + # Find the User struct definition symbols = ls_with_ignored_dirs.request_document_symbols(definition_file) user_symbol = None @@ -55,7 +52,7 @@ def test_find_references_ignores_dir(ls_with_ignored_dirs: SolidLanguageServer): user_symbol = next((s for s in symbol_group if "User" in s.get("name", "")), None) if user_symbol: break - + if not user_symbol or "selectionRange" not in user_symbol: pytest.skip("User symbol not found for reference testing") @@ -73,13 +70,13 @@ def test_refs_and_symbols_with_glob_patterns(repo_path: Path) -> None: ignored_paths = ["*cripts", "ignored_*"] # codespell:ignore cripts ls = create_ls(ignored_paths=ignored_paths, repo_path=str(repo_path), language=Language.ELIXIR) ls.start() - + try: # Same as in the above tests root = ls.request_full_symbol_tree()[0] root_children = root["children"] children_names = {child["name"] for child in root_children} - + # Should have lib and test directories, but not scripts or ignored_dir expected_dirs = {"lib", "test"} assert expected_dirs.issubset(children_names), f"Expected {expected_dirs} to be in {children_names}" @@ -88,7 +85,7 @@ def test_refs_and_symbols_with_glob_patterns(repo_path: Path) -> None: # Test that the refs and symbols with glob patterns are ignored definition_file = "lib/models.ex" - + # Find the User struct definition symbols = ls.request_document_symbols(definition_file) user_symbol = None @@ -96,11 +93,11 @@ def test_refs_and_symbols_with_glob_patterns(repo_path: Path) -> None: user_symbol = next((s for s in symbol_group if "User" in s.get("name", "")), None) if user_symbol: break - + if user_symbol and "selectionRange" in user_symbol: sel_start = user_symbol["selectionRange"]["start"] references = ls.request_references(definition_file, sel_start["line"], sel_start["character"]) - + # Assert that scripts and ignored_dir do not appear in references assert not any("scripts" in ref["relativePath"] for ref in references), "scripts should be ignored (glob)" assert not any("ignored_dir" in ref["relativePath"] for ref in references), "ignored_dir should be ignored (glob)" @@ -117,7 +114,7 @@ def test_default_ignored_directories(language_server: SolidLanguageServer): assert language_server.is_ignored_dirname(".elixir_ls"), ".elixir_ls should be ignored" assert language_server.is_ignored_dirname("cover"), "cover should be ignored" assert language_server.is_ignored_dirname("node_modules"), "node_modules should be ignored" - + # Test that important directories are not ignored assert not language_server.is_ignored_dirname("lib"), "lib should not be ignored" assert not language_server.is_ignored_dirname("test"), "test should not be ignored" @@ -129,17 +126,17 @@ def test_default_ignored_directories(language_server: SolidLanguageServer): def test_symbol_tree_excludes_build_dirs(language_server: SolidLanguageServer): """Test that symbol tree excludes build and dependency directories.""" symbol_tree = language_server.request_full_symbol_tree() - + if symbol_tree: root = symbol_tree[0] children_names = {child["name"] for child in root.get("children", [])} - + # Build and dependency directories should not appear ignored_dirs = {"_build", "deps", ".elixir_ls", "cover", "node_modules"} found_ignored = ignored_dirs.intersection(children_names) assert len(found_ignored) == 0, f"Found ignored directories in symbol tree: {found_ignored}" - + # Important directories should appear important_dirs = {"lib", "test"} found_important = important_dirs.intersection(children_names) - assert len(found_important) > 0, f"Expected to find important directories: {important_dirs}, got: {children_names}" \ No newline at end of file + assert len(found_important) > 0, f"Expected to find important directories: {important_dirs}, got: {children_names}" diff --git a/test/solidlsp/elixir/test_elixir_integration.py b/test/solidlsp/elixir/test_elixir_integration.py index 2377f0ee..25c86a8f 100644 --- a/test/solidlsp/elixir/test_elixir_integration.py +++ b/test/solidlsp/elixir/test_elixir_integration.py @@ -5,20 +5,18 @@ These tests verify that the language server works correctly with a real Elixir p and can perform advanced operations like cross-file symbol resolution. """ -import pytest import os from pathlib import Path +import pytest + from solidlsp import SolidLanguageServer from solidlsp.ls_config import Language from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON # These marks will be applied to all tests in this module -pytestmark = [ - pytest.mark.elixir, - pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}") -] +pytestmark = [pytest.mark.elixir, pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")] class TestElixirIntegration: @@ -33,7 +31,7 @@ class TestElixirIntegration: def test_elixir_repo_structure(self, elixir_test_repo_path): """Test that the Elixir test repository has the expected structure.""" repo_path = Path(elixir_test_repo_path) - + # Check that key files exist assert (repo_path / "mix.exs").exists(), "mix.exs should exist" assert (repo_path / "lib" / "test_repo.ex").exists(), "main module should exist" @@ -49,8 +47,7 @@ class TestElixirIntegration: """Test that symbols can be resolved across different files.""" # Test that User struct from models.ex can be found when referenced in services.ex services_file = os.path.join("lib", "services.ex") - models_file = os.path.join("lib", "models.ex") - + # Find where User is referenced in services.ex content = language_server.retrieve_full_file_content(services_file) lines = content.split("\n") @@ -59,13 +56,13 @@ class TestElixirIntegration: if "alias TestRepo.Models.{User" in line: user_reference_line = i break - + if user_reference_line is None: pytest.skip("Could not find User reference in services.ex") # Try to find the definition defining_symbol = language_server.request_defining_symbol(services_file, user_reference_line, 30) - + if defining_symbol and "location" in defining_symbol: # Should point to models.ex assert "models.ex" in defining_symbol["location"]["uri"] @@ -76,14 +73,14 @@ class TestElixirIntegration: # Search for all function definitions function_pattern = r"def\s+\w+\s*[\(\s]" function_matches = language_server.search_files_for_pattern(function_pattern) - + # Should find functions across multiple files if function_matches: files_with_functions = set() for match in function_matches: if match.source_file_path: files_with_functions.add(os.path.basename(match.source_file_path)) - + # Should find functions in multiple files expected_files = {"models.ex", "services.ex", "examples.ex", "utils.ex", "test_repo.ex"} found_files = expected_files.intersection(files_with_functions) @@ -92,7 +89,7 @@ class TestElixirIntegration: # Search for struct definitions struct_pattern = r"defstruct\s+\[" struct_matches = language_server.search_files_for_pattern(struct_pattern) - + if struct_matches: # Should find structs primarily in models.ex models_structs = [m for m in struct_matches if m.source_file_path and "models.ex" in m.source_file_path] @@ -103,7 +100,7 @@ class TestElixirIntegration: """Test that the language server understands Elixir module hierarchy.""" models_file = os.path.join("lib", "models.ex") symbols = language_server.request_document_symbols(models_file) - + if symbols: # Flatten symbol structure all_symbols = [] @@ -112,9 +109,9 @@ class TestElixirIntegration: all_symbols.extend(symbol_group) else: all_symbols.append(symbol_group) - + symbol_names = [s.get("name", "") for s in all_symbols] - + # Should understand nested module structure expected_modules = ["TestRepo.Models", "User", "Item", "Order"] found_modules = [name for name in expected_modules if any(name in symbol_name for symbol_name in symbol_names)] @@ -124,7 +121,7 @@ class TestElixirIntegration: """Test that the Elixir language recognizes the correct file extensions.""" language = Language.ELIXIR matcher = language.get_source_fn_matcher() - + # Test Elixir file extensions assert matcher.is_relevant_filename("lib/test_repo.ex") assert matcher.is_relevant_filename("test/test_repo_test.exs") @@ -132,7 +129,7 @@ class TestElixirIntegration: assert matcher.is_relevant_filename("mix.exs") assert matcher.is_relevant_filename("lib/models.ex") assert matcher.is_relevant_filename("lib/services.ex") - + # Test non-Elixir files assert not matcher.is_relevant_filename("README.md") assert not matcher.is_relevant_filename("lib/test_repo.py") @@ -142,12 +139,10 @@ class TestElixirIntegration: @pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True) def test_protocol_and_implementation_understanding(self, language_server: SolidLanguageServer): """Test that the language server understands Elixir protocols and implementations.""" - models_file = os.path.join("lib", "models.ex") - # Search for protocol definitions protocol_pattern = r"defprotocol\s+\w+" protocol_matches = language_server.search_files_for_pattern(protocol_pattern, paths_include_glob="**/models.ex") - + if protocol_matches: # Should find the Serializable protocol serializable_matches = [m for m in protocol_matches if "Serializable" in str(m)] @@ -156,7 +151,7 @@ class TestElixirIntegration: # Search for protocol implementations impl_pattern = r"defimpl\s+\w+" impl_matches = language_server.search_files_for_pattern(impl_pattern, paths_include_glob="**/models.ex") - + if impl_matches: # Should find multiple implementations - assert len(impl_matches) >= 3, f"Should find at least 3 protocol implementations, found {len(impl_matches)}" \ No newline at end of file + assert len(impl_matches) >= 3, f"Should find at least 3 protocol implementations, found {len(impl_matches)}" diff --git a/test/solidlsp/elixir/test_elixir_symbol_retrieval.py b/test/solidlsp/elixir/test_elixir_symbol_retrieval.py index ed36b9ea..a6c31b6d 100644 --- a/test/solidlsp/elixir/test_elixir_symbol_retrieval.py +++ b/test/solidlsp/elixir/test_elixir_symbol_retrieval.py @@ -8,6 +8,7 @@ These tests focus on the following methods: """ import os + import pytest from solidlsp import SolidLanguageServer @@ -17,10 +18,7 @@ from solidlsp.ls_types import SymbolKind from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON # These marks will be applied to all tests in this module -pytestmark = [ - pytest.mark.elixir, - pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}") -] +pytestmark = [pytest.mark.elixir, pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")] class TestElixirLanguageServerSymbols: @@ -31,7 +29,7 @@ class TestElixirLanguageServerSymbols: """Test request_containing_symbol for a function.""" # Test for a position inside the create_user function file_path = os.path.join("lib", "services.ex") - + # Find the create_user function in the file content = language_server.retrieve_full_file_content(file_path) lines = content.split("\n") @@ -40,7 +38,7 @@ class TestElixirLanguageServerSymbols: if "def create_user(" in line: create_user_line = i + 2 # Go inside the function body break - + if create_user_line is None: pytest.skip("Could not find create_user function") @@ -59,7 +57,7 @@ class TestElixirLanguageServerSymbols: """Test request_containing_symbol for a module.""" # Test for a position inside the UserService module but outside any function file_path = os.path.join("lib", "services.ex") - + # Find the UserService module definition content = language_server.retrieve_full_file_content(file_path) lines = content.split("\n") @@ -68,7 +66,7 @@ class TestElixirLanguageServerSymbols: if "defmodule UserService do" in line: user_service_line = i + 1 # Go inside the module break - + if user_service_line is None: pytest.skip("Could not find UserService module") @@ -84,7 +82,7 @@ class TestElixirLanguageServerSymbols: """Test request_containing_symbol with nested scopes.""" # Test for a position inside a function which is inside a module file_path = os.path.join("lib", "services.ex") - + # Find a function inside UserService content = language_server.retrieve_full_file_content(file_path) lines = content.split("\n") @@ -93,7 +91,7 @@ class TestElixirLanguageServerSymbols: if "def create_user(" in line: function_body_line = i + 3 # Go deeper into the function body break - + if function_body_line is None: pytest.skip("Could not find function body") @@ -116,29 +114,27 @@ class TestElixirLanguageServerSymbols: # This is acceptable behavior for module-level positions assert containing_symbol is None or containing_symbol == {} or "TestRepo.Services" in str(containing_symbol) - - @pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True) def test_request_referencing_symbols_struct(self, language_server: SolidLanguageServer) -> None: """Test request_referencing_symbols for a struct.""" # Test referencing symbols for User struct file_path = os.path.join("lib", "models.ex") - + symbols = language_server.request_document_symbols(file_path) user_symbol = None for symbol_group in symbols: user_symbol = next((s for s in symbol_group if "User" in s.get("name", "")), None) if user_symbol: break - + if not user_symbol or "selectionRange" not in user_symbol: pytest.skip("User symbol or its selectionRange not found") - + sel_start = user_symbol["selectionRange"]["start"] ref_symbols = [ ref.symbol for ref in language_server.request_referencing_symbols(file_path, sel_start["line"], sel_start["character"]) ] - + if ref_symbols: services_references = [ symbol @@ -175,7 +171,7 @@ class TestElixirLanguageServerSymbols: if "User.new(" in line: user_new_call_line = i break - + if user_new_call_line is None: pytest.skip("Could not find User.new call") @@ -199,7 +195,7 @@ class TestElixirLanguageServerSymbols: if "alias TestRepo.Models.{User" in line: user_usage_line = i break - + if user_usage_line is None: pytest.skip("Could not find User struct usage") @@ -210,9 +206,9 @@ class TestElixirLanguageServerSymbols: @pytest.mark.xfail( reason="Known intermittent bug in Next LS v0.23.3: Protocol.UndefinedError for :timeout atom. " - "Occurs in CI environments but may pass locally. " - "See https://github.com/elixir-tools/next-ls/issues/543", - strict=False + "Occurs in CI environments but may pass locally. " + "See https://github.com/elixir-tools/next-ls/issues/543", + strict=False, ) @pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True) def test_request_defining_symbol_none(self, language_server: SolidLanguageServer) -> None: @@ -229,7 +225,7 @@ class TestElixirLanguageServerSymbols: def test_symbol_methods_integration(self, language_server: SolidLanguageServer) -> None: """Test integration between different symbol methods.""" file_path = os.path.join("lib", "models.ex") - + # Find User struct definition content = language_server.retrieve_full_file_content(file_path) lines = content.split("\n") @@ -238,20 +234,20 @@ class TestElixirLanguageServerSymbols: if "defmodule User do" in line: user_struct_line = i break - + if user_struct_line is None: pytest.skip("Could not find User struct") # Test containing symbol containing = language_server.request_containing_symbol(file_path, user_struct_line + 5, 10) - + if containing: # Test that we can find references to this symbol if "location" in containing and "range" in containing["location"]: start_pos = containing["location"]["range"]["start"] - refs = [ref.symbol for ref in language_server.request_referencing_symbols( - file_path, start_pos["line"], start_pos["character"] - )] + refs = [ + ref.symbol for ref in language_server.request_referencing_symbols(file_path, start_pos["line"], start_pos["character"]) + ] # We should find some references or none (both are valid outcomes) assert isinstance(refs, list) @@ -259,21 +255,21 @@ class TestElixirLanguageServerSymbols: def test_symbol_tree_structure(self, language_server: SolidLanguageServer) -> None: """Test that symbol tree structure is correctly built.""" symbol_tree = language_server.request_full_symbol_tree() - + # Should get a tree structure assert len(symbol_tree) > 0 - + # Should have our test repository structure root = symbol_tree[0] assert "children" in root - + # Look for lib directory lib_dir = None for child in root["children"]: if child["name"] == "lib": lib_dir = child break - + if lib_dir: # Next LS returns module names instead of file names (e.g., 'services' instead of 'services.ex') file_names = [child["name"] for child in lib_dir.get("children", [])] @@ -285,14 +281,14 @@ class TestElixirLanguageServerSymbols: def test_request_dir_overview(self, language_server: SolidLanguageServer) -> None: """Test request_dir_overview functionality.""" lib_overview = language_server.request_dir_overview("lib") - + # Should get an overview of the lib directory assert lib_overview is not None # Next LS returns keys like 'lib/services.ex' instead of just 'lib' - overview_keys = list(lib_overview.keys()) if hasattr(lib_overview, 'keys') else [] - lib_files = [key for key in overview_keys if key.startswith('lib/')] + overview_keys = list(lib_overview.keys()) if hasattr(lib_overview, "keys") else [] + lib_files = [key for key in overview_keys if key.startswith("lib/")] assert len(lib_files) > 0, f"Expected to find lib/ files in overview keys: {overview_keys}" - + # Should contain information about our modules overview_text = str(lib_overview).lower() expected_terms = ["models", "services", "user", "item"] @@ -309,10 +305,10 @@ class TestElixirLanguageServerSymbols: # # # file_path = os.path.join("lib", "models.ex") # doc_overview = language_server.request_document_overview(file_path) - # + # # # Should get an overview of the models.ex file # assert doc_overview is not None - # + # # # Should contain information about our structs and functions # overview_text = str(doc_overview).lower() # expected_terms = ["user", "item", "order", "struct", "defmodule"] @@ -323,7 +319,7 @@ class TestElixirLanguageServerSymbols: def test_containing_symbol_of_module_attribute(self, language_server: SolidLanguageServer) -> None: """Test containing symbol for module attributes.""" file_path = os.path.join("lib", "models.ex") - + # Find a module attribute like @type or @doc content = language_server.retrieve_full_file_content(file_path) lines = content.split("\n") @@ -332,15 +328,15 @@ class TestElixirLanguageServerSymbols: if line.strip().startswith("@type") or line.strip().startswith("@doc"): attribute_line = i break - + if attribute_line is None: pytest.skip("Could not find module attribute") containing_symbol = language_server.request_containing_symbol(file_path, attribute_line, 5) - + if containing_symbol: # Should be contained within a module assert "name" in containing_symbol # The containing symbol should be a module expected_names = ["User", "Item", "Order", "TestRepo.Models"] - assert any(name in containing_symbol["name"] for name in expected_names) \ No newline at end of file + assert any(name in containing_symbol["name"] for name in expected_names) From ab24304ceb26a3a766e17043567642f0b955f987 Mon Sep 17 00:00:00 2001 From: Yen <5915590+antigenius0910@users.noreply.github.com> Date: Sun, 6 Jul 2025 10:15:54 -0500 Subject: [PATCH 15/25] feature/terraform language server support (#277) Adds terraform support --- .github/workflows/pytest.yml | 35 ++- .vscode/settings.json | 3 +- pyproject.toml | 1 + .../terraform_ls/initialize_params.json | 46 ++++ .../terraform_ls/runtime_dependencies.json | 37 +++ .../terraform_ls/terraform_ls.py | 249 ++++++++++++++++++ src/solidlsp/ls.py | 5 + src/solidlsp/ls_config.py | 3 + .../repos/terraform/test_repo/data.tf | 28 ++ .../repos/terraform/test_repo/main.tf | 126 +++++++++ .../repos/terraform/test_repo/outputs.tf | 46 ++++ .../repos/terraform/test_repo/variables.tf | 61 +++++ .../terraform/test_terraform_basic.py | 55 ++++ 13 files changed, 693 insertions(+), 2 deletions(-) create mode 100644 src/solidlsp/language_servers/terraform_ls/initialize_params.json create mode 100644 src/solidlsp/language_servers/terraform_ls/runtime_dependencies.json create mode 100644 src/solidlsp/language_servers/terraform_ls/terraform_ls.py create mode 100644 test/resources/repos/terraform/test_repo/data.tf create mode 100644 test/resources/repos/terraform/test_repo/main.tf create mode 100644 test/resources/repos/terraform/test_repo/outputs.tf create mode 100644 test/resources/repos/terraform/test_repo/variables.tf create mode 100644 test/solidlsp/terraform/test_terraform_basic.py diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index fe9b15ce..20d0567e 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -14,7 +14,6 @@ jobs: cpu: name: Tests on ${{ matrix.os }} runs-on: ${{ matrix.os }} - timeout-minutes: 15 if: "!contains(github.event.head_commit.message, 'ci skip')" strategy: fail-fast: false @@ -52,6 +51,40 @@ jobs: uses: DeLaGuardo/setup-clojure@13.4 with: cli: latest + - name: Install Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: "1.5.0" + terraform_wrapper: false + - name: Install terraform-ls (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + Write-Host "Installing terraform-ls manually for Windows" + $ver = '0.32.7' + $zip = "terraform-ls_${ver}_windows_amd64.zip" + Invoke-WebRequest -Uri "https://releases.hashicorp.com/terraform-ls/$ver/$zip" -OutFile $zip + + $dest = "$env:USERPROFILE\terraform-ls" + New-Item -ItemType Directory -Force -Path $dest | Out-Null + Expand-Archive $zip -DestinationPath $dest -Force + + Write-Host "terraform-ls installed to: $dest" + echo "$dest" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + - name: Install terraform-ls (Linux/macOS) + if: runner.os != 'Windows' + shell: bash + run: | + ver=0.32.7 + os=$(uname | tr '[:upper:]' '[:lower:]') + echo "Installing terraform-ls ${ver} for ${os}" + curl -sSL -o tfls.zip \ + "https://releases.hashicorp.com/terraform-ls/${ver}/terraform-ls_${ver}_${os}_amd64.zip" + mkdir -p "$HOME/bin" + unzip -q tfls.zip -d "$HOME/bin" + chmod +x "$HOME/bin/terraform-ls" + echo "$HOME/bin" >> "$GITHUB_PATH" + echo "terraform-ls installed to $HOME/bin" - name: Install uv shell: bash run: curl -LsSf https://astral.sh/uv/install.sh | sh diff --git a/.vscode/settings.json b/.vscode/settings.json index 56a7855f..6d3f3ba2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,4 +11,5 @@ "sensai", "vibing" ], -} \ No newline at end of file +} + diff --git a/pyproject.toml b/pyproject.toml index 610dbd09..ad37014b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -248,6 +248,7 @@ markers = [ "php: language server running for PHP", "csharp: language server running for C#", "elixir: language server running for Elixir", + "terraform: language server running for Terraform", "snapshot: snapshot tests for symbolic editing operations", ] diff --git a/src/solidlsp/language_servers/terraform_ls/initialize_params.json b/src/solidlsp/language_servers/terraform_ls/initialize_params.json new file mode 100644 index 00000000..40592e1a --- /dev/null +++ b/src/solidlsp/language_servers/terraform_ls/initialize_params.json @@ -0,0 +1,46 @@ +{ + "_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/solidlsp/language_servers/terraform_ls/runtime_dependencies.json b/src/solidlsp/language_servers/terraform_ls/runtime_dependencies.json new file mode 100644 index 00000000..873e7ecf --- /dev/null +++ b/src/solidlsp/language_servers/terraform_ls/runtime_dependencies.json @@ -0,0 +1,37 @@ +{ + "_description": "Used to download the runtime dependencies for running terraform-ls. Obtained from https://releases.hashicorp.com/terraform-ls/", + "runtimeDependencies": [ + { + "id": "TerraformLS", + "description": "terraform-ls for macOS (ARM64)", + "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_darwin_arm64.zip", + "platformId": "osx-arm64", + "archiveType": "zip", + "binaryName": "terraform-ls" + }, + { + "id": "TerraformLS", + "description": "terraform-ls for macOS (x64)", + "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_darwin_amd64.zip", + "platformId": "osx-x64", + "archiveType": "zip", + "binaryName": "terraform-ls" + }, + { + "id": "TerraformLS", + "description": "terraform-ls for Linux (x64)", + "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_linux_amd64.zip", + "platformId": "linux-x64", + "archiveType": "zip", + "binaryName": "terraform-ls" + }, + { + "id": "TerraformLS", + "description": "terraform-ls for Windows (x64)", + "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_windows_amd64.zip", + "platformId": "win-x64", + "archiveType": "zip", + "binaryName": "terraform-ls.exe" + } + ] +} diff --git a/src/solidlsp/language_servers/terraform_ls/terraform_ls.py b/src/solidlsp/language_servers/terraform_ls/terraform_ls.py new file mode 100644 index 00000000..10868c12 --- /dev/null +++ b/src/solidlsp/language_servers/terraform_ls/terraform_ls.py @@ -0,0 +1,249 @@ +import json +import logging +import os +import pathlib +import shutil +import stat +import subprocess +import threading + +from overrides import override + +from solidlsp.ls import SolidLanguageServer +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_utils import FileUtils, PlatformUtils +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo + + +class TerraformLS(SolidLanguageServer): + """ + Provides Terraform specific instantiation of the LanguageServer class using terraform-ls. + """ + + @override + def is_ignored_dirname(self, dirname: str) -> bool: + # For Terraform projects, we should ignore: + # - .terraform: Terraform working directory with providers and modules + # - terraform.tfstate.d: Terraform workspace state directories + # - .git: Version control + # - node_modules: If the project has JavaScript components + return super().is_ignored_dirname(dirname) or dirname in [".terraform", "terraform.tfstate.d", "node_modules"] + + @staticmethod + def _get_terraform_version(logger=None): + """Get the installed Terraform version or None if not found.""" + if logger: + logger.log("Starting terraform version detection...", logging.DEBUG) + else: + logging.debug("Starting terraform version detection...") + + # 1. Try to find terraform using shutil.which (standard Python way) + terraform_cmd = shutil.which("terraform") + if terraform_cmd: + if logger: + logger.log(f"Found terraform via shutil.which: {terraform_cmd}", logging.DEBUG) + else: + logging.debug(f"Found terraform via shutil.which: {terraform_cmd}") + else: + if logger: + logger.log("terraform not found via shutil.which", logging.DEBUG) + else: + logging.debug("terraform not found via shutil.which") + + # 2. Fallback to TERRAFORM_CLI_PATH (set by hashicorp/setup-terraform action) + if not terraform_cmd: + terraform_cli_path = os.environ.get('TERRAFORM_CLI_PATH') + if terraform_cli_path: + if logger: + logger.log(f"Trying TERRAFORM_CLI_PATH: {terraform_cli_path}", logging.DEBUG) + else: + logging.debug(f"Trying TERRAFORM_CLI_PATH: {terraform_cli_path}") + terraform_exe = os.path.join(terraform_cli_path, "terraform.exe") + if os.path.exists(terraform_exe): + terraform_cmd = terraform_exe + if logger: + logger.log(f"Found terraform via TERRAFORM_CLI_PATH: {terraform_cmd}", logging.DEBUG) + else: + logging.debug(f"Found terraform via TERRAFORM_CLI_PATH: {terraform_cmd}") + else: + if logger: + logger.log(f"terraform.exe not found at {terraform_exe}", logging.DEBUG) + else: + logging.debug(f"terraform.exe not found at {terraform_exe}") + else: + if logger: + logger.log("TERRAFORM_CLI_PATH not set", logging.DEBUG) + else: + logging.debug("TERRAFORM_CLI_PATH not set") + + # 3. Try to run the terraform command if found + if terraform_cmd: + try: + if logger: + logger.log(f"Attempting to run: {terraform_cmd} version (with 15s timeout)", logging.DEBUG) + else: + logging.debug(f"Attempting to run: {terraform_cmd} version (with 15s timeout)") + result = subprocess.run( + [terraform_cmd, "version"], + capture_output=True, + text=True, + check=False, + timeout=15 # CRITICAL: 15 second timeout to prevent hangs + ) + if result.returncode == 0: + if logger: + logger.log("terraform version command succeeded", logging.DEBUG) + else: + logging.debug("terraform version command succeeded") + return result.stdout.strip() + else: + if logger: + logger.log(f"terraform version command failed with return code {result.returncode}", logging.DEBUG) + logger.log(f"stderr: {result.stderr}", logging.DEBUG) + else: + logging.debug(f"terraform version command failed with return code {result.returncode}") + logging.debug(f"stderr: {result.stderr}") + except subprocess.TimeoutExpired: + if logger: + logger.log("terraform version command timed out after 15 seconds", logging.ERROR) + else: + logging.error("terraform version command timed out after 15 seconds") + except (FileNotFoundError, OSError) as e: + if logger: + logger.log(f"Failed to run terraform command: {e}", logging.DEBUG) + else: + logging.debug(f"Failed to run terraform command: {e}") + else: + if logger: + logger.log("No terraform executable found", logging.DEBUG) + else: + logging.debug("No terraform executable found") + + return None + + + def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: + """ + Setup runtime dependencies for terraform-ls. + Downloads and installs terraform-ls if not already present. + """ + # First check if Terraform is available + terraform_version = self._get_terraform_version(logger) + if not terraform_version: + raise RuntimeError( + "Terraform executable not found or failed to execute. " + "Please ensure Terraform is installed and accessible in your system's PATH.\n" + "If it's installed, check for permission issues or corrupted installation.\n" + "Download from https://www.terraform.io/downloads" + ) + + platform_id = PlatformUtils.get_platform_id() + + 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["runtimeDependencies"] + runtime_dependencies = [dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value] + assert len(runtime_dependencies) == 1, f"Expected exactly one runtime dependency for platform {platform_id.value}, found {len(runtime_dependencies)}" + dependency = runtime_dependencies[0] + + terraform_ls_dir = os.path.join(os.path.dirname(__file__), "static", "TerraformLS") + terraform_ls_executable_path = os.path.join(terraform_ls_dir, dependency["binaryName"]) + + if not os.path.exists(terraform_ls_dir): + os.makedirs(terraform_ls_dir) + + if not os.path.exists(terraform_ls_executable_path): + logger.log(f"Downloading terraform-ls from {dependency['url']}", logging.INFO) + FileUtils.download_and_extract_archive(logger, dependency["url"], terraform_ls_dir, dependency["archiveType"]) + + assert os.path.exists(terraform_ls_executable_path), f"terraform-ls executable not found at {terraform_ls_executable_path}" + + # Make the executable file executable on Unix-like systems + if platform_id.value != "win-x64": + os.chmod(terraform_ls_executable_path, stat.S_IEXEC | stat.S_IREAD) + + return terraform_ls_executable_path + + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): + """ + Creates a TerraformLS instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. + """ + terraform_ls_executable_path = self.setup_runtime_dependencies(logger, config) + + super().__init__( + config, + logger, + repository_root_path, + ProcessLaunchInfo(cmd=f"{terraform_ls_executable_path} serve", cwd=repository_root_path), + "terraform", + ) + self.server_ready = threading.Event() + self.request_id = 0 + + + def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + """ + Returns the initialize params for the Terraform Language Server. + """ + with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), 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 _start_server(self): + """Start terraform-ls server process""" + + def register_capability_handler(params): + return + + def window_log_message(msg): + self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) + + 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) + + self.logger.log("Starting terraform-ls server process", logging.INFO) + 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 = 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() + + # terraform-ls server is typically ready immediately after initialization + self.server_ready.set() + self.server_ready.wait() diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index a6b8be81..468f11ca 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -187,6 +187,11 @@ class SolidLanguageServer(ABC): ls = ElixirTools(config, logger, repository_root_path) + elif config.code_language == Language.TERRAFORM: + from solidlsp.language_servers.terraform_ls.terraform_ls import TerraformLS + + ls = TerraformLS(config, logger, repository_root_path) + else: logger.log(f"Language {config.code_language} is not supported", logging.ERROR) raise LanguageServerException(f"Language {config.code_language} is not supported") diff --git a/src/solidlsp/ls_config.py b/src/solidlsp/ls_config.py index 3a279d8c..e5df9011 100644 --- a/src/solidlsp/ls_config.py +++ b/src/solidlsp/ls_config.py @@ -39,6 +39,7 @@ class Language(str, Enum): PHP = "php" CLOJURE = "clojure" ELIXIR = "elixir" + TERRAFORM = "terraform" def __str__(self) -> str: return self.value @@ -77,6 +78,8 @@ class Language(str, Enum): return FilenameMatcher("*.clj", "*.cljs", "*.cljc", "*.edn") # codespell:ignore edn case self.ELIXIR: return FilenameMatcher("*.ex", "*.exs") + case self.TERRAFORM: + return FilenameMatcher("*.tf", "*.tfvars", "*.tfstate") case _: raise ValueError(f"Unhandled language: {self}") diff --git a/test/resources/repos/terraform/test_repo/data.tf b/test/resources/repos/terraform/test_repo/data.tf new file mode 100644 index 00000000..0dd2d93f --- /dev/null +++ b/test/resources/repos/terraform/test_repo/data.tf @@ -0,0 +1,28 @@ +# Data sources for the Terraform configuration + +# Get the latest Ubuntu AMI +data "aws_ami" "ubuntu" { + most_recent = true + owners = ["099720109477"] # Canonical + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } +} + +# Get available availability zones +data "aws_availability_zones" "available" { + state = "available" +} + +# Get current AWS caller identity +data "aws_caller_identity" "current" {} + +# Get current AWS region +data "aws_region" "current" {} diff --git a/test/resources/repos/terraform/test_repo/main.tf b/test/resources/repos/terraform/test_repo/main.tf new file mode 100644 index 00000000..d186c206 --- /dev/null +++ b/test/resources/repos/terraform/test_repo/main.tf @@ -0,0 +1,126 @@ +# Main Terraform configuration +terraform { + required_version = ">= 1.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +provider "aws" { + region = var.aws_region +} + +# EC2 Instance +resource "aws_instance" "web_server" { + ami = data.aws_ami.ubuntu.id + instance_type = var.instance_type + + vpc_security_group_ids = [aws_security_group.web_sg.id] + subnet_id = aws_subnet.public.id + + user_data = <<-EOF + #!/bin/bash + apt-get update + apt-get install -y nginx + systemctl start nginx + systemctl enable nginx + EOF + + tags = { + Name = "${var.project_name}-web-server" + Environment = var.environment + Project = var.project_name + } +} + +# S3 Bucket +resource "aws_s3_bucket" "app_bucket" { + bucket = "${var.project_name}-${var.environment}-bucket" + + tags = { + Name = "${var.project_name}-bucket" + Environment = var.environment + Project = var.project_name + } +} + +resource "aws_s3_bucket_versioning" "app_bucket_versioning" { + bucket = aws_s3_bucket.app_bucket.id + versioning_configuration { + status = "Enabled" + } +} + +# VPC +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + enable_dns_support = true + + tags = { + Name = "${var.project_name}-vpc" + Environment = var.environment + Project = var.project_name + } +} + +# Internet Gateway +resource "aws_internet_gateway" "main" { + vpc_id = aws_vpc.main.id + + tags = { + Name = "${var.project_name}-igw" + Environment = var.environment + Project = var.project_name + } +} + +# Public Subnet +resource "aws_subnet" "public" { + vpc_id = aws_vpc.main.id + cidr_block = "10.0.1.0/24" + availability_zone = data.aws_availability_zones.available.names[0] + map_public_ip_on_launch = true + + tags = { + Name = "${var.project_name}-public-subnet" + Environment = var.environment + Project = var.project_name + } +} + +# Security Group +resource "aws_security_group" "web_sg" { + name_prefix = "${var.project_name}-web-" + vpc_id = aws_vpc.main.id + + ingress { + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + ingress { + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "${var.project_name}-web-sg" + Environment = var.environment + Project = var.project_name + } +} diff --git a/test/resources/repos/terraform/test_repo/outputs.tf b/test/resources/repos/terraform/test_repo/outputs.tf new file mode 100644 index 00000000..856ed6dd --- /dev/null +++ b/test/resources/repos/terraform/test_repo/outputs.tf @@ -0,0 +1,46 @@ +# Output values for the Terraform configuration + +output "instance_id" { + description = "ID of the EC2 instance" + value = aws_instance.web_server.id +} + +output "instance_public_ip" { + description = "Public IP address of the EC2 instance" + value = aws_instance.web_server.public_ip +} + +output "instance_public_dns" { + description = "Public DNS name of the EC2 instance" + value = aws_instance.web_server.public_dns +} + +output "s3_bucket_name" { + description = "Name of the S3 bucket" + value = aws_s3_bucket.app_bucket.bucket +} + +output "s3_bucket_arn" { + description = "ARN of the S3 bucket" + value = aws_s3_bucket.app_bucket.arn +} + +output "vpc_id" { + description = "ID of the VPC" + value = aws_vpc.main.id +} + +output "subnet_id" { + description = "ID of the public subnet" + value = aws_subnet.public.id +} + +output "security_group_id" { + description = "ID of the security group" + value = aws_security_group.web_sg.id +} + +output "application_url" { + description = "URL to access the application" + value = "http://${aws_instance.web_server.public_dns}" +} diff --git a/test/resources/repos/terraform/test_repo/variables.tf b/test/resources/repos/terraform/test_repo/variables.tf new file mode 100644 index 00000000..92437bd9 --- /dev/null +++ b/test/resources/repos/terraform/test_repo/variables.tf @@ -0,0 +1,61 @@ +# Input variables for the Terraform configuration + +variable "aws_region" { + description = "AWS region for resources" + type = string + default = "us-west-2" +} + +variable "instance_type" { + description = "EC2 instance type" + type = string + default = "t3.micro" + + validation { + condition = contains([ + "t3.micro", "t3.small", "t3.medium", + "t2.micro", "t2.small", "t2.medium" + ], var.instance_type) + error_message = "Instance type must be a valid t2 or t3 instance type." + } +} + +variable "environment" { + description = "Environment name (dev, staging, prod)" + type = string + default = "dev" + + validation { + condition = contains(["dev", "staging", "prod"], var.environment) + error_message = "Environment must be dev, staging, or prod." + } +} + +variable "project_name" { + description = "Name of the project" + type = string + default = "terraform-test" + + validation { + condition = can(regex("^[a-z0-9-]+$", var.project_name)) + error_message = "Project name must contain only lowercase letters, numbers, and hyphens." + } +} + +variable "enable_monitoring" { + description = "Enable CloudWatch monitoring" + type = bool + default = false +} + +variable "allowed_cidr_blocks" { + description = "List of CIDR blocks allowed to access the application" + type = list(string) + default = ["0.0.0.0/0"] +} + +variable "tags" { + description = "Additional tags to apply to resources" + type = map(string) + default = {} +} diff --git a/test/solidlsp/terraform/test_terraform_basic.py b/test/solidlsp/terraform/test_terraform_basic.py new file mode 100644 index 00000000..eb08aab9 --- /dev/null +++ b/test/solidlsp/terraform/test_terraform_basic.py @@ -0,0 +1,55 @@ +""" +Basic integration tests for the Terraform language server functionality. + +These tests validate the functionality of the language server APIs +like request_references using the test repository. +""" + +import os + +import pytest + +from solidlsp import SolidLanguageServer +from solidlsp.ls_config import Language + + +@pytest.mark.terraform +class TestLanguageServerBasics: + """Test basic functionality of the Terraform language server.""" + + @pytest.mark.parametrize("language_server", [Language.TERRAFORM], indirect=True) + def test_basic_definition(self, language_server: SolidLanguageServer) -> None: + """Test basic definition lookup functionality.""" + # Simple test to verify the language server is working + file_path = "main.tf" + # Just try to get document symbols - this should work without hanging + symbols = language_server.request_document_symbols(file_path) + assert len(symbols) > 0, "Should find at least some symbols in main.tf" + + @pytest.mark.parametrize("language_server", [Language.TERRAFORM], indirect=True) + def test_request_references_aws_instance(self, language_server: SolidLanguageServer) -> None: + """Test request_references on an aws_instance resource.""" + # Get references to an aws_instance resource in main.tf + file_path = "main.tf" + # Find aws_instance resources + symbols = language_server.request_document_symbols(file_path) + aws_instance_symbol = next((s for s in symbols[0] if s.get("name") == 'resource "aws_instance" "web_server"'), None) + if not aws_instance_symbol or "selectionRange" not in aws_instance_symbol: + raise AssertionError("aws_instance symbol or its selectionRange not found") + sel_start = aws_instance_symbol["selectionRange"]["start"] + references = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) + assert len(references) >= 1, "aws_instance should be referenced at least once" + + @pytest.mark.parametrize("language_server", [Language.TERRAFORM], indirect=True) + def test_request_references_variable(self, language_server: SolidLanguageServer) -> None: + """Test request_references on a variable.""" + # Get references to a variable in variables.tf + file_path = "variables.tf" + # Find variable definitions + symbols = language_server.request_document_symbols(file_path) + var_symbol = next((s for s in symbols[0] if s.get("name") == 'variable "instance_type"'), None) + if not var_symbol or "selectionRange" not in var_symbol: + raise AssertionError("variable symbol or its selectionRange not found") + sel_start = var_symbol["selectionRange"]["start"] + references = language_server.request_references(file_path, sel_start["line"], sel_start["character"]) + assert len(references) >= 1, "variable should be referenced at least once" From 65975f4c4055cd8a1358511d4c182649fbf46d79 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sun, 6 Jul 2025 17:27:48 +0200 Subject: [PATCH 16/25] Minor simplification of tf LS, formatting Also fixed call to terraform.exe on non-windows platforms --- src/solidlsp/.gitignore | 1 + .../terraform_ls/terraform_ls.py | 149 ++++++------------ .../terraform/test_terraform_basic.py | 2 - 3 files changed, 46 insertions(+), 106 deletions(-) create mode 100644 src/solidlsp/.gitignore diff --git a/src/solidlsp/.gitignore b/src/solidlsp/.gitignore new file mode 100644 index 00000000..c6d33567 --- /dev/null +++ b/src/solidlsp/.gitignore @@ -0,0 +1 @@ +language_servers/static \ No newline at end of file diff --git a/src/solidlsp/language_servers/terraform_ls/terraform_ls.py b/src/solidlsp/language_servers/terraform_ls/terraform_ls.py index 10868c12..3eeea13b 100644 --- a/src/solidlsp/language_servers/terraform_ls/terraform_ls.py +++ b/src/solidlsp/language_servers/terraform_ls/terraform_ls.py @@ -24,113 +24,53 @@ class TerraformLS(SolidLanguageServer): @override def is_ignored_dirname(self, dirname: str) -> bool: - # For Terraform projects, we should ignore: - # - .terraform: Terraform working directory with providers and modules - # - terraform.tfstate.d: Terraform workspace state directories - # - .git: Version control - # - node_modules: If the project has JavaScript components - return super().is_ignored_dirname(dirname) or dirname in [".terraform", "terraform.tfstate.d", "node_modules"] + return super().is_ignored_dirname(dirname) or dirname in [".terraform", "terraform.tfstate.d"] - @staticmethod - def _get_terraform_version(logger=None): - """Get the installed Terraform version or None if not found.""" - if logger: - logger.log("Starting terraform version detection...", logging.DEBUG) - else: - logging.debug("Starting terraform version detection...") - - # 1. Try to find terraform using shutil.which (standard Python way) + def _get_terraform_version(self) -> str: + self.logger.log("Starting terraform version detection...", logging.DEBUG) + + # 1. Try to find terraform using shutil.which terraform_cmd = shutil.which("terraform") - if terraform_cmd: - if logger: - logger.log(f"Found terraform via shutil.which: {terraform_cmd}", logging.DEBUG) - else: - logging.debug(f"Found terraform via shutil.which: {terraform_cmd}") - else: - if logger: - logger.log("terraform not found via shutil.which", logging.DEBUG) - else: - logging.debug("terraform not found via shutil.which") - + if terraform_cmd is not None: + self.logger.log(f"Found terraform via shutil.which: {terraform_cmd}", logging.DEBUG) + # 2. Fallback to TERRAFORM_CLI_PATH (set by hashicorp/setup-terraform action) if not terraform_cmd: - terraform_cli_path = os.environ.get('TERRAFORM_CLI_PATH') + terraform_cli_path = os.environ.get("TERRAFORM_CLI_PATH") if terraform_cli_path: - if logger: - logger.log(f"Trying TERRAFORM_CLI_PATH: {terraform_cli_path}", logging.DEBUG) + self.logger.log(f"Trying TERRAFORM_CLI_PATH: {terraform_cli_path}", logging.DEBUG) + if os.name == "nt": + terraform_binary = os.path.join(terraform_cli_path, "terraform.exe") else: - logging.debug(f"Trying TERRAFORM_CLI_PATH: {terraform_cli_path}") - terraform_exe = os.path.join(terraform_cli_path, "terraform.exe") - if os.path.exists(terraform_exe): - terraform_cmd = terraform_exe - if logger: - logger.log(f"Found terraform via TERRAFORM_CLI_PATH: {terraform_cmd}", logging.DEBUG) - else: - logging.debug(f"Found terraform via TERRAFORM_CLI_PATH: {terraform_cmd}") - else: - if logger: - logger.log(f"terraform.exe not found at {terraform_exe}", logging.DEBUG) - else: - logging.debug(f"terraform.exe not found at {terraform_exe}") - else: - if logger: - logger.log("TERRAFORM_CLI_PATH not set", logging.DEBUG) - else: - logging.debug("TERRAFORM_CLI_PATH not set") - - # 3. Try to run the terraform command if found - if terraform_cmd: - try: - if logger: - logger.log(f"Attempting to run: {terraform_cmd} version (with 15s timeout)", logging.DEBUG) - else: - logging.debug(f"Attempting to run: {terraform_cmd} version (with 15s timeout)") - result = subprocess.run( - [terraform_cmd, "version"], - capture_output=True, - text=True, - check=False, - timeout=15 # CRITICAL: 15 second timeout to prevent hangs - ) - if result.returncode == 0: - if logger: - logger.log("terraform version command succeeded", logging.DEBUG) - else: - logging.debug("terraform version command succeeded") - return result.stdout.strip() - else: - if logger: - logger.log(f"terraform version command failed with return code {result.returncode}", logging.DEBUG) - logger.log(f"stderr: {result.stderr}", logging.DEBUG) - else: - logging.debug(f"terraform version command failed with return code {result.returncode}") - logging.debug(f"stderr: {result.stderr}") - except subprocess.TimeoutExpired: - if logger: - logger.log("terraform version command timed out after 15 seconds", logging.ERROR) - else: - logging.error("terraform version command timed out after 15 seconds") - except (FileNotFoundError, OSError) as e: - if logger: - logger.log(f"Failed to run terraform command: {e}", logging.DEBUG) - else: - logging.debug(f"Failed to run terraform command: {e}") + terraform_binary = os.path.join(terraform_cli_path, "terraform") + if os.path.exists(terraform_binary): + terraform_cmd = terraform_binary + self.logger.log(f"Found terraform via TERRAFORM_CLI_PATH: {terraform_cmd}", logging.DEBUG) + + if not terraform_cmd: + raise RuntimeError("Terraform executable not found. Please ensure Terraform is installed and accessible in your system's PATH.") + + self.logger.log(f"Attempting to run: {terraform_cmd} version (with 15s timeout)", logging.DEBUG) + result = subprocess.run( + [terraform_cmd, "version"], + capture_output=True, + text=True, + check=False, + timeout=15, # CRITICAL: 15 second timeout to prevent hangs + ) + if result.returncode == 0: + self.logger.log("terraform version command succeeded", logging.DEBUG) + return result.stdout.strip() else: - if logger: - logger.log("No terraform executable found", logging.DEBUG) - else: - logging.debug("No terraform executable found") - - return None + raise RuntimeError(f"terraform version command failed with return code {result.returncode}: {result.stderr}") - - def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: + def setup_runtime_dependencies(self) -> str: """ Setup runtime dependencies for terraform-ls. Downloads and installs terraform-ls if not already present. """ # First check if Terraform is available - terraform_version = self._get_terraform_version(logger) + terraform_version = self._get_terraform_version() if not terraform_version: raise RuntimeError( "Terraform executable not found or failed to execute. " @@ -147,21 +87,23 @@ class TerraformLS(SolidLanguageServer): runtime_dependencies = d["runtimeDependencies"] runtime_dependencies = [dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value] - assert len(runtime_dependencies) == 1, f"Expected exactly one runtime dependency for platform {platform_id.value}, found {len(runtime_dependencies)}" + assert ( + len(runtime_dependencies) == 1 + ), f"Expected exactly one runtime dependency for platform {platform_id.value}, found {len(runtime_dependencies)}" dependency = runtime_dependencies[0] terraform_ls_dir = os.path.join(os.path.dirname(__file__), "static", "TerraformLS") terraform_ls_executable_path = os.path.join(terraform_ls_dir, dependency["binaryName"]) - + if not os.path.exists(terraform_ls_dir): os.makedirs(terraform_ls_dir) - + if not os.path.exists(terraform_ls_executable_path): - logger.log(f"Downloading terraform-ls from {dependency['url']}", logging.INFO) - FileUtils.download_and_extract_archive(logger, dependency["url"], terraform_ls_dir, dependency["archiveType"]) - + self.logger.log(f"Downloading terraform-ls from {dependency['url']}", logging.INFO) + FileUtils.download_and_extract_archive(self.logger, dependency["url"], terraform_ls_dir, dependency["archiveType"]) + assert os.path.exists(terraform_ls_executable_path), f"terraform-ls executable not found at {terraform_ls_executable_path}" - + # Make the executable file executable on Unix-like systems if platform_id.value != "win-x64": os.chmod(terraform_ls_executable_path, stat.S_IEXEC | stat.S_IREAD) @@ -172,8 +114,8 @@ class TerraformLS(SolidLanguageServer): """ Creates a TerraformLS instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ - terraform_ls_executable_path = self.setup_runtime_dependencies(logger, config) - + terraform_ls_executable_path = self.setup_runtime_dependencies() + super().__init__( config, logger, @@ -184,7 +126,6 @@ class TerraformLS(SolidLanguageServer): self.server_ready = threading.Event() self.request_id = 0 - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the Terraform Language Server. diff --git a/test/solidlsp/terraform/test_terraform_basic.py b/test/solidlsp/terraform/test_terraform_basic.py index eb08aab9..ff20ac66 100644 --- a/test/solidlsp/terraform/test_terraform_basic.py +++ b/test/solidlsp/terraform/test_terraform_basic.py @@ -5,8 +5,6 @@ These tests validate the functionality of the language server APIs like request_references using the test repository. """ -import os - import pytest from solidlsp import SolidLanguageServer From 790f17711b7f3420527d3749a7db1191f33956b5 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sun, 6 Jul 2025 17:35:57 +0200 Subject: [PATCH 17/25] Inlined TF params and runtime deps --- .../{terraform_ls => }/terraform_ls.py | 91 +++++++++++++------ .../terraform_ls/initialize_params.json | 46 ---------- .../terraform_ls/runtime_dependencies.json | 37 -------- 3 files changed, 62 insertions(+), 112 deletions(-) rename src/solidlsp/language_servers/{terraform_ls => }/terraform_ls.py (71%) delete mode 100644 src/solidlsp/language_servers/terraform_ls/initialize_params.json delete mode 100644 src/solidlsp/language_servers/terraform_ls/runtime_dependencies.json diff --git a/src/solidlsp/language_servers/terraform_ls/terraform_ls.py b/src/solidlsp/language_servers/terraform_ls.py similarity index 71% rename from src/solidlsp/language_servers/terraform_ls/terraform_ls.py rename to src/solidlsp/language_servers/terraform_ls.py index 3eeea13b..3cc916b7 100644 --- a/src/solidlsp/language_servers/terraform_ls/terraform_ls.py +++ b/src/solidlsp/language_servers/terraform_ls.py @@ -1,7 +1,5 @@ -import json import logging import os -import pathlib import shutil import stat import subprocess @@ -12,7 +10,7 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger -from solidlsp.ls_utils import FileUtils, PlatformUtils +from solidlsp.ls_utils import FileUtils, PathUtils, PlatformUtils from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo @@ -80,19 +78,47 @@ class TerraformLS(SolidLanguageServer): ) platform_id = PlatformUtils.get_platform_id() - - 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["runtimeDependencies"] + runtime_dependencies = [ + { + "id": "TerraformLS", + "description": "terraform-ls for macOS (ARM64)", + "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_darwin_arm64.zip", + "platformId": "osx-arm64", + "archiveType": "zip", + "binaryName": "terraform-ls", + }, + { + "id": "TerraformLS", + "description": "terraform-ls for macOS (x64)", + "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_darwin_amd64.zip", + "platformId": "osx-x64", + "archiveType": "zip", + "binaryName": "terraform-ls", + }, + { + "id": "TerraformLS", + "description": "terraform-ls for Linux (x64)", + "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_linux_amd64.zip", + "platformId": "linux-x64", + "archiveType": "zip", + "binaryName": "terraform-ls", + }, + { + "id": "TerraformLS", + "description": "terraform-ls for Windows (x64)", + "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_windows_amd64.zip", + "platformId": "win-x64", + "archiveType": "zip", + "binaryName": "terraform-ls.exe", + }, + ] runtime_dependencies = [dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value] assert ( len(runtime_dependencies) == 1 ), f"Expected exactly one runtime dependency for platform {platform_id.value}, found {len(runtime_dependencies)}" dependency = runtime_dependencies[0] - terraform_ls_dir = os.path.join(os.path.dirname(__file__), "static", "TerraformLS") + terraform_ls_dir = os.path.join(os.path.dirname(__file__), "static", "terraform-ls") terraform_ls_executable_path = os.path.join(terraform_ls_dir, dependency["binaryName"]) if not os.path.exists(terraform_ls_dir): @@ -130,25 +156,32 @@ class TerraformLS(SolidLanguageServer): """ Returns the initialize params for the Terraform Language Server. """ - with open(os.path.join(os.path.dirname(__file__), "initialize_params.json"), 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 + root_uri = PathUtils.path_to_uri(repository_absolute_path) + return { + "processId": os.getpid(), + "locale": "en", + "rootPath": repository_absolute_path, + "rootUri": root_uri, + "capabilities": { + "textDocument": { + "synchronization": {"didSave": True, "dynamicRegistration": True}, + "completion": {"dynamicRegistration": True, "completionItem": {"snippetSupport": True}}, + "definition": {"dynamicRegistration": True}, + "documentSymbol": { + "dynamicRegistration": True, + "hierarchicalDocumentSymbolSupport": True, + "symbolKind": {"valueSet": list(range(1, 27))}, + }, + }, + "workspace": {"workspaceFolders": True, "didChangeConfiguration": {"dynamicRegistration": True}}, + }, + "workspaceFolders": [ + { + "name": os.path.basename(repository_absolute_path), + "uri": root_uri, + } + ], + } def _start_server(self): """Start terraform-ls server process""" diff --git a/src/solidlsp/language_servers/terraform_ls/initialize_params.json b/src/solidlsp/language_servers/terraform_ls/initialize_params.json deleted file mode 100644 index 40592e1a..00000000 --- a/src/solidlsp/language_servers/terraform_ls/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/solidlsp/language_servers/terraform_ls/runtime_dependencies.json b/src/solidlsp/language_servers/terraform_ls/runtime_dependencies.json deleted file mode 100644 index 873e7ecf..00000000 --- a/src/solidlsp/language_servers/terraform_ls/runtime_dependencies.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "_description": "Used to download the runtime dependencies for running terraform-ls. Obtained from https://releases.hashicorp.com/terraform-ls/", - "runtimeDependencies": [ - { - "id": "TerraformLS", - "description": "terraform-ls for macOS (ARM64)", - "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_darwin_arm64.zip", - "platformId": "osx-arm64", - "archiveType": "zip", - "binaryName": "terraform-ls" - }, - { - "id": "TerraformLS", - "description": "terraform-ls for macOS (x64)", - "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_darwin_amd64.zip", - "platformId": "osx-x64", - "archiveType": "zip", - "binaryName": "terraform-ls" - }, - { - "id": "TerraformLS", - "description": "terraform-ls for Linux (x64)", - "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_linux_amd64.zip", - "platformId": "linux-x64", - "archiveType": "zip", - "binaryName": "terraform-ls" - }, - { - "id": "TerraformLS", - "description": "terraform-ls for Windows (x64)", - "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_windows_amd64.zip", - "platformId": "win-x64", - "archiveType": "zip", - "binaryName": "terraform-ls.exe" - } - ] -} From caa25759e42d1325d56cdadb81071c8e753bb60d Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sun, 6 Jul 2025 17:48:19 +0200 Subject: [PATCH 18/25] TF LS: fixed method order execution Previously a method relying on self.logger being set was called before it was set --- .../terraform_ls/terraform_ls.py | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/solidlsp/language_servers/terraform_ls/terraform_ls.py b/src/solidlsp/language_servers/terraform_ls/terraform_ls.py index 3eeea13b..b77de536 100644 --- a/src/solidlsp/language_servers/terraform_ls/terraform_ls.py +++ b/src/solidlsp/language_servers/terraform_ls/terraform_ls.py @@ -26,31 +26,32 @@ class TerraformLS(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in [".terraform", "terraform.tfstate.d"] - def _get_terraform_version(self) -> str: - self.logger.log("Starting terraform version detection...", logging.DEBUG) + @staticmethod + def _get_terraform_version(logger: LanguageServerLogger) -> str: + logger.log("Starting terraform version detection...", logging.DEBUG) # 1. Try to find terraform using shutil.which terraform_cmd = shutil.which("terraform") if terraform_cmd is not None: - self.logger.log(f"Found terraform via shutil.which: {terraform_cmd}", logging.DEBUG) + logger.log(f"Found terraform via shutil.which: {terraform_cmd}", logging.DEBUG) # 2. Fallback to TERRAFORM_CLI_PATH (set by hashicorp/setup-terraform action) if not terraform_cmd: terraform_cli_path = os.environ.get("TERRAFORM_CLI_PATH") if terraform_cli_path: - self.logger.log(f"Trying TERRAFORM_CLI_PATH: {terraform_cli_path}", logging.DEBUG) + logger.log(f"Trying TERRAFORM_CLI_PATH: {terraform_cli_path}", logging.DEBUG) if os.name == "nt": terraform_binary = os.path.join(terraform_cli_path, "terraform.exe") else: terraform_binary = os.path.join(terraform_cli_path, "terraform") if os.path.exists(terraform_binary): terraform_cmd = terraform_binary - self.logger.log(f"Found terraform via TERRAFORM_CLI_PATH: {terraform_cmd}", logging.DEBUG) + logger.log(f"Found terraform via TERRAFORM_CLI_PATH: {terraform_cmd}", logging.DEBUG) if not terraform_cmd: raise RuntimeError("Terraform executable not found. Please ensure Terraform is installed and accessible in your system's PATH.") - self.logger.log(f"Attempting to run: {terraform_cmd} version (with 15s timeout)", logging.DEBUG) + logger.log(f"Attempting to run: {terraform_cmd} version (with 15s timeout)", logging.DEBUG) result = subprocess.run( [terraform_cmd, "version"], capture_output=True, @@ -59,18 +60,20 @@ class TerraformLS(SolidLanguageServer): timeout=15, # CRITICAL: 15 second timeout to prevent hangs ) if result.returncode == 0: - self.logger.log("terraform version command succeeded", logging.DEBUG) + logger.log("terraform version command succeeded", logging.DEBUG) return result.stdout.strip() else: raise RuntimeError(f"terraform version command failed with return code {result.returncode}: {result.stderr}") - def setup_runtime_dependencies(self) -> str: + # Note: needs to remain static because it's called before init is complete + @staticmethod + def _setup_runtime_dependencies(logger: LanguageServerLogger) -> str: """ Setup runtime dependencies for terraform-ls. Downloads and installs terraform-ls if not already present. """ # First check if Terraform is available - terraform_version = self._get_terraform_version() + terraform_version = TerraformLS._get_terraform_version(logger) if not terraform_version: raise RuntimeError( "Terraform executable not found or failed to execute. " @@ -99,8 +102,8 @@ class TerraformLS(SolidLanguageServer): os.makedirs(terraform_ls_dir) if not os.path.exists(terraform_ls_executable_path): - self.logger.log(f"Downloading terraform-ls from {dependency['url']}", logging.INFO) - FileUtils.download_and_extract_archive(self.logger, dependency["url"], terraform_ls_dir, dependency["archiveType"]) + logger.info(f"Downloading terraform-ls from {dependency['url']}", logging.INFO) + FileUtils.download_and_extract_archive(logger, dependency["url"], terraform_ls_dir, dependency["archiveType"]) assert os.path.exists(terraform_ls_executable_path), f"terraform-ls executable not found at {terraform_ls_executable_path}" @@ -114,7 +117,7 @@ class TerraformLS(SolidLanguageServer): """ Creates a TerraformLS instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ - terraform_ls_executable_path = self.setup_runtime_dependencies() + terraform_ls_executable_path = self._setup_runtime_dependencies(logger) super().__init__( config, @@ -126,7 +129,8 @@ class TerraformLS(SolidLanguageServer): self.server_ready = threading.Event() self.request_id = 0 - def _get_initialize_params(self, repository_absolute_path: str) -> InitializeParams: + @staticmethod + def _get_initialize_params(repository_absolute_path: str) -> InitializeParams: """ Returns the initialize params for the Terraform Language Server. """ From b2d6d33cbfc8d4f58eccc781f3de71dd15a98e5a Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sun, 6 Jul 2025 17:58:13 +0200 Subject: [PATCH 19/25] Typo --- src/solidlsp/language_servers/terraform_ls/terraform_ls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solidlsp/language_servers/terraform_ls/terraform_ls.py b/src/solidlsp/language_servers/terraform_ls/terraform_ls.py index b77de536..62f31cb5 100644 --- a/src/solidlsp/language_servers/terraform_ls/terraform_ls.py +++ b/src/solidlsp/language_servers/terraform_ls/terraform_ls.py @@ -102,7 +102,7 @@ class TerraformLS(SolidLanguageServer): os.makedirs(terraform_ls_dir) if not os.path.exists(terraform_ls_executable_path): - logger.info(f"Downloading terraform-ls from {dependency['url']}", logging.INFO) + logger.log(f"Downloading terraform-ls from {dependency['url']}", logging.INFO) FileUtils.download_and_extract_archive(logger, dependency["url"], terraform_ls_dir, dependency["archiveType"]) assert os.path.exists(terraform_ls_executable_path), f"terraform-ls executable not found at {terraform_ls_executable_path}" From 7e06148648bda78f6ca6f56c7847529bb29a64ca Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sun, 6 Jul 2025 18:28:22 +0200 Subject: [PATCH 20/25] Refactoring: added base method ls_resources_dir, used in C# LS --- .../csharp_language_server.py | 64 ++++++++----------- src/solidlsp/ls.py | 11 ++++ test/solidlsp/csharp/test_csharp_basic.py | 61 +++++++++--------- 3 files changed, 65 insertions(+), 71 deletions(-) diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index 153c6b6a..d07e9ba0 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -8,6 +8,7 @@ import os import platform import shutil import stat +import subprocess import tarfile import threading import urllib.request @@ -27,12 +28,12 @@ from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -@dataclass +@dataclass(kw_only=True) class RuntimeDependency: """Represents a runtime dependency for the C# language server.""" id: str - description: str + description: str | None platform_id: str archive_type: str binary_name: str @@ -194,13 +195,13 @@ class CSharpLanguageServer(SolidLanguageServer): Creates a CSharpLanguageServer instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ - dotnet_path, language_server_path, cache_dir = self._ensure_server_installed(logger, config) + dotnet_path, language_server_path = self._ensure_server_installed(logger, config) # Find solution or project file solution_or_project = find_solution_or_project_file(repository_root_path) # Create log directory - log_dir = cache_dir / "logs" + log_dir = Path(self.ls_resources_dir()) / "logs" log_dir.mkdir(parents=True, exist_ok=True) # Build command using dotnet directly @@ -228,29 +229,18 @@ class CSharpLanguageServer(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["bin", "obj", "packages", ".vs"] - @staticmethod - def _ensure_server_installed(logger: LanguageServerLogger, config: LanguageServerConfig) -> tuple[str, str, Path]: + @classmethod + def _ensure_server_installed(cls, logger: LanguageServerLogger, config: LanguageServerConfig) -> tuple[str, str]: """ Ensure .NET runtime and Microsoft.CodeAnalysis.LanguageServer are available. - Returns a tuple of (dotnet_path, language_server_dll_path, cache_dir). + Returns a tuple of (dotnet_path, language_server_dll_path). """ - # Determine platform runtime ID runtime_id = CSharpLanguageServer._get_runtime_id() - - # Set up cache directory - cache_dir = Path.home() / ".cache" / "serena" / "language-servers" / "csharp" - cache_dir.mkdir(parents=True, exist_ok=True) - - # Get runtime dependencies for this platform lang_server_dep, dotnet_runtime_dep = CSharpLanguageServer._get_runtime_dependencies(runtime_id) + dotnet_path = CSharpLanguageServer._ensure_dotnet_runtime(logger, dotnet_runtime_dep) + server_dll_path = CSharpLanguageServer._ensure_language_server(logger, lang_server_dep) - # Ensure .NET runtime is available - dotnet_path = CSharpLanguageServer._ensure_dotnet_runtime(logger, cache_dir, dotnet_runtime_dep) - - # Ensure language server is available - server_dll_path = CSharpLanguageServer._ensure_language_server(logger, cache_dir, lang_server_dep) - - return dotnet_path, server_dll_path, cache_dir + return dotnet_path, server_dll_path @staticmethod def _get_runtime_id() -> str: @@ -287,15 +277,13 @@ class CSharpLanguageServer(SolidLanguageServer): return lang_server_dep, dotnet_runtime_dep @staticmethod - def _ensure_dotnet_runtime(logger: LanguageServerLogger, cache_dir: Path, runtime_dep: RuntimeDependency) -> str: + def _ensure_dotnet_runtime(logger: LanguageServerLogger, runtime_dep: RuntimeDependency) -> str: """Ensure .NET runtime is available and return the dotnet executable path.""" - # Check if dotnet is already available in system + # Check if dotnet is already available on the system system_dotnet = shutil.which("dotnet") if system_dotnet: # Check if it's .NET 9 try: - import subprocess - result = subprocess.run([system_dotnet, "--list-runtimes"], capture_output=True, text=True, check=True) if "Microsoft.NETCore.App 9." in result.stdout: logger.log("Found system .NET 9 runtime", logging.INFO) @@ -304,15 +292,15 @@ class CSharpLanguageServer(SolidLanguageServer): pass # Download .NET 9 runtime using config - return CSharpLanguageServer._ensure_dotnet_runtime_from_config(logger, cache_dir, runtime_dep) + return CSharpLanguageServer._ensure_dotnet_runtime_from_config(logger, runtime_dep) - @staticmethod - def _ensure_language_server(logger: LanguageServerLogger, cache_dir: Path, lang_server_dep: RuntimeDependency) -> str: + @classmethod + def _ensure_language_server(cls, logger: LanguageServerLogger, lang_server_dep: RuntimeDependency) -> str: """Ensure language server is available and return the DLL path.""" package_name = lang_server_dep.package_name package_version = lang_server_dep.package_version - server_dir = cache_dir / f"{package_name}.{package_version}" + server_dir = Path(cls.ls_resources_dir()) / f"{package_name}.{package_version}" server_dll = server_dir / lang_server_dep.binary_name if server_dll.exists(): @@ -321,7 +309,7 @@ class CSharpLanguageServer(SolidLanguageServer): # Download and install the language server logger.log(f"Downloading {package_name} version {package_version}...", logging.INFO) - package_path = CSharpLanguageServer._download_nuget_package_direct(logger, package_name, package_version, cache_dir) + package_path = CSharpLanguageServer._download_nuget_package_direct(logger, package_name, package_version) # Extract and install CSharpLanguageServer._extract_language_server(lang_server_dep, package_path, server_dir) @@ -359,8 +347,8 @@ class CSharpLanguageServer(SolidLanguageServer): server_dir.mkdir(parents=True, exist_ok=True) shutil.copytree(source_dir, server_dir, dirs_exist_ok=True) - @staticmethod - def _download_nuget_package_direct(logger: LanguageServerLogger, package_name: str, package_version: str, cache_dir: Path) -> Path: + @classmethod + def _download_nuget_package_direct(cls, logger: LanguageServerLogger, package_name: str, package_version: str) -> Path: """ Download a NuGet package directly from the Azure NuGet feed. Returns the path to the extracted package directory. @@ -368,7 +356,7 @@ class CSharpLanguageServer(SolidLanguageServer): azure_feed_url = "https://pkgs.dev.azure.com/azure-public/vside/_packaging/vs-impl/nuget/v3/index.json" # Create temporary directory for package download - temp_dir = cache_dir / "temp_downloads" + temp_dir = Path(cls.ls_resources_dir()) / "temp_downloads" temp_dir.mkdir(parents=True, exist_ok=True) try: @@ -416,19 +404,17 @@ class CSharpLanguageServer(SolidLanguageServer): f"Failed to download package {package_name} version {package_version} from Azure NuGet feed: {e}" ) from e - @staticmethod - def _ensure_dotnet_runtime_from_config(logger: LanguageServerLogger, cache_dir: Path, runtime_dep: RuntimeDependency) -> str: + @classmethod + def _ensure_dotnet_runtime_from_config(cls, logger: LanguageServerLogger, runtime_dep: RuntimeDependency) -> str: """ Ensure .NET 9 runtime is available using runtime dependency configuration. Returns the path to the dotnet executable. """ - # Check if dotnet is already available in system + # Check if dotnet is already available on the system system_dotnet = shutil.which("dotnet") if system_dotnet: # Check if it's .NET 9 try: - import subprocess - result = subprocess.run([system_dotnet, "--list-runtimes"], capture_output=True, text=True, check=True) if "Microsoft.NETCore.App 9." in result.stdout: logger.log("Found system .NET 9 runtime", logging.INFO) @@ -437,7 +423,7 @@ class CSharpLanguageServer(SolidLanguageServer): pass # Download .NET 9 runtime using config - dotnet_dir = cache_dir / "dotnet-runtime-9.0" + dotnet_dir = Path(cls.ls_resources_dir()) / "dotnet-runtime-9.0" dotnet_exe = dotnet_dir / runtime_dep.binary_name if dotnet_exe.exists(): diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index d02ef321..3448ea72 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -89,6 +89,17 @@ class SolidLanguageServer(ABC): """ return dirname.startswith(".") + @classmethod + def ls_resources_dir(cls, mkdir: bool = True) -> str: + """ + Returns the directory where the language server resources are downloaded. + This is used to store language server binaries, configuration files, etc. + """ + result = os.path.join(os.path.dirname(__file__), "language_servers", "static", cls.__name__) + if mkdir: + os.makedirs(result, exist_ok=True) + return result + @classmethod def create( cls, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str, timeout: float | None = None diff --git a/test/solidlsp/csharp/test_csharp_basic.py b/test/solidlsp/csharp/test_csharp_basic.py index bb74397a..59f393ac 100644 --- a/test/solidlsp/csharp/test_csharp_basic.py +++ b/test/solidlsp/csharp/test_csharp_basic.py @@ -200,51 +200,48 @@ class TestCSharpSolutionProjectOpening: @patch("solidlsp.language_servers.csharp_language_server.CSharpLanguageServer._start_server") def test_csharp_language_server_logs_solution_discovery(self, mock_start_server, mock_ensure_server_installed): """Test that CSharpLanguageServer logs solution/project discovery during initialization.""" - with tempfile.TemporaryDirectory() as cache_dir: - # Mock the server installation - mock_ensure_server_installed.return_value = ("/usr/bin/dotnet", "/path/to/server.dll", Path(cache_dir)) + mock_ensure_server_installed.return_value = ("/usr/bin/dotnet", "/path/to/server.dll") - # Create test directory with solution file - with tempfile.TemporaryDirectory() as temp_dir: - temp_path = Path(temp_dir) - solution_file = temp_path / "TestSolution.sln" - solution_file.touch() + # Create test directory with solution file + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + solution_file = temp_path / "TestSolution.sln" + solution_file.touch() - # Mock logger to capture log messages - mock_logger = Mock() - mock_config = Mock(spec=LanguageServerConfig) - mock_config.ignored_paths = [] + # Mock logger to capture log messages + mock_logger = Mock() + mock_config = Mock(spec=LanguageServerConfig) + mock_config.ignored_paths = [] - # Create CSharpLanguageServer instance - CSharpLanguageServer(mock_config, mock_logger, str(temp_path)) + # Create CSharpLanguageServer instance + CSharpLanguageServer(mock_config, mock_logger, str(temp_path)) - # Verify that logger was called with solution file discovery - mock_logger.log.assert_any_call(f"Found solution/project file: {solution_file}", 20) # logging.INFO + # Verify that logger was called with solution file discovery + mock_logger.log.assert_any_call(f"Found solution/project file: {solution_file}", 20) # logging.INFO @patch("solidlsp.language_servers.csharp_language_server.CSharpLanguageServer._ensure_server_installed") @patch("solidlsp.language_servers.csharp_language_server.CSharpLanguageServer._start_server") def test_csharp_language_server_logs_no_solution_warning(self, mock_start_server, mock_ensure_server_installed): """Test that CSharpLanguageServer logs warning when no solution/project files are found.""" - with tempfile.TemporaryDirectory() as cache_dir: - # Mock the server installation - mock_ensure_server_installed.return_value = ("/usr/bin/dotnet", "/path/to/server.dll", Path(cache_dir)) + # Mock the server installation + mock_ensure_server_installed.return_value = ("/usr/bin/dotnet", "/path/to/server.dll") - # Create empty test directory - with tempfile.TemporaryDirectory() as temp_dir: - temp_path = Path(temp_dir) + # Create empty test directory + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) - # Mock logger to capture log messages - mock_logger = Mock() - mock_config = Mock(spec=LanguageServerConfig) - mock_config.ignored_paths = [] + # Mock logger to capture log messages + mock_logger = Mock() + mock_config = Mock(spec=LanguageServerConfig) + mock_config.ignored_paths = [] - # Create CSharpLanguageServer instance - CSharpLanguageServer(mock_config, mock_logger, str(temp_path)) + # Create CSharpLanguageServer instance + CSharpLanguageServer(mock_config, mock_logger, str(temp_path)) - # Verify that logger was called with warning about no solution/project files - mock_logger.log.assert_any_call( - "No .sln or .csproj file found, language server will attempt auto-discovery", 30 # logging.WARNING - ) + # Verify that logger was called with warning about no solution/project files + mock_logger.log.assert_any_call( + "No .sln or .csproj file found, language server will attempt auto-discovery", 30 # logging.WARNING + ) def test_solution_and_project_opening_with_real_test_repo(self): """Test solution and project opening with the actual C# test repository.""" From 3eba4c4a1ce1fee12836da2cc58f4fb614843adb Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sun, 6 Jul 2025 18:36:44 +0200 Subject: [PATCH 21/25] Refactoring: use ls_resources_dir in all other LS --- .../language_servers/dart_language_server.py | 6 +++--- src/solidlsp/language_servers/eclipse_jdtls.py | 16 ++++++---------- src/solidlsp/language_servers/intelephense.py | 6 +++--- .../language_servers/kotlin_language_server.py | 6 +++--- src/solidlsp/language_servers/omnisharp.py | 10 +++++----- src/solidlsp/language_servers/rust_analyzer.py | 6 +++--- src/solidlsp/language_servers/terraform_ls.py | 11 +++-------- .../typescript_language_server.py | 6 +++--- 8 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/solidlsp/language_servers/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server.py index 702bc9a9..42ba5cfa 100644 --- a/src/solidlsp/language_servers/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server.py @@ -27,8 +27,8 @@ class DartLanguageServer(SolidLanguageServer): "dart", ) - @staticmethod - def _setup_runtime_dependencies(logger: "LanguageServerLogger") -> str: + @classmethod + def _setup_runtime_dependencies(cls, logger: "LanguageServerLogger") -> str: platform_id = PlatformUtils.get_platform_id() runtime_dependencies = [ @@ -79,7 +79,7 @@ class DartLanguageServer(SolidLanguageServer): assert len(runtime_dependencies) == 1 dependency = runtime_dependencies[0] - dart_ls_dir = os.path.join(os.path.dirname(__file__), "static", "dart-language-server") + dart_ls_dir = cls.ls_resources_dir() dart_executable_path = os.path.join(dart_ls_dir, dependency["binaryName"]) if not os.path.exists(dart_ls_dir): diff --git a/src/solidlsp/language_servers/eclipse_jdtls.py b/src/solidlsp/language_servers/eclipse_jdtls.py index 0dce4cfe..5ebb378c 100644 --- a/src/solidlsp/language_servers/eclipse_jdtls.py +++ b/src/solidlsp/language_servers/eclipse_jdtls.py @@ -152,8 +152,8 @@ class EclipseJDTLS(SolidLanguageServer): "lib", # General ] - @staticmethod - def _setupRuntimeDependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> RuntimeDependencyPaths: + @classmethod + def _setupRuntimeDependencies(cls, logger: LanguageServerLogger, config: LanguageServerConfig) -> RuntimeDependencyPaths: """ Setup runtime dependencies for EclipseJDTLS and return the paths. """ @@ -231,8 +231,6 @@ class EclipseJDTLS(SolidLanguageServer): }, } - os.makedirs(str(PurePath(os.path.abspath(os.path.dirname(__file__)), "static")), exist_ok=True) - # assert platformId.value in [ # "linux-x64", # "win-x64", @@ -240,8 +238,8 @@ class EclipseJDTLS(SolidLanguageServer): gradle_path = str( PurePath( - os.path.abspath(os.path.dirname(__file__)), - "static/gradle-7.3.3", + cls.ls_resources_dir(), + "gradle-7.3.3", ) ) @@ -256,7 +254,7 @@ class EclipseJDTLS(SolidLanguageServer): assert os.path.exists(gradle_path) dependency = runtime_dependencies["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(cls.ls_resources_dir(), 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"])) @@ -285,9 +283,7 @@ class EclipseJDTLS(SolidLanguageServer): assert os.path.exists(jdtls_readonly_config_path) dependency = runtime_dependencies["intellicode"]["platform-agnostic"] - intellicode_directory_path = str( - PurePath(os.path.abspath(os.path.dirname(__file__)), "static", dependency["relative_extraction_path"]) - ) + intellicode_directory_path = str(PurePath(cls.ls_resources_dir(), 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"])) diff --git a/src/solidlsp/language_servers/intelephense.py b/src/solidlsp/language_servers/intelephense.py index e971c7dd..e52c8228 100644 --- a/src/solidlsp/language_servers/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -32,8 +32,8 @@ class Intelephense(SolidLanguageServer): # - cache: commonly used for caching return super().is_ignored_dirname(dirname) or dirname in ["node_modules", "vendor", "cache"] - @staticmethod - def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> str: + @classmethod + def _setup_runtime_dependencies(cls, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ Setup runtime dependencies for Intelephense and return the command to start the server. """ @@ -57,7 +57,7 @@ class Intelephense(SolidLanguageServer): 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 - intelephense_ls_dir = os.path.join(os.path.dirname(__file__), "static", "php-lsp") + intelephense_ls_dir = os.path.join(cls.ls_resources_dir(), "php-lsp") os.makedirs(intelephense_ls_dir, exist_ok=True) intelephense_executable_path = os.path.join(intelephense_ls_dir, "node_modules", ".bin", "intelephense") if not os.path.exists(intelephense_executable_path): diff --git a/src/solidlsp/language_servers/kotlin_language_server.py b/src/solidlsp/language_servers/kotlin_language_server.py index e89e5c7e..ef2a77ca 100644 --- a/src/solidlsp/language_servers/kotlin_language_server.py +++ b/src/solidlsp/language_servers/kotlin_language_server.py @@ -53,8 +53,8 @@ class KotlinLanguageServer(SolidLanguageServer): "kotlin", ) - @staticmethod - def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> KotlinRuntimeDependencyPaths: + @classmethod + def _setup_runtime_dependencies(cls, logger: LanguageServerLogger, config: LanguageServerConfig) -> KotlinRuntimeDependencyPaths: """ Setup runtime dependencies for Kotlin Language Server and return the paths. """ @@ -111,7 +111,7 @@ class KotlinLanguageServer(SolidLanguageServer): java_dependency = runtime_dependencies["java"][platform_id.value] # Setup paths for dependencies - static_dir = os.path.join(os.path.dirname(__file__), "static", "kotlin_language_server") + static_dir = os.path.join(cls.ls_resources_dir(), "kotlin_language_server") os.makedirs(static_dir, exist_ok=True) # Setup Java paths diff --git a/src/solidlsp/language_servers/omnisharp.py b/src/solidlsp/language_servers/omnisharp.py index 2f043b62..046d1e84 100644 --- a/src/solidlsp/language_servers/omnisharp.py +++ b/src/solidlsp/language_servers/omnisharp.py @@ -63,7 +63,7 @@ class OmniSharp(SolidLanguageServer): """ 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) + omnisharp_executable_path, dll_path = self._setup_runtime_dependencies(logger, config) slnfilename = find_least_depth_sln_file(repository_root_path) if slnfilename is None: @@ -137,8 +137,8 @@ class OmniSharp(SolidLanguageServer): return d - @staticmethod - def setupRuntimeDependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> tuple[str, str]: + @classmethod + def _setup_runtime_dependencies(cls, logger: LanguageServerLogger, config: LanguageServerConfig) -> tuple[str, str]: """ Setup runtime dependencies for OmniSharp. """ @@ -181,7 +181,7 @@ class OmniSharp(SolidLanguageServer): assert "OmniSharp" in runtime_dependencies assert "RazorOmnisharp" in runtime_dependencies - omnisharp_ls_dir = os.path.join(os.path.dirname(__file__), "static", "OmniSharp") + omnisharp_ls_dir = os.path.join(cls.ls_resources_dir(), "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") @@ -189,7 +189,7 @@ class OmniSharp(SolidLanguageServer): 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") + razor_omnisharp_ls_dir = os.path.join(cls.ls_resources_dir(), "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") diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index 7edab82c..4d968ae3 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -44,8 +44,8 @@ class RustAnalyzer(SolidLanguageServer): def is_ignored_dirname(self, dirname: str) -> bool: return super().is_ignored_dirname(dirname) or dirname in ["target"] - @staticmethod - def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> str: + @classmethod + def _setup_runtime_dependencies(cls, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ Setup runtime dependencies for rust_analyzer and return the command to start the server. """ @@ -87,7 +87,7 @@ class RustAnalyzer(SolidLanguageServer): assert len(runtime_dependencies) == 1 dependency = runtime_dependencies[0] - rustanalyzer_ls_dir = os.path.join(os.path.dirname(__file__), "static", "RustAnalyzer") + rustanalyzer_ls_dir = os.path.join(cls.ls_resources_dir(), "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) diff --git a/src/solidlsp/language_servers/terraform_ls.py b/src/solidlsp/language_servers/terraform_ls.py index df6fea42..658c5345 100644 --- a/src/solidlsp/language_servers/terraform_ls.py +++ b/src/solidlsp/language_servers/terraform_ls.py @@ -63,9 +63,8 @@ class TerraformLS(SolidLanguageServer): else: raise RuntimeError(f"terraform version command failed with return code {result.returncode}: {result.stderr}") - # Note: needs to remain static because it's called before init is complete - @staticmethod - def _setup_runtime_dependencies(logger: LanguageServerLogger) -> str: + @classmethod + def _setup_runtime_dependencies(cls, logger: LanguageServerLogger) -> str: """ Setup runtime dependencies for terraform-ls. Downloads and installs terraform-ls if not already present. @@ -121,12 +120,8 @@ class TerraformLS(SolidLanguageServer): ), f"Expected exactly one runtime dependency for platform {platform_id.value}, found {len(runtime_dependencies)}" dependency = runtime_dependencies[0] - terraform_ls_dir = os.path.join(os.path.dirname(__file__), "static", "terraform-ls") + terraform_ls_dir = os.path.join(cls.ls_resources_dir(), "terraform-ls") terraform_ls_executable_path = os.path.join(terraform_ls_dir, dependency["binaryName"]) - - if not os.path.exists(terraform_ls_dir): - os.makedirs(terraform_ls_dir) - if not os.path.exists(terraform_ls_executable_path): logger.log(f"Downloading terraform-ls from {dependency['url']}", logging.INFO) FileUtils.download_and_extract_archive(logger, dependency["url"], terraform_ls_dir, dependency["archiveType"]) diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index c0130d35..175adfa7 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -64,8 +64,8 @@ class TypeScriptLanguageServer(SolidLanguageServer): "coverage", ] - @staticmethod - def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> str: + @classmethod + def _setup_runtime_dependencies(cls, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ Setup runtime dependencies for TypeScript Language Server and return the command to start the server. """ @@ -94,7 +94,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): "command": "npm install --prefix ./ typescript-language-server@4.3.3", }, ] - tsserver_ls_dir = os.path.join(os.path.dirname(__file__), "static", "ts-lsp") + tsserver_ls_dir = os.path.join(cls.ls_resources_dir(), "ts-lsp") tsserver_executable_path = os.path.join(tsserver_ls_dir, "typescript-language-server") # Verify both node and npm are installed From 03745a9f5209f5ad33401c60621d2b2b924f2e03 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sun, 6 Jul 2025 18:38:54 +0200 Subject: [PATCH 22/25] Typo in import --- src/solidlsp/ls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index 3448ea72..b19c4708 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -203,7 +203,7 @@ class SolidLanguageServer(ABC): ls = ElixirTools(config, logger, repository_root_path) elif config.code_language == Language.TERRAFORM: - from solidlsp.language_servers.terraform_ls.terraform_ls import TerraformLS + from solidlsp.language_servers.terraform_ls import TerraformLS ls = TerraformLS(config, logger, repository_root_path) From aa0c0e5373c31ea871f94c1af31a6de5fdfecfa5 Mon Sep 17 00:00:00 2001 From: Michael Panchenko <--get> Date: Sun, 6 Jul 2025 18:59:51 +0200 Subject: [PATCH 23/25] Minor fixes in tf download, simplifications in tf detection --- src/solidlsp/language_servers/terraform_ls.py | 41 +++++-------------- src/solidlsp/ls_utils.py | 4 +- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/src/solidlsp/language_servers/terraform_ls.py b/src/solidlsp/language_servers/terraform_ls.py index 658c5345..14b9092f 100644 --- a/src/solidlsp/language_servers/terraform_ls.py +++ b/src/solidlsp/language_servers/terraform_ls.py @@ -2,7 +2,6 @@ import logging import os import shutil import stat -import subprocess import threading from overrides import override @@ -25,19 +24,22 @@ class TerraformLS(SolidLanguageServer): return super().is_ignored_dirname(dirname) or dirname in [".terraform", "terraform.tfstate.d"] @staticmethod - def _get_terraform_version(logger: LanguageServerLogger) -> str: + def _ensure_tf_command_available(logger: LanguageServerLogger): logger.log("Starting terraform version detection...", logging.DEBUG) # 1. Try to find terraform using shutil.which terraform_cmd = shutil.which("terraform") if terraform_cmd is not None: logger.log(f"Found terraform via shutil.which: {terraform_cmd}", logging.DEBUG) + return + # TODO: is this needed? # 2. Fallback to TERRAFORM_CLI_PATH (set by hashicorp/setup-terraform action) if not terraform_cmd: terraform_cli_path = os.environ.get("TERRAFORM_CLI_PATH") if terraform_cli_path: logger.log(f"Trying TERRAFORM_CLI_PATH: {terraform_cli_path}", logging.DEBUG) + # TODO: use binary name from runtime dependencies if we keep this code if os.name == "nt": terraform_binary = os.path.join(terraform_cli_path, "terraform.exe") else: @@ -45,23 +47,12 @@ class TerraformLS(SolidLanguageServer): if os.path.exists(terraform_binary): terraform_cmd = terraform_binary logger.log(f"Found terraform via TERRAFORM_CLI_PATH: {terraform_cmd}", logging.DEBUG) + return - if not terraform_cmd: - raise RuntimeError("Terraform executable not found. Please ensure Terraform is installed and accessible in your system's PATH.") - - logger.log(f"Attempting to run: {terraform_cmd} version (with 15s timeout)", logging.DEBUG) - result = subprocess.run( - [terraform_cmd, "version"], - capture_output=True, - text=True, - check=False, - timeout=15, # CRITICAL: 15 second timeout to prevent hangs + raise RuntimeError( + "Terraform executable not found, please ensure Terraform is installed." + "See https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli for instructions." ) - if result.returncode == 0: - logger.log("terraform version command succeeded", logging.DEBUG) - return result.stdout.strip() - else: - raise RuntimeError(f"terraform version command failed with return code {result.returncode}: {result.stderr}") @classmethod def _setup_runtime_dependencies(cls, logger: LanguageServerLogger) -> str: @@ -69,16 +60,7 @@ class TerraformLS(SolidLanguageServer): Setup runtime dependencies for terraform-ls. Downloads and installs terraform-ls if not already present. """ - # First check if Terraform is available - terraform_version = TerraformLS._get_terraform_version(logger) - if not terraform_version: - raise RuntimeError( - "Terraform executable not found or failed to execute. " - "Please ensure Terraform is installed and accessible in your system's PATH.\n" - "If it's installed, check for permission issues or corrupted installation.\n" - "Download from https://www.terraform.io/downloads" - ) - + cls._ensure_tf_command_available(logger) platform_id = PlatformUtils.get_platform_id() runtime_dependencies = [ { @@ -120,11 +102,10 @@ class TerraformLS(SolidLanguageServer): ), f"Expected exactly one runtime dependency for platform {platform_id.value}, found {len(runtime_dependencies)}" dependency = runtime_dependencies[0] - terraform_ls_dir = os.path.join(cls.ls_resources_dir(), "terraform-ls") - terraform_ls_executable_path = os.path.join(terraform_ls_dir, dependency["binaryName"]) + terraform_ls_executable_path = os.path.join(cls.ls_resources_dir(), dependency["binaryName"]) if not os.path.exists(terraform_ls_executable_path): logger.log(f"Downloading terraform-ls from {dependency['url']}", logging.INFO) - FileUtils.download_and_extract_archive(logger, dependency["url"], terraform_ls_dir, dependency["archiveType"]) + FileUtils.download_and_extract_archive(logger, dependency["url"], cls.ls_resources_dir(), dependency["archiveType"]) assert os.path.exists(terraform_ls_executable_path), f"terraform-ls executable not found at {terraform_ls_executable_path}" diff --git a/src/solidlsp/ls_utils.py b/src/solidlsp/ls_utils.py index 32aab581..b46491ec 100644 --- a/src/solidlsp/ls_utils.py +++ b/src/solidlsp/ls_utils.py @@ -206,10 +206,10 @@ class FileUtils: os.makedirs(os.path.dirname(tmp_file_name), exist_ok=True) FileUtils.download_file(logger, url, tmp_file_name) if archive_type in ["zip", "tar", "gztar", "bztar", "xztar"]: - assert os.path.isdir(target_path) + os.makedirs(target_path, exist_ok=True) shutil.unpack_archive(tmp_file_name, target_path, archive_type) elif archive_type == "zip.gz": - assert os.path.isdir(target_path) + os.makedirs(target_path, exist_ok=True) tmp_file_name_ungzipped = tmp_file_name + ".zip" tmp_files.append(tmp_file_name_ungzipped) with gzip.open(tmp_file_name, "rb") as f_in, open(tmp_file_name_ungzipped, "wb") as f_out: From bb595eb7e78bd5ebafded118968ace1ef8e0d15a Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Sun, 6 Jul 2025 22:10:11 +0200 Subject: [PATCH 24/25] Refine runtime dependency helpers (#286) --- .../clangd_language_server.py | 83 ++++++-------- src/solidlsp/language_servers/clojure_lsp.py | 78 +++++++------ src/solidlsp/language_servers/common.py | 107 ++++++++++++++++++ .../csharp_language_server.py | 18 +-- .../language_servers/dart_language_server.py | 96 ++++++++-------- src/solidlsp/language_servers/intelephense.py | 37 ++---- .../language_servers/rust_analyzer.py | 66 ++++++----- src/solidlsp/language_servers/terraform_ls.py | 83 +++++++------- .../typescript_language_server.py | 53 +++------ 9 files changed, 334 insertions(+), 287 deletions(-) create mode 100644 src/solidlsp/language_servers/common.py diff --git a/src/solidlsp/language_servers/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server.py index 871d7c45..f787d2e2 100644 --- a/src/solidlsp/language_servers/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server.py @@ -12,6 +12,7 @@ from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger from solidlsp.ls_utils import FileUtils, PlatformUtils +from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo @@ -47,58 +48,44 @@ class ClangdLanguageServer(SolidLanguageServer): """ platform_id = PlatformUtils.get_platform_id() - runtime_dependencies = [ - { - "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": "win-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", - }, - ] - - assert platform_id.value in [ - "linux-x64", - "win-x64", - "osx-arm64", - ], ( - "Unsupported platform: " + platform_id.value + deps = RuntimeDependencyCollection( + [ + RuntimeDependency( + id="Clangd", + description="Clangd for Linux (x64)", + url="https://github.com/clangd/clangd/releases/download/19.1.2/clangd-linux-19.1.2.zip", + platform_id="linux-x64", + archive_type="zip", + binary_name="clangd_19.1.2/bin/clangd", + ), + RuntimeDependency( + id="Clangd", + description="Clangd for Windows (x64)", + url="https://github.com/clangd/clangd/releases/download/19.1.2/clangd-windows-19.1.2.zip", + platform_id="win-x64", + archive_type="zip", + binary_name="clangd_19.1.2/bin/clangd.exe", + ), + RuntimeDependency( + id="Clangd", + description="Clangd for macOS (Arm64)", + url="https://github.com/clangd/clangd/releases/download/19.1.2/clangd-mac-19.1.2.zip", + platform_id="osx-arm64", + archive_type="zip", + binary_name="clangd_19.1.2/bin/clangd", + ), + ] ) - 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"]) + dep = deps.single_for_current_platform() + clangd_executable_path = deps.binary_path(clangd_ls_dir) 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']}") + logger.log( + f"Clangd executable not found at {clangd_executable_path}. Downloading from {dep.url}", + logging.INFO, + ) + deps.install(logger, clangd_ls_dir) if not os.path.exists(clangd_executable_path): raise FileNotFoundError( f"Clangd executable not found at {clangd_executable_path}.\n" diff --git a/src/solidlsp/language_servers/clojure_lsp.py b/src/solidlsp/language_servers/clojure_lsp.py index 0fe06e61..5a8d1241 100644 --- a/src/solidlsp/language_servers/clojure_lsp.py +++ b/src/solidlsp/language_servers/clojure_lsp.py @@ -14,6 +14,7 @@ from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger from solidlsp.ls_utils import FileUtils, PlatformUtils +from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo @@ -44,32 +45,38 @@ class ClojureLSP(SolidLanguageServer): """ clojure_lsp_releases = "https://github.com/clojure-lsp/clojure-lsp/releases/latest/download" - runtime_dependencies = [ - { - "url": f"{clojure_lsp_releases}/clojure-lsp-native-macos-aarch64.zip", - "platformId": "osx-arm64", - "archiveType": "zip", - "binaryName": "clojure-lsp", - }, - { - "url": f"{clojure_lsp_releases}/clojure-lsp-native-linux-aarch64.zip", - "platformId": "linux-arm64", - "archiveType": "zip", - "binaryName": "clojure-lsp", - }, - { - "url": f"{clojure_lsp_releases}/clojure-lsp-native-linux-amd64.zip", - "platformId": "linux-x64", - "archiveType": "zip", - "binaryName": "clojure-lsp", - }, - { - "url": f"{clojure_lsp_releases}/clojure-lsp-native-windows-amd64.zip", - "platformId": "win-x64", - "archiveType": "zip", - "binaryName": "clojure-lsp.exe", - }, - ] + runtime_dependencies = RuntimeDependencyCollection( + [ + RuntimeDependency( + id="clojure-lsp", + url=f"{clojure_lsp_releases}/clojure-lsp-native-macos-aarch64.zip", + platform_id="osx-arm64", + archive_type="zip", + binary_name="clojure-lsp", + ), + RuntimeDependency( + id="clojure-lsp", + url=f"{clojure_lsp_releases}/clojure-lsp-native-linux-aarch64.zip", + platform_id="linux-arm64", + archive_type="zip", + binary_name="clojure-lsp", + ), + RuntimeDependency( + id="clojure-lsp", + url=f"{clojure_lsp_releases}/clojure-lsp-native-linux-amd64.zip", + platform_id="linux-x64", + archive_type="zip", + binary_name="clojure-lsp", + ), + RuntimeDependency( + id="clojure-lsp", + url=f"{clojure_lsp_releases}/clojure-lsp-native-windows-amd64.zip", + platform_id="win-x64", + archive_type="zip", + binary_name="clojure-lsp.exe", + ), + ] + ) def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): """ @@ -92,21 +99,18 @@ class ClojureLSP(SolidLanguageServer): def _setup_runtime_dependencies(logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """Setup runtime dependencies for clojure-lsp and return the command to start the server.""" verify_clojure_cli() - platform_id = PlatformUtils.get_platform_id() - - runtime_dependencies = [ - dependency for dependency in ClojureLSP.runtime_dependencies if dependency["platformId"] == platform_id.value - ] - if len(runtime_dependencies) != 1: - raise RuntimeError(f"Could not find a suitable clojure-lsp runtime dependency for platform {platform_id.value}.") - dependency = runtime_dependencies[0] + deps = ClojureLSP.runtime_dependencies + dependency = deps.single_for_current_platform() clojurelsp_ls_dir = os.path.join(os.path.dirname(__file__), "static", "clojure-lsp") - clojurelsp_executable_path = os.path.join(clojurelsp_ls_dir, dependency["binaryName"]) + clojurelsp_executable_path = deps.binary_path(clojurelsp_ls_dir) if not os.path.exists(clojurelsp_executable_path): os.makedirs(clojurelsp_ls_dir, exist_ok=True) - logger.log(f"Downloading and extracting clojure-lsp from {dependency['url']} to {clojurelsp_ls_dir}", logging.INFO) - FileUtils.download_and_extract_archive(logger, dependency["url"], clojurelsp_ls_dir, dependency["archiveType"]) + logger.log( + f"Downloading and extracting clojure-lsp from {dependency.url} to {clojurelsp_ls_dir}", + logging.INFO, + ) + deps.install(logger, clojurelsp_ls_dir) if not os.path.exists(clojurelsp_executable_path): raise FileNotFoundError(f"Download failed? Could not find clojure-lsp executable at {clojurelsp_executable_path}") os.chmod(clojurelsp_executable_path, stat.S_IEXEC) diff --git a/src/solidlsp/language_servers/common.py b/src/solidlsp/language_servers/common.py new file mode 100644 index 00000000..78ae7cb8 --- /dev/null +++ b/src/solidlsp/language_servers/common.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +import os +import subprocess +from dataclasses import dataclass +from typing import Sequence, Dict + +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls_utils import FileUtils, PlatformUtils + + +@dataclass(kw_only=True) +class RuntimeDependency: + """Represents a runtime dependency for a language server.""" + + id: str + platform_id: str | None = None + url: str | None = None + archive_type: str | None = None + binary_name: str | None = None + command: str | None = None + package_name: str | None = None + package_version: str | None = None + extract_path: str | None = None + description: str | None = None + + +class RuntimeDependencyCollection: + """Utility to handle installation of runtime dependencies.""" + + def __init__(self, dependencies: Sequence[RuntimeDependency]): + self._dependencies = list(dependencies) + + def for_platform(self, platform_id: str) -> list[RuntimeDependency]: + return [ + d + for d in self._dependencies + if d.platform_id in (platform_id, "any", "platform-agnostic", None) + ] + + def for_current_platform(self) -> list[RuntimeDependency]: + return self.for_platform(PlatformUtils.get_platform_id().value) + + def single_for_current_platform(self) -> RuntimeDependency: + deps = self.for_current_platform() + if len(deps) != 1: + raise RuntimeError( + f"Expected exactly one runtime dependency for {PlatformUtils.get_platform_id().value}, found {len(deps)}" + ) + return deps[0] + + def binary_path(self, target_dir: str) -> str: + dep = self.single_for_current_platform() + if not dep.binary_name: + return target_dir + return os.path.join(target_dir, dep.binary_name) + + def install(self, logger: LanguageServerLogger, target_dir: str) -> Dict[str, str]: + """Install all dependencies for the current platform into *target_dir*. + + Returns a mapping from dependency id to the resolved binary path. + """ + os.makedirs(target_dir, exist_ok=True) + results: Dict[str, str] = {} + for dep in self.for_current_platform(): + if dep.url: + self._install_from_url(dep, logger, target_dir) + if dep.command: + self._run_command(dep.command, target_dir) + if dep.binary_name: + results[dep.id] = os.path.join(target_dir, dep.binary_name) + else: + results[dep.id] = target_dir + return results + + @staticmethod + def _run_command(command: str, cwd: str) -> None: + if PlatformUtils.get_platform_id().value.startswith("win"): + subprocess.run( + command, + shell=True, + check=True, + cwd=cwd, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + else: + import pwd + + user = pwd.getpwuid(os.getuid()).pw_name + subprocess.run( + command, + shell=True, + check=True, + user=user, + cwd=cwd, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + @staticmethod + def _install_from_url(dep: RuntimeDependency, logger: LanguageServerLogger, target_dir: str) -> None: + if dep.archive_type == "gz" and dep.binary_name: + dest = os.path.join(target_dir, dep.binary_name) + FileUtils.download_and_extract_archive(logger, dep.url, dest, dep.archive_type) + else: + FileUtils.download_and_extract_archive(logger, dep.url, target_dir, dep.archive_type or "zip") diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index d07e9ba0..4ead95de 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -13,10 +13,11 @@ import tarfile import threading import urllib.request import zipfile -from dataclasses import dataclass from pathlib import Path from typing import cast +from .common import RuntimeDependency + from overrides import override from solidlsp.ls import SolidLanguageServer @@ -28,21 +29,6 @@ from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo -@dataclass(kw_only=True) -class RuntimeDependency: - """Represents a runtime dependency for the C# language server.""" - - id: str - description: str | None - platform_id: str - archive_type: str - binary_name: str - package_name: str | None = None - package_version: str | None = None - extract_path: str | None = None - url: str | None = None - - # Runtime dependencies configuration RUNTIME_DEPENDENCIES = [ RuntimeDependency( diff --git a/src/solidlsp/language_servers/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server.py index 42ba5cfa..b369f860 100644 --- a/src/solidlsp/language_servers/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server.py @@ -6,6 +6,7 @@ import stat from solidlsp.ls import SolidLanguageServer from solidlsp.ls_logger import LanguageServerLogger from solidlsp.ls_utils import FileUtils, PlatformUtils +from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo @@ -31,60 +32,57 @@ class DartLanguageServer(SolidLanguageServer): def _setup_runtime_dependencies(cls, logger: "LanguageServerLogger") -> str: platform_id = PlatformUtils.get_platform_id() - runtime_dependencies = [ - { - "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", - }, - ] - - runtime_dependencies = [dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value] - - assert len(runtime_dependencies) == 1 - dependency = runtime_dependencies[0] + deps = RuntimeDependencyCollection( + [ + RuntimeDependency( + 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", + platform_id="linux-x64", + archive_type="zip", + binary_name="dart-sdk/bin/dart", + ), + RuntimeDependency( + 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", + platform_id="win-x64", + archive_type="zip", + binary_name="dart-sdk/bin/dart.exe", + ), + RuntimeDependency( + 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", + platform_id="win-arm64", + archive_type="zip", + binary_name="dart-sdk/bin/dart.exe", + ), + RuntimeDependency( + 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", + platform_id="osx-x64", + archive_type="zip", + binary_name="dart-sdk/bin/dart", + ), + RuntimeDependency( + 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", + platform_id="osx-arm64", + archive_type="zip", + binary_name="dart-sdk/bin/dart", + ), + ] + ) dart_ls_dir = cls.ls_resources_dir() - dart_executable_path = os.path.join(dart_ls_dir, dependency["binaryName"]) + dart_executable_path = deps.binary_path(dart_ls_dir) 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"]) + deps.install(logger, dart_ls_dir) assert os.path.exists(dart_executable_path) os.chmod(dart_executable_path, stat.S_IEXEC) diff --git a/src/solidlsp/language_servers/intelephense.py b/src/solidlsp/language_servers/intelephense.py index e52c8228..277b0452 100644 --- a/src/solidlsp/language_servers/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -15,6 +15,7 @@ from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger from solidlsp.ls_utils import PlatformId, PlatformUtils +from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.lsp_types import DefinitionParams, InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo @@ -61,32 +62,16 @@ class Intelephense(SolidLanguageServer): os.makedirs(intelephense_ls_dir, exist_ok=True) intelephense_executable_path = os.path.join(intelephense_ls_dir, "node_modules", ".bin", "intelephense") if not os.path.exists(intelephense_executable_path): - install_command = "npm install --prefix ./ intelephense@1.14.4" - is_windows = PlatformUtils.get_platform_id().value.startswith("win") - # Windows doesn't support the 'user' parameter and doesn't have pwd module - if is_windows: - subprocess.run( - install_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( - install_command, - shell=True, - check=True, - user=user, - cwd=intelephense_ls_dir, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) + deps = RuntimeDependencyCollection( + [ + RuntimeDependency( + id="intelephense", + command="npm install --prefix ./ intelephense@1.14.4", + platform_id="any", + ) + ] + ) + deps.install(logger, intelephense_ls_dir) assert os.path.exists( intelephense_executable_path diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index 4d968ae3..0b8442d4 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -14,6 +14,7 @@ from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger from solidlsp.ls_utils import FileUtils, PlatformUtils +from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo @@ -51,50 +52,47 @@ class RustAnalyzer(SolidLanguageServer): """ platform_id = PlatformUtils.get_platform_id() - runtime_dependencies = [ - { - "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", - }, - ] + deps = RuntimeDependencyCollection( + [ + RuntimeDependency( + id="RustAnalyzer", + description="RustAnalyzer for macOS (arm64)", + url="https://github.com/rust-lang/rust-analyzer/releases/download/2023-10-09/rust-analyzer-aarch64-apple-darwin.gz", + platform_id="osx-arm64", + archive_type="gz", + binary_name="rust_analyzer", + ), + RuntimeDependency( + 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", + platform_id="linux-x64", + archive_type="gz", + binary_name="rust_analyzer", + ), + RuntimeDependency( + 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", + platform_id="win-x64", + archive_type="zip", + binary_name="rust-analyzer.exe", + ), + ] + ) # 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 = [dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value] - assert len(runtime_dependencies) == 1 - dependency = runtime_dependencies[0] + dependency = deps.single_for_current_platform() rustanalyzer_ls_dir = os.path.join(cls.ls_resources_dir(), "RustAnalyzer") - rustanalyzer_executable_path = os.path.join(rustanalyzer_ls_dir, dependency["binaryName"]) + rustanalyzer_executable_path = deps.binary_path(rustanalyzer_ls_dir) 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"]) + deps.install(logger, rustanalyzer_ls_dir) assert os.path.exists(rustanalyzer_executable_path) os.chmod(rustanalyzer_executable_path, stat.S_IEXEC) diff --git a/src/solidlsp/language_servers/terraform_ls.py b/src/solidlsp/language_servers/terraform_ls.py index 14b9092f..b60aa2de 100644 --- a/src/solidlsp/language_servers/terraform_ls.py +++ b/src/solidlsp/language_servers/terraform_ls.py @@ -10,6 +10,7 @@ from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger from solidlsp.ls_utils import FileUtils, PathUtils, PlatformUtils +from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo @@ -62,50 +63,48 @@ class TerraformLS(SolidLanguageServer): """ cls._ensure_tf_command_available(logger) platform_id = PlatformUtils.get_platform_id() - runtime_dependencies = [ - { - "id": "TerraformLS", - "description": "terraform-ls for macOS (ARM64)", - "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_darwin_arm64.zip", - "platformId": "osx-arm64", - "archiveType": "zip", - "binaryName": "terraform-ls", - }, - { - "id": "TerraformLS", - "description": "terraform-ls for macOS (x64)", - "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_darwin_amd64.zip", - "platformId": "osx-x64", - "archiveType": "zip", - "binaryName": "terraform-ls", - }, - { - "id": "TerraformLS", - "description": "terraform-ls for Linux (x64)", - "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_linux_amd64.zip", - "platformId": "linux-x64", - "archiveType": "zip", - "binaryName": "terraform-ls", - }, - { - "id": "TerraformLS", - "description": "terraform-ls for Windows (x64)", - "url": "https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_windows_amd64.zip", - "platformId": "win-x64", - "archiveType": "zip", - "binaryName": "terraform-ls.exe", - }, - ] - runtime_dependencies = [dependency for dependency in runtime_dependencies if dependency["platformId"] == platform_id.value] - assert ( - len(runtime_dependencies) == 1 - ), f"Expected exactly one runtime dependency for platform {platform_id.value}, found {len(runtime_dependencies)}" - dependency = runtime_dependencies[0] + deps = RuntimeDependencyCollection( + [ + RuntimeDependency( + id="TerraformLS", + description="terraform-ls for macOS (ARM64)", + url="https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_darwin_arm64.zip", + platform_id="osx-arm64", + archive_type="zip", + binary_name="terraform-ls", + ), + RuntimeDependency( + id="TerraformLS", + description="terraform-ls for macOS (x64)", + url="https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_darwin_amd64.zip", + platform_id="osx-x64", + archive_type="zip", + binary_name="terraform-ls", + ), + RuntimeDependency( + id="TerraformLS", + description="terraform-ls for Linux (x64)", + url="https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_linux_amd64.zip", + platform_id="linux-x64", + archive_type="zip", + binary_name="terraform-ls", + ), + RuntimeDependency( + id="TerraformLS", + description="terraform-ls for Windows (x64)", + url="https://releases.hashicorp.com/terraform-ls/0.36.5/terraform-ls_0.36.5_windows_amd64.zip", + platform_id="win-x64", + archive_type="zip", + binary_name="terraform-ls.exe", + ), + ] + ) + dependency = deps.single_for_current_platform() - terraform_ls_executable_path = os.path.join(cls.ls_resources_dir(), dependency["binaryName"]) + terraform_ls_executable_path = deps.binary_path(cls.ls_resources_dir()) if not os.path.exists(terraform_ls_executable_path): - logger.log(f"Downloading terraform-ls from {dependency['url']}", logging.INFO) - FileUtils.download_and_extract_archive(logger, dependency["url"], cls.ls_resources_dir(), dependency["archiveType"]) + logger.log(f"Downloading terraform-ls from {dependency.url}", logging.INFO) + deps.install(logger, cls.ls_resources_dir()) assert os.path.exists(terraform_ls_executable_path), f"terraform-ls executable not found at {terraform_ls_executable_path}" diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index 175adfa7..54dd1245 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -16,6 +16,7 @@ from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger from solidlsp.ls_utils import PlatformId, PlatformUtils +from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo @@ -82,18 +83,22 @@ class TypeScriptLanguageServer(SolidLanguageServer): ] assert platform_id in valid_platforms, f"Platform {platform_id} is not supported for multilspy javascript/typescript at the moment" - runtime_dependencies = [ - { - "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", - }, - ] + deps = RuntimeDependencyCollection( + [ + RuntimeDependency( + id="typescript", + description="typescript package", + command="npm install --prefix ./ typescript@5.5.4", + platform_id="any", + ), + RuntimeDependency( + id="typescript-language-server", + description="typescript-language-server package", + command="npm install --prefix ./ typescript-language-server@4.3.3", + platform_id="any", + ), + ] + ) tsserver_ls_dir = os.path.join(cls.ls_resources_dir(), "ts-lsp") tsserver_executable_path = os.path.join(tsserver_ls_dir, "typescript-language-server") @@ -106,29 +111,7 @@ class TypeScriptLanguageServer(SolidLanguageServer): # 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, - ) + deps.install(logger, tsserver_ls_dir) tsserver_executable_path = os.path.join(tsserver_ls_dir, "node_modules", ".bin", "typescript-language-server") From 1f38ec5f7b919cb20c29505b34decde6e0b03266 Mon Sep 17 00:00:00 2001 From: Michael Panchenko Date: Sun, 6 Jul 2025 23:13:10 +0200 Subject: [PATCH 25/25] Format, removed unused vars --- .../language_servers/clangd_language_server.py | 6 ++---- src/solidlsp/language_servers/clojure_lsp.py | 4 ++-- src/solidlsp/language_servers/common.py | 16 +++++----------- .../language_servers/csharp_language_server.py | 3 +-- .../language_servers/dart_language_server.py | 6 ++---- src/solidlsp/language_servers/intelephense.py | 4 ++-- src/solidlsp/language_servers/rust_analyzer.py | 8 ++------ src/solidlsp/language_servers/terraform_ls.py | 5 +++-- .../typescript_language_server.py | 6 +++--- 9 files changed, 22 insertions(+), 36 deletions(-) diff --git a/src/solidlsp/language_servers/clangd_language_server.py b/src/solidlsp/language_servers/clangd_language_server.py index f787d2e2..b8845d84 100644 --- a/src/solidlsp/language_servers/clangd_language_server.py +++ b/src/solidlsp/language_servers/clangd_language_server.py @@ -11,11 +11,11 @@ import threading from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger -from solidlsp.ls_utils import FileUtils, PlatformUtils -from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from .common import RuntimeDependency, RuntimeDependencyCollection + class ClangdLanguageServer(SolidLanguageServer): """ @@ -46,8 +46,6 @@ class ClangdLanguageServer(SolidLanguageServer): """ Setup runtime dependencies for ClangdLanguageServer and return the command to start the server. """ - platform_id = PlatformUtils.get_platform_id() - deps = RuntimeDependencyCollection( [ RuntimeDependency( diff --git a/src/solidlsp/language_servers/clojure_lsp.py b/src/solidlsp/language_servers/clojure_lsp.py index 5a8d1241..cae8abc2 100644 --- a/src/solidlsp/language_servers/clojure_lsp.py +++ b/src/solidlsp/language_servers/clojure_lsp.py @@ -13,11 +13,11 @@ import threading from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger -from solidlsp.ls_utils import FileUtils, PlatformUtils -from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from .common import RuntimeDependency, RuntimeDependencyCollection + def run_command(cmd: list, capture_output: bool = True) -> subprocess.CompletedProcess: return subprocess.run( diff --git a/src/solidlsp/language_servers/common.py b/src/solidlsp/language_servers/common.py index 78ae7cb8..a0973e56 100644 --- a/src/solidlsp/language_servers/common.py +++ b/src/solidlsp/language_servers/common.py @@ -2,8 +2,8 @@ from __future__ import annotations import os import subprocess +from collections.abc import Sequence from dataclasses import dataclass -from typing import Sequence, Dict from solidlsp.ls_logger import LanguageServerLogger from solidlsp.ls_utils import FileUtils, PlatformUtils @@ -32,11 +32,7 @@ class RuntimeDependencyCollection: self._dependencies = list(dependencies) def for_platform(self, platform_id: str) -> list[RuntimeDependency]: - return [ - d - for d in self._dependencies - if d.platform_id in (platform_id, "any", "platform-agnostic", None) - ] + return [d for d in self._dependencies if d.platform_id in (platform_id, "any", "platform-agnostic", None)] def for_current_platform(self) -> list[RuntimeDependency]: return self.for_platform(PlatformUtils.get_platform_id().value) @@ -44,9 +40,7 @@ class RuntimeDependencyCollection: def single_for_current_platform(self) -> RuntimeDependency: deps = self.for_current_platform() if len(deps) != 1: - raise RuntimeError( - f"Expected exactly one runtime dependency for {PlatformUtils.get_platform_id().value}, found {len(deps)}" - ) + raise RuntimeError(f"Expected exactly one runtime dependency for {PlatformUtils.get_platform_id().value}, found {len(deps)}") return deps[0] def binary_path(self, target_dir: str) -> str: @@ -55,13 +49,13 @@ class RuntimeDependencyCollection: return target_dir return os.path.join(target_dir, dep.binary_name) - def install(self, logger: LanguageServerLogger, target_dir: str) -> Dict[str, str]: + def install(self, logger: LanguageServerLogger, target_dir: str) -> dict[str, str]: """Install all dependencies for the current platform into *target_dir*. Returns a mapping from dependency id to the resolved binary path. """ os.makedirs(target_dir, exist_ok=True) - results: Dict[str, str] = {} + results: dict[str, str] = {} for dep in self.for_current_platform(): if dep.url: self._install_from_url(dep, logger, target_dir) diff --git a/src/solidlsp/language_servers/csharp_language_server.py b/src/solidlsp/language_servers/csharp_language_server.py index 4ead95de..7d11a1df 100644 --- a/src/solidlsp/language_servers/csharp_language_server.py +++ b/src/solidlsp/language_servers/csharp_language_server.py @@ -16,8 +16,6 @@ import zipfile from pathlib import Path from typing import cast -from .common import RuntimeDependency - from overrides import override from solidlsp.ls import SolidLanguageServer @@ -28,6 +26,7 @@ from solidlsp.ls_utils import PathUtils from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from .common import RuntimeDependency # Runtime dependencies configuration RUNTIME_DEPENDENCIES = [ diff --git a/src/solidlsp/language_servers/dart_language_server.py b/src/solidlsp/language_servers/dart_language_server.py index b369f860..5bc6d670 100644 --- a/src/solidlsp/language_servers/dart_language_server.py +++ b/src/solidlsp/language_servers/dart_language_server.py @@ -5,10 +5,10 @@ import stat from solidlsp.ls import SolidLanguageServer from solidlsp.ls_logger import LanguageServerLogger -from solidlsp.ls_utils import FileUtils, PlatformUtils -from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from .common import RuntimeDependency, RuntimeDependencyCollection + class DartLanguageServer(SolidLanguageServer): """ @@ -30,8 +30,6 @@ class DartLanguageServer(SolidLanguageServer): @classmethod def _setup_runtime_dependencies(cls, logger: "LanguageServerLogger") -> str: - platform_id = PlatformUtils.get_platform_id() - deps = RuntimeDependencyCollection( [ RuntimeDependency( diff --git a/src/solidlsp/language_servers/intelephense.py b/src/solidlsp/language_servers/intelephense.py index 277b0452..f9c83976 100644 --- a/src/solidlsp/language_servers/intelephense.py +++ b/src/solidlsp/language_servers/intelephense.py @@ -6,7 +6,6 @@ import logging import os import pathlib import shutil -import subprocess from time import sleep from overrides import override @@ -15,10 +14,11 @@ from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger from solidlsp.ls_utils import PlatformId, PlatformUtils -from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.lsp_types import DefinitionParams, InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from .common import RuntimeDependency, RuntimeDependencyCollection + class Intelephense(SolidLanguageServer): """ diff --git a/src/solidlsp/language_servers/rust_analyzer.py b/src/solidlsp/language_servers/rust_analyzer.py index 0b8442d4..bf79fa3b 100644 --- a/src/solidlsp/language_servers/rust_analyzer.py +++ b/src/solidlsp/language_servers/rust_analyzer.py @@ -13,11 +13,11 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger -from solidlsp.ls_utils import FileUtils, PlatformUtils -from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from .common import RuntimeDependency, RuntimeDependencyCollection + class RustAnalyzer(SolidLanguageServer): """ @@ -50,8 +50,6 @@ class RustAnalyzer(SolidLanguageServer): """ Setup runtime dependencies for rust_analyzer and return the command to start the server. """ - platform_id = PlatformUtils.get_platform_id() - deps = RuntimeDependencyCollection( [ RuntimeDependency( @@ -86,8 +84,6 @@ class RustAnalyzer(SolidLanguageServer): # "win-x64", # ], "Only linux-x64 and win-x64 platform is supported for in multilspy at the moment" - dependency = deps.single_for_current_platform() - rustanalyzer_ls_dir = os.path.join(cls.ls_resources_dir(), "RustAnalyzer") rustanalyzer_executable_path = deps.binary_path(rustanalyzer_ls_dir) if not os.path.exists(rustanalyzer_ls_dir): diff --git a/src/solidlsp/language_servers/terraform_ls.py b/src/solidlsp/language_servers/terraform_ls.py index b60aa2de..5a230a23 100644 --- a/src/solidlsp/language_servers/terraform_ls.py +++ b/src/solidlsp/language_servers/terraform_ls.py @@ -9,11 +9,12 @@ from overrides import override from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger -from solidlsp.ls_utils import FileUtils, PathUtils, PlatformUtils -from .common import RuntimeDependency, RuntimeDependencyCollection +from solidlsp.ls_utils import PathUtils, PlatformUtils from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from .common import RuntimeDependency, RuntimeDependencyCollection + class TerraformLS(SolidLanguageServer): """ diff --git a/src/solidlsp/language_servers/typescript_language_server.py b/src/solidlsp/language_servers/typescript_language_server.py index 54dd1245..04ce473c 100644 --- a/src/solidlsp/language_servers/typescript_language_server.py +++ b/src/solidlsp/language_servers/typescript_language_server.py @@ -6,7 +6,6 @@ import logging import os import pathlib import shutil -import subprocess import threading from time import sleep @@ -16,10 +15,11 @@ from solidlsp.ls import SolidLanguageServer from solidlsp.ls_config import LanguageServerConfig from solidlsp.ls_logger import LanguageServerLogger from solidlsp.ls_utils import PlatformId, PlatformUtils -from .common import RuntimeDependency, RuntimeDependencyCollection from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from .common import RuntimeDependency, RuntimeDependencyCollection + # Platform-specific imports if os.name != "nt": # Unix-like systems import pwd @@ -33,7 +33,7 @@ else: # Conditionally import pwd module (Unix-only) if not PlatformUtils.get_platform_id().value.startswith("win"): - import pwd + pass class TypeScriptLanguageServer(SolidLanguageServer):