diff --git a/.lsp/config.edn b/.lsp/config.edn index cf721e3..4c54a8f 100644 --- a/.lsp/config.edn +++ b/.lsp/config.edn @@ -1 +1,5 @@ -{:cljfmt {:indents {proxy+ [[:block 2] [:inner 1]]}}} +{:cljfmt {:indents {proxy+ [[:block 2] [:inner 1]]}} + :project-specs [{:classpath-cmd ["./gradlew" "-q" "classpath"] + :project-path "build.gradle.kts"} + {:classpath-cmd ["clojure" "-Spath"] + :project-path "deps.edn"}]} diff --git a/CHANGELOG.md b/CHANGELOG.md index beee359..f41f311 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - Fix brace matcher to insert closing brace for some missing cases. - Add imcompatible tag with Cursive and Clojure-Kit plugins. +- Integrate with lsp4ij, a LSP client plugin, removing lots of logics from this plugin and fixing multiple bugs and issues. Fixes #63, #61, #59, #57, #53, #36, #21, #9, #5 +- Drop support for older intellijs, supporting 2023.3 onwards. ## 2.6.4 diff --git a/README.md b/README.md index 233625c..6ace6fe 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@

homepagefeatures • - capabilitiesdeveloping troubleshooting • @@ -30,7 +29,7 @@ IntelliJ is the only mainstream editor without a plugin with good, free, and dedicated support for LSP, there are already excellent plugins for Clojure like [Cursive](https://cursive-ide.com/) which provides lots of features with REPL support or [ClojureKit](https://github.com/gregsh/Clojure-Kit) which adds basic Clojure support for the language, but none uses clojure-lsp or follows the LSP standard which some users may prefer as [some features](https://clojure-lsp.io/features/) are only available in clojure-lsp. -Keep in mind that this plugin provides only LSP features relying on clojure-lsp (and clj-kondo under the hood) static analysis, so no runtime features exist, like REPL integration or support, for that a separated plugin is needed as it's not possible to use only the REPL part of other plugins like Cursive together with this plugin. +Keep in mind that this plugin provides only LSP features relying on clojure-lsp (and clj-kondo under the hood) static analysis, so no runtime features exist, like REPL integration or support, for that a separated plugin like [clojure-repl-intellij](https://github.com/afucher/clojure-repl-intellij) is recommended as it's not possible to use only the REPL part of other plugins like Cursive together with this plugin. Also, this plugin does not use official IntelliJ's LSP support yet for 2 reasons: - It's only available for Ultimate Edition (paid edition), making this plugin only available for those editions. diff --git a/build.gradle.kts b/build.gradle.kts index 9f42bf4..1203fae 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,20 +23,13 @@ repositories { } dependencies { - implementation ("org.clojure:clojure:1.11.1") + implementation ("org.clojure:clojure:1.12.0") implementation ("com.github.ericdallo:clj4intellij:0.5.4") implementation ("seesaw:seesaw:1.5.0") implementation ("camel-snake-kebab:camel-snake-kebab:0.4.3") - implementation ("babashka:process:0.5.18") - implementation ("com.github.clojure-lsp:lsp4clj:1.9.3") - implementation ("org.clojure:core.async:1.5.648") { - because("issue https://clojure.atlassian.net/browse/ASYNC-248") - } - implementation ("com.github.clojure-lsp:clojure-lsp:2024.03.01-11.37.51") { - exclude("org.clojure", "core.async") - } implementation ("com.rpl:proxy-plus:0.0.9") - implementation ("markdown-clj:markdown-clj:1.12.1") + implementation ("dev.weavejester:cljfmt:0.13.0") + implementation ("com.github.clojure-lsp:clojure-lsp:2025.01.22-23.28.23") } sourceSets { @@ -70,16 +63,22 @@ changelog { } java { - targetCompatibility = JavaVersion.VERSION_11 - sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_17 +} + +tasks.register("classpath") { + doFirst { + println(sourceSets["main"].compileClasspath.asPath) + } } tasks { compileKotlin { kotlinOptions { - jvmTarget = "11" - apiVersion = "1.5" - languageVersion = "1.5" + jvmTarget = "17" + apiVersion = "1.9" + languageVersion = "1.9" freeCompilerArgs = listOf("-Xjvm-default=all") } } diff --git a/deps.edn b/deps.edn index 168521a..19ea1b8 100644 --- a/deps.edn +++ b/deps.edn @@ -1,26 +1,5 @@ {:paths ["src/main/clojure"] - :mvn/repos {"intellij-1" {:url "https://cache-redirector.jetbrains.com/intellij-dependencies"} - "intellij-2" {:url "https://www.jetbrains.com/intellij-repository/releases"}} - :deps {org.clojure/clojure {:mvn/version "1.11.1"} - com.github.ericdallo/clj4intellij {:mvn/version "0.5.4"} - seesaw/seesaw {:mvn/version "1.5.0"} - camel-snake-kebab/camel-snake-kebab {:mvn/version "0.4.3"} - com.github.clojure-lsp/clojure-lsp {:mvn/version "2024.03.01-11.37.51"} - markdown-clj/markdown-clj {:mvn/version "1.12.1"} - com.rpl/proxy-plus {:mvn/version "0.0.9"}} - :aliases {:dev {:deps {nrepl/nrepl {:mvn/version "1.0.0"} - com.jetbrains.intellij.platform/ide-impl {:mvn/version "213.7172.48" - :exclusions [org.jetbrains.kotlin/kotlin-stdlib-jdk8 - org.jetbrains.kotlin/kotlin-reflect - org.jetbrains.kotlinx/kotlinx-coroutines-jdk8 - org.jetbrains.kotlinx/kotlinx-serialization-json-jvm - org.jetbrains.kotlinx/kotlinx-serialization-core-jvm - - com.jetbrains.intellij.platform/external-system-impl - com.jetbrains.intellij.platform/service-container - com.jetbrains.intellij.platform/statistics-devkit - com.jetbrains.infra/download-pgp-verifier]}}} - :build {:extra-paths ["src/main/resources"] + :aliases {:build {:extra-paths ["src/main/resources"] :deps {io.github.clojure/tools.build {:git/tag "v0.8.1" :git/sha "7d40500"} slipset/deps-deploy {:mvn/version "0.2.0"}} diff --git a/docs/capabilities.md b/docs/capabilities.md deleted file mode 100644 index 31fe2ce..0000000 --- a/docs/capabilities.md +++ /dev/null @@ -1,79 +0,0 @@ -# LSP implemented capabilities - -Below are all the currently supported LSP capabilities and their implementation status: - -| capability | done | notes | -|----------------------------------------|------|-------| -| initialize | √ | | -| initialized | √ | | -| shutdown | √ | | -| exit | | | -| $/cancelRequest | | | -| $/progress | √ | | -| window/showDocument | | | -| window/showMessage | √ | | -| window/showMessageRequest | √ | | -| window/logMessage | | | -| window/workDoneProgress/create | | | -| window/workDoneProgress/cancel | | | -| telemetry/event | | | -| client/registerCapability | | | -| client/unregisterCapability | | | -| workspace/workspaceFolders | | | -| workspace/didChangeWorkspaceFolders | | | -| workspace/didChangeConfiguration | | | -| workspace/configuration | | | -| workspace/didChangeWatchedFiles | | | -| workspace/symbol | | | -| workspace/executeCommand | √ | | -| workspace/applyEdit | √ | | -| workspace/willRenameFiles | √ | | -| workspace/didRenameFiles | | | -| workspace/willCreateFiles | | | -| workspace/didCreateFiles | | | -| workspace/willDeleteFiles | | | -| workspace/didDeleteFiles | | | -| textDocument/didOpen | √ | | -| textDocument/didChange | √ | | -| textDocument/willSave | | | -| textDocument/willSaveWaitUntil | | | -| textDocument/didSave | √ | | -| textDocument/didClose | √ | | -| textDocument/publishDiagnostics | √ | | -| textDocument/completion | √ | | -| completionItem/resolve | | | -| textDocument/hover | √ | | -| textDocument/signatureHelp | | | -| textDocument/declaration | | | -| textDocument/definition | √ | | -| textDocument/typeDefinition | | | -| textDocument/implementation | √ | | -| textDocument/references | √ | | -| textDocument/documentHighlight | | | -| textDocument/documentSymbol | | | -| textDocument/codeAction | √ | | -| codeAction/resolve | | | -| textDocument/codeLens | √ | | -| codeLens/resolve | √ | | -| textDocument/documentLink | | | -| documentLink/resolve | | | -| textDocument/documentColor | | | -| textDocument/colorPresentation | | | -| textDocument/formatting | √ | | -| textDocument/rangeFormatting | | | -| textDocument/onTypeFormatting | | | -| textDocument/rename | √ | | -| textDocument/prepareRename | √ | | -| textDocument/foldingRange | | | -| textDocument/selectionRange | | | -| textDocument/semanticTokens/full | | | -| textDocument/semanticTokens/full/delta | | | -| textDocument/semanticTokens/range | | | -| workspace/semanticTokens/refresh | | | -| workspace/codeLens/refresh | | | -| textDocument/linkedEditingRange | | | -| textDocument/prepareCallHierarchy | | | -| callHierarchy/incomingCalls | | | -| callHierarchy/outgoingCalls | | | -| textDocument/moniker | | | - diff --git a/docs/features.md b/docs/features.md index 815ffc4..21c60e4 100644 --- a/docs/features.md +++ b/docs/features.md @@ -1,6 +1,6 @@ # Features -This plugin relies on clojure-lsp features, so most work is done there, for more detailed information about all clojure-lsp features, check the [server documentation](https://clojure-lsp.io/features/). +This plugin relies on [clojure-lsp](https://clojure-lsp.io/) features and [lsp4ij](https://github.com/redhat-developer/lsp4ij), so most work is done there, for more detailed information about all clojure-lsp features, check the [server documentation](https://clojure-lsp.io/features/). ### Find a function/var definition @@ -10,13 +10,13 @@ This plugin relies on clojure-lsp features, so most work is done there, for more ### Find all references of a function, var, keyword or namespace alias -> Alt + F7 +> Ctrl + Alt + h ![](../images/find-references.png) ### Find all implementations of a defmulti or defprotocol -> Ctrl/Cmd + Alt + B +> Ctrl/Cmd + Alt + g ![](../images/find-implementations.png) @@ -50,6 +50,18 @@ This plugin relies on clojure-lsp features, so most work is done there, for more > Ctrl/Cmd + Alt + l +### Call hierarchy + +> Ctrl + Alt + h + +![](../images/call-hierarchy.png) + +### Document symbols + +> Ctrl/Cmd + F12 + +![](../images/document-symbols.png) + ### Wizard to create new Clojure projects ![](../images/wizard.png) @@ -60,7 +72,7 @@ This plugin relies on clojure-lsp features, so most work is done there, for more ![](../images/code-actions.png) -#### Clean namespaces +#### Clean namespaces (Organize/Optimize imports) > Ctrl/Cmd + Alt + o @@ -70,22 +82,34 @@ This plugin relies on clojure-lsp features, so most work is done there, for more > Alt + ] +![](../images/paredit-slurp-forward.gif) + ##### Forward barf > Alt + [ +![](../images/paredit-barf-forward.gif) + ##### Backward slurp > Alt + Shift + ] +![](../images/paredit-slurp-backward.gif) + ##### Backward barf > Alt + Shift + [ +![](../images/paredit-barf-backward.gif) + ##### Raise sexpr > Alt + r +![](../images/paredit-raise.gif) + ##### Kill sexpr > Alt + k + +![](../images/paredit-kill.gif) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 2de1b56..da831e8 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,5 +1,5 @@ # Troubleshooting -You can find details about the server in Intellij's `Settings` > `Tools` > `Clojure LSP`. +You can find details about the server in Intellij's `Settings` > `Tools` > `Clojure LSP` or checking [lsp4ij troubleshooting](https://github.com/redhat-developer/lsp4ij/blob/main/docs/UserGuide.md). For more troubleshooting details related to Clojure LSP server, check the [troubleshooting section](https://clojure-lsp.io/troubleshooting/). diff --git a/gradle.properties b/gradle.properties index 1be1c5b..43c6605 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,22 +7,22 @@ pluginVersion = 2.6.4 # See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html # for insight into build numbers and IntelliJ Platform versions. -pluginSinceBuild = 213 +pluginSinceBuild = 233 # Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl # See https://jb.gg/intellij-platform-builds-list for available build versions. -pluginVerifierIdeVersions = 2021.3.3, 2022.1, 2022.2, 2022.3, 2023.1, 2023.2 +pluginVerifierIdeVersions = 2023.3 # IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties platformType = IC -platformVersion = 2021.3 +platformVersion = 2023.3 # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22 -platformPlugins = com.intellij.java +platformPlugins = com.intellij.java, com.redhat.devtools.lsp4ij:0.10.0 # Gradle Releases -> https://github.com/gradle/gradle/releases -gradleVersion = 7.6.1 +gradleVersion = 8.5 # Opt-out flag for bundling Kotlin standard library. # See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details. diff --git a/images/call-hierarchy.png b/images/call-hierarchy.png new file mode 100644 index 0000000..3a1be00 Binary files /dev/null and b/images/call-hierarchy.png differ diff --git a/images/code-actions.png b/images/code-actions.png index 4ef99ed..8a63b06 100644 Binary files a/images/code-actions.png and b/images/code-actions.png differ diff --git a/images/code-lens.png b/images/code-lens.png index 1450cf8..d6d5743 100644 Binary files a/images/code-lens.png and b/images/code-lens.png differ diff --git a/images/completion.png b/images/completion.png index 37e1a94..bf82371 100644 Binary files a/images/completion.png and b/images/completion.png differ diff --git a/images/diagnostics.png b/images/diagnostics.png index 049cfee..70b5d66 100644 Binary files a/images/diagnostics.png and b/images/diagnostics.png differ diff --git a/images/document-symbols.png b/images/document-symbols.png new file mode 100644 index 0000000..0668464 Binary files /dev/null and b/images/document-symbols.png differ diff --git a/images/find-definition.png b/images/find-definition.png index 20bc659..b604628 100644 Binary files a/images/find-definition.png and b/images/find-definition.png differ diff --git a/images/find-implementations.png b/images/find-implementations.png index 4d79ff4..95ce874 100644 Binary files a/images/find-implementations.png and b/images/find-implementations.png differ diff --git a/images/find-references.png b/images/find-references.png index d155854..f63f5cd 100644 Binary files a/images/find-references.png and b/images/find-references.png differ diff --git a/images/hover.png b/images/hover.png index f9ff433..88a198c 100644 Binary files a/images/hover.png and b/images/hover.png differ diff --git a/images/paredit-barf-backward.gif b/images/paredit-barf-backward.gif new file mode 100644 index 0000000..c244404 Binary files /dev/null and b/images/paredit-barf-backward.gif differ diff --git a/images/paredit-barf-forward.gif b/images/paredit-barf-forward.gif new file mode 100644 index 0000000..34a9960 Binary files /dev/null and b/images/paredit-barf-forward.gif differ diff --git a/images/paredit-kill.gif b/images/paredit-kill.gif new file mode 100644 index 0000000..e1971a8 Binary files /dev/null and b/images/paredit-kill.gif differ diff --git a/images/paredit-raise.gif b/images/paredit-raise.gif new file mode 100644 index 0000000..5be4e09 Binary files /dev/null and b/images/paredit-raise.gif differ diff --git a/images/paredit-slurp-backward.gif b/images/paredit-slurp-backward.gif new file mode 100644 index 0000000..aac62bb Binary files /dev/null and b/images/paredit-slurp-backward.gif differ diff --git a/images/paredit-slurp-forward.gif b/images/paredit-slurp-forward.gif new file mode 100644 index 0000000..5ee2543 Binary files /dev/null and b/images/paredit-slurp-forward.gif differ diff --git a/images/rename.png b/images/rename.png index 22377fa..c45b6aa 100644 Binary files a/images/rename.png and b/images/rename.png differ diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/action/implementations.clj b/src/main/clojure/com/github/clojure_lsp/intellij/action/implementations.clj deleted file mode 100644 index 89c3ecf..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/action/implementations.clj +++ /dev/null @@ -1,60 +0,0 @@ -(ns com.github.clojure-lsp.intellij.action.implementations - (:require - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.github.clojure-lsp.intellij.psi :as psi] - [com.github.ericdallo.clj4intellij.util :as util]) - (:import - [com.intellij.codeInsight.hint HintManager] - [com.intellij.codeInsight.navigation NavigationUtil] - [com.intellij.find.findUsages FindUsagesOptions] - [com.intellij.openapi.actionSystem CommonDataKeys] - [com.intellij.openapi.actionSystem AnActionEvent] - [com.intellij.openapi.editor Editor] - [com.intellij.openapi.project Project] - [com.intellij.psi PsiElement] - [com.intellij.usages - Usage - UsageInfo2UsageAdapter - UsageTarget - UsageViewManager - UsageViewPresentation] - [com.intellij.usageView UsageInfo])) - -(set! *warn-on-reflection* true) - -(defn show-implementations [^Editor editor line character] - (if-let [client (lsp-client/connected-client (.getProject editor))] - (let [implementations @(lsp-client/request! client [:textDocument/implementation - {:text-document {:uri (editor/editor->uri editor)} - :position {:line line - :character character}}]) - project ^Project (.getProject editor)] - (if (seq implementations) - (if (= 1 (count implementations)) - (NavigationUtil/activateFileWithPsiElement (psi/element->psi-element (first implementations) project) true) - (let [elements (->> implementations - (mapv #(psi/element->psi-element % project))) - usages (mapv (fn [^PsiElement element] - (UsageInfo2UsageAdapter. - (UsageInfo. element false))) elements) - options (FindUsagesOptions. project)] - (.showUsages (UsageViewManager/getInstance project) - (into-array UsageTarget []) - (into-array Usage usages) - (doto (UsageViewPresentation.) - (.setScopeText (.getDisplayName (.searchScope options))) - (.setSearchString (.generateUsagesString options)) - (.setTabText (str (count implementations) " implementations")) - (.setTabName "implementations") - (.setShowCancelButton true) - (.setOpenInNewTab false))))) - (.showErrorHint (HintManager/getInstance) - editor - "No implementations found"))) - (.showErrorHint (HintManager/getInstance) ^Editor editor "LSP not connected" HintManager/RIGHT))) - -(defn find-implementations-action [^AnActionEvent event] - (when-let [editor ^Editor (.getData event CommonDataKeys/EDITOR)] - (let [[line character] (util/editor->cursor-position editor)] - (show-implementations editor line character)))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/action/refactors.clj b/src/main/clojure/com/github/clojure_lsp/intellij/action/refactors.clj deleted file mode 100644 index 9f124da..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/action/refactors.clj +++ /dev/null @@ -1,26 +0,0 @@ -(ns com.github.clojure-lsp.intellij.action.refactors - (:require - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.github.ericdallo.clj4intellij.tasks :as tasks] - [com.github.ericdallo.clj4intellij.util :as util]) - (:import - [com.intellij.codeInsight.hint HintManager] - [com.intellij.openapi.actionSystem AnActionEvent] - [com.intellij.openapi.actionSystem CommonDataKeys] - [com.intellij.openapi.editor Editor])) - -(set! *warn-on-reflection* true) - -(defn execute-refactor-action [command-name ^AnActionEvent event] - (when-let [editor ^Editor (.getData event CommonDataKeys/EDITOR)] - (if-let [client (lsp-client/connected-client (.getProject editor))] - (let [[line character] (util/editor->cursor-position editor)] - (tasks/run-background-task! - (.getProject editor) - "LSP: refactoring" - (fn [_] - (lsp-client/request! client [:workspace/executeCommand - {:command command-name - :arguments [(editor/editor->uri editor) line character]}])))) - (.showErrorHint (HintManager/getInstance) ^Editor editor "LSP not connected" HintManager/RIGHT)))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/action/references.clj b/src/main/clojure/com/github/clojure_lsp/intellij/action/references.clj deleted file mode 100644 index c1ac686..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/action/references.clj +++ /dev/null @@ -1,63 +0,0 @@ -(ns com.github.clojure-lsp.intellij.action.references - (:require - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.github.clojure-lsp.intellij.psi :as psi] - [com.github.ericdallo.clj4intellij.util :as util]) - (:import - [com.intellij.codeInsight.hint HintManager] - [com.intellij.codeInsight.navigation NavigationUtil] - [com.intellij.find.findUsages FindUsagesOptions] - [com.intellij.openapi.actionSystem AnActionEvent CommonDataKeys] - [com.intellij.openapi.editor Editor] - [com.intellij.openapi.project Project] - [com.intellij.psi PsiElement] - [com.intellij.usages - Usage - UsageInfo2UsageAdapter - UsageTarget - UsageViewManager - UsageViewPresentation] - [com.intellij.usageView UsageInfo])) - -(set! *warn-on-reflection* true) - -(defn get-references [^Editor editor line character client] - (let [project ^Project (.getProject editor)] - (->> (lsp-client/request! client [:textDocument/references - {:text-document {:uri (editor/editor->uri editor)} - :position {:line line - :character character}}]) - deref - (mapv #(psi/element->psi-element % project))))) - -(defn show-references [^Editor editor line character client] - (when-let [references (get-references editor line character client)] - (if (seq references) - (if (= 1 (count references)) - (NavigationUtil/activateFileWithPsiElement (first references) true) - (let [project ^Project (.getProject editor) - usages (mapv (fn [^PsiElement element] - (UsageInfo2UsageAdapter. - (UsageInfo. element false))) references) - options (FindUsagesOptions. project)] - (.showUsages (UsageViewManager/getInstance project) - (into-array UsageTarget []) - (into-array Usage usages) - (doto (UsageViewPresentation.) - (.setScopeText (.getDisplayName (.searchScope options))) - (.setSearchString (.generateUsagesString options)) - (.setTabText (str (count references) " references")) - (.setTabName "references") - (.setShowCancelButton true) - (.setOpenInNewTab false))))) - (.showErrorHint (HintManager/getInstance) - editor - "No references found")))) - -(defn find-references-action [^AnActionEvent event] - (when-let [editor ^Editor (.getData event CommonDataKeys/EDITOR)] - (if-let [client (lsp-client/connected-client (.getProject editor))] - (let [[line character] (util/editor->cursor-position editor)] - (show-references editor line character client)) - (.showErrorHint (HintManager/getInstance) ^Editor editor "LSP not connected" HintManager/RIGHT)))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/client.clj b/src/main/clojure/com/github/clojure_lsp/intellij/client.clj index 53474d1..583ccde 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/client.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/client.clj @@ -1,150 +1,57 @@ (ns com.github.clojure-lsp.intellij.client (:require - [clojure.core.async :as async] - [clojure.string :as string] - [com.github.clojure-lsp.intellij.db :as db] - [com.github.ericdallo.clj4intellij.logger :as logger] - [lsp4clj.coercer :as coercer] - [lsp4clj.io-chan :as io-chan] - [lsp4clj.lsp.requests :as lsp.requests] - [lsp4clj.lsp.responses :as lsp.responses] - [lsp4clj.protocols.endpoint :as protocols.endpoint]) + [clojure.walk :as walk] + [com.github.clojure-lsp.intellij.client :as lsp-client] + [com.github.ericdallo.clj4intellij.logger :as logger]) (:import - [com.intellij.openapi.project Project])) + [com.github.clojure_lsp.intellij ClojureLanguageServer] + [com.intellij.openapi.project Project] + [com.redhat.devtools.lsp4ij LanguageServerItem LanguageServerManager] + [com.redhat.devtools.lsp4ij.commands CommandExecutor LSPCommandContext] + [java.util List] + [org.eclipse.lsp4j + Command + Position + ReferenceContext + ReferenceParams + TextDocumentIdentifier])) (set! *warn-on-reflection* true) -(defmulti show-message (fn [_context args] args)) -(defmulti show-document (fn [_context args] args)) -(defmulti show-message-request identity) -(defmulti progress (fn [_context {:keys [token]}] token)) -(defmulti workspace-apply-edit (fn [_context {:keys [label]}] label)) - -(defn ^:private publish-diagnostics [{:keys [project]} {:keys [uri diagnostics]}] - (db/assoc-in project [:diagnostics uri] diagnostics)) - -(defn ^:private receive-message - [client context message] - (let [message-type (coercer/input-message-type message)] - (try - (let [response - (case message-type - (:parse-error :invalid-request) - (protocols.endpoint/log client :error "Error reading message" message) - :request - (protocols.endpoint/receive-request client context message) - (:response.result :response.error) - (protocols.endpoint/receive-response client message) - :notification - (protocols.endpoint/receive-notification client context message))] - ;; Ensure client only responds to requests - (when (identical? :request message-type) - response)) - (catch Throwable e - (protocols.endpoint/log client :error "Error receiving:" e) - (throw e))))) - -(defrecord Client [client-id - input-ch - output-ch - join - request-id - sent-requests - trace-level] - protocols.endpoint/IEndpoint - (start [this context] - (protocols.endpoint/log this :verbose "lifecycle:" "starting") - (let [pipeline (async/pipeline-blocking - 1 ;; no parallelism preserves server message order - output-ch - ;; TODO: return error until initialize request is received? https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#initialize - ;; `keep` means we do not reply to responses and notifications - (keep #(receive-message this context %)) - input-ch)] - (async/thread - ;; wait for pipeline to close, indicating input closed - (async/!! output-ch req) - p)) - (send-notification [this method body] - (let [notif (lsp.requests/notification method body)] - (protocols.endpoint/log this :messages "sending notification:" notif) - (async/>!! output-ch notif))) - (receive-response [this {:keys [id] :as resp}] - (if-let [{:keys [request start-ns]} (get @sent-requests id)] - (let [ms (float (/ (- (System/nanoTime) start-ns) 1000000))] - (protocols.endpoint/log this :messages (format "received response (%.0fms):" ms) resp) - (swap! sent-requests dissoc id) - (deliver request (if (:error resp) - resp - (:result resp)))) - (protocols.endpoint/log this :error "received response for unmatched request:" resp))) - (receive-request [this context {:keys [id method params] :as req}] - (protocols.endpoint/log this :messages "received request:" req) - (when-let [response-body (case method - "window/showMessageRequest" (show-message-request params) - "window/showDocument" (show-document context params) - "workspace/applyEdit" (workspace-apply-edit context params) - (logger/warn "Unknown LSP request method" method))] - (let [resp (lsp.responses/response id response-body)] - (protocols.endpoint/log this :messages "sending response:" resp) - resp))) - (receive-notification [this context {:keys [method params] :as notif}] - (protocols.endpoint/log this :messages "received notification:" notif) - (case method - "window/showMessage" (show-message context params) - "$/progress" (progress context params) - "textDocument/publishDiagnostics" (publish-diagnostics context params) - - (logger/warn "Unknown LSP notification method" method)))) - -(defn client [in out trace-level] - (map->Client - {:client-id 1 - :input-ch (io-chan/input-stream->input-chan out) - :output-ch (io-chan/output-stream->output-chan in) - :join (promise) - :sent-requests (atom {}) - :request-id (atom 0) - :trace-level trace-level})) - -(defn start-client! [client context] - (protocols.endpoint/start client context)) - -(defn request! [client [method body]] - (protocols.endpoint/send-request client (subs (str method) 1) body)) - -(defn notify! [client [method body]] - (protocols.endpoint/send-notification client (subs (str method) 1) body)) - -(defn connected-client [^Project project] - (when (identical? :connected (db/get-in project [:status])) - (db/get-in project [:client]))) +(defn server-status [^Project project] + (when-let [manager (LanguageServerManager/getInstance project)] + (keyword (.toString (.getServerStatus manager "clojure-lsp"))))) + +(defn server-info [^Project project] + (when (identical? :started (lsp-client/server-status project)) + (when-let [manager (LanguageServerManager/getInstance project)] + (when-let [server (.getServer ^LanguageServerItem @(.getLanguageServer manager "clojure-lsp"))] + (some->> (.serverInfo ^ClojureLanguageServer server) + deref + (into {}) + walk/keywordize-keys))))) + +(defn dependency-contents [^String uri ^Project project] + (when-let [manager (LanguageServerManager/getInstance project)] + (when-let [server (.getServer ^LanguageServerItem @(.getLanguageServer manager "clojure-lsp"))] + (some->> (.dependencyContents ^ClojureLanguageServer server {"uri" uri}) + deref)))) + +(defn references [^String uri line character ^Project project] + (when-let [manager (LanguageServerManager/getInstance project)] + (when-let [server (.getServer ^LanguageServerItem @(.getLanguageServer manager "clojure-lsp"))] + (some-> (.getTextDocumentService ^ClojureLanguageServer server) + (.references (ReferenceParams. (TextDocumentIdentifier. uri) + (Position. line character) + (ReferenceContext. false))) + deref)))) + +(defn execute-command [^String name ^String text ^List args ^Project project] + (try + (-> (CommandExecutor/executeCommand + (doto (LSPCommandContext. (Command. text name args) project) + (.setPreferredLanguageServerId "clojure-lsp"))) + (.response) + deref) + (catch Exception e + (logger/error "Error appllying command" name (with-out-str (.printStackTrace e)))))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/db.clj b/src/main/clojure/com/github/clojure_lsp/intellij/db.clj index 4953154..59454ba 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/db.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/db.clj @@ -1,9 +1,6 @@ (ns com.github.clojure-lsp.intellij.db (:refer-clojure :exclude [get-in assoc-in update-in]) - (:require - [clojure.core.async :as async]) (:import - [com.github.clojure_lsp.intellij.extension SettingsState] [com.intellij.openapi.project Project])) (set! *warn-on-reflection* true) @@ -11,18 +8,10 @@ (def ^:private empty-project {:status :disconnected :downloaded-server-path nil - :client nil - :server-process nil :project nil - :diagnostics {} - :settings {:trace-level "off" - :server-path nil}}) + :on-status-changed-fns []}) -(defonce db* (atom {:projects {} - :on-status-changed-fns []})) - -(defn empty-db? [] - (empty? (:projects @db*))) +(defonce db* (atom {:projects {}})) (defn get-in ([project fields] @@ -38,45 +27,9 @@ (defn init-db-for-project [^Project project] (swap! db* update :projects (fn [projects] - (if (clojure.core/get projects (.getBasePath project)) + (if (clojure.core/get-in projects [(.getBasePath project) :project]) projects - (assoc projects (.getBasePath project) (assoc empty-project :project project)))))) - -(defn await-field [project field fn] - (async/thread - (loop [] - (Thread/sleep 100) - (let [value (get-in project [field])] - (if value - (fn value) - (recur)))))) - -(defn load-settings-from-state! [^Project project ^SettingsState settings-state] - (update-in project [:settings] (fn [settings] - (if-not (:loaded-settings? settings) - (-> settings - (assoc :loaded-settings? true) - (update :server-path #(or (.getServerPath settings-state) %)) - (update :trace-level #(or (.getTraceLevel settings-state) %)) - (update :log-path #(or (.getServerLogPath settings-state) %))) - settings)))) - -(defn set-trace-level-setting! [^SettingsState settings-state trace-level] - (doseq [project-path (keys (:projects @db*))] - (.setTraceLevel settings-state trace-level) - (swap! db* clojure.core/assoc-in [:projects project-path :settings :trace-level] trace-level))) - -(defn set-server-log-path-setting! [^SettingsState settings-state log-path] - (doseq [project-path (keys (:projects @db*))] - (let [log-path (not-empty log-path)] - (.setServerLogPath settings-state log-path) - (swap! db* clojure.core/assoc-in [:projects project-path :settings :log-path] log-path)))) - -(defn set-server-path-setting! [^SettingsState settings-state server-path] - (doseq [project-path (keys (:projects @db*))] - (let [server-path (not-empty server-path)] - (.setServerPath settings-state server-path) - (swap! db* clojure.core/assoc-in [:projects project-path :settings :server-path] server-path)))) + (update projects (.getBasePath project) #(merge (assoc empty-project :project project) %)))))) (defn all-projects [] (remove nil? diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/editor.clj b/src/main/clojure/com/github/clojure_lsp/intellij/editor.clj index 42dc7eb..1ae12d6 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/editor.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/editor.clj @@ -1,56 +1,25 @@ (ns com.github.clojure-lsp.intellij.editor (:require - [clojure-lsp.shared :as lsp.shared] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.github.ericdallo.clj4intellij.util :as util]) + [com.github.clojure-lsp.intellij.db :as db] + [com.github.clojure-lsp.intellij.editor :as editor]) (:import - [com.intellij.openapi.editor Document Editor] - [com.intellij.openapi.fileEditor - FileDocumentManager] - [com.intellij.openapi.project Project ProjectLocator] - [com.intellij.openapi.util TextRange] + [com.intellij.openapi.editor Editor] + [com.intellij.openapi.fileEditor FileDocumentManager] + [com.intellij.openapi.project ProjectLocator] [com.intellij.openapi.util.text StringUtil] - [com.intellij.openapi.vfs VirtualFile] - [com.intellij.psi PsiFile] - [com.intellij.psi PsiManager])) + [com.intellij.openapi.vfs VirtualFile])) (set! *warn-on-reflection* true) -(defn offset->cursor-position [^Editor editor offset] - (let [text (.getCharsSequence (.getDocument editor)) - line-col (StringUtil/offsetToLineColumn text offset)] - [(.line line-col) (.column line-col)])) - -(defn position->offset [text line character] - (StringUtil/lineColToOffset text line character)) - -(defn document+position->offset ^Integer [{:keys [line character]} ^Document document] - (position->offset (.getText document) line character)) - -(defn range->text-range ^TextRange [range ^Document document] - (TextRange/create (document+position->offset (:start range) document) - (document+position->offset (:end range) document))) - -(defn text-range->range [^TextRange range ^Editor editor] - {:start (offset->cursor-position editor (.getStartOffset range)) - :end (offset->cursor-position editor (.getEndOffset range))}) - -(defn uri->psi-file ^PsiFile [^String uri ^Project project] - (.findFile (PsiManager/getInstance project) - (util/uri->v-file uri))) - (defn editor->uri [^Editor editor] ;; TODO sanitize URL, encode, etc (.getUrl (.getFile (FileDocumentManager/getInstance) (.getDocument editor)))) -(defn filename->project-relative-filename [filename ^Project project] - (lsp.shared/relativize-filepath - filename - (.getBasePath project))) - -(defn virtual->psi-file ^PsiFile [^VirtualFile v-file ^Project project] - (.findFile (PsiManager/getInstance project) v-file)) +(defn offset->cursor-position [^Editor editor offset] + (let [text (.getCharsSequence (.getDocument editor)) + line-col (StringUtil/offsetToLineColumn text offset)] + [(.line line-col) (.column line-col)])) -(defn v-file->project ^Project [^VirtualFile v-file] - (.guessProjectForFile (ProjectLocator/getInstance) - v-file)) +(defn guess-project-for [^VirtualFile file] + (or (.guessProjectForFile (ProjectLocator/getInstance) file) + (first (db/all-projects)))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/annotator.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/annotator.clj deleted file mode 100644 index 0e96a0a..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/annotator.clj +++ /dev/null @@ -1,36 +0,0 @@ -(ns com.github.clojure-lsp.intellij.extension.annotator - (:gen-class - :name com.github.clojure_lsp.intellij.extension.Annotator - :extends com.intellij.lang.annotation.ExternalAnnotator) - (:require - [com.github.clojure-lsp.intellij.db :as db] - [com.github.clojure-lsp.intellij.editor :as editor]) - (:import - (com.intellij.lang.annotation AnnotationHolder HighlightSeverity) - [com.intellij.openapi.editor Document] - (com.intellij.psi PsiDocumentManager PsiFile))) - -(set! *warn-on-reflection* true) - -(defn ^:private severity->highlight-severity [^long severity] - (case severity - 1 HighlightSeverity/ERROR - 2 HighlightSeverity/WARNING - 3 HighlightSeverity/INFO)) - -(defn -collectInformation - ([_ psi-file] psi-file) - ([_ psi-file _ _] psi-file)) - -(defn -doAnnotate [_ ^PsiFile psi-file] - (when-let [virtual-file (.getVirtualFile psi-file)] - (db/get-in (.getProject psi-file) [:diagnostics (.getUrl virtual-file)]))) - -(defn -apply [_ ^PsiFile psi-file result ^AnnotationHolder holder] - (when-let [document ^Document (some-> (PsiDocumentManager/getInstance (.getProject psi-file)) - (.getDocument psi-file))] - (doseq [{:keys [range message code severity _source]} result] - (-> holder - (.newAnnotation (severity->highlight-severity severity) (str message " [" code "]")) - (.range (editor/range->text-range range document)) - (.create))))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/clojure_module_builder.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/clojure_module_builder.clj index 9bf4af4..f04d588 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/clojure_module_builder.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/extension/clojure_module_builder.clj @@ -17,7 +17,11 @@ [com.intellij.openapi.module ModifiableModuleModel ModuleType] [com.intellij.openapi.project Project] [com.intellij.openapi.roots ModifiableRootModel] - [java.io File])) + [java.io File] + [java.net JarURLConnection] + [java.util.jar JarEntry])) + +(set! *warn-on-reflection* true) (def clojure-module (proxy+ ClojureModuleType ["CLOJURE_MODULE"] ModuleType @@ -77,8 +81,7 @@ (updateDataModel [_] (swap! wizard* assoc :project-type (s/id-of (s/selection (:button-group @wizard*))))))) -(defn -setupRootModel [^ModuleBuilder this ^ModifiableRootModel model] - (.doAddContentEntry this model)) +(defn -setupRootModel [^ModuleBuilder _ ^ModifiableRootModel _]) (defn ^:private normalize-entry-name [entry-name project-template project-name] (-> entry-name @@ -98,10 +101,10 @@ ;; We need to update current thread class loader to be able to ;; load resource-paths from our plugin. (.setContextClassLoader (Thread/currentThread) (.getClassLoader clojure.lang.Symbol)) - (let [connection (.openConnection (io/resource project-template)) + (let [connection ^JarURLConnection (.openConnection (io/resource project-template)) project-root-entry (.getJarEntry connection)] (with-open [jar (.getJarFile connection)] - (doseq [entry (enumeration-seq (.entries jar))] + (doseq [^JarEntry entry (enumeration-seq (.entries jar))] (when (and (not (.isDirectory entry)) (string/starts-with? (str entry) (str project-root-entry))) (with-open [stream (.getInputStream jar entry)] @@ -110,9 +113,13 @@ (io/make-parents new-file) (spit new-file content)))))))) +(set! *warn-on-reflection* false) + (defn -commitModule [this ^Project project ^ModifiableModuleModel model] (let [project-path (.getBasePath project) project-name (.getName project) template (project-template (:project-type @wizard*))] (copy-template template project-path project-name) (.superCommitModule this project model))) + +(set! *warn-on-reflection* true) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/code_action.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/code_action.clj deleted file mode 100644 index d80f8b9..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/code_action.clj +++ /dev/null @@ -1,110 +0,0 @@ -(ns com.github.clojure-lsp.intellij.extension.code-action - (:require - [camel-snake-kebab.core :as csk] - [clojure.core.memoize :as memoize] - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.db :as db] - [com.github.ericdallo.clj4intellij.util :as util]) - (:import - [com.intellij.openapi.editor Editor] - [com.intellij.openapi.project Project] - [com.intellij.psi PsiFile])) - -(set! *warn-on-reflection* true) - -(defonce code-action-name->title* (atom {})) - -(defn ^:private req-code-actions [project uri [line character]] - (when-let [client (lsp-client/connected-client project)] - (let [diagnostics (->> (db/get-in project [:diagnostics uri]) - (filterv (fn [{{:keys [start end]} :range}] - (and (<= (:line start) line (:line end)) - (<= (:character start) character (:character end)))))) - response @(lsp-client/request! client [:textDocument/codeAction - {:text-document {:uri uri} - :context {:diagnostics diagnostics} - :range {:start {:line line - :character character} - :end {:start {:line line - :character character}}}}]) - code-actions (reduce - (fn [a b] - (let [command-name (-> b :command :command)] - (swap! code-action-name->title* assoc command-name (:title b)) - (assoc a - command-name - (assoc (:command b) :kind (:kind b))))) - {} - response)] - code-actions))) - -(def ^:private memoized-code-actions - (memoize/ttl req-code-actions :ttl/threshold 1000)) - -(defn ^:private editor+command->code-action [^PsiFile file ^Editor editor command] - (when-let [vfile (.getVirtualFile file)] - (let [uri (.getUrl vfile) - pos (util/editor->cursor-position editor)] - (get (memoized-code-actions (.getProject editor) uri pos) command)))) - -(defn ^:private is-available [name _ _project editor file] - (boolean (editor+command->code-action file editor name))) - -(defn ^:private get-text [name _] - (get @code-action-name->title* name name)) - -(defn ^:private invoke [name _ ^Project project editor file] - (when-let [{:keys [command arguments]} (editor+command->code-action file editor name)] - (let [client (db/get-in project [:client])] - (lsp-client/request! client [:workspace/executeCommand - {:command command - :arguments arguments}])))) - -(defmacro ^:private gen-code-action [& {:keys [name]}] - `(do - (def ~(symbol (str (str name "-") "getText")) (partial get-text ~name)) - (defn ~(symbol (str (str name "-") "getFamilyName")) [_#] "Clojure LSP code actions") - (defn ~(symbol (str (str name "-") "startInWriteAction")) [_#] true) - (def ~(symbol (str (str name "-") "isAvailable")) (partial is-available ~name)) - (def ~(symbol (str (str name "-") "invoke")) (partial invoke ~name)) - (gen-class - :name ~(str "com.github.clojure_lsp.intellij.extension.code_action." (csk/->PascalCase name)) - :prefix ~(str name "-") - :extends "com.intellij.codeInsight.intention.impl.BaseIntentionAction"))) - -(gen-code-action :name "add-missing-import") -(gen-code-action :name "add-missing-libspec") -(gen-code-action :name "add-require-suggestion") -(gen-code-action :name "cycle-coll") -(gen-code-action :name "cycle-keyword-auto-resolve") -(gen-code-action :name "clean-ns") -(gen-code-action :name "cycle-privacy") -(gen-code-action :name "create-test") -(gen-code-action :name "drag-param-backward") -(gen-code-action :name "drag-param-forward") -(gen-code-action :name "drag-backward") -(gen-code-action :name "drag-forward") -(gen-code-action :name "demote-fn") -(gen-code-action :name "destructure-keys") -(gen-code-action :name "extract-to-def") -(gen-code-action :name "extract-function") -(gen-code-action :name "expand-let") -(gen-code-action :name "create-function") -(gen-code-action :name "get-in-all") -(gen-code-action :name "get-in-less") -(gen-code-action :name "get-in-more") -(gen-code-action :name "get-in-none") -(gen-code-action :name "introduce-let") -(gen-code-action :name "inline-symbol") -(gen-code-action :name "resolve-macro-as") -(gen-code-action :name "move-form") -(gen-code-action :name "move-to-let") -(gen-code-action :name "promote-fn") -(gen-code-action :name "replace-refer-all-with-refer") -(gen-code-action :name "replace-refer-all-with-alias") -(gen-code-action :name "restructure-keys") -(gen-code-action :name "change-coll") -(gen-code-action :name "sort-clauses") -(gen-code-action :name "thread-first-all") -(gen-code-action :name "thread-last-all") -(gen-code-action :name "unwind-all") diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/code_lens.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/code_lens.clj deleted file mode 100644 index 2ce2b87..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/code_lens.clj +++ /dev/null @@ -1,122 +0,0 @@ -(ns com.github.clojure-lsp.intellij.extension.code-lens - (:gen-class - :name com.github.clojure_lsp.intellij.extension.CodeLensProvider - :implements [com.intellij.codeInsight.hints.InlayHintsProvider]) - (:require - [com.github.clojure-lsp.intellij.action.references :as action.references] - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.rpl.proxy-plus :refer [proxy+]]) - (:import - [com.github.clojure_lsp.intellij ClojureLanguage] - [com.intellij.codeInsight.hints - FactoryInlayHintsCollector - ImmediateConfigurable - InlayGroup - InlayHintsSink - InlayPresentationFactory$ClickListener - InlayPresentationFactory$HoverListener - NoSettings - SettingsKey] - [com.intellij.codeInsight.hints.presentation ChangeOnHoverPresentation PresentationFactory] - [com.intellij.lang Language] - [com.intellij.openapi.editor Document Editor] - [com.intellij.openapi.fileTypes FileType] - [com.intellij.openapi.project Project] - [com.intellij.psi PsiFileFactory] - [javax.swing JPanel] - [kotlin.jvm.functions Function0 Function1])) - -(set! *warn-on-reflection* true) - -(defn -isLanguageSupported [_ ^Language language] - (instance? ClojureLanguage language)) - -(defn -getName [_] - "LSP code lens") - -(defn -getDescription [_] - "Clojure LSP code lens") - -(defn -getPreviewText [_] - "LSP Code lens") - -(defn -getKey [_] (SettingsKey. "LSP code lens")) - -(defn -createSettings [_] (NoSettings.)) - -(defn -getGroup [_] InlayGroup/OTHER_GROUP) - -(defn -isVisibleInSettings [_] true) - -(defn -createConfigurable [_ _] - (reify - ImmediateConfigurable - (createComponent [_ _] (JPanel.)))) - -(defn -createFile [_ ^Project project ^FileType file-type ^Document document] - (.createFileFromText - (PsiFileFactory/getInstance project) - "dummy" - file-type - (.getText document))) - -(defn ^:private handle-command [^Editor editor command arguments client] - (case command - "code-lens-references" - (let [[line character] (rest arguments)] - (action.references/show-references editor (dec line) (dec character) client)))) - -(set! *warn-on-reflection* false) -(defn ^:private code-lens-presentation - [^PresentationFactory factory title on-click-fn] - (let [base (.text factory title) - hover-button (.roundWithBackgroundAndSmallInset - factory - (.getFirst - (.button - factory - base - base - (reify InlayPresentationFactory$ClickListener - (onClick [_ _ _] (on-click-fn))) - (reify InlayPresentationFactory$HoverListener - (onHover [_ _ _]) - (onHoverFinished [_])) - false)))] - (ChangeOnHoverPresentation. (.inset factory - base - 3 3 6 0) - (reify Function0 (invoke [_] hover-button)) - (reify Function1 (invoke [_ _] true))))) -(set! *warn-on-reflection* true) - -(def lens-added-by-uri* (atom {})) - -(defn -getCollectorFor [_ _file ^Editor editor _settings ^InlayHintsSink sink] - (let [uri (editor/editor->uri editor)] - (when-let [client (lsp-client/connected-client (.getProject editor))] - ;; For some reason `(PresentationFactory. editor)` does not work - (proxy+ [editor] FactoryInlayHintsCollector - (collect [^FactoryInlayHintsCollector this _ _ _] - - (let [document (.getDocument editor) - code-lens @(lsp-client/request! client [:textDocument/codeLens {:text-document {:uri uri}}]) - factory (.getFactory ^FactoryInlayHintsCollector this)] - (when-not (get @lens-added-by-uri* uri) - (swap! lens-added-by-uri* assoc uri true) - (doseq [code-len code-lens] - (let [{:keys [range] {:keys [title command arguments]} :command} @(lsp-client/request! client [:codeLens/resolve code-len])] - (when range - (.addInlineElement sink - (editor/document+position->offset (:start range) document) - false - (code-lens-presentation - factory - title - (fn onClick [] - (handle-command editor command arguments client))) - true)))) - - (swap! lens-added-by-uri* assoc uri false))) - false))))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/color_settings_page.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/color_settings_page.clj index 612438f..c2edb4a 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/color_settings_page.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/extension/color_settings_page.clj @@ -60,7 +60,7 @@ (or (= key ClojureColors/FN_ARGUMENT) (= key ClojureColors/LET_BINDING))) -(defn -getDisplayName [_] "clojure") +(defn -getDisplayName [_] "Clojure") (defn -getIcon [_] Icons/CLOJURE) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/completion.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/completion.clj deleted file mode 100644 index dfe167f..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/completion.clj +++ /dev/null @@ -1,101 +0,0 @@ -(ns com.github.clojure-lsp.intellij.extension.completion - (:gen-class - :name com.github.clojure_lsp.intellij.extension.CompletionContributor - :extends com.intellij.codeInsight.completion.CompletionContributor - :exposes-methods {fillCompletionVariants fillCompletionVariantsSuper}) - (:require - [clojure.string :as string] - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.github.ericdallo.clj4intellij.logger :as logger]) - (:import - [com.github.clojure_lsp.intellij Icons] - [com.intellij.codeInsight.completion - CompletionParameters - CompletionResultSet - InsertHandler - InsertionContext] - [com.intellij.codeInsight.lookup AutoCompletionPolicy] - [com.intellij.codeInsight.lookup LookupElementBuilder] - [com.intellij.openapi.progress ProcessCanceledException])) - -(set! *warn-on-reflection* true) - -(def completion-kinds - {1 :text 2 :method 3 :function 4 :constructor 5 :field 6 :variable 7 :class 8 :interface 9 :module 10 :property - 11 :unit 12 :value 13 :enum 14 :keyword 15 :snippet 16 :color 17 :file 18 :reference 19 :folder - 20 :enummember 21 :constant 22 :struct 23 :event 24 :operator 25 :typeparameter}) - -(defn ^:private completion-kind->icon [kind] - (case (get completion-kinds kind) - :class Icons/SYMBOL_CLASS - :color Icons/SYMBOL_COLOR - :constant Icons/SYMBOL_CONSTANT - :constructor Icons/SYMBOL_METHOD - :enum-mber Icons/SYMBOL_ENUMERATOR_MEMBER - :enum Icons/SYMBOL_ENUMERATOR - :event Icons/SYMBOL_EVENT - :field Icons/SYMBOL_FIELD - :file Icons/SYMBOL_FILE - :interface Icons/SYMBOL_INTERFACE - :keyword Icons/SYMBOL_KEYWORD - :method Icons/SYMBOL_METHOD - :function Icons/SYMBOL_METHOD - :module Icons/SYMBOL_NAMESPACE - :numeric Icons/SYMBOL_NUMERIC - :operator Icons/SYMBOL_OPERATOR - :property Icons/SYMBOL_PROPERTY - :reference Icons/SYMBOL_REFERENCES - :snippet Icons/SYMBOL_SNIPPET - :string Icons/SYMBOL_STRING - :struct Icons/SYMBOL_STRUCTURE - :text Icons/SYMBOL_KEY - :type-parameter Icons/SYMBOL_PARAMETER - :unit Icons/SYMBOL_RULER - :value Icons/SYMBOL_ENUMERATOR - :variable Icons/SYMBOL_VARIABLE - Icons/SYMBOL_MISC)) - -(def tag-number->tag {1 :deprecated}) - -(defn ^:private completion-item->lookup-element [{:keys [label kind detail tags]}] - (let [normalized-label (if-let [i (string/index-of label "/")] - (subs label (inc i)) - label)] - (cond-> (LookupElementBuilder/create normalized-label) - - (some #(identical? :deprecated %) (mapv tag-number->tag tags)) - (.strikeout) - - :always - (-> - (.withInsertHandler (reify InsertHandler - (handleInsert [_ context item] - (let [context ^InsertionContext context] - ;; For some reason intellij duplicate the first char if a `|`, for example on `|foo` completion. - (when-not (= (.getText (.findElementAt (.getFile context) (.getStartOffset context))) - (.getLookupString item)) - (.replaceString (.getDocument (.getEditor context)) - (dec (.getStartOffset context)) - (.getTailOffset context) - normalized-label) - (.commitDocument context)))))) - (.withTypeText (or detail "") true) - (.withPresentableText label) - (.withIcon (completion-kind->icon kind)) - (.withAutoCompletionPolicy AutoCompletionPolicy/SETTINGS_DEPENDENT))))) - -(defn -fillCompletionVariants [_ ^CompletionParameters params ^CompletionResultSet result] - (when-let [client (lsp-client/connected-client (.getProject (.getEditor params)))] - (try - (let [file (.getOriginalFile params) - uri (.getUrl (.getVirtualFile file)) - editor (.getEditor params) - [line character] (editor/offset->cursor-position editor (.getOffset params)) - items @(lsp-client/request! client [:textDocument/completion - {:text-document {:uri uri} - :position {:line line :character character}}])] - (.addAllElements result (mapv completion-item->lookup-element items))) - (catch ProcessCanceledException _ignore) - (catch Exception e - (logger/error "Completion exception:" (str e)))))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/definition.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/definition.clj deleted file mode 100644 index c7014ba..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/definition.clj +++ /dev/null @@ -1,63 +0,0 @@ -(ns com.github.clojure-lsp.intellij.extension.definition - (:gen-class - :name com.github.clojure_lsp.intellij.extension.Definition - :extends com.intellij.codeInsight.navigation.actions.GotoDeclarationHandlerBase) - (:require - [clojure.java.io :as io] - [clojure.string :as string] - [com.github.clojure-lsp.intellij.action.references :as action.references] - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.config :as config] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.github.clojure-lsp.intellij.file-system :as file-system] - [com.github.clojure-lsp.intellij.psi :as psi] - [com.github.ericdallo.clj4intellij.util :as util]) - (:import - [com.intellij.openapi.editor Editor] - [com.intellij.openapi.project Project] - [com.intellij.openapi.vfs VirtualFile] - [com.intellij.psi PsiElement] - [com.intellij.psi PsiElement])) - -(set! *warn-on-reflection* true) - -(defn ^:private definition->psi-element - [^VirtualFile v-file ^Project project definition text] - (let [{{:keys [start]} :range} definition - text (or text (slurp (:uri definition))) - offset (editor/position->offset text (:line start) (:character start))] - [(psi/->LSPPsiElement "" project (editor/virtual->psi-file v-file project) offset offset (:line start))])) - -(defn ^:private dependency-content [client uri project definition path] - (let [text @(lsp-client/request! client [:clojure/dependencyContents {:uri uri}])] - (when (string? text) - (let [v-file (file-system/create-temp-file project path text)] - (definition->psi-element v-file project definition text))))) - -(defn ^:private show-definition [definition ^VirtualFile current-v-file client project] - (when-let [uri (:uri definition)] - (if (string/starts-with? uri "jar:") - (let [jar-pattern (re-pattern (str "^(jar|zip):(file:.+)!" (System/getProperty "file.separator") "(.+)"))] - (dependency-content client uri project definition (last (re-find jar-pattern uri)))) - ;; TODO improve this - (if-let [v-file (or (util/uri->v-file uri) - (when (= uri (.getUrl current-v-file)) current-v-file))] - (definition->psi-element v-file project definition nil) - (dependency-content client uri project definition - (str (.relativize (.toPath (io/file (config/project-cache-path project))) - (.toPath (io/file (java.net.URI. uri)))))))))) - -(defn -getGotoDeclarationTargets [_ ^PsiElement element _ ^Editor editor] - (let [[line character] (:start (editor/text-range->range (.getTextRange element) editor)) - project ^Project (.getProject editor) - current-v-file (some-> element .getContainingFile .getVirtualFile)] - (when-let [client (lsp-client/connected-client project)] - (when-let [definition @(lsp-client/request! client [:textDocument/definition - {:text-document {:uri (editor/editor->uri editor)} - :position {:line line - :character character}}])] - (when-let [elements (if (and (= line (-> definition :range :start :line)) - (= character (-> definition :range :start :character))) - (action.references/get-references editor line character client) - (show-definition definition current-v-file client project))] - (into-array PsiElement elements)))))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/documentation.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/documentation.clj deleted file mode 100644 index 983951f..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/documentation.clj +++ /dev/null @@ -1,109 +0,0 @@ -(ns com.github.clojure-lsp.intellij.extension.documentation - (:gen-class - :name com.github.clojure_lsp.intellij.extension.Documentation - :implements [com.intellij.lang.documentation.DocumentationProvider]) - (:require - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.db :as db] - [markdown.core :as markdown]) - (:import - [com.intellij.openapi.util.text StringUtil] - [com.intellij.openapi.util.text HtmlBuilder] - [com.intellij.psi PsiDocumentManager PsiElement PsiFile] - [com.intellij.openapi.fileTypes SyntaxHighlighterFactory] - [com.intellij.openapi.editor.colors EditorColorsManager] - [com.github.clojure_lsp.intellij ClojureLanguage] - [java.awt Color])) - -(set! *warn-on-reflection* true) - -(defn ^:private rgb-html-style [^Color color] - (str "rgb(" (.getRed color) ", " (.getGreen color) ", " (.getBlue color) ")")) - -(defn ^:private html-style [prop value] - (if (some? value) - (str prop ":" value ";") - "")) - -(defn ^:private highlight-html-text - [^String text {:keys [foreground-color background-color font-type]}] - (let [color-style (html-style "color" (when foreground-color - (rgb-html-style foreground-color))) - background-style (html-style "background-color" (when background-color - (rgb-html-style foreground-color))) - font-weight-style (html-style "font-weight" (case font-type - :bold "bold" - :bold-italic "bold" - "normal")) - font-style (html-style "font-style" (case font-type - :italic "italic" - :bold-italic "italic" - "normal"))] - (str "" text ""))) - -(defn ^:private highlight-html-code [^String code] - (let [highlighter (SyntaxHighlighterFactory/getSyntaxHighlighter (ClojureLanguage/INSTANCE) nil nil) - lexer (.getHighlightingLexer highlighter) - color-scheme (.getGlobalScheme (EditorColorsManager/getInstance))] - (loop [highlighted-code ""] - (if (empty? highlighted-code) - (.start lexer code) - (.advance lexer)) - (let [lexer-not-finished? (.getTokenType lexer)] - (if lexer-not-finished? - (let [text (.getTokenText lexer) - highlight (first (.getTokenHighlights highlighter (.getTokenType lexer))) - highlight-attrs (.getAttributes color-scheme highlight) - foreground-color (some-> highlight-attrs .getForegroundColor) - background-color (some-> highlight-attrs .getBackgroundColor) - font-type (some-> highlight-attrs .getFontType (#(case (int %) - 1 :bold - 2 :italic - 3 :bold-italic - nil)))] - (if (some some? [foreground-color background-color font-type]) - (recur (str highlighted-code (highlight-html-text text {:foreground-color foreground-color - :background-color background-color - :font-type font-type}))) - (recur (str highlighted-code text)))) - highlighted-code))))) - -(defn ^:private build-doc [^PsiElement element] - (when-let [client (db/get-in (.getProject element) [:client])] - (when-let [psi-file ^PsiFile (.getContainingFile element)] - (let [project (.getProject element) - document (.getDocument (PsiDocumentManager/getInstance project) psi-file) - text (.getCharsSequence document) - line-col (StringUtil/offsetToLineColumn text (.getTextOffset element)) - {:keys [contents]} @(lsp-client/request! client [:textDocument/hover - {:text-document {:uri (.getUrl (.getVirtualFile psi-file))} - :position {:line (.line line-col) - :character (.column line-col)}}])] - - (when-let [html (markdown/md-to-html-string (:value contents) - :codeblock-no-escape? true - :codeblock-callback (fn [code language] - (if (= language "clojure") - (highlight-html-code code) - code)))] - (-> (HtmlBuilder.) - (.appendRaw html) - (.toString))))))) - -(defn -getCustomDocumentationElement - [_ _ _ context-element _] - context-element) - -(defn -generateDoc [_ element _] - (build-doc element)) - -(defn -getQuickNavigateInfo [_ element _] - (build-doc element)) - -(defn -generateHoverDoc [_ _ _]) -(defn -getUrlFor [_ _ _]) -(defn -generateRenderedDoc [_ _]) -(defn -collectDocComments [_ _ _]) -(defn -getLocalImageForElement [_ _ _]) -(defn -getDocumentationElementForLookupItem [_ _ _ _]) -(defn -getDocumentationElementForLink [_ _ _ _]) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/file_manager.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/file_manager.clj deleted file mode 100644 index d9709ab..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/file_manager.clj +++ /dev/null @@ -1,26 +0,0 @@ -(ns com.github.clojure-lsp.intellij.extension.file-manager - (:gen-class - :name com.github.clojure_lsp.intellij.extension.FileDocumentManagerListener - :implements [com.intellij.openapi.fileEditor.FileDocumentManagerListener]) - (:require - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.editor :as editor]) - (:import - [com.intellij.openapi.editor Document] - [com.intellij.openapi.fileEditor FileDocumentManager])) - -(defn -beforeAllDocumentsSaving [_]) -(defn -beforeAnyDocumentSaving [_ _ _]) -(defn -beforeFileContentReload [_ _ _]) -(defn -fileWithNoDocumentChanged [_ _]) -(defn -fileContentReloaded [_ _ _]) -(defn -fileContentLoaded [_ _ _]) -(defn -unsavedDocumentDropped [_ _]) -(defn -unsavedDocumentsDropped [_]) -(defn -afterDocumentUnbound [_ _ _]) - -(defn -beforeDocumentSaving [_ ^Document document] - (let [vfile (.getFile (FileDocumentManager/getInstance) document)] - (when-let [client (some-> vfile editor/v-file->project lsp-client/connected-client)] - (lsp-client/notify! client [:textDocument/didSave - {:textDocument {:uri (.getUrl vfile)}}])))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/formatting.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/formatting.clj deleted file mode 100644 index 37ddefd..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/formatting.clj +++ /dev/null @@ -1,44 +0,0 @@ -(ns com.github.clojure-lsp.intellij.extension.formatting - (:gen-class - :name com.github.clojure_lsp.intellij.extension.Formatting - :extends com.intellij.formatting.service.AsyncDocumentFormattingService) - (:require - [com.github.clojure-lsp.intellij.client :as lsp-client]) - (:import - [com.github.clojure_lsp.intellij ClojureFileType] - [com.intellij.formatting.service AsyncDocumentFormattingService$FormattingTask AsyncFormattingRequest] - [com.intellij.psi PsiFile])) - -(set! *warn-on-reflection* true) - -(defn -getFeatures [_] - ;; TODO add other features, like range formatting, organizeImports - #{}) - -(defn -canFormat [_ ^PsiFile psi-file] - (and (instance? ClojureFileType (.getFileType psi-file)) - (boolean (lsp-client/connected-client (.getProject psi-file))))) - -(defn -getName [_] - "LSP format") - -(defn -getNotificationGroupId [_] - "LSP format") - -(defn -createFormattingTask [_ ^AsyncFormattingRequest request] - (let [context (.getContext request) - file (.getContainingFile context) - uri (.getUrl (.getVirtualFile file))] - (when-let [client (lsp-client/connected-client (.getProject file))] - (reify AsyncDocumentFormattingService$FormattingTask - (run [_] - (try - (let [[{:keys [new-text]}] @(lsp-client/request! client [:textDocument/formatting - {:text-document {:uri uri}}])] - (if new-text - (.onTextReady request new-text) - (.onTextReady request (.getDocumentText request)))) - (catch Exception e - (.onError request "LSP format error" (.getMessage e))))) - (cancel [_] true) - (isRunUnderProgress [_] true))))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/general_settings.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/general_settings.clj index a787827..08f3144 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/general_settings.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/extension/general_settings.clj @@ -5,18 +5,16 @@ (:require [clojure.pprint :as pprint] [clojure.string :as str] - [clojure.walk :as walk] [com.github.clojure-lsp.intellij.client :as lsp-client] [com.github.clojure-lsp.intellij.config :as config] [com.github.clojure-lsp.intellij.db :as db] [com.github.clojure-lsp.intellij.server :as server] + [com.github.clojure-lsp.intellij.settings :as settings] [seesaw.color :as s.color] [seesaw.core :as s] [seesaw.font :as s.font] [seesaw.mig :as s.mig]) (:import - [com.github.clojure_lsp.intellij.extension SettingsState] - [com.intellij.openapi.project Project] [com.intellij.ui IdeBorderFactory] [java.awt Toolkit] [java.awt.datatransfer StringSelection])) @@ -27,11 +25,11 @@ (def ^:private server-not-started-message "Server not started") -(defn ^:private build-component [{:keys [server-version log-path] :as server-info} settings] +(defn ^:private build-component [{:keys [server-version log-path] :as server-info}] (let [server-running? (boolean server-info) - custom-server-path (:server-path settings) + custom-server-path (settings/server-path) server-path (or custom-server-path (.getCanonicalPath (config/download-server-path))) - custom-server-log-path (:log-path settings) + custom-server-log-path (settings/server-log-path) server-log-path (or custom-server-log-path log-path)] (s.mig/mig-panel :items (->> [(when-not server-running? @@ -89,22 +87,16 @@ :listen [:action (fn [_] (doseq [project (db/all-projects)] (server/shutdown! project) - (server/start-server! project)))]) "wrap"]]) "span"] + (server/start! project)))]) "wrap"]]) "span"] [(s/label :text "* requires LSP restart" :font (s.font/font :size 14) :foreground (s.color/color 110 110 110)) "wrap"]] (remove nil?))))) -(defn ^:private server-info! [^Project project] - (some-> (lsp-client/connected-client project) - (lsp-client/request! [":clojure/serverInfo/raw" {}]) - deref - walk/keywordize-keys)) - (defn -createComponent [_] (let [project (first (db/all-projects)) - server-info (server-info! project) - component (build-component server-info (db/get-in project [:settings]))] + server-info (lsp-client/server-info project) + component (build-component server-info)] (reset! component* component) component)) @@ -112,24 +104,22 @@ (s/select @component* [:#copy-server-info])) (defn -isModified [_] - (let [project (first (db/all-projects)) - settings-state (SettingsState/get) - server-path (s/config (s/select @component* [:#server-path]) :text) + (let [server-path (s/config (s/select @component* [:#server-path]) :text) trace-level-combo-box (s/config (s/select @component* [:#trace-level]) :selected-item) server-log-path (s/config (s/select @component* [:#server-log]) :text)] (boolean - (or (not= server-path (or (.getServerPath settings-state) "")) - (not= trace-level-combo-box (.getTraceLevel settings-state)) - (not= server-log-path (or (.getServerLogPath settings-state) "")) + (or (not= server-path (or (settings/server-path) "")) + (not= trace-level-combo-box (settings/server-trace-level)) + (not= server-log-path (or (settings/server-log-path) "")) (and (str/blank? server-log-path) - (db/get-in project [:settings :log-path])))))) + (settings/server-log-path)))))) (defn -reset [_] (let [project (first (db/all-projects)) - server-info (server-info! project) - trace-level-combo-box (db/get-in project [:settings :trace-level]) - server-log-path (or (db/get-in project [:settings :log-path]) (:log-path server-info)) - server-path (or (db/get-in project [:settings :server-path]) (.getCanonicalPath (config/download-server-path)))] + server-info (lsp-client/server-info project) + trace-level-combo-box (settings/server-trace-level) + server-log-path (or (settings/server-log-path) (:log-path server-info)) + server-path (or (settings/server-path) (.getCanonicalPath (config/download-server-path)))] (s/config! (s/select @component* [:#trace-level]) :selected-item trace-level-combo-box) (s/config! (s/select @component* [:#server-log]) :text server-log-path) (s/config! (s/select @component* [:#server-path]) :text server-path))) @@ -138,14 +128,13 @@ (reset! component* nil)) (defn -apply [_] - (let [settings-state (SettingsState/get) - trace-level (s/config (s/select @component* [:#trace-level]) :selected-item) + (let [trace-level (s/config (s/select @component* [:#trace-level]) :selected-item) server-log-path (when (s/config (s/select @component* [:#custom-server-log?]) :selected?) (s/config (s/select @component* [:#server-log]) :text)) server-path (when (s/config (s/select @component* [:#custom-server-path?]) :selected?) (s/config (s/select @component* [:#server-path]) :text))] - (db/set-server-path-setting! settings-state server-path) - (db/set-server-log-path-setting! settings-state server-log-path) - (db/set-trace-level-setting! settings-state trace-level))) + (settings/set-server-path! server-path) + (settings/set-server-log-path! server-log-path) + (settings/set-server-trace-level! trace-level))) (defn -cancel [_]) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/init_db_startup.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/init_db_startup.clj index b0bbc12..ab211ce 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/init_db_startup.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/extension/init_db_startup.clj @@ -4,15 +4,11 @@ :implements [com.intellij.openapi.startup.StartupActivity com.intellij.openapi.project.DumbAware]) (:require - [com.github.clojure-lsp.intellij.db :as db] - [com.github.ericdallo.clj4intellij.logger :as logger]) + [com.github.clojure-lsp.intellij.db :as db]) (:import - [com.github.clojure_lsp.intellij.extension SettingsState] [com.intellij.openapi.project Project])) (set! *warn-on-reflection* true) (defn -runActivity [_this ^Project project] - (db/init-db-for-project project) - (db/load-settings-from-state! project (SettingsState/get)) - (logger/info "Loaded settings to memory:" (db/get-in project [:settings]))) + (db/init-db-for-project project)) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/language_server_factory.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/language_server_factory.clj new file mode 100644 index 0000000..a6b20d9 --- /dev/null +++ b/src/main/clojure/com/github/clojure_lsp/intellij/extension/language_server_factory.clj @@ -0,0 +1,123 @@ +(ns com.github.clojure-lsp.intellij.extension.language-server-factory + (:gen-class + :name com.github.clojure_lsp.intellij.extension.LanguageServerFactory + :implements [com.redhat.devtools.lsp4ij.LanguageServerFactory]) + (:require + [clojure.java.io :as io] + [clojure.string :as string] + [com.github.clojure-lsp.intellij.client :as lsp-client] + [com.github.clojure-lsp.intellij.config :as config] + [com.github.clojure-lsp.intellij.db :as db] + [com.github.clojure-lsp.intellij.editor :as editor] + [com.github.clojure-lsp.intellij.server :as server] + [com.github.clojure-lsp.intellij.settings :as settings] + [com.rpl.proxy-plus :refer [proxy+]]) + (:import + [com.intellij.execution.configurations GeneralCommandLine] + [com.intellij.openapi.progress ProgressIndicator] + [com.intellij.openapi.project Project] + [com.intellij.openapi.vfs LocalFileSystem VirtualFile] + [com.redhat.devtools.lsp4ij LSPIJUtils ServerStatus] + [com.redhat.devtools.lsp4ij.client LanguageClientImpl] + [com.redhat.devtools.lsp4ij.client.features LSPClientFeatures LSPProgressFeature] + [com.redhat.devtools.lsp4ij.server OSProcessStreamConnectionProvider] + [java.io File] + [java.util List] + [org.eclipse.lsp4j InitializeParams])) + +(set! *warn-on-reflection* true) + +(defonce ^:private server (atom {:status :not-found + :path nil})) + +(defn -createConnectionProvider [_ ^Project _project] + (let [server-path (loop [] + (Thread/sleep 100) + (or (settings/server-path) + (some-> ^File (:path @server) .getCanonicalPath) + (recur))) + command [server-path "listen"]] + (doto (proxy+ + [] + OSProcessStreamConnectionProvider) + (.setCommandLine (GeneralCommandLine. ^List command))))) + +(defn -createLanguageClient [_ ^Project project] + (LanguageClientImpl. project)) + +(defn -getServerInterface [_] + com.github.clojure_lsp.intellij.ClojureLanguageServer) + +(defn ^:private install-server [project] + (swap! server assoc :status :installing) + (server/install-server + project + (fn [{:keys [status path]}] + (swap! server assoc :status status :path path) + (server/start! project)))) + +(defn ^:private create-temp-file ^VirtualFile + [^Project project ^String path ^String text] + (let [temp-file (io/file (config/project-cache-path project) path)] + (io/make-parents temp-file) + (spit temp-file text) + (proxy+ [] VirtualFile + (getName [_] (.getName temp-file)) + (getFileSystem [_] (LocalFileSystem/getInstance)) + (getPath [_] (.getCanonicalPath temp-file)) + (isWritable [_] false) + (isDirectory [_] false) + (isValid [_] true) + (getParent [_] nil) + (getChildren [_] []) + (contentsToByteArray [_] (.getBytes text)) + (getTimeStamp [_] 0) + (getLength [_] (count (.getBytes text))) + (refresh [_ _ _ _]) + (getInputStream [_] (io/input-stream temp-file)) + (getOutputStream [_ _ _ _] (io/output-stream temp-file)) + (getModificationStamp [_] -1)))) + +(defn ^:private find-file-by-uri [^String uri] + (if (and (string/starts-with? uri "file:") + (string/includes? uri ".jar!")) + (let [fixed-uri (string/replace-first uri "file:" "jar:file:") + old-vfile (LSPIJUtils/findResourceFor fixed-uri) + project (editor/guess-project-for old-vfile) + dependency-contents (lsp-client/dependency-contents fixed-uri project) + jar-pattern (re-pattern (str "^(jar|zip):(file:.+)!" (System/getProperty "file.separator") "(.+)")) + path (last (re-find jar-pattern fixed-uri)) + _tmp-file (create-temp-file project path dependency-contents)] + ;; TODO fix support for clojure/dependencyContents + #_tmp-file + (LSPIJUtils/findResourceFor fixed-uri)) + (LSPIJUtils/findResourceFor uri))) + +(defn -createClientFeatures [_] + (doto + (proxy+ [] LSPClientFeatures + (isEnabled [_this ^VirtualFile file] + (case (:status @server) + :installing + false + + :installed + true + + :not-found + (do (install-server (editor/guess-project-for file)) + false))) + (initializeParams [_ ^InitializeParams params] + (.setWorkDoneToken params "clojure-lsp-startup") + (.setInitializationOptions params {"dependency-scheme" "jar" + "hover" {"arity-on-same-line?" true}})) + (findFileByUri ^VirtualFile [_ ^String uri] + (find-file-by-uri uri)) + (keepServerAlive [_] true) + (handleServerStatusChanged [^LSPClientFeatures this ^ServerStatus server-status] + (let [status (keyword (.toString server-status))] + (db/assoc-in (.getProject this) [:status] status) + (run! #(% status) (db/get-in (.getProject this) [:on-status-changed-fns]))))) + (.setProgressFeature (proxy+ [] LSPProgressFeature + (updateMessage [_ ^String message ^ProgressIndicator indicator] + (.setText indicator (str "LSP: " message))))))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/register_actions_startup.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/register_actions_startup.clj index a63dc36..5d9e38c 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/register_actions_startup.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/extension/register_actions_startup.clj @@ -5,18 +5,31 @@ com.intellij.openapi.project.DumbAware]) (:require [camel-snake-kebab.core :as csk] - [com.github.clojure-lsp.intellij.action.implementations :as a.implementations] - [com.github.clojure-lsp.intellij.action.refactors :as a.refactors] - [com.github.clojure-lsp.intellij.action.references :as a.references] + [com.github.clojure-lsp.intellij.client :as lsp-client] + [com.github.clojure-lsp.intellij.db :as db] + [com.github.clojure-lsp.intellij.editor :as editor] [com.github.ericdallo.clj4intellij.action :as action] - [com.github.ericdallo.clj4intellij.logger :as logger]) + [com.github.ericdallo.clj4intellij.logger :as logger] + [com.github.ericdallo.clj4intellij.tasks :as tasks] + [com.github.ericdallo.clj4intellij.util :as util] + [com.rpl.proxy-plus :refer [proxy+]]) (:import [com.github.clojure_lsp.intellij Icons] - [com.intellij.openapi.project Project])) + [com.google.gson JsonPrimitive] + [com.intellij.openapi.actionSystem + ActionManager + ActionUpdateThread + AnActionEvent + CommonDataKeys] + [com.intellij.openapi.editor Editor] + [com.intellij.openapi.project Project] + [com.redhat.devtools.lsp4ij LanguageServerManager] + [com.redhat.devtools.lsp4ij.commands LSPCommand LSPCommandAction] + [com.redhat.devtools.lsp4ij.usages LSPUsageType LSPUsagesManager LocationData])) (set! *warn-on-reflection* true) -(def clojure-lsp-refactors +(def clojure-lsp-commands [{:name "add-missing-import" :text "Add import to namespace" :description "Add import to namespace"} {:name "add-missing-libspec" :text "Add missing require" :description "Add missing require"} {:name "add-require-suggestion" :text "Add require suggestion" :description "Add require suggestion"} @@ -63,39 +76,60 @@ {:name "raise-sexp" :text "Raise sexpr" :description "Raise current sexpr (Paredit)" :keyboard-shortcut {:first "alt R" :replace-all true}} {:name "kill-sexp" :text "Kill sexpr" :description "Kill current sexpr (Paredit)" :keyboard-shortcut {:first "alt K" :replace-all true}}]) -(defn -runActivity [_this ^Project _project] - (action/register-action! :id "ClojureLSP.FindReferences" - :title "Find references" - :description "Find all references of the element at cursor." - :icon Icons/CLOJURE - :use-shortcut-of "FindUsages" - :on-performed a.references/find-references-action) - (action/register-action! :id "ClojureLSP.FindImplementations" - :title "Find implementations" - :description "Find all implementations of the element at cursor if any." - :icon Icons/CLOJURE - :use-shortcut-of "GotoImplementation" - :on-performed a.implementations/find-implementations-action) - (action/register-group! :id "ClojureLSP.Find" - :children [{:type :add-to-group :group-id "EditorPopupMenu.GoTo" :anchor :first} - {:type :add-to-group :group-id "GoToMenu" :anchor :before :relative-to "GotoDeclaration"} - {:type :reference :ref "ClojureLSP.FindReferences"} - {:type :reference :ref "ClojureLSP.FindImplementations"} - {:type :separator}]) - (doseq [{:keys [name text description use-shortcut-of keyboard-shortcut]} clojure-lsp-refactors] +(defn ^:private on-action-performed [command-name text project ^AnActionEvent event] + (when-let [editor ^Editor (.getData event CommonDataKeys/EDITOR_EVEN_IF_INACTIVE)] + (let [[line character] (util/editor->cursor-position editor)] + (tasks/run-background-task! + project + "LSP: refactoring" + (fn [_] + (lsp-client/execute-command command-name text [(editor/editor->uri editor) line character] project)))))) + +(defn register-command! + [& {:keys [id on-performed]}] + (let [manager (ActionManager/getInstance) + action (proxy+ [] LSPCommandAction + (commandPerformed [_ command event] (on-performed command event)) + (getCommandPerformedThread [_] ActionUpdateThread/EDT))] + (when-not (.getAction manager id) + (.registerAction manager id action) + action))) + +(defn ^:private code-lens-references-performed [^Project project ^LSPCommand command ^AnActionEvent event] + (let [uri (.getAsString ^JsonPrimitive (.getArgumentAt command 0)) + line (dec (.getAsInt ^JsonPrimitive (.getArgumentAt command 1))) + character (dec (.getAsInt ^JsonPrimitive (.getArgumentAt command 2))) + references (lsp-client/references uri line character project) + language-server @(.getLanguageServer (LanguageServerManager/getInstance project) "clojure-lsp")] + (.findShowUsagesInPopup + (LSPUsagesManager/getInstance project) + (mapv #(LocationData. % language-server) references) + LSPUsageType/References + (.getDataContext event) + (.getInputEvent event)))) + +(defn -runActivity [_this ^Project project] + (doseq [{:keys [name text description use-shortcut-of keyboard-shortcut]} clojure-lsp-commands] (action/register-action! :id (str "ClojureLSP." (csk/->PascalCase name)) :title text :description description :icon Icons/CLOJURE :keyboard-shortcut keyboard-shortcut :use-shortcut-of use-shortcut-of - :on-performed (partial #'a.refactors/execute-refactor-action name))) - + :on-performed (partial on-action-performed name text project))) + (register-command! :id "code-lens-references" + :on-performed (partial code-lens-references-performed project)) (action/register-group! :id "ClojureLSP.Refactors" :popup true :text "Clojure refactors" :icon Icons/CLOJURE :children (concat [{:type :add-to-group :group-id "RefactoringMenu" :anchor :first}] - (mapv (fn [{:keys [name]}] {:type :reference :ref (str "ClojureLSP." (csk/->PascalCase name))}) clojure-lsp-refactors) + (mapv (fn [{:keys [name]}] {:type :reference :ref (str "ClojureLSP." (csk/->PascalCase name))}) clojure-lsp-commands) [{:type :separator}])) (logger/info "Actions registered")) + +(comment + (do + (.unregisterAction (ActionManager/getInstance) "ClojureLSP.ForwardSlurp") + (.unregisterAction (ActionManager/getInstance) "ClojureLSP.ForwardBarf") + (-runActivity nil (first (db/all-projects))))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/rename.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/rename.clj deleted file mode 100644 index ed73b92..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/rename.clj +++ /dev/null @@ -1,57 +0,0 @@ -(ns com.github.clojure-lsp.intellij.extension.rename - (:gen-class - :name com.github.clojure_lsp.intellij.extension.RenameHandler - :implements [com.intellij.refactoring.rename.RenameHandler]) - (:require - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.github.clojure-lsp.intellij.file-system :as file-system] - [com.github.ericdallo.clj4intellij.util :as util]) - (:import - [com.intellij.openapi.actionSystem CommonDataKeys DataContext] - [com.intellij.openapi.editor Editor] - [com.intellij.openapi.project Project] - [com.intellij.openapi.ui Messages NonEmptyInputValidator] - [com.intellij.openapi.util TextRange] - [com.intellij.psi PsiDocumentManager PsiFile])) - -(set! *warn-on-reflection* true) - -(defn -isAvailableOnDataContext [_ ^DataContext data-context] - (boolean - (and (.getData data-context CommonDataKeys/PROJECT) - (.getData data-context CommonDataKeys/EDITOR) - (.getPsiFile (PsiDocumentManager/getInstance (.getData data-context CommonDataKeys/PROJECT)) - (.getDocument ^Editor (.getData data-context CommonDataKeys/EDITOR)))))) - -(defn -isRenaming [this data-context] - (-isAvailableOnDataContext this data-context)) - -(defn ^:private prepare-rename-current-name [client ^Editor editor line character] - (let [document (.getDocument editor) - project (.getProject editor) - response @(lsp-client/request! client [:textDocument/prepareRename - {:text-document {:uri (editor/editor->uri editor)} - :position {:line line - :character character}}])] - (if-let [{:keys [message]} (:error response)] - (lsp-client/show-message {:project project} {:type 1 :message message}) - (let [start ^int (editor/document+position->offset (:start response) document) - end ^int (editor/document+position->offset (:end response) document)] - (.getText document (TextRange. start end)))))) - -(defn -invoke - ;; rename a file outside editor - ([_this ^Project _project ^"[Lcom.intellij.psi.PsiElement;" _elements ^DataContext _data-context]) - ;; rename a element in a editor - ([_ ^Project project ^Editor editor ^PsiFile _psi-file ^DataContext _data-context] - (when-let [client (lsp-client/connected-client project)] - (let [[line character] (util/editor->cursor-position editor)] - (when-let [current-name ^String (prepare-rename-current-name client editor line character)] - (when-let [new-name (Messages/showInputDialog project "Enter new name: " "Rename" (Messages/getQuestionIcon) current-name (NonEmptyInputValidator.))] - (->> @(lsp-client/request! client [:textDocument/rename - {:text-document {:uri (editor/editor->uri editor)} - :position {:line line - :character character} - :new-name new-name}]) - (file-system/apply-workspace-edit project "LSP Rename" true)))))))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/semantic_token_provider.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/semantic_token_provider.clj new file mode 100644 index 0000000..3e9cd20 --- /dev/null +++ b/src/main/clojure/com/github/clojure_lsp/intellij/extension/semantic_token_provider.clj @@ -0,0 +1,40 @@ +(ns com.github.clojure-lsp.intellij.extension.semantic-token-provider + (:gen-class + :name com.github.clojure_lsp.intellij.extension.ClojureSemanticTokensColorsProvider + :implements [com.redhat.devtools.lsp4ij.features.semanticTokens.SemanticTokensColorsProvider]) + (:import + [com.intellij.openapi.editor.colors TextAttributesKey] + [com.intellij.psi PsiFile] + [com.redhat.devtools.lsp4ij.features.semanticTokens DefaultSemanticTokensColorsProvider SemanticTokensHighlightingColors] + [java.awt Font] + [java.util List] + [org.eclipse.lsp4j SemanticTokenModifiers SemanticTokenTypes])) + +(set! *warn-on-reflection* true) + +(def ^:private default-provider (DefaultSemanticTokensColorsProvider.)) + +(defn ^:private modifier? [modifier token-modifiers] + (some #(= modifier %) token-modifiers)) + +(defn ^:private make-bold [^TextAttributesKey text-attribute-key] + (TextAttributesKey/createTextAttributesKey + (str (.getExternalName text-attribute-key) "_BOLD") + (doto (.clone (.getDefaultAttributes text-attribute-key)) + (.setFontType Font/BOLD)))) + +(defn ^:private bold-function-declaration* [] + (make-bold SemanticTokensHighlightingColors/FUNCTION_DECLARATION)) + +(def ^:private bold-function-declaration (memoize bold-function-declaration*)) + +(defn -getTextAttributesKey [_ ^String token-type ^List token-modifiers ^PsiFile psi-file] + (condp = token-type + SemanticTokenTypes/Namespace SemanticTokensHighlightingColors/STATIC_PROPERTY + SemanticTokenTypes/Function (if (modifier? SemanticTokenModifiers/Definition token-modifiers) + (bold-function-declaration) + SemanticTokensHighlightingColors/MACRO) + SemanticTokenTypes/Type SemanticTokensHighlightingColors/STATIC_PROPERTY + SemanticTokenTypes/Variable SemanticTokensHighlightingColors/READONLY_VARIABLE + SemanticTokenTypes/Keyword SemanticTokensHighlightingColors/NUMBER + (.getTextAttributesKey ^DefaultSemanticTokensColorsProvider default-provider token-type token-modifiers psi-file))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/extension/status_bar_factory.clj b/src/main/clojure/com/github/clojure_lsp/intellij/extension/status_bar_factory.clj index 6503a28..a03318c 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/extension/status_bar_factory.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/extension/status_bar_factory.clj @@ -1,6 +1,5 @@ (ns com.github.clojure-lsp.intellij.extension.status-bar-factory (:gen-class - :post-init post-init :name com.github.clojure_lsp.intellij.extension.StatusBarFactory :implements [com.intellij.openapi.wm.StatusBarWidgetFactory]) (:require @@ -24,7 +23,8 @@ [com.intellij.ui.awt RelativePoint] [com.intellij.util Consumer] [java.awt Point] - [java.awt.event MouseEvent])) + [java.awt.event MouseEvent] + [kotlinx.coroutines CoroutineScope])) (set! *warn-on-reflection* true) @@ -50,46 +50,47 @@ (.updateWidget status-bar widget-id) (.updateWidget ^StatusBarWidgetsManager (.getService project StatusBarWidgetsManager) factory))) -(defn -post-init [this] - (swap! db/db* update :on-status-changed-fns conj (fn [project _status] - (refresh-status-bar this project)))) - (defn ^:private restart-lsp-action [^Project project] (proxy+ ["Restart server"] DumbAwareAction (actionPerformed [_ _event] (server/shutdown! project) - (server/start-server! project)))) + (server/start! project)))) (defn ^:private status-bar-title [project] - (str "Clojure LSP: " (name (db/get-in project [:status] "disconnected")))) + (str "Clojure LSP: " (name (lsp-client/server-status project)))) -(defn -createWidget ^StatusBarWidget [_this ^Project project] - (proxy+ - [] - StatusBarWidget - (ID [_] widget-id) - (dispose [_]) - (install [_ _]) - (getPresentation [this] this) - StatusBarWidget$IconPresentation - (getClickConsumer [_] - (reify Consumer - (consume [_ e] - (let [component (.getComponent ^MouseEvent e) - popup (.createActionGroupPopup - (JBPopupFactory/getInstance) - (status-bar-title project) - (doto (DefaultActionGroup.) - (.add (restart-lsp-action project))) - (.getDataContext (DataManager/getInstance) component) - JBPopupFactory$ActionSelectionAid/SPEEDSEARCH - true)] - (.show popup (RelativePoint. component (Point. 0 (-> popup .getContent .getPreferredSize .getHeight -))))) - true))) - (getTooltipText [_] (status-bar-title project)) - (getIcon [_] - (if (lsp-client/connected-client project) - Icons/STATUS_CONNECTED - Icons/STATUS_DISCONNECTED)))) +(defn -createWidget ^StatusBarWidget + ([this ^Project project ^CoroutineScope _] + (-createWidget this project)) + ([this ^Project project] + (db/update-in project [:on-status-changed-fns] #(conj % (fn [_status] + (refresh-status-bar this project)))) + (proxy+ + [] + StatusBarWidget + (ID [_] widget-id) + (dispose [_]) + (install [_ _]) + (getPresentation [this] this) + StatusBarWidget$IconPresentation + (getClickConsumer [_] + (reify Consumer + (consume [_ e] + (let [component (.getComponent ^MouseEvent e) + popup (.createActionGroupPopup + (JBPopupFactory/getInstance) + (status-bar-title project) + (doto (DefaultActionGroup.) + (.add (restart-lsp-action project))) + (.getDataContext (DataManager/getInstance) component) + JBPopupFactory$ActionSelectionAid/SPEEDSEARCH + true)] + (.show popup (RelativePoint. component (Point. 0 (-> popup .getContent .getPreferredSize .getHeight -))))) + true))) + (getTooltipText [_] (status-bar-title project)) + (getIcon [_] + (if (= :started (lsp-client/server-status project)) + Icons/STATUS_CONNECTED + Icons/STATUS_DISCONNECTED))))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/file_system.clj b/src/main/clojure/com/github/clojure_lsp/intellij/file_system.clj deleted file mode 100644 index 4c53de4..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/file_system.clj +++ /dev/null @@ -1,117 +0,0 @@ -(ns com.github.clojure-lsp.intellij.file-system - (:require - [clojure.java.io :as io] - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.config :as config] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.github.ericdallo.clj4intellij.app-manager :as app-manager] - [com.github.ericdallo.clj4intellij.util :as util] - [com.rpl.proxy-plus :refer [proxy+]]) - (:import - [com.intellij.openapi.fileEditor - FileDocumentManager] - [com.intellij.openapi.project Project] - [com.intellij.openapi.vfs LocalFileSystem VfsUtil VirtualFile] - [java.net URI])) - -(set! *warn-on-reflection* true) - -(defn create-temp-file ^VirtualFile - [^Project project ^String path ^String text] - (let [temp-file (io/file (config/project-cache-path project) path)] - (io/make-parents temp-file) - (spit temp-file text) - (proxy+ [] VirtualFile - (getName [_] (.getName temp-file)) - (getFileSystem [_] (LocalFileSystem/getInstance)) - (getPath [_] (.getCanonicalPath temp-file)) - (isWritable [_] false) - (isDirectory [_] false) - (isValid [_] true) - (getParent [_] nil) - (getChildren [_] []) - (contentsToByteArray [_] (.getBytes text)) - (getTimeStamp [_] 0) - (getLength [_] (count (.getBytes text))) - (refresh [_ _ _ _]) - (getInputStream [_] (io/input-stream temp-file)) - (getOutputStream [_ _ _ _] (io/output-stream temp-file)) - (getModificationStamp [_] -1)))) - -(defn ^:private create-document - [{:keys [uri]}] - (let [f (io/file (URI. uri)) - parent-vfile (VfsUtil/createDirectories (.getAbsolutePath (.getParentFile f)))] - (.findOrCreateChildData parent-vfile nil (.getName f)))) - -(declare apply-workspace-edit-sync) - -(defn will-rename-file [project old-uri new-uri] - (let [client (lsp-client/connected-client project)] - (->> (lsp-client/request! client [:workspace/willRenameFiles - {:files [{:old-uri old-uri - :new-uri new-uri}]}]) - deref - (apply-workspace-edit-sync project false)))) - -(defn ^:private rename-document - [{:keys [old-uri new-uri]} project] - (will-rename-file project old-uri new-uri) - (let [old-file (io/file (URI. old-uri)) - new-file (io/file (URI. new-uri)) - vfile (util/uri->v-file old-uri)] - (.rename vfile nil (.getName new-file)) - (when-not (= (.getAbsolutePath (.getParentFile old-file)) - (.getAbsolutePath (.getParentFile new-file))) - (.move vfile nil (util/uri->v-file (str (.toURI (.getParentFile new-file)))))))) - -(defn ^:private apply-document-change - [{{:keys [uri]} :text-document :keys [edits]} project move-caret?] - (let [editor (util/uri->editor uri project false) - document (.getDocument editor) - sorted-edits (sort-by (comp #(editor/document+position->offset % document) :start :range) > edits)] - (doseq [{:keys [new-text range]} sorted-edits - :let [start (editor/document+position->offset (:start range) document) - end (editor/document+position->offset (:end range) document)]] - (cond - (>= end 0) - (if (<= (- end start) 0) - (.insertString document start new-text) - (.replaceString document start end new-text)) - - (= 0 start) - (.setText document new-text) - - (> start 0) - (.insertString document start new-text) - - :else - (.insertString document (.getTextLength document) (str "\n" new-text))) - (when move-caret? - (.moveToOffset (.getCaretModel editor) - (+ (count new-text) start)))) - (.saveDocument (FileDocumentManager/getInstance) document))) - -(defn ^:private apply-workspace-edit-sync [project move-caret? {:keys [document-changes]}] - (doseq [document-change document-changes] - (case (:kind document-change) - "create" (create-document document-change) - "rename" (rename-document document-change project) - (apply-document-change document-change project move-caret?)))) - -(defn apply-workspace-edit ^Boolean - [^Project project label move-caret? edit] - ;; TODO Handle more resourceOperations like renaming and deleting files - ;; TODO Improve to check version to known if file changed - (app-manager/invoke-later! - {:invoke-fn - (fn [] - (app-manager/write-action! - {:run-fn - (fn [] - (app-manager/execute-command! - {:name label - :project project - :command-fn - (fn [] - (apply-workspace-edit-sync project move-caret? edit))}))}))})) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/listener/file.clj b/src/main/clojure/com/github/clojure_lsp/intellij/listener/file.clj deleted file mode 100644 index f3c8366..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/listener/file.clj +++ /dev/null @@ -1,98 +0,0 @@ -(ns com.github.clojure-lsp.intellij.listener.file - (:gen-class - :name com.github.clojure_lsp.intellij.listener.FileListener - :implements [com.intellij.openapi.fileEditor.FileEditorManagerListener - com.intellij.openapi.editor.event.DocumentListener]) - (:require - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.db :as db] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.github.clojure-lsp.intellij.project-lsp :as project] - [com.github.clojure-lsp.intellij.server :as server]) - (:import - [com.github.clojure_lsp.intellij.extension SettingsState] - [com.intellij.openapi.editor.event DocumentEvent] - [com.intellij.openapi.fileEditor FileDocumentManager FileEditorManager] - [com.intellij.openapi.project Project] - [com.intellij.openapi.vfs VirtualFile])) - -(set! *warn-on-reflection* true) - -(def ^:private valid-extensions #{"clj" "cljs" "cljc" "cljd" "edn" "bb" "clj_kondo"}) - -(defn ^:private ensure-server-up! - "If server was not started before, check if it's a Clojure project and starts it." - [^VirtualFile file ^Project project] - (cond - (and (not (db/empty-db?)) - (contains? #{:connecting :connected} (db/get-in project [:status]))) - true - - (not (contains? valid-extensions (.getExtension file))) - false - - (not (project/clojure-project? project)) - false - - :else - (do - (db/init-db-for-project project) - (db/load-settings-from-state! project (SettingsState/get)) - (server/start-server! project)))) - -(defn -fileOpened [_this ^FileEditorManager source ^VirtualFile file] - (let [project (.getProject source)] - (when (ensure-server-up! file project) - (db/await-field - project - :client - (fn [client] - (when (contains? valid-extensions (.getExtension file)) - (let [url (.getUrl file) - text (slurp (.getInputStream file))] - (lsp-client/notify! - client - [:textDocument/didOpen - {:text-document {:uri url - :language-id "clojure" - :version 0 - :text text}}]) - (db/assoc-in project [:documents url] {:version 0 - :text text})))))))) - -(defn -fileClosed [_ ^FileEditorManager file-editor-manager ^VirtualFile file] - (let [project (.getProject file-editor-manager)] - (db/await-field - project - :client - (fn [client] - (let [url (.getUrl file) - documents (db/get-in project [:documents])] - (when (get documents url) - (lsp-client/notify! - client - [:textDocument/didClose - {:text-document {:uri url}}]) - (db/assoc-in project [:documents] (dissoc documents url)))))))) - -(defn -fileOpenedSync [_ _ _ _]) -(defn -selectionChanged [_ _]) - -(defn -beforeDocumentChange [_ _]) -(defn -bulkUpdateStarting [_ _]) -(defn -bulkUpdateFinished [_ _]) - -(defn -documentChanged [_ ^DocumentEvent event] - (let [file-document-manager (FileDocumentManager/getInstance)] - (when-let [vfile (.getFile file-document-manager (.getDocument event))] - (when-let [project (editor/v-file->project vfile)] - (when-let [client (lsp-client/connected-client project)] - (let [url (.getUrl vfile)] - (when-let [{:keys [version]} (db/get-in project [:documents url])] - (lsp-client/notify! - client - [:textDocument/didChange - {:text-document {:uri url - :version (inc version)} - :content-changes [{:text (.getText (.getDocument event))}]}]) - (db/update-in project [:documents url :version] inc)))))))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/listener/project_manager.clj b/src/main/clojure/com/github/clojure_lsp/intellij/listener/project_manager.clj deleted file mode 100644 index 2982a7d..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/listener/project_manager.clj +++ /dev/null @@ -1,18 +0,0 @@ -(ns com.github.clojure-lsp.intellij.listener.project-manager - (:gen-class - :name com.github.clojure_lsp.intellij.listener.ProjectManagerListener - :implements [com.intellij.openapi.project.ProjectManagerListener]) - (:require - [com.github.clojure-lsp.intellij.project-lsp :as project] - [com.github.clojure-lsp.intellij.server :as server]) - (:import - [com.intellij.openapi.project Project])) - -(defn -projectOpened [_ _]) -(defn -canCloseProject [_ _]) -(defn -projectClosingBeforeSave [_ _]) -(defn -projectClosed [_ _]) - -(defn -projectClosing [_ ^Project project] - (when (project/clojure-project? project) - (server/shutdown! project))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/notification.clj b/src/main/clojure/com/github/clojure_lsp/intellij/notification.clj index e08ac2e..2a67ea9 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/notification.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/notification.clj @@ -1,12 +1,5 @@ (ns com.github.clojure-lsp.intellij.notification - (:require - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.ericdallo.clj4intellij.app-manager :as app-manager] - [com.github.ericdallo.clj4intellij.logger :as logger] - [com.github.ericdallo.clj4intellij.tasks :as tasks] - [seesaw.core :as see]) (:import - [com.github.clojure_lsp.intellij Icons] [com.intellij.notification NotificationGroupManager NotificationType] [com.intellij.openapi.project Project])) @@ -17,40 +10,8 @@ :warning NotificationType/WARNING :info NotificationType/INFORMATION}) -(def ^:private message-type->notification-type - {1 :error - 2 :warning - 3 :info - 4 :info}) - (defn show-notification! [{:keys [project type title message]}] (-> (NotificationGroupManager/getInstance) (.getNotificationGroup "Clojure LSP notifications") (.createNotification ^String title ^String message ^NotificationType (type->notification-type type)) (.notify ^Project project))) - -(defmethod lsp-client/show-message :default [{:keys [project]} {:keys [type message]}] - (show-notification! {:project project - :type (message-type->notification-type type) - :title "Clojure LSP" - :message message})) - -(defmethod lsp-client/show-message-request :default [{:keys [_type message actions]}] - @(app-manager/invoke-later! - {:invoke-fn - (fn [] - (see/input message - :title "Clojure LSP" - :type :question - :icon Icons/CLOJURE - :choices actions - :to-string :title))})) - -(defmethod lsp-client/progress :default [_ progress] - (logger/warn "Unknown progress token" progress)) - -(defmethod lsp-client/progress "lsp-startup" [{:keys [progress-indicator]} {{:keys [title message percentage]} :value}] - (let [msg (str "LSP: " (or title message))] - (if percentage - (tasks/set-progress progress-indicator msg percentage) - (tasks/set-progress progress-indicator msg)))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/project_lsp.clj b/src/main/clojure/com/github/clojure_lsp/intellij/project_lsp.clj index 631dafb..1eccdc9 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/project_lsp.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/project_lsp.clj @@ -7,14 +7,11 @@ (set! *warn-on-reflection* true) -(defn project->root-uri ^String [^Project project] - (-> (.getBasePath project) io/file .toPath .toUri str)) - (defn clojure-project? "If a project file was not opened before, we didn't start the server yet, so we guess if it's Clojure project checking for common deps management files." [^Project project] - (or (boolean (lsp-client/connected-client project)) + (or (not (= :none (lsp-client/server-status project))) (let [project-path (.getBasePath project)] (or (.exists (io/file project-path "deps.edn")) (.exists (io/file project-path "project.clj")) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/psi.clj b/src/main/clojure/com/github/clojure_lsp/intellij/psi.clj deleted file mode 100644 index 376c444..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/psi.clj +++ /dev/null @@ -1,125 +0,0 @@ -(ns com.github.clojure-lsp.intellij.psi - (:require - [clojure.string :as string] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.github.ericdallo.clj4intellij.logger :as logger] - [com.github.ericdallo.clj4intellij.util :as util]) - (:import - [com.github.clojure_lsp.intellij ClojureLanguage] - [com.github.clojure_lsp.intellij Icons] - [com.intellij.navigation ItemPresentation NavigationItem PsiElementNavigationItem] - [com.intellij.openapi.editor ScrollType] - [com.intellij.openapi.fileEditor FileEditorManager] - [com.intellij.openapi.project Project] - [com.intellij.openapi.util TextRange] - [com.intellij.psi - PsiElement - PsiElementVisitor - PsiFile - PsiManager - PsiNameIdentifierOwner - PsiReference])) - -(set! *warn-on-reflection* true) - -(defn ->LSPPsiElement - [^String text ^Project project ^PsiFile file - ^Integer start-offset ^Integer end-offset - ^Integer start-line] - (let [psi-manager (PsiManager/getInstance project) - file-editor-manager (FileEditorManager/getInstance project)] - (reify - PsiNameIdentifierOwner - (getName [_] - (str (editor/filename->project-relative-filename - (.getCanonicalPath (.getVirtualFile file)) - project) - " " - start-line)) - (setName [_ _]) - (getNameIdentifier [this] this) - (getIdentifyingElement [this] this) - (getProject [_] project) - (getLanguage [_] ClojureLanguage/INSTANCE) - (getManager [_] psi-manager) - (getChildren [_] (into-array PsiElement [])) - (getParent [this] (.getContainingFile this)) - (getFirstChild [_]) - (getLastChild [_]) - (getNextSibling [_]) - (getPrevSibling [_]) - (getTextRange [_] (TextRange. start-offset end-offset)) - (getTextOffset [_] start-offset) - (getStartOffsetInParent [_] start-offset) - (getTextLength [_] (- end-offset start-offset)) - (findElementAt [_ _]) - (findReferenceAt [_ _]) - (textToCharArray [_] (char-array text)) - (getNavigationElement [this] this) - (getOriginalElement [this] this) - (^boolean textMatches [this ^CharSequence text] - (= (.getText this) text)) - (^boolean textMatches [this ^PsiElement element] - (.equals (.getText this) (.getText element))) - (^boolean textContains [this ^char c] - (string/includes? (.getText this) (str c))) - (getText [_] text) - (^void accept [this ^PsiElementVisitor visitor] - (.visitElement visitor this)) - (acceptChildren [_ _]) - (copy [_]) - (add [_ _]) - (addBefore [_ _ _]) - (addAfter [_ _ _]) - (checkAdd [_ _]) - (addRange [_ _ _]) - (addRangeBefore [_ _ _ _]) - (addRangeAfter [_ _ _ _]) - (delete [_]) - (checkDelete [_]) - (deleteChildRange [_ _ _]) - (replace [_ _]) - (isValid [_] true) - (isWritable [_] true) - (getReference [_]) - (getReferences [_] (into-array PsiReference [])) - (processDeclarations [_ _ _ _ _] false) - (getContext [_]) - (isPhysical [_] true) - (getResolveScope [this] (.getResolveScope (.getContainingFile this))) - (getUseScope [this] (.getResolveScope (.getContainingFile this))) - (getNode [_]) - (isEquivalentTo [this other] (= this other)) - (toString [_] (str text ":" start-offset ":" end-offset)) - (getContainingFile [_] file) - - PsiElementNavigationItem - (getTargetElement [this] this) - - NavigationItem - (getPresentation [this] - (reify ItemPresentation - (getPresentableText [_] (.getName this)) - (getIcon [_ _] Icons/CLOJURE))) - (canNavigate [_] true) - (canNavigateToSource [_] true) - (navigate [_ _] - (let [editor (util/v-file->editor (.getVirtualFile file) project true)] - (.openFile file-editor-manager (.getVirtualFile file) true) - (.moveToOffset (.getCaretModel editor) start-offset) - (.scrollToCaret (.getScrollingModel editor) ScrollType/CENTER))) - - (getUserData [_ _] nil) - (putUserData [_ _ _]) - (getCopyableUserData [_ _]) - (putCopyableUserData [_ _ _]) - - (getIcon [_ _] Icons/CLOJURE)))) - -(defn element->psi-element [{:keys [uri] {:keys [start end]} :range} project] - (let [text (slurp uri) - start-offset (editor/position->offset text (:line start) (:character start)) - end-offset (editor/position->offset text (:line end) (:character end)) - file (editor/uri->psi-file uri project) - name (subs text start-offset end-offset)] - (->LSPPsiElement name project file start-offset end-offset (:line start)))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/server.clj b/src/main/clojure/com/github/clojure_lsp/intellij/server.clj index 74179a6..6736dcc 100644 --- a/src/main/clojure/com/github/clojure_lsp/intellij/server.clj +++ b/src/main/clojure/com/github/clojure_lsp/intellij/server.clj @@ -1,50 +1,28 @@ (ns com.github.clojure-lsp.intellij.server (:require - [babashka.process :as p] [clojure.java.io :as io] [clojure.string :as string] - [com.github.clojure-lsp.intellij.client :as lsp-client] [com.github.clojure-lsp.intellij.config :as config] [com.github.clojure-lsp.intellij.db :as db] [com.github.clojure-lsp.intellij.notification :as notification] - [com.github.clojure-lsp.intellij.project-lsp :as project] - [com.github.clojure-lsp.intellij.workspace-edit] + [com.github.clojure-lsp.intellij.server :as server] + [com.github.clojure-lsp.intellij.settings :as settings] [com.github.ericdallo.clj4intellij.logger :as logger] [com.github.ericdallo.clj4intellij.tasks :as tasks]) (:import - [com.github.ericdallo.clj4intellij ClojureClassLoader] [com.intellij.openapi.project Project] - [com.intellij.util EnvironmentUtil] + [com.intellij.openapi.project Project] + [com.redhat.devtools.lsp4ij LanguageServerManager] [java.io File] [java.util.zip ZipInputStream])) (set! *warn-on-reflection* true) -(def ^:private client-capabilities - {:text-document {:hover {:content-format ["markdown"]} - :implementation {}} - :workspace {:workspace-edit {:document-changes true - :resource-operations ["create" "rename"]} - :file-operations {:will-rename true}} - :window {:show-document true}}) - -(def ^:private artifacts - {:linux {:amd64 "clojure-lsp-native-static-linux-amd64.zip" - :aarch64 "clojure-lsp-native-linux-aarch64.zip"} - :macos {:amd64 "clojure-lsp-native-macos-amd64.zip" - :aarch64 "clojure-lsp-native-macos-aarch64.zip"} - :windows {:amd64 "clojure-lsp-native-windows-amd64.zip"}}) +(def ^:private latest-version-uri + "https://raw.githubusercontent.com/clojure-lsp/clojure-lsp/master/lib/resources/CLOJURE_LSP_RELEASED_VERSION") -(defn ^:private clean-up-server [^Project project] - (let [server-process (db/get-in project [:server-process])] - (when (some-> server-process p/alive?) - (p/destroy server-process))) - (db/update-in project [] (fn [db] - (assoc db :status :disconnected - :client nil - :server-process nil - :diagnostics {}))) - (run! #(% project :disconnected) (:on-status-changed-fns @db/db*))) +(def ^:private download-artifact-uri + "https://github.com/clojure-lsp/clojure-lsp/releases/download/%s/%s") (defn ^:private os-name [] (let [os-name (string/lower-case (System/getProperty "os.name" "generic"))] @@ -58,11 +36,12 @@ :aarch64 :amd64)) -(def ^:private latest-version-uri - "https://raw.githubusercontent.com/clojure-lsp/clojure-lsp/master/lib/resources/CLOJURE_LSP_RELEASED_VERSION") - -(def ^:private download-artifact-uri - "https://github.com/clojure-lsp/clojure-lsp/releases/download/%s/%s") +(def ^:private artifacts + {:linux {:amd64 "clojure-lsp-native-static-linux-amd64.zip" + :aarch64 "clojure-lsp-native-linux-aarch64.zip"} + :macos {:amd64 "clojure-lsp-native-macos-amd64.zip" + :aarch64 "clojure-lsp-native-macos-aarch64.zip"} + :windows {:amd64 "clojure-lsp-native-windows-amd64.zip"}}) (defn ^:private unzip-file [input ^File dest-file] (with-open [stream (-> input io/input-stream ZipInputStream.)] @@ -75,7 +54,7 @@ (recur (.getNextEntry stream)))))) (defn ^:private download-server! [project indicator ^File download-path ^File server-version-path latest-version] - (tasks/set-progress indicator "LSP: Downloading clojure-lsp") + (tasks/set-progress indicator "LSP: downloading clojure-lsp") (let [platform (os-name) arch (os-arch) artifact-name (get-in artifacts [platform arch]) @@ -92,91 +71,37 @@ (db/assoc-in project [:downloaded-server-path] dest-path) (logger/info "Downloaded clojure-lsp to" dest-path))) -(defn ^:private spawn-server! [^Project project indicator server-path] - (logger/info "Spawning LSP server process using path" server-path) - (tasks/set-progress indicator "LSP: Starting server...") - (let [trace-level (keyword (db/get-in project [:settings :trace-level])) - process (p/process [server-path "listen"] - {:dir (.getBasePath project) - :env (EnvironmentUtil/getEnvironmentMap) - :err :string}) - client (lsp-client/client (:in process) (:out process) trace-level)] - (db/assoc-in project [:server-process] process) - (lsp-client/start-client! client {:progress-indicator indicator - :project project}) - - (tasks/set-progress indicator "LSP: Initializing...") - (let [request-initiatilize (lsp-client/request! client [:initialize - {:root-uri (project/project->root-uri project) - :work-done-token "lsp-startup" - :initialization-options (merge {:dependency-scheme "jar" - :hover {:arity-on-same-line? true}} - (db/get-in project [:settings])) - :capabilities client-capabilities}])] - (loop [count 0] - (Thread/sleep 500) - (cond - (and (not (realized? request-initiatilize)) - (not (p/alive? process))) - (notification/show-notification! {:project project - :type :error - :title "Clojure LSP process error" - :message @(:err process)}) - - (and (realized? request-initiatilize) - (p/alive? process)) - (do (lsp-client/notify! client [:initialized {}]) - (db/assoc-in project [:client] client)) - - :else - (do - (logger/info "Checking if server initialized, try number:" count) - (recur (inc count)))))))) - -(defn start-server! [^Project project] - (db/assoc-in project [:status] :connecting) - (run! #(% project :connecting) (:on-status-changed-fns @db/db*)) +(defn install-server [project installed-fn] (tasks/run-background-task! project - "Clojure LSP startup" + "LSP: install clojure-lsp" (fn [indicator] - (ClojureClassLoader/bind) (let [download-path (config/download-server-path) server-version-path (config/download-server-version-path) latest-version* (delay (try (string/trim (slurp latest-version-uri)) (catch Exception _ nil))) - custom-server-path (db/get-in project [:settings :server-path])] + custom-server-path (settings/server-path)] (cond custom-server-path - (spawn-server! project indicator custom-server-path) + (installed-fn {:status :installed :path custom-server-path}) (and (.exists download-path) (or (not @latest-version*) ;; on network connection issues we use any downloaded server (= (try (slurp server-version-path) (catch Exception _ :error-checking-local-version)) @latest-version*))) - (spawn-server! project indicator download-path) + (installed-fn {:status :installed :path download-path}) @latest-version* (do (download-server! project indicator download-path server-version-path @latest-version*) - (spawn-server! project indicator download-path)) + (installed-fn {:status :installed :path download-path})) :else (notification/show-notification! {:project project :type :error :title "Clojure LSP download error" - :message "There is no server downloaded and there was a network issue to download the latest one"})) + :message "There is no server downloaded and there was a network issue to download the latest one"})))))) - (db/assoc-in project [:status] :connected) - (run! #(% project :connected) (:on-status-changed-fns @db/db*)) - ;; For race conditions when server starts too fast - ;; and other places that listen didn't setup yet - (future - (Thread/sleep 1000) - (run! #(% project :connected) (:on-status-changed-fns @db/db*))) - (logger/info "Initialized LSP server")))) - true) +(defn start! [^Project project] + (.start (LanguageServerManager/getInstance project) "clojure-lsp")) (defn shutdown! [^Project project] - (when-let [client (lsp-client/connected-client project)] - (db/assoc-in project [:status] :shutting-down) - @(lsp-client/request! client [:shutdown {}]) - (clean-up-server project))) + (.stop (LanguageServerManager/getInstance project) "clojure-lsp")) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/settings.clj b/src/main/clojure/com/github/clojure_lsp/intellij/settings.clj new file mode 100644 index 0000000..3716da2 --- /dev/null +++ b/src/main/clojure/com/github/clojure_lsp/intellij/settings.clj @@ -0,0 +1,23 @@ +(ns com.github.clojure-lsp.intellij.settings + (:import + [com.github.clojure_lsp.intellij.extension SettingsState])) + +(set! *warn-on-reflection* true) + +(defn server-path [] + (.getServerPath (SettingsState/get))) + +(defn server-trace-level [] + (or (.getTraceLevel (SettingsState/get)) "off")) + +(defn server-log-path [] + (.getServerLogPath (SettingsState/get))) + +(defn set-server-trace-level! [^String trace-level] + (.setTraceLevel (SettingsState/get) trace-level)) + +(defn set-server-log-path! [^String log-path] + (.setServerLogPath (SettingsState/get) (not-empty log-path))) + +(defn set-server-path! [^String server-path] + (.setServerPath (SettingsState/get) (not-empty server-path))) diff --git a/src/main/clojure/com/github/clojure_lsp/intellij/workspace_edit.clj b/src/main/clojure/com/github/clojure_lsp/intellij/workspace_edit.clj deleted file mode 100644 index 1246f33..0000000 --- a/src/main/clojure/com/github/clojure_lsp/intellij/workspace_edit.clj +++ /dev/null @@ -1,21 +0,0 @@ -(ns com.github.clojure-lsp.intellij.workspace-edit - (:require - [com.github.clojure-lsp.intellij.client :as lsp-client] - [com.github.clojure-lsp.intellij.editor :as editor] - [com.github.clojure-lsp.intellij.file-system :as file-system] - [com.github.ericdallo.clj4intellij.app-manager :as app-manager] - [com.github.ericdallo.clj4intellij.util :as util])) - -(set! *warn-on-reflection* true) - -(defmethod lsp-client/workspace-apply-edit :default [{:keys [project]} {:keys [label edit]}] - (file-system/apply-workspace-edit project label false edit) - {:applied true}) - -(defmethod lsp-client/show-document :default [{:keys [project]} {:keys [uri take-focus selection]}] - (app-manager/invoke-later! - {:invoke-fn (fn [] - (let [editor (util/uri->editor uri project (boolean take-focus))] - (.moveToOffset (.getCaretModel editor) - (editor/document+position->offset (:start selection) (.getDocument editor)))))}) - {:done true}) diff --git a/src/main/kotlin/clojure_language_server.kt b/src/main/kotlin/clojure_language_server.kt new file mode 100644 index 0000000..fd36a19 --- /dev/null +++ b/src/main/kotlin/clojure_language_server.kt @@ -0,0 +1,14 @@ +package com.github.clojure_lsp.intellij + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest +import org.eclipse.lsp4j.services.LanguageServer +import java.util.concurrent.CompletableFuture + +interface ClojureLanguageServer : LanguageServer { + + @JsonRequest("clojure/dependencyContents") + fun dependencyContents(params: Any): CompletableFuture + + @JsonRequest("clojure/serverInfo/raw") + fun serverInfo(): CompletableFuture +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 2ff1eac..9924563 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -4,81 +4,63 @@ clojure-lsp com.intellij.modules.platform + com.redhat.devtools.lsp4ij + + + + Clojure LSP: The Clojure language server that provides features using static analysis. + +

For more details, check clojure-lsp.io

+ ]]> + + + + + org.jetbrains.idea.clojure com.cursiveclojure.cursive + - - - + - - + + - - - - - - - + - - - - clojurecom.github.clojure_lsp.intellij.extension.code_action.AddMissingImport - clojurecom.github.clojure_lsp.intellij.extension.code_action.AddMissingLibspec - clojurecom.github.clojure_lsp.intellij.extension.code_action.AddRequireSuggestion - clojurecom.github.clojure_lsp.intellij.extension.code_action.CycleColl - clojurecom.github.clojure_lsp.intellij.extension.code_action.CycleKeywordAutoResolve - clojurecom.github.clojure_lsp.intellij.extension.code_action.CleanNs - clojurecom.github.clojure_lsp.intellij.extension.code_action.CyclePrivacy - clojurecom.github.clojure_lsp.intellij.extension.code_action.CreateTest - clojurecom.github.clojure_lsp.intellij.extension.code_action.DragParamBackward - clojurecom.github.clojure_lsp.intellij.extension.code_action.DragParamForward - clojurecom.github.clojure_lsp.intellij.extension.code_action.DragBackward - clojurecom.github.clojure_lsp.intellij.extension.code_action.DragForward - clojurecom.github.clojure_lsp.intellij.extension.code_action.DemoteFn - clojurecom.github.clojure_lsp.intellij.extension.code_action.DestructureKeys - clojurecom.github.clojure_lsp.intellij.extension.code_action.ExtractToDef - clojurecom.github.clojure_lsp.intellij.extension.code_action.ExtractFunction - clojurecom.github.clojure_lsp.intellij.extension.code_action.ExpandLet - clojurecom.github.clojure_lsp.intellij.extension.code_action.CreateFunction - clojurecom.github.clojure_lsp.intellij.extension.code_action.GetInAll - clojurecom.github.clojure_lsp.intellij.extension.code_action.GetInLess - clojurecom.github.clojure_lsp.intellij.extension.code_action.GetInMore - clojurecom.github.clojure_lsp.intellij.extension.code_action.GetInNone - clojurecom.github.clojure_lsp.intellij.extension.code_action.IntroduceLet - clojurecom.github.clojure_lsp.intellij.extension.code_action.InlineSymbol - clojurecom.github.clojure_lsp.intellij.extension.code_action.ResolveMacroAs - clojurecom.github.clojure_lsp.intellij.extension.code_action.MoveForm - clojurecom.github.clojure_lsp.intellij.extension.code_action.MoveToLet - clojurecom.github.clojure_lsp.intellij.extension.code_action.PromoteFn - clojurecom.github.clojure_lsp.intellij.extension.code_action.ReplaceReferAllWithRefer - clojurecom.github.clojure_lsp.intellij.extension.code_action.ReplaceReferAllWithAlias - clojurecom.github.clojure_lsp.intellij.extension.code_action.RestructureKeys - clojurecom.github.clojure_lsp.intellij.extension.code_action.ChangeColl - clojurecom.github.clojure_lsp.intellij.extension.code_action.SortClauses - clojurecom.github.clojure_lsp.intellij.extension.code_action.ThreadFirstAll - clojurecom.github.clojure_lsp.intellij.extension.code_action.ThreadLastAll - clojurecom.github.clojure_lsp.intellij.extension.code_action.UnwindAll + implementationClass="com.redhat.devtools.lsp4ij.features.signatureHelp.LSPParameterInfoHandler"/> + + + @@ -93,21 +75,12 @@ id="com.github.clojure_lsp.intellij.extension.GeneralSettingsConfigurable" displayName="Clojure LSP"/> + - - - - - - - - - diff --git a/src/main/resources/project-templates/lein/project.clj b/src/main/resources/project-templates/lein/project.clj index ad77aba..4596bc3 100644 --- a/src/main/resources/project-templates/lein/project.clj +++ b/src/main/resources/project-templates/lein/project.clj @@ -3,5 +3,5 @@ :url "http://example.com/FIXME" :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" :url "https://www.eclipse.org/legal/epl-2.0/"} - :dependencies [[org.clojure/clojure "1.11.1"]] + :dependencies [[org.clojure/clojure "1.12.0"]] :repl-options {:init-ns project-name.core})