diff --git a/src/multilspy/language_servers/clojure_lsp/clojure_lsp.py b/src/solidlsp/language_servers/clojure_lsp/clojure_lsp.py similarity index 65% rename from src/multilspy/language_servers/clojure_lsp/clojure_lsp.py rename to src/solidlsp/language_servers/clojure_lsp/clojure_lsp.py index 5fab017..7422e77 100644 --- a/src/multilspy/language_servers/clojure_lsp/clojure_lsp.py +++ b/src/solidlsp/language_servers/clojure_lsp/clojure_lsp.py @@ -2,30 +2,28 @@ Provides Clojure specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Clojure. """ -import asyncio +import threading import json import logging import os import stat import pathlib -from contextlib import asynccontextmanager -from typing import AsyncIterator -from multilspy.multilspy_logger import MultilspyLogger -from multilspy.language_server import LanguageServer -from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo -from multilspy.lsp_protocol_handler.lsp_types import InitializeParams -from multilspy.multilspy_config import MultilspyConfig -from multilspy.multilspy_utils import FileUtils -from multilspy.multilspy_utils import PlatformUtils +from solidlsp.ls_logger import LanguageServerLogger +from solidlsp.ls import SolidLanguageServer +from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo +from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams +from solidlsp.ls_config import LanguageServerConfig +from solidlsp.ls_utils import FileUtils +from solidlsp.ls_utils import PlatformUtils -class ClojureLSP(LanguageServer): +class ClojureLSP(SolidLanguageServer): """ Provides a clojure-lsp specific instantiation of the LanguageServer class. Contains various configurations and settings specific to clojure. """ - def __init__(self, config: MultilspyConfig, logger: MultilspyLogger, repository_root_path: str): + def __init__(self, config: LanguageServerConfig, logger: LanguageServerLogger, repository_root_path: str): """ Creates a ClojureLSP instance. This class is not meant to be instantiated directly. Use LanguageServer.create() instead. """ @@ -37,12 +35,12 @@ class ClojureLSP(LanguageServer): ProcessLaunchInfo(cmd=clojure_lsp_executable_path, cwd=repository_root_path), "clojure", ) - self.server_ready = asyncio.Event() - self.initialize_searcher_command_available = asyncio.Event() - self.resolve_main_method_available = asyncio.Event() - self.service_ready_event = asyncio.Event() + self.server_ready = threading.Event() + self.initialize_searcher_command_available = threading.Event() + self.resolve_main_method_available = threading.Event() + self.service_ready_event = threading.Event() - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str: + def setup_runtime_dependencies(self, logger: LanguageServerLogger, config: LanguageServerConfig) -> str: """ Setup runtime dependencies for clojure-lsp. """ @@ -95,14 +93,13 @@ class ClojureLSP(LanguageServer): return d - @asynccontextmanager - async def start_server(self) -> AsyncIterator["ClojureLSP"]: + def _start_server(self): """ Starts the Clojure Language Server, waits for the server to be ready and yields the LanguageServer instance. Usage: ``` - async with lsp.start_server(): + with lsp.start_server(): # LanguageServer has been initialized and ready to serve requests await lsp.request_definition(...) await lsp.request_references(...) @@ -110,7 +107,7 @@ class ClojureLSP(LanguageServer): # LanguageServer has been shutdown """ - async def register_capability_handler(params): + def register_capability_handler(params): assert "registrations" in params for registration in params["registrations"]: if registration["method"] == "workspace/executeCommand": @@ -118,24 +115,24 @@ class ClojureLSP(LanguageServer): self.resolve_main_method_available.set() return - async def lang_status_handler(params): + def lang_status_handler(params): # TODO: Should we wait for # server -> client: {'jsonrpc': '2.0', 'method': 'language/status', 'params': {'type': 'ProjectStatus', 'message': 'OK'}} # Before proceeding? if params["type"] == "ServiceReady" and params["message"] == "ServiceReady": self.service_ready_event.set() - async def execute_client_command_handler(params): + def execute_client_command_handler(params): return [] - async def do_nothing(params): + def do_nothing(params): return - async def check_experimental_status(params): + def check_experimental_status(params): if params["quiescent"] == True: self.server_ready.set() - async def window_log_message(msg): + def window_log_message(msg): self.logger.log(f"LSP: window/logMessage: {msg}", logging.INFO) self.server.on_request("client/registerCapability", register_capability_handler) @@ -147,28 +144,23 @@ class ClojureLSP(LanguageServer): self.server.on_notification("language/actionableNotification", do_nothing) self.server.on_notification("experimental/serverStatus", check_experimental_status) - async with super().start_server(): - self.logger.log("Starting clojure-lsp server process", logging.INFO) - await self.server.start() - initialize_params = self._get_initialize_params(self.repository_root_path) + self.logger.log("Starting clojure-lsp 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 = await self.server.send.initialize(initialize_params) - assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 - assert "completionProvider" in init_response["capabilities"] - # Clojure-lsp completion provider capabilities are more flexible than other servers - completion_provider = init_response["capabilities"]["completionProvider"] - assert completion_provider["resolveProvider"] == True - assert "triggerCharacters" in completion_provider - self.server.notify.initialized({}) - # after initialize, Clojure-lsp is ready to serve - self.server_ready.set() - self.completions_available.set() - - yield self - - await self.server.shutdown() - await self.server.stop() + 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"] + # Clojure-lsp completion provider capabilities are more flexible than other servers' + completion_provider = init_response["capabilities"]["completionProvider"] + assert completion_provider["resolveProvider"] == True + assert "triggerCharacters" in completion_provider + self.server.notify.initialized({}) + # after initialize, Clojure-lsp is ready to serve + self.server_ready.set() + self.completions_available.set() diff --git a/src/multilspy/language_servers/clojure_lsp/initialize_params.json b/src/solidlsp/language_servers/clojure_lsp/initialize_params.json similarity index 99% rename from src/multilspy/language_servers/clojure_lsp/initialize_params.json rename to src/solidlsp/language_servers/clojure_lsp/initialize_params.json index 808c1eb..3ab9e2a 100644 --- a/src/multilspy/language_servers/clojure_lsp/initialize_params.json +++ b/src/solidlsp/language_servers/clojure_lsp/initialize_params.json @@ -2,7 +2,7 @@ "_description": "Parameters sent by multilspy to initialize clojure-lsp (LSP v3.17)", "processId": null, "clientInfo": { - "name": "multilspy", + "name": "solidlsp", "version": "0.1.0" }, "rootUri": "$rootUri", diff --git a/src/multilspy/language_servers/clojure_lsp/runtime_dependencies.json b/src/solidlsp/language_servers/clojure_lsp/runtime_dependencies.json similarity index 100% rename from src/multilspy/language_servers/clojure_lsp/runtime_dependencies.json rename to src/solidlsp/language_servers/clojure_lsp/runtime_dependencies.json diff --git a/src/solidlsp/ls.py b/src/solidlsp/ls.py index c26e32b..e80ef0e 100644 --- a/src/solidlsp/ls.py +++ b/src/solidlsp/ls.py @@ -172,7 +172,7 @@ class SolidLanguageServer(ABC): ls = Intelephense(config, logger, repository_root_path) elif config.code_language == Language.CLOJURE: - from multilspy.language_servers.clojure_lsp.clojure_lsp import ClojureLSP + from solidlsp.language_servers.clojure_lsp.clojure_lsp import ClojureLSP ls = ClojureLSP(config, logger, repository_root_path) diff --git a/test/multilspy/clojure/test_clojure_basic.py b/test/solidlsp/clojure/test_clojure_basic.py similarity index 92% rename from test/multilspy/clojure/test_clojure_basic.py rename to test/solidlsp/clojure/test_clojure_basic.py index 29a4681..3d8e57d 100644 --- a/test/multilspy/clojure/test_clojure_basic.py +++ b/test/solidlsp/clojure/test_clojure_basic.py @@ -1,15 +1,15 @@ import pytest -from multilspy.language_server import SyncLanguageServer -from multilspy.multilspy_config import Language -from multilspy.multilspy_types import UnifiedSymbolInformation +from solidlsp.ls import SolidLanguageServer +from solidlsp.ls_config import Language +from solidlsp.ls_types import UnifiedSymbolInformation @pytest.mark.clojure class TestLanguageServerBasics: @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_basic_definition(self, language_server: SyncLanguageServer): + def test_basic_definition(self, language_server: SolidLanguageServer): """ Test finding definition of 'greet' function call in core.clj """ @@ -26,7 +26,7 @@ class TestLanguageServerBasics: @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_cross_file_references(self, language_server: SyncLanguageServer): + def test_cross_file_references(self, language_server: SolidLanguageServer): """ Test finding references to 'multiply' function from core.clj """ @@ -45,7 +45,7 @@ class TestLanguageServerBasics: @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_completions(self, language_server: SyncLanguageServer): + def test_completions(self, language_server: SolidLanguageServer): filepath = "src/test_app/utils.clj" with language_server.open_file(filepath): # After "core/" in calculate-area @@ -60,7 +60,7 @@ class TestLanguageServerBasics: @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_document_symbols(self, language_server: SyncLanguageServer): + def test_document_symbols(self, language_server: SolidLanguageServer): filepath = "src/test_app/core.clj" symbols, _ = language_server.request_document_symbols(filepath) @@ -76,7 +76,7 @@ class TestLanguageServerBasics: @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_hover(self, language_server: SyncLanguageServer): + def test_hover(self, language_server: SolidLanguageServer): # Test hover on greet function filepath = "src/test_app/core.clj" result = language_server.request_hover(filepath, 2, 7) # Position on 'greet' function name @@ -94,7 +94,7 @@ class TestLanguageServerBasics: @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_workspace_symbols(self, language_server: SyncLanguageServer): + def test_workspace_symbols(self, language_server: SolidLanguageServer): # Search for functions containing "add" result = language_server.request_workspace_symbol("add") @@ -108,7 +108,7 @@ class TestLanguageServerBasics: f"Should find 'add' function in symbols: {symbol_names}" @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_retrieve_content_around_line(self, language_server: SyncLanguageServer): + def test_retrieve_content_around_line(self, language_server: SolidLanguageServer): """Test retrieving content around specific lines""" filepath = "src/test_app/core.clj" @@ -128,7 +128,7 @@ class TestLanguageServerBasics: assert "multiply" in content_str, "Should contain multiply function" @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_namespace_functions(self, language_server: SyncLanguageServer): + def test_namespace_functions(self, language_server: SolidLanguageServer): """Test definition lookup for core/greet usage in utils.clj""" filepath = "src/test_app/utils.clj" # Position of 'greet' in core/greet call @@ -142,7 +142,7 @@ class TestLanguageServerBasics: "Should find the definition of greet in core.clj" @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_search_files_for_pattern(self, language_server: SyncLanguageServer): + def test_search_files_for_pattern(self, language_server: SolidLanguageServer): result = language_server.search_files_for_pattern("defn.*greet") assert result is not None, "Pattern search should return results" @@ -158,7 +158,7 @@ class TestLanguageServerBasics: assert len(utils_matches) > 0, "Should find require statement in utils.clj" @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_request_references_with_content(self, language_server: SyncLanguageServer): + def test_request_references_with_content(self, language_server: SolidLanguageServer): """Test references to multiply function with content""" filepath = "src/test_app/core.clj" result = language_server.request_references_with_content(filepath, 12, 6, 3) @@ -181,7 +181,7 @@ class TestLanguageServerBasics: assert "calculate-area" in utils_content @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_request_full_symbol_tree(self, language_server: SyncLanguageServer): + def test_request_full_symbol_tree(self, language_server: SolidLanguageServer): """Test retrieving the full symbol tree for project overview We just check that we find some expected symbols. """ @@ -222,7 +222,7 @@ class TestLanguageServerBasics: @pytest.mark.parametrize("language_server", [Language.CLOJURE], indirect=True) - def test_request_referencing_symbols(self, language_server: SyncLanguageServer): + def test_request_referencing_symbols(self, language_server: SolidLanguageServer): """Test finding symbols that reference a given symbol Finds references to the 'multiply' function. """