Skip to content

Commit c5e4e43

Browse files
committed
Support nbb as native cljs repl
If no repl init form is given, logic assumes it is repl is cljs from the start.
1 parent 5064287 commit c5e4e43

File tree

3 files changed

+148
-39
lines changed

3 files changed

+148
-39
lines changed

cider.el

+88-39
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,27 @@ By default we favor the project-specific shadow-cljs over the system-wide."
230230
:safe #'stringp
231231
:package-version '(cider . "1.2.0"))
232232

233+
(defcustom cider-nbb-command
234+
"nbb"
235+
"The command used to execute nbb."
236+
:type 'string
237+
:safe #'stringp
238+
:package-version '(cider . "1.2.0"))
239+
240+
(defcustom cider-nbb-global-options
241+
nil
242+
"Command line options used to execute nbb."
243+
:type 'string
244+
:safe #'stringp
245+
:package-version '(cider . "1.2.0"))
246+
247+
(defcustom cider-nbb-parameters
248+
"nrepl-server"
249+
"Params passed to nbb to start an nREPL server via `cider-jack-in'."
250+
:type 'string
251+
:safe #'stringp
252+
:package-version '(cider . "1.2.0"))
253+
233254
(defcustom cider-jack-in-default (if (executable-find "clojure") 'clojure-cli 'lein)
234255
"The default tool to use when doing `cider-jack-in' outside a project.
235256
This value will only be consulted when no identifying file types, i.e.
@@ -243,7 +264,8 @@ to Leiningen."
243264
(const clojure-cli)
244265
(const shadow-cljs)
245266
(const gradle)
246-
(const babashka))
267+
(const babashka)
268+
(const nbb))
247269
:safe #'symbolp
248270
:package-version '(cider . "0.9.0"))
249271

@@ -262,6 +284,7 @@ command when there is no ambiguity."
262284
(const shadow-cljs)
263285
(const gradle)
264286
(const babashka)
287+
(const nbb)
265288
(const :tag "Always ask" nil))
266289
:safe #'symbolp
267290
:package-version '(cider . "0.13.0"))
@@ -337,6 +360,7 @@ Sub-match 1 must be the project path.")
337360
('babashka cider-babashka-command)
338361
('shadow-cljs cider-shadow-cljs-command)
339362
('gradle cider-gradle-command)
363+
('nbb cider-nbb-command)
340364
(_ (user-error "Unsupported project type `%S'" project-type))))
341365

342366
(defun cider-jack-in-resolve-command (project-type)
@@ -357,6 +381,7 @@ Throws an error if PROJECT-TYPE is unknown."
357381
;; relative path like "./gradlew" use locate file instead of checking
358382
;; the exec-path
359383
('gradle (cider--resolve-project-command cider-gradle-command))
384+
('nbb (cider--resolve-command cider-nbb-command))
360385
(_ (user-error "Unsupported project type `%S'" project-type))))
361386

362387
(defun cider-jack-in-global-options (project-type)
@@ -368,6 +393,7 @@ Throws an error if PROJECT-TYPE is unknown."
368393
('babashka cider-babashka-global-options)
369394
('shadow-cljs cider-shadow-cljs-global-options)
370395
('gradle cider-gradle-global-options)
396+
('nbb cider-nbb-global-options)
371397
(_ (user-error "Unsupported project type `%S'" project-type))))
372398

373399
(defun cider-jack-in-params (project-type)
@@ -383,6 +409,7 @@ Throws an error if PROJECT-TYPE is unknown."
383409
('babashka cider-babashka-parameters)
384410
('shadow-cljs cider-shadow-cljs-parameters)
385411
('gradle cider-gradle-parameters)
412+
('nbb cider-nbb-parameters)
386413
(_ (user-error "Unsupported project type `%S'" project-type))))
387414

388415

@@ -766,6 +793,10 @@ dependencies."
766793
(cider-add-clojure-dependencies-maybe
767794
cider-jack-in-dependencies)
768795
(cider-jack-in-normalized-nrepl-middlewares)))
796+
('nbb (concat
797+
global-opts
798+
(unless (seq-empty-p global-opts) " ")
799+
params))
769800
(_ (error "Unsupported project type `%S'" project-type))))
770801

771802

@@ -984,30 +1015,35 @@ The supplied string will be wrapped in a do form if needed."
9841015
(def config (edn/read-string (slurp (io/file \"build.edn\"))))
9851016
(apply cider.piggieback/cljs-repl (krell.repl/repl-env) (mapcat identity config))"
9861017
cider-check-krell-requirements)
1018+
;; native cljs repl, no form required.
1019+
(nbb)
9871020
(custom cider-custom-cljs-repl-init-form nil))
9881021
"A list of supported ClojureScript REPLs.
9891022
990-
For each one we have its name, the form we need to evaluate in a Clojure
991-
REPL to start the ClojureScript REPL and functions to verify their requirements.
1023+
For each one we have its name, and then, if the repl is not a native
1024+
ClojureScript REPL, the form we need to evaluate in a Clojure REPL to
1025+
switch to the ClojureScript REPL and functions to verify their
1026+
requirements.
9921027
993-
The form should be either a string or a function producing a string.")
1028+
The form, if any, should be either a string or a function producing a
1029+
string.")
9941030

995-
(defun cider-register-cljs-repl-type (type init-form &optional requirements-fn)
1031+
(defun cider-register-cljs-repl-type (type &optional init-form requirements-fn)
9961032
"Register a new ClojureScript REPL type.
9971033
9981034
Types are defined by the following:
9991035
10001036
- TYPE - symbol identifier that will be used to refer to the REPL type
1001-
- INIT-FORM - string or function (symbol) producing string
1037+
- INIT-FORM - (optional) string or function (symbol) producing string
10021038
- REQUIREMENTS-FN - function to check whether the REPL can be started.
10031039
This param is optional.
10041040
10051041
All this function does is modifying `cider-cljs-repl-types'.
10061042
It's intended to be used in your Emacs config."
10071043
(unless (symbolp type)
10081044
(user-error "The REPL type must be a symbol"))
1009-
(unless (or (stringp init-form) (symbolp init-form))
1010-
(user-error "The init form must be a string or a symbol referring to a function"))
1045+
(unless (or (null init-form) (stringp init-form) (symbolp init-form))
1046+
(user-error "The init form must be a string or a symbol referring to a function or nil"))
10111047
(unless (or (null requirements-fn) (symbolp requirements-fn))
10121048
(user-error "The requirements-fn must be a symbol referring to a function"))
10131049
(add-to-list 'cider-cljs-repl-types (list type init-form requirements-fn)))
@@ -1027,6 +1063,7 @@ you're working on."
10271063
(const :tag "Shadow" shadow)
10281064
(const :tag "Shadow w/o Server" shadow-select)
10291065
(const :tag "Krell" krell)
1066+
(const :tag "Nbb" nbb)
10301067
(const :tag "Custom" custom))
10311068
:safe #'symbolp
10321069
:package-version '(cider . "0.17.0"))
@@ -1045,15 +1082,16 @@ DEFAULT is the default ClojureScript REPL to offer in completion."
10451082
(or default (car cider--select-cljs-repl-history))))))
10461083

10471084
(defun cider-cljs-repl-form (repl-type)
1048-
"Get the cljs REPL form for REPL-TYPE."
1049-
(if-let* ((repl-form (cadr (seq-find
1050-
(lambda (entry)
1051-
(eq (car entry) repl-type))
1052-
cider-cljs-repl-types))))
1053-
;; repl-form can be either a string or a function producing a string
1054-
(if (symbolp repl-form)
1055-
(funcall repl-form)
1056-
repl-form)
1085+
"Get the cljs REPL form for REPL-TYPE, if any."
1086+
(if-let* ((repl-type-info (seq-find
1087+
(lambda (entry)
1088+
(eq (car entry) repl-type))
1089+
cider-cljs-repl-types)))
1090+
(when-let ((repl-form (cadr repl-type-info)))
1091+
;; repl-form can be either a string or a function producing a string
1092+
(if (symbolp repl-form)
1093+
(funcall repl-form)
1094+
repl-form))
10571095
(user-error "No ClojureScript REPL type %s found. Please make sure that `cider-cljs-repl-types' has an entry for it" repl-type)))
10581096

10591097
(defun cider-verify-cljs-repl-requirements (&optional repl-type)
@@ -1248,8 +1286,7 @@ server buffer, in which case a new session for that server is created."
12481286
(append other-params)
12491287
(cider--update-cljs-type)
12501288
(cider--update-cljs-init-function)
1251-
(plist-put :session-name ses-name)
1252-
(plist-put :repl-type 'pending-cljs)))))
1289+
(plist-put :session-name ses-name)))))
12531290

12541291
;;;###autoload
12551292
(defun cider-connect-clj (&optional params)
@@ -1282,8 +1319,7 @@ parameters regardless of their supplied or default values."
12821319
(cider--check-existing-session)
12831320
(cider--update-cljs-type)
12841321
(cider--update-cljs-init-function)
1285-
(plist-put :session-name nil)
1286-
(plist-put :repl-type 'pending-cljs))))
1322+
(plist-put :session-name nil))))
12871323

12881324
;;;###autoload
12891325
(defun cider-connect-clj&cljs (params &optional soft-cljs-start)
@@ -1455,27 +1491,39 @@ non-nil, don't start if ClojureScript requirements are not met."
14551491
(plist-put :port (cdr endpoint)))))))
14561492

14571493
(defun cider--update-cljs-init-function (params)
1458-
"Update PARAMS :repl-init-function for cljs connections."
1494+
"Update repl type and any init PARAMS for cljs connections.
1495+
1496+
The updated params are:
1497+
1498+
:cljs-type 'cljs if it is a cljs REPL, or 'pending-cljs when the init form
1499+
is required to be sent to the REPL to switch over to cljs.
1500+
1501+
:repl-init-form The form that can switch the REPL over to cljs.
1502+
1503+
:repl-init-function The fn that switches the REPL over to cljs."
14591504
(with-current-buffer (or (plist-get params :--context-buffer)
14601505
(current-buffer))
14611506
(let* ((cljs-type (plist-get params :cljs-repl-type))
14621507
(repl-init-form (cider-cljs-repl-form cljs-type)))
1463-
(thread-first
1464-
params
1465-
(plist-put :repl-init-function
1466-
(lambda ()
1467-
(cider--check-cljs cljs-type)
1468-
;; FIXME: ideally this should be done in the state handler
1469-
(setq-local cider-cljs-repl-type cljs-type)
1470-
(cider-nrepl-send-request
1471-
(list "op" "eval"
1472-
"ns" (cider-current-ns)
1473-
"code" repl-init-form)
1474-
(cider-repl-handler (current-buffer)))
1475-
(when (and (buffer-live-p nrepl-server-buffer)
1476-
cider-offer-to-open-cljs-app-in-browser)
1477-
(cider--offer-to-open-app-in-browser nrepl-server-buffer))))
1478-
(plist-put :repl-init-form repl-init-form)))))
1508+
(if (null repl-init-form)
1509+
(plist-put params :repl-type 'cljs)
1510+
(thread-first
1511+
params
1512+
(plist-put :repl-init-function
1513+
(lambda ()
1514+
(cider--check-cljs cljs-type)
1515+
;; FIXME: ideally this should be done in the state handler
1516+
(setq-local cider-cljs-repl-type cljs-type)
1517+
(cider-nrepl-send-request
1518+
(list "op" "eval"
1519+
"ns" (cider-current-ns)
1520+
"code" repl-init-form)
1521+
(cider-repl-handler (current-buffer)))
1522+
(when (and (buffer-live-p nrepl-server-buffer)
1523+
cider-offer-to-open-cljs-app-in-browser)
1524+
(cider--offer-to-open-app-in-browser nrepl-server-buffer))))
1525+
(plist-put :repl-init-form repl-init-form)
1526+
(plist-put :repl-type 'pending-cljs))))))
14791527

14801528
(defun cider--check-existing-session (params)
14811529
"Ask for confirmation if a session with similar PARAMS already exists.
@@ -1655,7 +1703,8 @@ PROJECT-DIR defaults to current project."
16551703
(babashka . "bb.edn")
16561704
(shadow-cljs . "shadow-cljs.edn")
16571705
(gradle . "build.gradle")
1658-
(gradle . "build.gradle.kts"))))
1706+
(gradle . "build.gradle.kts")
1707+
(nbb . "package.json"))))
16591708
(delq nil
16601709
(mapcar (lambda (candidate)
16611710
(when (file-exists-p (cdr candidate))

test/cider-tests.el

+43
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,49 @@
578578
(expect (cider--resolve-project-command "command")
579579
:to-equal (shell-quote-argument "/bin/command"))))
580580

581+
(describe "cider-connect-sibling-cljs"
582+
:var (-cider-cljs-repl-types bu-cider-shadow-default-options)
583+
(before-all
584+
(setq -cider-cljs-repl-types cider-cljs-repl-types
585+
-cider-shadow-default-options cider-shadow-default-options))
586+
(after-each
587+
(setq cider-cljs-repl-types -cider-cljs-repl-types
588+
cider-shadow-default-options -cider-shadow-default-options))
589+
590+
(describe "sets nrepl client local vars correctly"
591+
(it "for nbb project"
592+
(let* ((server-process (nrepl-start-mock-server-process))
593+
(server-buffer (process-buffer server-process))
594+
(client-buffer (cider-connect-sibling-cljs '(:cljs-repl-type nbb) server-buffer)))
595+
(expect (buffer-local-value 'cider-repl-type client-buffer) :to-equal 'cljs)
596+
(expect (buffer-local-value 'cider-repl-init-function client-buffer) :to-be nil)
597+
(delete-process (get-buffer-process client-buffer))))
598+
(it "for shadow project"
599+
(setq cider-shadow-default-options "a-shadow-alias")
600+
(let* ((server-process (nrepl-start-mock-server-process))
601+
(server-buffer (process-buffer server-process))
602+
(client-buffer (cider-connect-sibling-cljs '(:cljs-repl-type shadow) server-buffer)))
603+
(expect (buffer-local-value 'cider-repl-type client-buffer) :to-equal 'pending-cljs)
604+
(expect (buffer-local-value 'cider-repl-init-function client-buffer) :not :to-be nil)
605+
(delete-process (get-buffer-process client-buffer))))
606+
(it "for a custom cljs REPL type project"
607+
(cider-register-cljs-repl-type 'native-cljs)
608+
(let* ((server-process (nrepl-start-mock-server-process))
609+
(server-buffer (process-buffer server-process))
610+
(client-buffer (cider-connect-sibling-cljs '(:cljs-repl-type native-cljs)
611+
server-buffer)))
612+
(expect (buffer-local-value 'cider-repl-type client-buffer) :to-equal 'cljs)
613+
(delete-process (get-buffer-process client-buffer))))
614+
(it "for a custom REPL type project that needs to switch to cljs"
615+
(cider-register-cljs-repl-type 'not-cljs-initially "(form-to-switch-to-cljs-repl)")
616+
(let* ((server-process (nrepl-start-mock-server-process))
617+
(server-buffer (process-buffer server-process))
618+
(client-buffer (cider-connect-sibling-cljs '(:cljs-repl-type not-cljs-initially)
619+
server-buffer)))
620+
(expect (buffer-local-value 'cider-repl-type client-buffer) :to-equal 'pending-cljs)
621+
(expect (buffer-local-value 'cider-repl-init-function client-buffer) :not :to-be nil)
622+
(delete-process (get-buffer-process client-buffer))))))
623+
581624
(provide 'cider-tests)
582625

583626
;;; cider-tests.el ends here

test/utils/nrepl-tests-utils.el

+17
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
;;; Code:
2727

28+
(require 'nrepl-client)
29+
2830
(defmacro nrepl-tests-log/init! (enable? name log-filename &optional clean?)
2931
"Create a NAME/log! elisp function to log messages to LOG-FILENAME,
3032
taking the same arguments as `message'. Messages are appended to
@@ -97,5 +99,20 @@ calling process."
9799
;; invoke mock server
98100
" -l test/nrepl-server-mock.el -f nrepl-server-mock-start"))
99101

102+
(defun nrepl-start-mock-server-process ()
103+
"Start and return the mock nrepl server process."
104+
(let* ((up? nil)
105+
(server-process (nrepl-start-server-process
106+
default-directory
107+
(nrepl-server-mock-invocation-string)
108+
(lambda (server-buffer)
109+
(setq up? t))))
110+
server-buffer (process-buffer server-process))
111+
;; server has reported its endpoint
112+
(nrepl-tests-sleep-until 2 up?)
113+
server-process))
100114

101115
(provide 'nrepl-tests-utils)
116+
117+
;;; nrepl-tests-utils.el ends here
118+

0 commit comments

Comments
 (0)