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

Reply via email to