Skip to content

Commit 8e99af4

Browse files
committed
jack-in support for the Basilisp Clojure dialect in Python
1 parent 8be2bcf commit 8e99af4

File tree

7 files changed

+224
-7
lines changed

7 files changed

+224
-7
lines changed

.github/workflows/test.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ jobs:
100100
- run: npm install [email protected] -g
101101
- run: npm install [email protected] -g
102102

103+
- uses: actions/setup-python@v5
104+
with:
105+
python-version: '3.12'
106+
- run: |
107+
pip install basilisp==0.1.0b1
108+
103109
- name: Test integration
104110
run: |
105111
# The tests occasionally fail on macos&win in what is seems to

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
- adds `cider-ns-code-reload-tool` defcustom, defaulting to `'tools.namespace`.
1414
- you can change it to `'clj-reload` to use [clj-reload](https://github.com/tonsky/clj-reload) instead of [tools.namespace](https://github.com/clojure/tools.namespace).
1515

16+
- [#3682](https://github.com/clojure-emacs/cider/issues/3682): Add `cider-jack-in` support for [Basilisp](https://github.com/basilisp-lang/basilisp) (Python)
17+
1618
### Changes
1719

1820
- [#3626](https://github.com/clojure-emacs/cider/issues/3626): `cider-ns-refresh`: jump to the relevant file/line on errors.

cider.el

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
;; Maintainer: Bozhidar Batsov <[email protected]>
1313
;; URL: https://www.github.com/clojure-emacs/cider
1414
;; Version: 1.14.0-snapshot
15-
;; Package-Requires: ((emacs "26") (clojure-mode "5.18.1") (parseedn "1.2.1") (queue "0.2") (spinner "1.7") (seq "2.22") (sesman "0.3.2") (transient "0.4.1"))
15+
;; Package-Requires: ((emacs "26") (clojure-mode "5.19") (parseedn "1.2.1") (queue "0.2") (spinner "1.7") (seq "2.22") (sesman "0.3.2") (transient "0.4.1"))
1616
;; Keywords: languages, clojure, cider
1717

1818
;; This program is free software: you can redistribute it and/or modify
@@ -273,13 +273,38 @@ By default we favor the project-specific shadow-cljs over the system-wide."
273273
:safe #'stringp
274274
:package-version '(cider . "1.6.0"))
275275

276+
(defcustom cider-basilisp-command
277+
"basilisp"
278+
"The command used to execute Basilisp.
279+
280+
If Basilisp is installed in a virtual environment, update this to the
281+
full path of the Basilisp executable within that virtual environment."
282+
:type 'string
283+
:safe #'stringp
284+
:package-version '(cider . "1.14.0"))
285+
286+
(defcustom cider-basilisp-global-options
287+
nil
288+
"Command line options used to execute Basilisp."
289+
:type 'string
290+
:safe #'stringp
291+
:package-version '(cider . "1.14.0"))
292+
293+
(defcustom cider-basilisp-parameters
294+
"nrepl-server"
295+
"Params passed to Basilisp to start an nREPL server via `cider-jack-in'."
296+
:type 'string
297+
:safe #'stringp
298+
:package-version '(cider . "1.14.0"))
299+
276300
(make-obsolete-variable 'cider-lein-global-options 'cider-lein-parameters "1.8.0")
277301
(make-obsolete-variable 'cider-boot-global-options 'cider-boot-parameters "1.8.0")
278302
(make-obsolete-variable 'cider-clojure-cli-global-options 'cider-clojure-cli-parameters "1.8.0")
279303
(make-obsolete-variable 'cider-shadow-cljs-global-options 'cider-shadow-cljs-parameters "1.8.0")
280304
(make-obsolete-variable 'cider-gradle-global-options 'cider-gradle-parameters "1.8.0")
281305
(make-obsolete-variable 'cider-babashka-global-options 'cider-babashka-parameters "1.8.0")
282306
(make-obsolete-variable 'cider-nbb-global-options 'cider-nbb-parameters "1.8.0")
307+
(make-obsolete-variable 'cider-basilip-global-options 'cider-basilisp-parameters "1.8.0")
283308

284309
(defcustom cider-jack-in-default
285310
(if (executable-find "clojure") 'clojure-cli 'lein)
@@ -296,7 +321,8 @@ to Leiningen."
296321
(const shadow-cljs)
297322
(const gradle)
298323
(const babashka)
299-
(const nbb))
324+
(const nbb)
325+
(const basilisp))
300326
:safe #'symbolp
301327
:package-version '(cider . "0.9.0"))
302328

@@ -316,6 +342,7 @@ command when there is no ambiguity."
316342
(const gradle)
317343
(const babashka)
318344
(const nbb)
345+
(const basilisp)
319346
(const :tag "Always ask" nil))
320347
:safe #'symbolp
321348
:package-version '(cider . "0.13.0"))
@@ -380,7 +407,8 @@ Sub-match 1 must be the project path.")
380407
'((clojure-cli (:prefix-arg 1 :cmd (:jack-in-type clj :project-type clojure-cli :edit-project-dir t)))
381408
(lein (:prefix-arg 2 :cmd (:jack-in-type clj :project-type lein :edit-project-dir t)))
382409
(babashka (:prefix-arg 3 :cmd (:jack-in-type clj :project-type babashka :edit-project-dir t)))
383-
(nbb (:prefix-arg 4 :cmd (:jack-in-type cljs :project-type nbb :cljs-repl-type nbb :edit-project-dir t))))
410+
(nbb (:prefix-arg 4 :cmd (:jack-in-type cljs :project-type nbb :cljs-repl-type nbb :edit-project-dir t)))
411+
(basilisp (:prefix-arg 5 :cmd (:jack-in-type clj :project-type basilisp :edit-project-dir t))))
384412
"The list of project tools that are supported by the universal jack in command.
385413
386414
Each item in the list consists of the tool name and its plist options.
@@ -412,6 +440,7 @@ The plist supports the following keys
412440
('shadow-cljs cider-shadow-cljs-command)
413441
('gradle cider-gradle-command)
414442
('nbb cider-nbb-command)
443+
('basilisp cider-basilisp-command)
415444
(_ (user-error "Unsupported project type `%S'" project-type))))
416445

417446
(defcustom cider-enrich-classpath nil
@@ -476,6 +505,7 @@ Throws an error if PROJECT-TYPE is unknown."
476505
;; relative path like "./gradlew" use locate file instead of checking
477506
;; the exec-path
478507
('gradle (cider--resolve-project-command cider-gradle-command))
508+
('basilisp (cider--resolve-command cider-basilisp-command))
479509
(_ (user-error "Unsupported project type `%S'" project-type))))
480510

481511
(defun cider-jack-in-global-options (project-type)
@@ -488,6 +518,7 @@ Throws an error if PROJECT-TYPE is unknown."
488518
('shadow-cljs cider-shadow-cljs-global-options)
489519
('gradle cider-gradle-global-options)
490520
('nbb cider-nbb-global-options)
521+
('basilisp cider-basilisp-global-options)
491522
(_ (user-error "Unsupported project type `%S'" project-type))))
492523

493524
(defun cider-jack-in-params (project-type)
@@ -504,6 +535,7 @@ Throws an error if PROJECT-TYPE is unknown."
504535
('shadow-cljs cider-shadow-cljs-parameters)
505536
('gradle cider-gradle-parameters)
506537
('nbb cider-nbb-parameters)
538+
('basilisp cider-basilisp-parameters)
507539
(_ (user-error "Unsupported project type `%S'" project-type))))
508540

509541

@@ -963,6 +995,10 @@ middleware and dependencies."
963995
global-opts
964996
(unless (seq-empty-p global-opts) " ")
965997
params))
998+
('basilisp (concat
999+
global-opts
1000+
(unless (seq-empty-p global-opts) " ")
1001+
params))
9661002
(_ (error "Unsupported project type `%S'" project-type))))
9671003

9681004

@@ -1245,6 +1281,7 @@ you're working on."
12451281
(const :tag "Shadow w/o Server" shadow-select)
12461282
(const :tag "Krell" krell)
12471283
(const :tag "Nbb" nbb)
1284+
(const :tag "Basilisp" basilisp)
12481285
(const :tag "Custom" custom))
12491286
:safe #'symbolp
12501287
:package-version '(cider . "0.17.0"))
@@ -2038,7 +2075,8 @@ PROJECT-DIR defaults to current project."
20382075
(shadow-cljs . "shadow-cljs.edn")
20392076
(gradle . "build.gradle")
20402077
(gradle . "build.gradle.kts")
2041-
(nbb . "nbb.edn"))))
2078+
(nbb . "nbb.edn")
2079+
(basilisp . "basilisp.edn"))))
20422080
(delq nil
20432081
(mapcar (lambda (candidate)
20442082
(when (file-exists-p (cdr candidate))

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ The following Clojure build tools are supported so far
152152
- kbd:[M-2 C-c C-x j u] jack-in using leiningen.
153153
- kbd:[M-3 C-c C-x j u] jack-in using babashka.
154154
- kbd:[M-4 C-c C-x j u] jack-in using nbb.
155+
- kbd:[M-5 C-c C-x j u] jack-in using basilisp.
155156

156157
Here is an example of how to bind kbd:[F12] for quickly bringing up a
157158
babashka REPL:
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
= Basilisp Integration with CIDER
2+
https://github.com/basilisp-lang/basilisp[basilisp]
3+
4+
== Overview
5+
6+
Basilisp aims to enable writing Clojure programs on Python with full Python interoperability. It is highly compatible with Clojure.
7+
8+
To install Basilisp, run:
9+
10+
$ pip install basilisp
11+
12+
== Usage
13+
14+
There are several ways to connect to Basilisp.
15+
16+
* kbd:[M-x cider-jack-in] and kbd:[M-5 M-x cider-jack-in-universal]
17+
18+
If you have created a `basilisp.edn` project file at your root of your project tree, you can jack in to the project `M-x cider-jack-in`. The `basilisp.edn` is similar to `deps.edn` for clojure-cli projects. It can be left empty just to mark the root of your project.
19+
20+
If you don't have or want a basilisp project file, you can use universal jack in with a numerical argument of 5:
21+
22+
- kbd:[M-5 M-x cider-jack-in-universal], or
23+
- kbd:[M-5 C-c C-x j u], from within file in clojure-mode
24+
25+
(Note: an alternative to kbd:[M-5] is kbd:[C-u 5])
26+
27+
You can also bind the universal jack-in to Basilisp to a function to use as a shortcut, for example
28+
29+
[source,lisp]
30+
----
31+
(global-set-key (kbd "<f12>") (lambda ()
32+
(interactive)
33+
(cider-jack-in-universal 5)))
34+
----
35+
36+
* kbd:[M-x cider-connect]
37+
38+
You can start its bundled nREPL server:
39+
40+
$ basilisp nrepl-server
41+
42+
and connect to it afterward using `M-x cider-connect`.
43+
44+
To see available options, type `basilisp nrepl-server -h` in a shell prompt.
45+
46+
== Configuration
47+
48+
The jack-in command can be configured via several defcustoms:
49+
50+
* `cider-basilisp-command` (default is `basilisp`).
51+
52+
If Basilisp is installed in a virtual environment, update this to the full path of the `basilisp` executable within that virtual environment.
53+
54+
* `cider-basilisp-parameters` (default is `nrepl-server`).
55+
56+
There at few ways to setup (custom) variables in Emacs
57+
58+
- https://www.gnu.org/software/emacs/manual/html_node/emacs/Easy-Customization.html[Examining and Setting Variables]
59+
60+
kbd:[C-h v cider-basilisp-command], and
61+
kbd:[C-h v cider-basilisp-parameters]
62+
63+
- https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html[Per-Diretory Local Variables]
64+
65+
Uses `.dir-locals.el` to setup per mode variables. This file is typically stored at the root of the project.
66+
67+
For example, to set the path to the basilisp executable within a virtual environment
68+
69+
kbd:[M-x add-dir-local-variable]
70+
Mode or subdirectory: `clojure-mode`
71+
Add directory-local variable: `cider-basilisp-command`
72+
Add cider-basilisp-command with value: `"c:/dev/venvs/312/Scripts/basilisp"`
73+
74+
This should result to updating or creating a `.dir-local.el` file like below
75+
76+
[source,lisp]
77+
----
78+
;;; Directory Local Variables -*- no-byte-compile: t -*-
79+
;;; For more information see (info "(emacs) Directory Variables")
80+
81+
((clojure-mode . ((cider-basilisp-command . "c:/dev/venvs/312/Scripts/basilisp"))))
82+
----
83+
84+
- https://www.gnu.org/software/emacs/manual/html_node/emacs/Specifying-File-Variables.html[Specifying File Variables]
85+
86+
It is best to put this in the top of your project's `basilisp.edn` file, and always jack-in from there
87+
88+
For example, setting `cider-basilisp-command` to start basilisp from within a virtual environment
89+
90+
kbd:[M-x add-dir-local-variable]
91+
Add file-local variable: `cider-basilisp-command`
92+
Add cider-basilisp-command with value: `"c:/dev/venvs/312/Scripts/basilisp"`
93+
94+
This will result in the following in `basilisp.edn`
95+
96+
[source,clojure]
97+
----
98+
;; Local Variables:
99+
;; cider-basilisp-command: "c:/dev/venvs/312/Scripts/basilisp"
100+
;; End:
101+
{}
102+
----

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Right now CIDER the supports to some extent the following:
3030
* xref:platforms/clojureclr.adoc[ClojureCLR]
3131
* Lumo (via https://github.com/djblue/nrepl-cljs)
3232
* xref:platforms/other_platforms.adoc[scittle, joyride & friends]
33+
* xref:platforms/basilisp.adoc[Basilisp]
3334

3435
All of them are derived from Clojure, so supporting them didn't really require much work.
3536

test/integration/integration-tests.el

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,16 +183,83 @@ 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 Basilisp"
187+
;; temporarily suspended on MS-Windows until the following is released on PyPi
188+
;;
189+
;; https://github.com/basilisp-lang/basilisp/pull/866
190+
(assume (not (eq system-type 'windows-nt)) "temporarily skipping on MS-Windows ...")
191+
(with-cider-test-sandbox
192+
(with-temp-dir temp-dir
193+
;; Create a project in temp dir
194+
(let* ((project-dir temp-dir)
195+
(basilisp-edn (expand-file-name "basilisp.edn" project-dir)))
196+
(write-region "" nil basilisp-edn)
197+
198+
(with-temp-buffer
199+
;; set default directory to temp project
200+
(setq-local default-directory project-dir)
201+
202+
(let* (;; Get a gv reference so as to poll if the client has
203+
;; connected to the nREPL server.
204+
(client-is-connected* (cider-itu-nrepl-client-connected-ref-make!))
205+
206+
;; jack in and get repl buffer
207+
(nrepl-proc (cider-jack-in-clj '()))
208+
(nrepl-buf (process-buffer nrepl-proc)))
209+
210+
;; wait until the client successfully connects to the nREPL
211+
;; server. A high timeout is set because Basilisp usually needs to
212+
;; be compiled the first time it is run.
213+
(cider-itu-poll-until (eq (gv-deref client-is-connected*) 'connected) 60)
214+
215+
;; give it some time to setup the clj REPL
216+
(cider-itu-poll-until (cider-repls 'clj nil) 5)
217+
218+
;; send command to the REPL, and push stdout/stderr to
219+
;; corresponding eval-xxx variables.
220+
(let ((repl-buffer (cider-current-repl))
221+
(eval-err '())
222+
(eval-out '()))
223+
(expect repl-buffer :not :to-be nil)
224+
225+
;; send command to the REPL
226+
(cider-interactive-eval
227+
;; ask REPL to return a string that uniquely identifies it.
228+
"(print :basilisp? (some? sys/version))"
229+
(lambda (return)
230+
(nrepl-dbind-response
231+
return
232+
(out err)
233+
(when err (push err eval-err))
234+
(when out (push out eval-out)))) )
235+
236+
;; wait for a response to come back.
237+
(cider-itu-poll-until (or eval-err eval-out) 5)
238+
239+
;; ensure there are no errors and response is as expected.
240+
(expect eval-err :to-equal '())
241+
;; The Basilisp nREPL server sends the message in three separate
242+
;; pieces, which is likely an area for improvement on the
243+
;; Basilisp side.
244+
(expect eval-out :to-equal '("true" " " ":basilisp?"))
245+
246+
;; exit the REPL.
247+
(cider-quit repl-buffer)
248+
249+
;; wait for the REPL to exit
250+
(cider-itu-poll-until (not (eq (process-status nrepl-proc) 'run)) 5)
251+
(expect (member (process-status nrepl-proc) '(exit signal))))))))))
252+
186253
(it "to clojure tools cli (default)"
187254
(jack-in-clojure-cli-test nil))
188255

189256
(when (eq system-type 'windows-nt)
190257
(it "to clojure tools cli (alternative pwsh)"
191258
(jack-in-clojure-cli-test "pwsh")))
192259

193-
(when (eq system-type 'windows-nt)
194-
(it "to clojure tools cli (alternative deps.exe)"
195-
(jack-in-clojure-cli-test "deps.exe")))
260+
(when (eq system-type 'windows-nt)
261+
(it "to clojure tools cli (alternative deps.exe)"
262+
(jack-in-clojure-cli-test "deps.exe")))
196263

197264
(it "to leiningen"
198265
(with-cider-test-sandbox

0 commit comments

Comments
 (0)