From 211b72042cbf8f5d983cea8b8ed63c342db06eeb Mon Sep 17 00:00:00 2001 From: Miguel de Benito Delgado Date: Sat, 7 Jun 2025 18:47:19 +0000 Subject: [PATCH] Support for clojure via clojure-lsp --- src/multilspy/language_server.py | 4 + .../clojure_lsp/clojure_lsp.py | 24 +- .../clojure_lsp/initialize_params.json | 227 ++---------------- src/multilspy/multilspy_config.py | 3 + 4 files changed, 31 insertions(+), 227 deletions(-) diff --git a/src/multilspy/language_server.py b/src/multilspy/language_server.py index dcf36ef..d21ff6a 100644 --- a/src/multilspy/language_server.py +++ b/src/multilspy/language_server.py @@ -197,6 +197,10 @@ class LanguageServer: from multilspy.language_servers.intelephense.intelephense import Intelephense return Intelephense(config, logger, repository_root_path) + elif config.code_language == Language.CLOJURE: + from multilspy.language_servers.clojure_lsp.clojure_lsp import ClojureLSP + + return ClojureLSP(config, logger, repository_root_path) else: logger.log(f"Language {config.code_language} is not supported", logging.ERROR) raise MultilspyException(f"Language {config.code_language} is not supported") diff --git a/src/multilspy/language_servers/clojure_lsp/clojure_lsp.py b/src/multilspy/language_servers/clojure_lsp/clojure_lsp.py index 796bc2b..5fab017 100644 --- a/src/multilspy/language_servers/clojure_lsp/clojure_lsp.py +++ b/src/multilspy/language_servers/clojure_lsp/clojure_lsp.py @@ -1,5 +1,5 @@ """ -Provides Rust specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Rust. +Provides Clojure specific instantiation of the LanguageServer class. Contains various configurations and settings specific to Clojure. """ import asyncio @@ -11,8 +11,6 @@ import pathlib from contextlib import asynccontextmanager from typing import AsyncIterator -from overrides import override - from multilspy.multilspy_logger import MultilspyLogger from multilspy.language_server import LanguageServer from multilspy.lsp_protocol_handler.server import ProcessLaunchInfo @@ -40,11 +38,10 @@ class ClojureLSP(LanguageServer): "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() - @override - def is_ignored_dirname(self, dirname: str) -> bool: - return super().is_ignored_dirname(dirname) or dirname in ["target"] - def setup_runtime_dependencies(self, logger: MultilspyLogger, config: MultilspyConfig) -> str: """ Setup runtime dependencies for clojure-lsp. @@ -162,16 +159,15 @@ class ClojureLSP(LanguageServer): init_response = await self.server.send.initialize(initialize_params) assert init_response["capabilities"]["textDocumentSync"]["change"] == 2 assert "completionProvider" in init_response["capabilities"] - assert init_response["capabilities"]["completionProvider"] == { - "resolveProvider": True, - "triggerCharacters": [":", ".", "'", "("], - "completionItem": {"labelDetailsSupport": True}, - } + # 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() - await self.server_ready.wait() - yield self await self.server.shutdown() diff --git a/src/multilspy/language_servers/clojure_lsp/initialize_params.json b/src/multilspy/language_servers/clojure_lsp/initialize_params.json index a63ef8e..808c1eb 100644 --- a/src/multilspy/language_servers/clojure_lsp/initialize_params.json +++ b/src/multilspy/language_servers/clojure_lsp/initialize_params.json @@ -1,30 +1,19 @@ { "_description": "Parameters sent by multilspy to initialize clojure-lsp (LSP v3.17)", - "processId": "os.getpid()", + "processId": null, "clientInfo": { "name": "multilspy", "version": "0.1.0" }, - "locale": "en", - "rootPath": "$rootPath", "rootUri": "$rootUri", + "rootPath": "$rootPath", "capabilities": { "workspace": { "applyEdit": true, "workspaceEdit": { - "documentChanges": true, - "resourceOperations": [ - "create", - "rename", - "delete" - ] - }, - "configuration": true, - "didChangeWatchedFiles": { - "dynamicRegistration": true + "documentChanges": true }, "symbol": { - "dynamicRegistration": true, "symbolKind": { "valueSet": [ 1, @@ -56,23 +45,12 @@ ] } }, - "executeCommand": { - "dynamicRegistration": true - }, - "workspaceFolders": true, - "semanticTokens": { - "refreshSupport": true - }, - "codeLens": { - "refreshSupport": true - }, - "fileOperations": { - "dynamicRegistration": true, - "didRename": true, - "willRename": true - } + "workspaceFolders": true }, "textDocument": { + "synchronization": { + "didSave": true + }, "publishDiagnostics": { "relatedInformation": true, "tagSupport": { @@ -80,107 +58,29 @@ 1, 2 ] - }, - "codeDescriptionSupport": true, - "dataSupport": true + } }, - "synchronization": { - "dynamicRegistration": true, - "didSave": true + "definition": { + "linkSupport": true }, + "references": {}, "completion": { - "dynamicRegistration": true, - "contextSupport": true, "completionItem": { "snippetSupport": true, "documentationFormat": [ "markdown", "plaintext" - ], - "deprecatedSupport": true, - "preselectSupport": true, - "tagSupport": { - "valueSet": [ - 1 - ] - }, - "insertReplaceSupport": true, - "resolveSupport": { - "properties": [ - "documentation", - "detail", - "additionalTextEdits" - ] - } - }, - "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 ] } }, "hover": { - "dynamicRegistration": true, "contentFormat": [ "markdown", "plaintext" ] }, - "signatureHelp": { - "dynamicRegistration": true, - "signatureInformation": { - "documentationFormat": [ - "markdown", - "plaintext" - ], - "parameterInformation": { - "labelOffsetSupport": true - } - } - }, - "declaration": { - "dynamicRegistration": true, - "linkSupport": true - }, - "definition": { - "dynamicRegistration": true, - "linkSupport": true - }, - "implementation": { - "dynamicRegistration": true, - "linkSupport": true - }, - "references": { - "dynamicRegistration": true - }, - "documentHighlight": { - "dynamicRegistration": true - }, "documentSymbol": { - "dynamicRegistration": true, + "hierarchicalDocumentSymbolSupport": true, "symbolKind": { "valueSet": [ 1, @@ -210,98 +110,6 @@ 25, 26 ] - }, - "hierarchicalDocumentSymbolSupport": true - }, - "codeAction": { - "dynamicRegistration": true, - "isPreferredSupport": true, - "resolveSupport": { - "properties": [ - "edit" - ] - }, - "codeActionLiteralSupport": { - "codeActionKind": { - "valueSet": [ - "", - "quickfix", - "refactor", - "refactor.extract", - "refactor.inline", - "refactor.rewrite", - "source", - "source.organizeImports" - ] - } - } - }, - "codeLens": { - "dynamicRegistration": true - }, - "formatting": { - "dynamicRegistration": true - }, - "rangeFormatting": { - "dynamicRegistration": true - }, - "rename": { - "dynamicRegistration": true, - "prepareSupport": true - }, - "foldingRange": { - "dynamicRegistration": true, - "lineFoldingOnly": true - }, - "selectionRange": { - "dynamicRegistration": true - }, - "semanticTokens": { - "dynamicRegistration": true, - "tokenTypes": [ - "namespace", - "class", - "function", - "method", - "variable", - "keyword", - "string", - "comment", - "number", - "regexp", - "operator", - "macro" - ], - "tokenModifiers": [ - "declaration", - "definition", - "readonly", - "static", - "deprecated", - "documentation" - ], - "formats": [ - "relative" - ], - "requests": { - "range": true, - "full": { - "delta": false - } - } - }, - "linkedEditingRange": { - "dynamicRegistration": true - }, - "callHierarchy": { - "dynamicRegistration": true - } - }, - "window": { - "workDoneProgress": true, - "showMessage": { - "messageActionItem": { - "additionalPropertiesSupport": true } } }, @@ -309,20 +117,13 @@ "positionEncodings": [ "utf-16" ] - }, - "experimental": { - "testTree": true, - "projectTree": true } }, "initializationOptions": { "dependency-scheme": "jar", - "document-formatting?": true, - "document-range-formatting?": true, - "text-document-sync-kind": "incremental", - "semantic-tokens?": true + "text-document-sync-kind": "incremental" }, - "trace": "verbose", + "trace": "off", "workspaceFolders": [ { "uri": "$uri", diff --git a/src/multilspy/multilspy_config.py b/src/multilspy/multilspy_config.py index 2b4f7ca..3d34bae 100644 --- a/src/multilspy/multilspy_config.py +++ b/src/multilspy/multilspy_config.py @@ -38,6 +38,7 @@ class Language(str, Enum): DART = "dart" CPP = "cpp" PHP = "php" + CLOJURE = "clojure" def __str__(self) -> str: return self.value @@ -68,6 +69,8 @@ class Language(str, Enum): return FilenameMatcher("*.dart") case self.PHP: return FilenameMatcher("*.php") + case self.CLOJURE: + return FilenameMatcher("*.clj", "*.cljs", "*.cljc", "*.edn") case _: raise ValueError(f"Unhandled language: {self}")