Skip to content

Commit 2eab4a0

Browse files
Add jack-in support for ClojureCLR
1 parent e711107 commit 2eab4a0

7 files changed

Lines changed: 154 additions & 15 deletions

File tree

.github/workflows/test.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,21 @@ jobs:
3232
os: [ubuntu-latest]
3333
emacs_version: ['28.2', '29.3', '30.1']
3434
java_version: ['21']
35+
dotnet_version: ['10.0.7']
3536
include:
3637
# For other OSes, test only the latest stable Emacs version.
3738
- os: macos-latest # aarch64
3839
emacs_version: '30.1'
3940
java_version: '21'
41+
dotnet_version: '10.0.7'
4042
- os: macos-15 # x64
4143
emacs_version: '30.1'
4244
java_version: '21'
45+
dotnet_version: '10.0.7'
4346
- os: windows-latest
4447
emacs_version: '30.1'
4548
java_version: '21'
49+
dotnet_version: '10.0.7'
4650

4751
steps:
4852
- name: Set up Emacs
@@ -109,6 +113,16 @@ jobs:
109113
- run: |
110114
pip install basilisp==0.5.0
111115
116+
- name: Prepare dotnet
117+
uses: xt0rted/setup-dotnet@v1.5.0
118+
with:
119+
dotnet-version: ${{matrix.dotnet_version}}
120+
121+
- name: Prepare ClojureCLR
122+
run: |
123+
dotnet tool install --global Clojure.Main --version 1.12.3-alpha8
124+
dotnet tool install --global Clojure.Cljr --version 0.1.0-alpha11
125+
112126
- name: Test integration
113127
run: |
114128
# The tests occasionally fail on macos&win in what is seems to

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- Cache the result of `cider--running-nrepl-paths` (used by `cider-locate-running-nrepl-ports`) for `cider-running-nrepl-paths-cache-ttl` seconds (default 5). Repeated `cider-connect` completions no longer re-spawn a fresh round of `ps`/`lsof` subprocesses each time. `cider-clear-running-nrepl-paths-cache` discards the cache on demand.
1111
- New `nrepl-make-eval-handler` with a keyword-arg API (`:on-value`, `:on-stdout`, `:on-stderr`, `:on-done`, `:on-eval-error`, `:on-content-type`, `:on-truncated`). Sub-handlers no longer take a buffer argument -- they close over whatever they need. `nrepl-make-response-handler`, the legacy 7-positional-arg form, is preserved as an obsolete shim that adapts the old (buffer x) lambdas to the new (x) lambdas, so existing extensions keep working.
1212
- Decouple the nREPL transport layer from CIDER's UI layer (closes [#1099](https://github.com/clojure-emacs/cider/issues/1099)). `nrepl-make-eval-handler` is now CIDER-agnostic: it no longer references `nrepl-namespace-handler-function`, `nrepl-err-handler-function`, `nrepl-need-input-handler-function`, or any hardcoded UI strings. New `:on-ns` and `:on-status` keyword slots let any consumer wire up their own namespace tracking and status handling. The editor-level `cider-make-eval-handler` wraps it with CIDER's UI behavior (ns tracking, default error handler, need-input prompt, "Evaluation interrupted." / "Namespace not found." messages); in-tree callers all use it.
13+
- [#3839](https://github.com/clojure-emacs/cider/pull/3839): Add jack-in support for ClojureCLR.
1314

1415
### Bugs fixed
1516

doc/modules/ROOT/pages/basics/up_and_running.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ The following Clojure build tools are supported so far
182182
- kbd:[M-3 C-c C-x j u] jack-in using babashka.
183183
- kbd:[M-4 C-c C-x j u] jack-in using nbb.
184184
- kbd:[M-5 C-c C-x j u] jack-in using basilisp.
185+
- kbd:[M-6 C-c C-x j u] jack-in using ClojureCLR.
185186

186187
Here is an example of how to bind kbd:[F12] for quickly bringing up a
187188
babashka REPL:

doc/modules/ROOT/pages/caveats.adoc

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,11 @@ from your Emacs config.
6060

6161
== ClojureCLR Support
6262

63-
CIDER currently has very basic support for ClojureCLR (via Arcadia's nREPL server). The reasons for this are the following:
63+
CIDER currently has very basic support for ClojureCLR (via either a port of Babashka's nREPL server or Arcadia's nREPL server). The reasons for this are the following:
6464

65-
* nREPL itself runs only on the JVM (because it leverages Java APIs
66-
internally). There's an
67-
https://github.com/clojure/clr.tools.nrepl[nREPL port for ClojureCLR], but
68-
it's not actively maintained and it doesn't behave like the Clojure nREPL.
69-
* `cider-nrepl` uses a lot of Java code internally itself.
65+
* The https://github.com/clojure/clr.tools.nrepl/tree/master/partial-nrepl-nrepl-port[nrepl/nrepl port to ClojureCLR] is not yet working
66+
* `cider-nrepl` uses a lot of Java code internally itself and would need to be adapted/ported like any
67+
other clojure library adapted/ported to ClojureCLR.
7068

7169
Those issues are not insurmountable, but are beyond the scope of our current roadmap.
7270
If someone would like to tackle them, we'd be happy to provide assistance.

doc/modules/ROOT/pages/platforms/clojureclr.adoc

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,27 @@
22

33
== Current Status
44

5-
ClojureCLR on CIDER is not great due to the lack of a fully-functional nREPL
6-
server for ClojureCLR. There are currently two options:
5+
You will get basic CIDER functionality with ClojureCLR. Three nREPL server options:
76

8-
- https://github.com/clojure/clr.tools.nrepl[clr.tools.nrepl]: A direct (but incomplete) port of the reference Clojure nREPL server.
7+
- https://github.com/clojure/clr.tools.nrepl[clr.tools.nrepl]: At present port of babashka's nREPL server (https://github.com/babashka/babashka.nrepl[babashka.nrepl]).
8+
- https://github.com/clojure/clr.tools.nrepl/tree/master/partial-nrepl-nrepl-port[port of nrepl/nrepl]: A non-working, work-in-progress port of nrepl/nrepl, which may
9+
ultimately better integrate with CIDER once CIDER's middleware (cider-nrepl) is also adapted/ported.
910
- https://github.com/arcadia-unity/Arcadia/blob/master/Editor/NRepl.cs[Arcadia's nREPL]: A basic, but working nREPL implementation in C#.
1011

11-
If you need to use CIDER with ClojureCLR today Arcadia's nREPL is your only usable option. That being said - `clr.tools.nrepl` is a much
12-
more sophisticated project and ideally we should get it over to the finish line.
12+
An alternative to CIDER & a nREPL server is inf-clojure with ClojureCLR's stock socket REPL server.
1313

1414
== Usage
1515

1616
NOTE: Contributions welcome!
1717

18-
As `cider-jack-in` doesn't support ClojureCLR projects out-of-the-box currently, you'll need to start an nREPL server externally and
19-
connect to it with `cider-connect`.
18+
`cider-jack-in-universal` will jack into a clr.tools.nrepl server as long as a `deps-clr.edn` file
19+
exists in the project directory, otherwise you may call `cider-jack-in-universal` with prefix
20+
argument 6, by either `M-6` or `C-u 6` followed by `M-x cider-jack-in-universal`.
2021

2122
== Plans
2223

2324
In an ideal world we'll achieve the following objectives:
2425

25-
- out-of-the-box ClojureCLR support with `cider-jack-in`
2626
- feature parity between Clojure's nREPL implementation and `clr.tools.nrepl` (the project can use some help)
2727
- adapting `cider-nrepl` for ClojureCLR (some of its codebase is JVM-specific)
2828

lisp/cider.el

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,27 @@ By default we favor the project-specific shadow-cljs over the system-wide."
239239
:safe #'stringp
240240
:package-version '(cider . "1.14.0"))
241241

242+
(defcustom cider-clr-command
243+
"cljr"
244+
"The command used to execute ClojureCLR."
245+
:type 'string
246+
:safe #'stringp
247+
:package-version '(cider . "1.20.0"))
248+
249+
(defcustom cider-clr-parameters
250+
"-X clojure.tools.nrepl/start-server!"
251+
"Params passed to ClojureCLR to start an nREPL server via `cider-jack-in'."
252+
:type 'string
253+
:safe #'stringp
254+
:package-version '(cider . "1.20.0"))
255+
256+
(defcustom cider-clr-nrepl-sha "a6bf822a5f72ec613f703eaf95420758591f2437"
257+
"The version of clr.tools.nrepl injected on jack-in with ClojureCLR."
258+
;; Unlike Maven, GitHub tags are not supported by Clojure
259+
:type 'string
260+
:safe #'stringp
261+
:package-version '(cider . "1.20.0"))
262+
242263
(defcustom cider-jack-in-default nil
243264
"The default tool to use when doing `cider-jack-in' outside a project.
244265
This value is consulted when no identifying file types (e.g. project.clj
@@ -254,7 +275,8 @@ explicitly to skip the auto-detection."
254275
(const gradle)
255276
(const babashka)
256277
(const nbb)
257-
(const basilisp))
278+
(const basilisp)
279+
(const clr))
258280
:safe #'symbolp
259281
:package-version '(cider . "0.9.0"))
260282

@@ -274,6 +296,7 @@ command when there is no ambiguity."
274296
(const babashka)
275297
(const nbb)
276298
(const basilisp)
299+
(const clr)
277300
(const :tag "Always ask" nil))
278301
:safe #'symbolp
279302
:package-version '(cider . "0.13.0"))
@@ -757,6 +780,30 @@ Does so by concatenating PARAMS and DEPENDENCIES."
757780
" "
758781
params)))
759782

783+
(defun cider-clr-jack-in-dependencies (params dependencies &optional command)
784+
"Create ClojureCLR clr.core.cli jack-in dependencies.
785+
Does so by concatenating DEPENDENCIES, and PARAMS into a
786+
suitable `cljr` invocation and quoting, also accounting for COMMAND if
787+
provided."
788+
(let* ((all-deps (thread-last dependencies
789+
(cider--dedupe-deps)
790+
(seq-map (lambda (dep)
791+
(if (listp (cadr dep))
792+
(format "%s {%s}"
793+
(car dep)
794+
(seq-reduce
795+
(lambda (acc v)
796+
(concat acc (format " :%s \"%s\" " (car v) (cdr v))))
797+
(cadr dep)
798+
""))
799+
(format "%s {:git/sha \"%s\"}" (car dep) (cadr dep)))))))
800+
(deps (format "{:deps {%s}}"
801+
(string-join all-deps " ")))
802+
(deps-quoted (cider--shell-quote-argument deps command)))
803+
(format "-Sdeps %s %s"
804+
deps-quoted
805+
(if params (format " %s" params) ""))))
806+
760807
(defun cider-add-clojure-dependencies-maybe (dependencies)
761808
"Return DEPENDENCIES with an added Clojure dependency if requested.
762809
See also `cider-jack-in-auto-inject-clojure'."
@@ -816,6 +863,12 @@ COMMAND is the resolved jack-in command, used to handle PowerShell quoting."
816863
(cider-add-clojure-dependencies-maybe cider-jack-in-dependencies)
817864
(cider-jack-in-normalized-nrepl-middlewares)))
818865

866+
(defun cider--clr-inject-deps (params _project-type _command)
867+
"Inject CIDER deps into PARAMS for a Gradle project."
868+
(cider-clr-jack-in-dependencies
869+
params
870+
`(("io.github.clojure/clr.tools.nrepl" ,cider-clr-nrepl-sha))))
871+
819872
(defun cider-inject-jack-in-dependencies (params project-type &optional command)
820873
"Return PARAMS with injected REPL dependencies for PROJECT-TYPE.
821874
Looks up the tool's :inject-fn in `cider-jack-in-tools' and calls it with
@@ -881,6 +934,16 @@ with its nREPL middleware and dependencies."
881934
:project-files '("basilisp.edn")
882935
:universal-prefix-arg 5)
883936

937+
(cider-register-jack-in-tool 'clr
938+
:command-var 'cider-clr-command
939+
:params-var 'cider-clr-parameters
940+
:project-files '("deps-clr.edn")
941+
:resolver #'cider--resolve-prefix-command
942+
:universal-prefix-arg 6
943+
:jack-in-type 'clj
944+
:cljs-repl-type 'nbb
945+
:inject-fn #'cider--clr-inject-deps)
946+
884947

885948
;;; ClojureScript REPL creation
886949

test/integration/integration-tests.el

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,68 @@ If CLI-COMMAND is nil, then use the default."
183183
(cider-itu-poll-until (not (eq (process-status nrepl-proc) 'run)) 5)
184184
(expect (member (process-status nrepl-proc) '(exit signal))))))))))
185185

186+
(it "to clr"
187+
(with-cider-test-sandbox
188+
(with-temp-dir temp-dir
189+
;; Create a project in temp dir
190+
(let* ((project-dir temp-dir)
191+
(clr-edn (expand-file-name "deps-clr.edn" project-dir)))
192+
(write-region "{}" nil clr-edn)
193+
194+
(with-temp-buffer
195+
;; set default directory to temp project
196+
(setq-local default-directory project-dir)
197+
198+
(let* (;; Get a gv reference so as to poll if the client has
199+
;; connected to the nREPL server.
200+
(client-is-connected* (cider-itu-nrepl-client-connected-ref-make!))
201+
202+
;; jack in and get repl buffer
203+
;;
204+
;; The numerical prefix arg for `clr` in
205+
;; `cider-jack-in-universal-options' is 6.
206+
(nrepl-proc (cider-jack-in-universal 6))
207+
(nrepl-buf (process-buffer nrepl-proc)))
208+
209+
;; wait until the client has successfully connected to the
210+
;; nREPL server.
211+
(cider-itu-poll-until (eq (gv-deref client-is-connected*) 'connected) 20)
212+
213+
;; give it some time to setup the clj REPL
214+
(cider-itu-poll-until (cider-repls 'clj nil) 60)
215+
216+
;; send command to the REPL, and push stdout/stderr to
217+
;; corresponding eval-xxx variables.
218+
(let ((repl-buffer (cider-current-repl))
219+
(eval-err '())
220+
(eval-out '()))
221+
(expect repl-buffer :not :to-be nil)
222+
223+
;; send command to the REPL
224+
(cider-interactive-eval
225+
;; ask REPL to return a string that uniquely identifies it.
226+
"(print :clr? (some? (. RuntimeInformation FrameworkDescription)))"
227+
(lambda (return)
228+
(nrepl-dbind-response
229+
return
230+
(out err)
231+
(when err (push err eval-err))
232+
(when out (push out eval-out)))) )
233+
234+
;; wait for a response to come back.
235+
(cider-itu-poll-until (or eval-err eval-out) 20)
236+
237+
;; ensure there are no errors and response is as expected.
238+
(expect eval-err :to-equal '())
239+
(expect eval-out :to-equal '(":clr? true"))
240+
241+
;; exit the REPL.
242+
(cider-quit repl-buffer)
243+
244+
;; wait for the REPL to exit
245+
(cider-itu-poll-until (not (eq (process-status nrepl-proc) 'run)) 5)
246+
(expect (member (process-status nrepl-proc) '(exit signal))))))))))
247+
186248
(it "to Basilisp"
187249
(with-cider-test-sandbox
188250
(with-temp-dir temp-dir

0 commit comments

Comments
 (0)