branch: elpa/cider
commit 61fad04bcdb23ae9c94bf3c788f9460456a1cf21
Author: ikappaki <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
jack-in support for the Basilisp Clojure dialect in Python
---
.github/workflows/test.yml | 6 ++
CHANGELOG.md | 2 +
cider.el | 46 +++++++++-
doc/modules/ROOT/pages/basics/up_and_running.adoc | 1 +
doc/modules/ROOT/pages/platforms/basilisp.adoc | 102 ++++++++++++++++++++++
doc/modules/ROOT/pages/platforms/overview.adoc | 1 +
test/integration/integration-tests.el | 73 +++++++++++++++-
7 files changed, 224 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index f54d4caa4c..749b9e81d2 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -100,6 +100,12 @@ jobs:
- run: npm install [email protected] -g
- run: npm install [email protected] -g
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.12'
+ - run: |
+ pip install basilisp==0.1.0b1
+
- name: Test integration
run: |
# The tests occasionally fail on macos&win in what is seems to
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97c8e10e4f..7715364835 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,8 @@
- adds `cider-ns-code-reload-tool` defcustom, defaulting to
`'tools.namespace`.
- 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).
+- [#3682](https://github.com/clojure-emacs/cider/issues/3682): Add
`cider-jack-in` support for
[Basilisp](https://github.com/basilisp-lang/basilisp) (Python)
+
### Changes
- [#3626](https://github.com/clojure-emacs/cider/issues/3626):
`cider-ns-refresh`: jump to the relevant file/line on errors.
diff --git a/cider.el b/cider.el
index c54863e6e7..c533880835 100644
--- a/cider.el
+++ b/cider.el
@@ -12,7 +12,7 @@
;; Maintainer: Bozhidar Batsov <[email protected]>
;; URL: https://www.github.com/clojure-emacs/cider
;; Version: 1.14.0-snapshot
-;; 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"))
+;; 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"))
;; Keywords: languages, clojure, cider
;; This program is free software: you can redistribute it and/or modify
@@ -273,6 +273,30 @@ By default we favor the project-specific shadow-cljs over
the system-wide."
:safe #'stringp
:package-version '(cider . "1.6.0"))
+(defcustom cider-basilisp-command
+ "basilisp"
+ "The command used to execute Basilisp.
+
+ If Basilisp is installed in a virtual environment, update this to the
+ full path of the Basilisp executable within that virtual environment."
+ :type 'string
+ :safe #'stringp
+ :package-version '(cider . "1.14.0"))
+
+(defcustom cider-basilisp-global-options
+ nil
+ "Command line options used to execute Basilisp."
+ :type 'string
+ :safe #'stringp
+ :package-version '(cider . "1.14.0"))
+
+(defcustom cider-basilisp-parameters
+ "nrepl-server"
+ "Params passed to Basilisp to start an nREPL server via `cider-jack-in'."
+ :type 'string
+ :safe #'stringp
+ :package-version '(cider . "1.14.0"))
+
(make-obsolete-variable 'cider-lein-global-options 'cider-lein-parameters
"1.8.0")
(make-obsolete-variable 'cider-boot-global-options 'cider-boot-parameters
"1.8.0")
(make-obsolete-variable 'cider-clojure-cli-global-options
'cider-clojure-cli-parameters "1.8.0")
@@ -280,6 +304,7 @@ By default we favor the project-specific shadow-cljs over
the system-wide."
(make-obsolete-variable 'cider-gradle-global-options 'cider-gradle-parameters
"1.8.0")
(make-obsolete-variable 'cider-babashka-global-options
'cider-babashka-parameters "1.8.0")
(make-obsolete-variable 'cider-nbb-global-options 'cider-nbb-parameters
"1.8.0")
+(make-obsolete-variable 'cider-basilip-global-options
'cider-basilisp-parameters "1.8.0")
(defcustom cider-jack-in-default
(if (executable-find "clojure") 'clojure-cli 'lein)
@@ -296,7 +321,8 @@ to Leiningen."
(const shadow-cljs)
(const gradle)
(const babashka)
- (const nbb))
+ (const nbb)
+ (const basilisp))
:safe #'symbolp
:package-version '(cider . "0.9.0"))
@@ -316,6 +342,7 @@ command when there is no ambiguity."
(const gradle)
(const babashka)
(const nbb)
+ (const basilisp)
(const :tag "Always ask" nil))
:safe #'symbolp
:package-version '(cider . "0.13.0"))
@@ -380,7 +407,8 @@ Sub-match 1 must be the project path.")
'((clojure-cli (:prefix-arg 1 :cmd (:jack-in-type clj :project-type
clojure-cli :edit-project-dir t)))
(lein (:prefix-arg 2 :cmd (:jack-in-type clj :project-type lein
:edit-project-dir t)))
(babashka (:prefix-arg 3 :cmd (:jack-in-type clj :project-type
babashka :edit-project-dir t)))
- (nbb (:prefix-arg 4 :cmd (:jack-in-type cljs :project-type nbb
:cljs-repl-type nbb :edit-project-dir t))))
+ (nbb (:prefix-arg 4 :cmd (:jack-in-type cljs :project-type nbb
:cljs-repl-type nbb :edit-project-dir t)))
+ (basilisp (:prefix-arg 5 :cmd (:jack-in-type clj :project-type
basilisp :edit-project-dir t))))
"The list of project tools that are supported by the universal jack in
command.
Each item in the list consists of the tool name and its plist options.
@@ -412,6 +440,7 @@ The plist supports the following keys
('shadow-cljs cider-shadow-cljs-command)
('gradle cider-gradle-command)
('nbb cider-nbb-command)
+ ('basilisp cider-basilisp-command)
(_ (user-error "Unsupported project type `%S'" project-type))))
(defcustom cider-enrich-classpath nil
@@ -476,6 +505,7 @@ Throws an error if PROJECT-TYPE is unknown."
;; relative path like "./gradlew" use locate file instead of checking
;; the exec-path
('gradle (cider--resolve-project-command cider-gradle-command))
+ ('basilisp (cider--resolve-command cider-basilisp-command))
(_ (user-error "Unsupported project type `%S'" project-type))))
(defun cider-jack-in-global-options (project-type)
@@ -488,6 +518,7 @@ Throws an error if PROJECT-TYPE is unknown."
('shadow-cljs cider-shadow-cljs-global-options)
('gradle cider-gradle-global-options)
('nbb cider-nbb-global-options)
+ ('basilisp cider-basilisp-global-options)
(_ (user-error "Unsupported project type `%S'" project-type))))
(defun cider-jack-in-params (project-type)
@@ -504,6 +535,7 @@ Throws an error if PROJECT-TYPE is unknown."
('shadow-cljs cider-shadow-cljs-parameters)
('gradle cider-gradle-parameters)
('nbb cider-nbb-parameters)
+ ('basilisp cider-basilisp-parameters)
(_ (user-error "Unsupported project type `%S'" project-type))))
@@ -963,6 +995,10 @@ middleware and dependencies."
global-opts
(unless (seq-empty-p global-opts) " ")
params))
+ ('basilisp (concat
+ global-opts
+ (unless (seq-empty-p global-opts) " ")
+ params))
(_ (error "Unsupported project type `%S'" project-type))))
@@ -1245,6 +1281,7 @@ you're working on."
(const :tag "Shadow w/o Server" shadow-select)
(const :tag "Krell" krell)
(const :tag "Nbb" nbb)
+ (const :tag "Basilisp" basilisp)
(const :tag "Custom" custom))
:safe #'symbolp
:package-version '(cider . "0.17.0"))
@@ -2038,7 +2075,8 @@ PROJECT-DIR defaults to current project."
(shadow-cljs . "shadow-cljs.edn")
(gradle . "build.gradle")
(gradle . "build.gradle.kts")
- (nbb . "nbb.edn"))))
+ (nbb . "nbb.edn")
+ (basilisp . "basilisp.edn"))))
(delq nil
(mapcar (lambda (candidate)
(when (file-exists-p (cdr candidate))
diff --git a/doc/modules/ROOT/pages/basics/up_and_running.adoc
b/doc/modules/ROOT/pages/basics/up_and_running.adoc
index b45a9a4357..b4af70b4fd 100644
--- a/doc/modules/ROOT/pages/basics/up_and_running.adoc
+++ b/doc/modules/ROOT/pages/basics/up_and_running.adoc
@@ -152,6 +152,7 @@ The following Clojure build tools are supported so far
- kbd:[M-2 C-c C-x j u] jack-in using leiningen.
- kbd:[M-3 C-c C-x j u] jack-in using babashka.
- kbd:[M-4 C-c C-x j u] jack-in using nbb.
+- kbd:[M-5 C-c C-x j u] jack-in using basilisp.
Here is an example of how to bind kbd:[F12] for quickly bringing up a
babashka REPL:
diff --git a/doc/modules/ROOT/pages/platforms/basilisp.adoc
b/doc/modules/ROOT/pages/platforms/basilisp.adoc
new file mode 100644
index 0000000000..03e0685d25
--- /dev/null
+++ b/doc/modules/ROOT/pages/platforms/basilisp.adoc
@@ -0,0 +1,102 @@
+= Basilisp Integration with CIDER
+https://github.com/basilisp-lang/basilisp[basilisp]
+
+== Overview
+
+Basilisp aims to enable writing Clojure programs on Python with full Python
interoperability. It is highly compatible with Clojure.
+
+To install Basilisp, run:
+
+ $ pip install basilisp
+
+== Usage
+
+There are several ways to connect to Basilisp.
+
+* kbd:[M-x cider-jack-in] and kbd:[M-5 M-x cider-jack-in-universal]
+
+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.
+
+If you don't have or want a basilisp project file, you can use universal jack
in with a numerical argument of 5:
+
+- kbd:[M-5 M-x cider-jack-in-universal], or
+- kbd:[M-5 C-c C-x j u], from within file in clojure-mode
+
+(Note: an alternative to kbd:[M-5] is kbd:[C-u 5])
+
+You can also bind the universal jack-in to Basilisp to a function to use as a
shortcut, for example
+
+[source,lisp]
+----
+(global-set-key (kbd "<f12>") (lambda ()
+ (interactive)
+ (cider-jack-in-universal 5)))
+----
+
+* kbd:[M-x cider-connect]
+
+You can start its bundled nREPL server:
+
+ $ basilisp nrepl-server
+
+and connect to it afterward using `M-x cider-connect`.
+
+To see available options, type `basilisp nrepl-server -h` in a shell prompt.
+
+== Configuration
+
+The jack-in command can be configured via several defcustoms:
+
+* `cider-basilisp-command` (default is `basilisp`).
+
+If Basilisp is installed in a virtual environment, update this to the full
path of the `basilisp` executable within that virtual environment.
+
+* `cider-basilisp-parameters` (default is `nrepl-server`).
+
+There at few ways to setup (custom) variables in Emacs
+
+-
https://www.gnu.org/software/emacs/manual/html_node/emacs/Easy-Customization.html[Examining
and Setting Variables]
+
+kbd:[C-h v cider-basilisp-command], and
+kbd:[C-h v cider-basilisp-parameters]
+
+-
https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html[Per-Diretory
Local Variables]
+
+Uses `.dir-locals.el` to setup per mode variables. This file is typically
stored at the root of the project.
+
+For example, to set the path to the basilisp executable within a virtual
environment
+
+kbd:[M-x add-dir-local-variable]
+Mode or subdirectory: `clojure-mode`
+Add directory-local variable: `cider-basilisp-command`
+Add cider-basilisp-command with value: `"c:/dev/venvs/312/Scripts/basilisp"`
+
+This should result to updating or creating a `.dir-local.el` file like below
+
+[source,lisp]
+----
+;;; Directory Local Variables -*- no-byte-compile: t -*-
+;;; For more information see (info "(emacs) Directory Variables")
+
+((clojure-mode . ((cider-basilisp-command .
"c:/dev/venvs/312/Scripts/basilisp"))))
+----
+
+-
https://www.gnu.org/software/emacs/manual/html_node/emacs/Specifying-File-Variables.html[Specifying
File Variables]
+
+It is best to put this in the top of your project's `basilisp.edn` file, and
always jack-in from there
+
+For example, setting `cider-basilisp-command` to start basilisp from within a
virtual environment
+
+kbd:[M-x add-dir-local-variable]
+Add file-local variable: `cider-basilisp-command`
+Add cider-basilisp-command with value: `"c:/dev/venvs/312/Scripts/basilisp"`
+
+This will result in the following in `basilisp.edn`
+
+[source,clojure]
+----
+;; Local Variables:
+;; cider-basilisp-command: "c:/dev/venvs/312/Scripts/basilisp"
+;; End:
+{}
+----
diff --git a/doc/modules/ROOT/pages/platforms/overview.adoc
b/doc/modules/ROOT/pages/platforms/overview.adoc
index 1903cd719e..f1a7c14dba 100644
--- a/doc/modules/ROOT/pages/platforms/overview.adoc
+++ b/doc/modules/ROOT/pages/platforms/overview.adoc
@@ -30,6 +30,7 @@ Right now CIDER the supports to some extent the following:
* xref:platforms/clojureclr.adoc[ClojureCLR]
* Lumo (via https://github.com/djblue/nrepl-cljs)
* xref:platforms/other_platforms.adoc[scittle, joyride & friends]
+* xref:platforms/basilisp.adoc[Basilisp]
All of them are derived from Clojure, so supporting them didn't really require
much work.
diff --git a/test/integration/integration-tests.el
b/test/integration/integration-tests.el
index e0ed66b79a..91722edad7 100644
--- a/test/integration/integration-tests.el
+++ b/test/integration/integration-tests.el
@@ -183,6 +183,73 @@ If CLI-COMMAND is nil, then use the default."
(cider-itu-poll-until (not (eq (process-status nrepl-proc)
'run)) 5)
(expect (member (process-status nrepl-proc) '(exit
signal))))))))))
+ (it "to Basilisp"
+ ;; temporarily suspended on MS-Windows until the following is released on
PyPi
+ ;;
+ ;; https://github.com/basilisp-lang/basilisp/pull/866
+ (assume (not (eq system-type 'windows-nt)) "temporarily skipping on
MS-Windows ...")
+ (with-cider-test-sandbox
+ (with-temp-dir temp-dir
+ ;; Create a project in temp dir
+ (let* ((project-dir temp-dir)
+ (basilisp-edn (expand-file-name "basilisp.edn" project-dir)))
+ (write-region "" nil basilisp-edn)
+
+ (with-temp-buffer
+ ;; set default directory to temp project
+ (setq-local default-directory project-dir)
+
+ (let* (;; Get a gv reference so as to poll if the client has
+ ;; connected to the nREPL server.
+ (client-is-connected*
(cider-itu-nrepl-client-connected-ref-make!))
+
+ ;; jack in and get repl buffer
+ (nrepl-proc (cider-jack-in-clj '()))
+ (nrepl-buf (process-buffer nrepl-proc)))
+
+ ;; wait until the client successfully connects to the nREPL
+ ;; server. A high timeout is set because Basilisp usually needs
to
+ ;; be compiled the first time it is run.
+ (cider-itu-poll-until (eq (gv-deref client-is-connected*)
'connected) 60)
+
+ ;; give it some time to setup the clj REPL
+ (cider-itu-poll-until (cider-repls 'clj nil) 5)
+
+ ;; send command to the REPL, and push stdout/stderr to
+ ;; corresponding eval-xxx variables.
+ (let ((repl-buffer (cider-current-repl))
+ (eval-err '())
+ (eval-out '()))
+ (expect repl-buffer :not :to-be nil)
+
+ ;; send command to the REPL
+ (cider-interactive-eval
+ ;; ask REPL to return a string that uniquely identifies it.
+ "(print :basilisp? (some? sys/version))"
+ (lambda (return)
+ (nrepl-dbind-response
+ return
+ (out err)
+ (when err (push err eval-err))
+ (when out (push out eval-out)))) )
+
+ ;; wait for a response to come back.
+ (cider-itu-poll-until (or eval-err eval-out) 5)
+
+ ;; ensure there are no errors and response is as expected.
+ (expect eval-err :to-equal '())
+ ;; The Basilisp nREPL server sends the message in three
separate
+ ;; pieces, which is likely an area for improvement on the
+ ;; Basilisp side.
+ (expect eval-out :to-equal '("true" " " ":basilisp?"))
+
+ ;; exit the REPL.
+ (cider-quit repl-buffer)
+
+ ;; wait for the REPL to exit
+ (cider-itu-poll-until (not (eq (process-status nrepl-proc)
'run)) 5)
+ (expect (member (process-status nrepl-proc) '(exit
signal))))))))))
+
(it "to clojure tools cli (default)"
(jack-in-clojure-cli-test nil))
@@ -190,9 +257,9 @@ If CLI-COMMAND is nil, then use the default."
(it "to clojure tools cli (alternative pwsh)"
(jack-in-clojure-cli-test "pwsh")))
-(when (eq system-type 'windows-nt)
- (it "to clojure tools cli (alternative deps.exe)"
- (jack-in-clojure-cli-test "deps.exe")))
+ (when (eq system-type 'windows-nt)
+ (it "to clojure tools cli (alternative deps.exe)"
+ (jack-in-clojure-cli-test "deps.exe")))
(it "to leiningen"
(with-cider-test-sandbox