Skip to content

Commit d4245b7

Browse files
committed
Add logic to download and run native clojure-lsp
1 parent c6aa457 commit d4245b7

File tree

6 files changed

+102
-29
lines changed

6 files changed

+102
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
- Fix --settings flag
6+
- Replace clojure-lsp JVM with native binary for way better performance.
67

78
## 1.4.21
89

project.clj

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,4 @@
99
:username :env/clojars_username
1010
:password :env/clojars_password
1111
:sign-releases false}]]
12-
:managed-dependencies [[com.fasterxml.jackson.core/jackson-core "2.18.0"]
13-
[org.clojure/tools.cli "1.1.230"]]
14-
:dependencies [[com.github.clojure-lsp/clojure-lsp-server "2025.03.27-20.21.36" :exclusions [babashka/fs
15-
cheshire
16-
org.jetbrains.kotlin/kotlin-stdlib-jdk7
17-
com.fasterxml.jackson.dataformat/jackson-dataformat-smile
18-
com.fasterxml.jackson.dataformat/jackson-dataformat-cbor
19-
org.jetbrains.kotlin/kotlin-stdlib
20-
org.jetbrains.kotlin/kotlin-stdlib-jdk8
21-
org.jetbrains.kotlin/kotlin-stdlib-jdk7
22-
io.opentelemetry/opentelemetry-context
23-
io.opentelemetry/opentelemetry-api
24-
org.clojure/tools.reader]]
25-
[cheshire/cheshire "5.13.0"]
26-
[babashka/fs "0.5.21"]
27-
[com.fasterxml.jackson.dataformat/jackson-dataformat-smile "2.18.0"]
28-
[com.fasterxml.jackson.dataformat/jackson-dataformat-cbor "2.18.0"]
29-
[org.clojure/tools.reader "1.5.0"]])
12+
:dependencies [[babashka/process "0.6.23"]])

resources/CLOJURE_LSP_VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2025.03.27-20.21.36

scripts/lein_clojure_lsp/ci.clj

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@
4646
(defn tag-patch-for-version [& [version]]
4747
(shell "git fetch origin")
4848
(shell "git pull origin HEAD")
49-
(replace-in-file "project.clj"
50-
#"com.github.clojure-lsp/clojure-lsp-server \".*\""
51-
(format "com.github.clojure-lsp/clojure-lsp-server \"%s\"" version))
49+
(spit "resources/CLOJURE_LSP_VERSION" version)
5250
(let [new-tag (get-patched-tag)]
5351
(replace-tag new-tag)
5452
(add-changelog-entry new-tag (str "Bump clojure-lsp to " version))

src/leiningen/clojure_lsp.clj

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
(:refer-clojure :exclude [run!])
33
(:require
44
[clojure.edn :as edn]
5-
[clojure-lsp.main :as lsp]
6-
[leiningen.core.main :as lein-core]))
5+
[leiningen.core.main :as lein-core]
6+
[leiningen.clojure-lsp.binary :as lsp-binary]))
77

88
(defn ^:private has-settings?
99
[command-and-options]
@@ -32,13 +32,10 @@
3232

3333
(defn ^:private run!
3434
[command-and-options project-settings]
35-
(let [result (apply lsp/run! (args command-and-options project-settings))]
36-
(when-let [message-fn (:message-fn result)]
37-
(println (message-fn)))
38-
(when (not= 0 (:result-code result))
39-
(lein-core/exit (:result-code result) "clojure-lsp found issues"))))
35+
(let [{:keys [exit]} (lsp-binary/run! (args command-and-options project-settings))]
36+
(when (not= 0 exit)
37+
(lein-core/exit exit "clojure-lsp found issues"))))
4038

41-
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
4239
(defn ^:no-project-needed clojure-lsp
4340
"Access clojure-lsp API features"
4441
[project & command-and-options]

src/leiningen/clojure_lsp/binary.clj

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
(ns leiningen.clojure-lsp.binary
2+
(:refer-clojure :exclude [run!])
3+
(:require
4+
[babashka.process :as process]
5+
[clojure.java.io :as io]
6+
[clojure.string :as string])
7+
(:import
8+
[java.io BufferedReader File]
9+
[java.util.zip ZipInputStream]))
10+
11+
(set! *warn-on-reflection* true)
12+
13+
(defn ^:private global-cache-dir []
14+
(let [cache-home (or (System/getenv "XDG_CACHE_HOME")
15+
(io/file (System/getProperty "user.home") ".cache"))]
16+
(io/file cache-home "lein-clojure-lsp")))
17+
18+
(def ^:private download-artifact-uri
19+
"https://github.com/clojure-lsp/clojure-lsp/releases/download/%s/%s")
20+
21+
(defn ^:private os-name []
22+
(let [os-name (string/lower-case (System/getProperty "os.name" "generic"))]
23+
(cond
24+
(string/includes? os-name "win") :windows
25+
(string/includes? os-name "mac") :macos
26+
:else :linux)))
27+
28+
(defn ^:private os-arch []
29+
(if (= "aarch64" (System/getProperty "os.arch"))
30+
:aarch64
31+
:amd64))
32+
33+
(def ^:private artifacts
34+
{:linux {:amd64 "clojure-lsp-native-static-linux-amd64.zip"
35+
:aarch64 "clojure-lsp-native-linux-aarch64.zip"}
36+
:macos {:amd64 "clojure-lsp-native-macos-amd64.zip"
37+
:aarch64 "clojure-lsp-native-macos-aarch64.zip"}
38+
:windows {:amd64 "clojure-lsp-native-windows-amd64.zip"}})
39+
40+
(defn ^:private unzip-file [input ^File dest-file]
41+
(with-open [stream (-> input io/input-stream ZipInputStream.)]
42+
(loop [entry (.getNextEntry stream)]
43+
(when entry
44+
(if (.isDirectory entry)
45+
(when-not (.exists dest-file)
46+
(.mkdirs dest-file))
47+
(clojure.java.io/copy stream dest-file))
48+
(recur (.getNextEntry stream))))))
49+
50+
(defn ^:private download! [^File download-path version]
51+
(let [platform (os-name)
52+
arch (os-arch)
53+
artifact-name (get-in artifacts [platform arch])
54+
uri (format download-artifact-uri version artifact-name)]
55+
(io/make-parents download-path)
56+
(unzip-file (io/input-stream uri) download-path)
57+
(doto download-path
58+
(.setWritable true)
59+
(.setReadable true)
60+
(.setExecutable true))))
61+
62+
(defn ^:private server-version []
63+
(string/trim (slurp (io/resource "CLOJURE_LSP_VERSION"))))
64+
65+
(defn ^:private server-path ^File []
66+
(io/file (global-cache-dir) "clojure-lsp"))
67+
68+
(defn ^:private run-lsp! [^File path args]
69+
(let [p (process/process {:cmd (concat [(.getAbsolutePath path)] args)})]
70+
(future
71+
(with-open [out-rdr ^BufferedReader (io/reader (:out p))]
72+
(loop [max 10]
73+
(when-let [line (.readLine out-rdr)]
74+
(println line)
75+
(when (pos? max)
76+
(recur (dec max)))))))
77+
(future
78+
(with-open [out-rdr ^BufferedReader (io/reader (:err p))]
79+
(loop [max 10]
80+
(when-let [line (.readLine out-rdr)]
81+
(println line)
82+
(when (pos? max)
83+
(recur (dec max)))))))
84+
@p))
85+
86+
(defn run! [args]
87+
(let [server-path (server-path)
88+
server-version (server-version)]
89+
(when-not (.exists server-path)
90+
(println "Downloading and caching clojure-lsp...")
91+
(download! server-path server-version)
92+
(println "Downloaded clojure-lsp!"))
93+
(run-lsp! server-path args)))

0 commit comments

Comments
 (0)