summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2018-09-08 10:27:30 -0700
committerSean Whitton <spwhitton@spwhitton.name>2018-09-08 10:27:30 -0700
commit9148631b2f93ae849cfa141ac11447be42c8f31c (patch)
tree037f9fa2cbb72d7ca0118c1f89961efbafb64065
parent42d3b3f307a8d59632bba30a35d2695f72cf63dc (diff)
parent58f79015f3a28d6aee2fb841b31db3d8a46829d5 (diff)
Merge tag 'v0.18.0+dfsg' into wip/master
DFSG-clean upstream version 0.18.0
-rw-r--r--.dir-locals.el7
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md (renamed from .github/ISSUE_TEMPLATE.md)9
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.md20
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.md13
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml18
-rw-r--r--CHANGELOG.md56
-rw-r--r--Makefile11
-rw-r--r--README.md25
-rw-r--r--ROADMAP.md21
-rw-r--r--cider-apropos.el5
-rw-r--r--cider-browse-ns.el15
-rw-r--r--cider-browse-spec.el14
-rw-r--r--cider-classpath.el4
-rw-r--r--cider-client.el906
-rw-r--r--cider-common.el89
-rw-r--r--cider-compat.el135
-rw-r--r--cider-completion.el253
-rw-r--r--cider-connection.el799
-rw-r--r--cider-debug.el6
-rw-r--r--cider-doc.el18
-rw-r--r--cider-eval.el1115
-rw-r--r--cider-find.el231
-rw-r--r--cider-format.el150
-rw-r--r--cider-inspector.el6
-rw-r--r--cider-interaction.el2146
-rw-r--r--cider-macroexpansion.el6
-rw-r--r--cider-mode.el379
-rw-r--r--cider-ns.el265
-rw-r--r--cider-popup.el4
-rw-r--r--cider-profile.el3
-rw-r--r--cider-repl-history.el20
-rw-r--r--cider-repl.el213
-rw-r--r--cider-resolve.el13
-rw-r--r--cider-scratch.el36
-rw-r--r--cider-selector.el31
-rw-r--r--cider-stacktrace.el128
-rw-r--r--cider-test.el127
-rw-r--r--cider-tracing.el90
-rw-r--r--cider-util.el28
-rw-r--r--cider.el1119
-rw-r--r--doc/about/logo.md2
-rw-r--r--doc/about/team.md7
-rw-r--r--doc/cider-refcard.tex7
-rw-r--r--doc/clojurescript.md126
-rw-r--r--doc/code_completion.md18
-rw-r--r--doc/configuration.md2
-rw-r--r--doc/debugging.md14
-rw-r--r--doc/faq.md36
-rw-r--r--doc/hacking_on_cider.md25
-rw-r--r--doc/index.md36
-rw-r--r--doc/installation.md33
-rw-r--r--doc/interactive_programming.md40
-rw-r--r--doc/managing_connections.md173
-rw-r--r--doc/miscellaneous_features.md37
-rw-r--r--doc/navigating_stacktraces.md16
-rw-r--r--doc/troubleshooting.md22
-rw-r--r--doc/up_and_running.md101
-rw-r--r--doc/using_the_repl.md2
-rw-r--r--mkdocs.yml9
-rw-r--r--nrepl-client.el343
-rw-r--r--test/cider-apropos-tests.el1
-rw-r--r--test/cider-classpath-tests.el1
-rw-r--r--test/cider-client-tests.el398
-rw-r--r--test/cider-common-tests.el1
-rw-r--r--test/cider-connection-tests.el365
-rw-r--r--test/cider-debug-tests.el3
-rw-r--r--test/cider-error-parsing-tests.el2
-rw-r--r--test/cider-find-tests.el37
-rw-r--r--test/cider-font-lock-tests.el34
-rw-r--r--test/cider-grimoire-tests.el1
-rw-r--r--test/cider-interaction-tests.el37
-rw-r--r--test/cider-locals-tests.el1
-rw-r--r--test/cider-ns-tests.el34
-rw-r--r--test/cider-repl-tests.el61
-rw-r--r--test/cider-selector-tests.el35
-rw-r--r--test/cider-stacktrace-tests.el1
-rw-r--r--test/cider-tests.el189
-rw-r--r--test/cider-util-tests.el5
-rw-r--r--test/nrepl-client-tests.el160
-rw-r--r--test/utils/cider-connection-test-utils.el23
81 files changed, 5844 insertions, 5129 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
index 4eefbbfe..fe7dacd3 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -3,6 +3,7 @@
((emacs-lisp-mode
(bug-reference-url-format . "https://github.com/clojure-emacs/cider/issues/%s")
+ (bug-reference-bug-regexp . "#\\(?2:[[:digit:]]+\\)")
(indent-tabs-mode)
(fill-column . 80)
(sentence-end-double-space . t)
@@ -17,6 +18,8 @@
(nrepl-dbind-response . 2)
(cider-save-marker . 1)
(cider-propertize-region . 1)
+ (cider-map-repls . 1)
+ (cider--jack-in . 1)
(cider--make-result-overlay . 1)
;; need better solution for indenting cl-flet bindings
(multiline-comment-handler . defun) ;; cl-flet
@@ -26,9 +29,7 @@
(cl-defun . 2)
(with-parsed-tramp-file-name . 2)
(thread-first . 1)
- (thread-last . 1))))
- (emacs-lisp-mode
- (bug-reference-bug-regexp . "#\\(?2:[[:digit:]]+\\)")))
+ (thread-last . 1)))))
;; To use the bug-reference stuff, do:
;; (add-hook 'text-mode-hook #'bug-reference-mode)
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 5ce03497..ab5aca76 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -1,11 +1,12 @@
+---
+name: Bug Report
+about: Report an issue with CIDER you've discovered.
+---
+
*Use the template below when reporting bugs. Please, make sure that
you're running the latest stable CIDER and that the problem you're reporting
hasn't been reported (and potentially fixed) already.*
-*When requesting new features or improvements to existing features you can
-discard the template completely. Just make sure to make a good case for your
-request.*
-
**Remove all of the placeholder text in your final report!**
## Expected behavior
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..9b8c9ca4
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature Request
+about: Suggest new CIDER features or improvements to existing features.
+---
+
+**Is your feature request related to a problem? Please describe.**
+
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+
+Add any other context or screenshots about the feature request here.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index d0a5e169..dbdef4bb 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -6,20 +6,17 @@ The more detailed you are, the better.**
Before submitting the PR make sure the following things have been done (and denote this
by checking the relevant checkboxes):
-- [ ] The commits are consistent with our [contribution guidelines][1]
+- [ ] The commits are consistent with our [contribution guidelines](../blob/master/CONTRIBUTING.md)
- [ ] You've added tests (if possible) to cover your change(s)
- [ ] All tests are passing (`make test`)
- [ ] All code passes the linter (`make lint`) which is based on [`elisp-lint`](https://github.com/gonewest818/elisp-lint) and includes
- [byte-compilation](https://www.gnu.org/software/emacs/manual/html_node/elisp/Byte-Compilation.html), [`checkdoc`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Tips.html), [check-declare](https://www.gnu.org/software/emacs/manual/html_node/elisp/Declaring-Functions.html), packaging metadata, indentation, and trailing whitespace checks.
-- [ ] You've updated the [changelog][3] (if adding/changing user-visible functionality)
-- [ ] You've updated the [user manual][4] (if adding/changing user-visible functionality)
+- [ ] You've updated the [changelog](../blob/master/CHANGELOG.md) (if adding/changing user-visible functionality)
+- [ ] You've updated the [user manual](../blog/master/doc) (if adding/changing user-visible functionality)
Thanks!
*If you're just starting out to hack on CIDER you might find this [section of its
-manual][2] extremely useful.*
+manual][1] extremely useful.*
-[1]: https://github.com/clojure-emacs/cider/blob/master/.github/CONTRIBUTING.md
-[2]: https://cider.readthedocs.io/en/latest/hacking_on_cider/
-[3]: https://github.com/clojure-emacs/cider/blob/master/CHANGELOG.md
-[4]: https://github.com/clojure-emacs/cider/tree/master/doc
+[1]: https://cider.readthedocs.io/en/latest/hacking_on_cider/
diff --git a/.gitignore b/.gitignore
index 72f1bb16..5f813a96 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ cider-autoloads.el
.dir-locals?.el
cider-refcard.aux
cider-refcard.log
+doc/auto/ \ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 0f61f262..f867d8fa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,12 +9,10 @@ env:
global:
- PATH=$HOME/local/bin:$HOME/local/evm/bin:$HOME/local/cask/bin:$PATH
matrix:
- - EMACS_BINARY=emacs-24.4-travis MAKE_TEST=test
- - EMACS_BINARY=emacs-24.5-travis MAKE_TEST=test
- EMACS_BINARY=emacs-25.1-travis MAKE_TEST=test
- EMACS_BINARY=emacs-25.2-travis MAKE_TEST=test
- EMACS_BINARY=emacs-25.3-travis MAKE_TEST=test
- - EMACS_BINARY=emacs-26-pretest-travis MAKE_TEST=test
+ - EMACS_BINARY=emacs-26.1-travis MAKE_TEST=test
- EMACS_BINARY=emacs-git-snapshot-travis MAKE_TEST=test
stages:
@@ -25,17 +23,13 @@ jobs:
include:
# linting for code quality
- stage: check
- env: EMACS_BINARY=emacs-24.4-travis MAKE_TEST=lint
- - stage: check
- env: EMACS_BINARY=emacs-24.5-travis MAKE_TEST=lint
- - stage: check
env: EMACS_BINARY=emacs-25.1-travis MAKE_TEST=lint
- stage: check
env: EMACS_BINARY=emacs-25.2-travis MAKE_TEST=lint
- stage: check
env: EMACS_BINARY=emacs-25.3-travis MAKE_TEST=lint
- stage: check
- env: EMACS_BINARY=emacs-26-pretest-travis MAKE_TEST=lint
+ env: EMACS_BINARY=emacs-26.1-travis MAKE_TEST=lint
- stage: check
env: EMACS_BINARY=emacs-git-snapshot-travis MAKE_TEST=lint
@@ -52,11 +46,3 @@ before_script:
script:
- emacs --version
- make $MAKE_TEST
-
-notifications:
- webhooks:
- urls:
- - https://webhooks.gitter.im/e/14f0f7b4d5b20a70d032
- on_success: change # options: [always|never|change] default: always
- on_failure: always # options: [always|never|change] default: always
- on_start: never # options: [always|never|change] default: always
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a69c5809..50d974cd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,61 @@
## master (unreleased)
+## 0.18.0 (2018-09-02)
+
+### New features
+
+* [#2375](https://github.com/clojure-emacs/cider/issues/2375): Move `cider-eval-toplevel-inside-comment-form` into clojure-mode as `clojure-toplevel-inside-comment-form` so `beginning-of-defun` is aware of comment forms.
+* Add new `cider-session-name-template` variable for flexible customization of cider session and REPL buffer names.
+* Bind `C-c M-r` to `cider-restart`.
+* Add new `cider-start-map` keymap (`C-c C-x`) for jack-in and connection commands.
+* Add new `cider-ns-map` keymap (`C-c M-n`) for namespace related functionality.
+* Allow evaling top level forms in a comment form rather than the entire comment form with `cider-eval-toplevel-inside-comment-form`.
+* Create keymap for inserting forms into the repl at `C-c C-j`.
+* Add new defcustom `cider-invert-insert-eval-p`: Set to cause insert-to-repl commands to eval the forms by default when inserted.
+* Add new defcustom `cider-switch-to-repl-after-insert-p`: Set to prevent cursor from going to the repl when inserting a form in the repl with the insert-to-repl commands.
+* Inject piggieback automatically on `cider-jack-in-clojurescript`.
+* Introduce a new command named `cider` (`C-c M-x`) that acts as a simple wrapper around all commands for starting/connecting to REPLs.
+* [#2305](https://github.com/clojure-emacs/cider/issues/2305): Make it possible to disable the REPL type auto-detection by customizing `cider-repl-auto-detect-type`.
+* [#2373](https://github.com/clojure-emacs/cider/issues/2373): Make it possible to configure the welcome message displayed in scratch buffers via `cider-scratch-initial-message`.
+* Add the ability to jump to the profiler buffer using `cider-selector`.
+* [#1980](https://github.com/clojure-emacs/cider/issues/1980): Echo back missing namespace name on interactive eval (requires nREPL 0.4.3+).
+* [#2397](https://github.com/clojure-emacs/cider/pull/2397): Add shadow-select CLJS REPL type.
+* [#2314](https://github.com/clojure-emacs/cider/pull/2314): Add `cider-ns-reload` and `cider-ns-reload-all` interactive commands.
+
+### Bugs fixed
+
+* [#2317](https://github.com/clojure-emacs/cider/issues/2317): The stdin prompt can now be cancelled.
+* [#2328](https://github.com/clojure-emacs/cider/issues/2328): Added `cider-eval-sexp-to-point`.
+* [#2310](https://github.com/clojure-emacs/cider/issues/2310): `cider-format-edn-last-sexp` will format the last sexp.
+* [#2294](https://github.com/clojure-emacs/cider/issues/2294): Fix setting default stacktrace filters.
+* [#2286](https://github.com/clojure-emacs/cider/issues/2286): Fix eldoc issue with images in the REPL.
+* [#2307](https://github.com/clojure-emacs/cider/pull/2307): Use a better error when a cljs repl form cannot be found.
+* Fix the broken test selector functionality.
+* [#2291](https://github.com/clojure-emacs/cider/issues/2291): `cider-use-tooltips` custom variable works as expected.
+* [#2424](https://github.com/clojure-emacs/cider/issues/2424): Fallback to `lein` as the default jack-in command when `clojure` is not present.
+
+### Changes
+
+* **(Breaking)** Move `cider-repl-set-ns`, previously on `C-c M-n`, on `C-c M-n (M-)n` in the `cider-ns-map`.
+* **(Breaking)** Move `cider-ns-refresh`, previously on `C-c C-x`, on `C-c M-n (M-)r` in the `cider-ns-map`.
+* **(Breaking)** Bump the minimum required Emacs version to 25.1.
+* **(Breaking)** Drop support for Java 7 and Clojure(Script) 1.7.
+* **(Breaking)** Use session name as part of CIDER buffers names (REPL, server, messages), and obsolete `nrepl-buffer-name-separator` and `nrepl-buffer-name-show-port`. See `cider-session-name-template` and `cider-format-connection-params` for how to customize CIDER buffer names.
+* Rename `cider-eval-defun-to-point` to `cider-eval-defun-up-to-point`.
+* Add support for printing to the current buffer to `cider-eval-defun-up-to-point`.
+* Remove `cider-ping` command.
+* Remove `cider-visit-error-buffer` in favour of using `cider-selector`.
+* Rename `cider-refresh` to `cider-ns-refresh` (and all the related defcustoms).
+* **(Breaking)** Rewrote connection management (see http://docs.cider.mx/en/latest/managing_connections/ for details).
+* **(Breaking)** `cider-jack-in-clojurescript` now creates only a ClojureScript REPL (use `cider-jack-in-clj&cljs` to create both REPLs).
+* [#2357](https://github.com/clojure-emacs/cider/issues/2357): Support both keywords and strings as test selectors (previously it was only strings).
+* [#2378](https://github.com/clojure-emacs/cider/pull/2378): Add autoloads target to Makefile.
+* Map `cider-pprint-eval-last-sexp` to `C-c C-v (C-)f (C-)e` in the `cider-eval-commands-map`.
+* Map `cider-pprint-eval-defun-at-point` to `C-c C-v (C-)f (C-)d` in the `cider-eval-commands-map`.
+* Accept bare figwheel-main build names (e.g., `dev`). Previously, a keyword (e.g., `:dev`) was required.
+* Stop releasing CIDER and cider-nrepl together. cider-nrepl now has its own release cycle and CIDER introduces `cider-required-middleware-version` to track it.
+
## 0.17.0 (2018-05-07)
### New features
@@ -451,6 +506,7 @@ and try to associate the created connection with this project automatically.
* [#1422](https://github.com/clojure-emacs/cider/issues/1422): Don't display mismatching parens error on incomplete expressions in REPL buffers.
* [#1412](https://github.com/clojure-emacs/cider/issues/1412): nREPL messages for separate sessions are tracked in separate buffers.
* Removed `cider-switch-to-repl-command`.
+* Renamed `cider-default-repl-command` to `cider-jack-in-default`.
### Bugs fixed
diff --git a/Makefile b/Makefile
index 5f95e4e9..507103d6 100644
--- a/Makefile
+++ b/Makefile
@@ -35,6 +35,12 @@ version:
test: version build
$(CASK) exec buttercup -L . -L ./test/utils/
+autoloads:
+ $(CASK) exec $(EMACS) -Q --batch \
+ -l autoload.el \
+ --eval "(let ((generated-autoload-file (expand-file-name \"cider-autoloads.el\"))) \
+ (update-directory-autoloads (expand-file-name \".\")))"
+
lint: version elpa
$(CASK) exec $(EMACS) -Q --batch \
--eval "(setq enable-local-variables :safe)" \
@@ -46,7 +52,7 @@ lint: version elpa
test-all: lint test
clean:
- rm -f .depend $(OBJECTS)
+ rm -f .depend $(OBJECTS) cider-autoloads.el
elpaclean: clean
rm -f elpa*
@@ -54,3 +60,6 @@ elpaclean: clean
run-cider: elpa
cask exec $(EMACS) -Q -L . --eval "(require 'cider)"
+
+html:
+ mkdocs build
diff --git a/README.md b/README.md
index 227c61ce..36e7f9d3 100644
--- a/README.md
+++ b/README.md
@@ -24,16 +24,13 @@ features are centered around `cider-mode`, an Emacs minor-mode that complements
compilation, debugging, definition and documentation lookup, running tests and
so on.
-CIDER is the successor to the now deprecated combination of using [SLIME][] +
-[swank-clojure][] for Clojure development.
-
**Please consider [supporting financially its ongoing development](#funding).**
## Quickstart
The instructions that follow are meant to get you from zero to a running CIDER
REPL in under 5 minutes. See the
-[official manual](http://cider.readthedocs.io/en/latest/) for (way) more
+[official manual](http://docs.cider.mx) for (way) more
details.
### Installation
@@ -60,8 +57,8 @@ connect to it.
Alternatively you can use <kbd>C-u M-x</kbd> `cider-jack-in` to specify the name
of a `lein` or `boot` project, without having to visit any file in it.
-In Clojure(Script) buffers the command `cider-jack-in` is bound to <kbd>C-c
-M-j</kbd>.
+In Clojure(Script) buffers the command `cider-jack-in` is bound to
+<kbd>C-c C-x (C-)j</kbd>.
### Connect to a running nREPL server
@@ -84,14 +81,15 @@ by your project's build tool (Gradle, Maven, etc).
After you get your nREPL server running go back to Emacs. Typing there <kbd>M-x</kbd>
`cider-connect` will allow you to connect to the running nREPL server.
-In Clojure(Script) buffers the command `cider-connect` is bound to <kbd>C-c M-c</kbd>.
+In Clojure(Script) buffers the command `cider-connect` is bound to
+<kbd>C-c C-x (C-)c (C-)c</kbd>.
## Diving Deeper
CIDER packs a ton of functionality and you really want to be familiar with it,
so you can fully empower your workflow. The best way to get acquainted with all
available features is to go over the entire
-[CIDER manual](http://cider.readthedocs.io/).
+[CIDER manual](http://docs.cider.mx/).
If you're into video lessons, you might also check out
this [intro to CIDER demo](https://www.youtube.com/watch?v=aYA4AAjLfT0) as well.
@@ -109,9 +107,7 @@ group of long-term contributors manage releases, evaluate pull-requests, and
does a lot of the groundwork on major new features.
* [Bozhidar Batsov](https://github.com/bbatsov) (author & head maintainer)
-* [Artur Malabarba](https://github.com/malabarba)
-* [Michael Griffiths](https://github.com/cichli)
-* [Jeff Valk](https://github.com/jeffvalk)
+* [Vitalie Spinu](https://github.com/vspinu)
* [Lars Andersen](https://github.com/expez)
### CIDER Alumni
@@ -123,6 +119,9 @@ core team members. Lovingly known as The Alumni:
* [Phil Hagelberg](https://github.com/technomancy)
* [Hugo Duncan](https://github.com/hugoduncan)
* [Steve Purcell](https://github.com/purcell)
+* [Artur Malabarba](https://github.com/malabarba)
+* [Michael Griffiths](https://github.com/cichli)
+* [Jeff Valk](https://github.com/jeffvalk)
## Release policy
@@ -144,7 +143,7 @@ branch of `cider-nrepl`.
## Logo
-CIDER's logo was created by [@ndr-qef](https://github.com/ndr-qef). You can find
+CIDER's logo was created by [@tapeinosyne](https://github.com/tapeinosyne). You can find
the logo in various formats
[here](https://github.com/clojure-emacs/cider/tree/master/logo).
@@ -251,8 +250,6 @@ Distributed under the GNU General Public License, version 3
[badge-license]: https://img.shields.io/badge/license-GPL_3-green.svg
[nREPL]:https://github.com/cemerick/nREPL
-[SLIME]: https://github.com/slime/slime
-[swank-clojure]: https://github.com/technomancy/swank-clojure
[Sly]: https://github.com/capitaomorte/sly
[Geiser]: https://github.com/jaor/geiser
[clojure-mode]: https://github.com/clojure-emacs/clojure-mode
diff --git a/ROADMAP.md b/ROADMAP.md
index daf9df1a..a38dd28c 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,4 +1,4 @@
-# CIDER Roadmap (as of May, 2018)
+# CIDER Roadmap (as of June, 2018)
That's a very high-level roadmap for CIDER. It focuses on the most
important challenges we need to tackle.
@@ -16,21 +16,22 @@ high-impact tasks to tackle.
## Internal improvements
-* replace usages of Elisp's `read` with `parseclj`
-* break down `cider-interaction.el` and remove this file completely
-* improve the connection management (https://github.com/clojure-emacs/cider/pull/2069)
-* improve nREPL callback handling (https://github.com/clojure-emacs/cider/issues/1099)
-* better handling for huge output/results (we can warn users about it, truncate it in the REPL and store the whole result internally, etc)
+* Replace usages of Elisp's `read` with `parseedn`.
+* ~~Break down `cider-interaction.el` and remove this file completely.~~ (**DONE/0.18**)
+* ~~Improve the connection management (https://github.com/clojure-emacs/cider/pull/2069)~~ (**DONE/0.18**)
+* Improve nREPL callback handling (https://github.com/clojure-emacs/cider/issues/1099)
+* Better handling for huge output/results (we can warn users about it,
+ truncate it in the REPL and store the whole result internally, etc).
## Better ClojureScript support
### Make it easier to start ClojureScript REPLs
* Implement some deps injection for ClojureScript REPLs
-* Providing meaningful errors when starting ClojureScript REPLs
-* Make it possible to have a project with only a ClojureScript REPL
+* ~~Providing meaningful errors when starting ClojureScript REPLs.~~ (**DONE/0.17**)
+* ~~Make it possible to have a project with only a ClojureScript REPL.~~(**DONE/0.18**)
* Merge cljs-tooling into orchard and evolve it a bit (under
- consideration, might be better to keep it a separate library)
+ consideration, might be better to keep it a separate library).
* Add ability to restart a ClojureScript REPL (https://github.com/clojure-emacs/cider/issues/1874)
### Add ClojureScript support for more commands
@@ -84,4 +85,4 @@ used to "encode/decode" EDN data.
### Transition everything non-nREPL specific to Orchard
-Already in progress, a lot of functionality already lives is orchard as of version 0.1.
+Already in progress, a lot of functionality already lives is orchard as of version 0.3.
diff --git a/cider-apropos.el b/cider-apropos.el
index c92d521c..97be9aa6 100644
--- a/cider-apropos.el
+++ b/cider-apropos.el
@@ -39,7 +39,6 @@
(require 'button)
(defconst cider-apropos-buffer "*cider-apropos*")
-(add-to-list 'cider-ancillary-buffers cider-apropos-buffer)
(defcustom cider-apropos-actions '(("display-doc" . cider-doc-lookup)
("find-def" . cider--find-var)
@@ -116,9 +115,8 @@ and be case-sensitive (based on CASE-SENSITIVE-P)."
(defun cider-show-apropos (summary results query docs-p)
"Show SUMMARY and RESULTS for QUERY in a pop-up buffer, formatted for DOCS-P."
- (with-current-buffer (cider-popup-buffer cider-apropos-buffer t)
+ (with-current-buffer (cider-popup-buffer cider-apropos-buffer 'select 'apropos-mode 'ancillary)
(let ((inhibit-read-only t))
- (apropos-mode)
(if (boundp 'header-line-format)
(setq-local header-line-format summary)
(insert summary "\n\n"))
@@ -174,7 +172,6 @@ optionally search doc strings (based on DOCS-P), include private vars
;;;###autoload
(defun cider-apropos-select (query &optional ns docs-p privates-p case-sensitive-p)
"Similar to `cider-apropos', but presents the results in a completing read.
-
Show all symbols whose names match QUERY, a regular expression.
QUERY can also be a list of space-separated words (e.g. take while) which
will be converted to a regular expression (like take.+while) automatically
diff --git a/cider-browse-ns.el b/cider-browse-ns.el
index f6a93fce..6f735353 100644
--- a/cider-browse-ns.el
+++ b/cider-browse-ns.el
@@ -35,16 +35,17 @@
;;; Code:
-(require 'cider-interaction)
(require 'cider-client)
-(require 'subr-x)
+(require 'cider-popup)
(require 'cider-compat)
(require 'cider-util)
(require 'nrepl-dict)
+
+(require 'subr-x)
(require 'easymenu)
+(require 'thingatpt)
(defconst cider-browse-ns-buffer "*cider-ns-browser*")
-(add-to-list 'cider-ancillary-buffers cider-browse-ns-buffer)
(defvar-local cider-browse-ns-current-ns nil)
@@ -78,6 +79,7 @@
\\{cider-browse-ns-mode-map}"
(setq-local electric-indent-chars nil)
+ (setq-local sesman-system 'CIDER)
(when cider-special-mode-truncate-lines
(setq-local truncate-lines t))
(setq-local cider-browse-ns-current-ns nil))
@@ -156,7 +158,7 @@ Each item consists of a ns-var and the first line of its docstring."
(defun cider-browse-ns (namespace)
"List all NAMESPACE's vars in BUFFER."
(interactive (list (completing-read "Browse namespace: " (cider-sync-request:ns-list))))
- (with-current-buffer (cider-popup-buffer cider-browse-ns-buffer t)
+ (with-current-buffer (cider-popup-buffer cider-browse-ns-buffer 'select nil 'ancillary)
(cider-browse-ns--list (current-buffer)
namespace
(cider-browse-ns--items namespace))
@@ -166,7 +168,7 @@ Each item consists of a ns-var and the first line of its docstring."
(defun cider-browse-ns-all ()
"List all loaded namespaces in BUFFER."
(interactive)
- (with-current-buffer (cider-popup-buffer cider-browse-ns-buffer t)
+ (with-current-buffer (cider-popup-buffer cider-browse-ns-buffer 'select nil 'ancillary)
(let ((names (cider-sync-request:ns-list)))
(cider-browse-ns--list (current-buffer)
"All loaded namespaces"
@@ -207,6 +209,9 @@ be displayed."
(cider-browse-ns value)
(cider-doc-lookup value))))
+(declare-function cider-find-ns "cider-find")
+(declare-function cider-find-var "cider-find")
+
(defun cider-browse-ns-find-at-point ()
"Find the definition of the thing at point."
(interactive)
diff --git a/cider-browse-spec.el b/cider-browse-spec.el
index b88ca300..d58352b1 100644
--- a/cider-browse-spec.el
+++ b/cider-browse-spec.el
@@ -45,10 +45,7 @@
;; The buffer names used by the spec browser
(defconst cider-browse-spec-buffer "*cider-spec-browser*")
-(add-to-list 'cider-ancillary-buffers cider-browse-spec-buffer)
-
(defconst cider-browse-spec-example-buffer "*cider-spec-example*")
-(add-to-list 'cider-ancillary-buffers cider-browse-spec-example-buffer)
;; Mode Definition
@@ -67,6 +64,7 @@
\\{cider-browse-spec-mode-map}"
(setq-local electric-indent-chars nil)
+ (setq-local sesman-system 'CIDER)
(when cider-special-mode-truncate-lines
(setq-local truncate-lines t)))
@@ -89,6 +87,7 @@
\\{cider-browse-spec-view-mode-map}"
(setq-local cider-browse-spec--current-spec nil)
(setq-local electric-indent-chars nil)
+ (setq-local sesman-system 'CIDER)
(when cider-special-mode-truncate-lines
(setq-local truncate-lines t)))
@@ -107,6 +106,7 @@
\\{cider-browse-spec-example-mode-map}"
(setq-local electric-indent-chars nil)
(setq-local revert-buffer-function #'cider-browse-spec--example-revert-buffer-function)
+ (setq-local sesman-system 'CIDER)
(when cider-special-mode-truncate-lines
(setq-local truncate-lines t)))
@@ -276,8 +276,7 @@ a more user friendly representation of SPEC-FORM."
"Browse SPEC."
(cider-ensure-connected)
(cider-ensure-op-supported "spec-form")
- (with-current-buffer (cider-popup-buffer cider-browse-spec-buffer t)
- (cider-browse-spec-view-mode)
+ (with-current-buffer (cider-popup-buffer cider-browse-spec-buffer 'select #'cider-browse-spec-view-mode 'ancillary)
(setq-local cider-browse-spec--current-spec spec)
(cider-browse-spec--draw-spec-buffer (current-buffer)
spec
@@ -305,8 +304,7 @@ property."
(cider-ensure-op-supported "spec-example")
(if-let* ((spec cider-browse-spec--current-spec))
(if-let* ((example (cider-sync-request:spec-example spec)))
- (with-current-buffer (cider-popup-buffer cider-browse-spec-example-buffer t)
- (cider-browse-spec-example-mode)
+ (with-current-buffer (cider-popup-buffer cider-browse-spec-example-buffer 'select #'cider-browse-spec-example-mode 'ancillary)
(setq-local cider-browse-spec--current-spec spec)
(let ((inhibit-read-only t))
(insert "Example of " (cider-font-lock-as-clojure spec))
@@ -337,7 +335,7 @@ Displays all specs when REGEX is nil."
(cider-ensure-connected)
(cider-ensure-op-supported "spec-list")
(let ((filter-regex (or regex "")))
- (with-current-buffer (cider-popup-buffer cider-browse-spec-buffer t)
+ (with-current-buffer (cider-popup-buffer cider-browse-spec-buffer 'select nil 'ancillary)
(let ((specs (cider-sync-request:spec-list filter-regex)))
(cider-browse-spec--draw-list-buffer (current-buffer)
(if (string-empty-p filter-regex)
diff --git a/cider-classpath.el b/cider-classpath.el
index 85223237..10141370 100644
--- a/cider-classpath.el
+++ b/cider-classpath.el
@@ -29,7 +29,6 @@
(require 'cider-compat)
(defvar cider-classpath-buffer "*cider-classpath*")
-(add-to-list 'cider-ancillary-buffers cider-classpath-buffer)
(defvar cider-classpath-mode-map
(let ((map (make-sparse-keymap)))
@@ -49,6 +48,7 @@
\\{cider-classpath-mode-map}"
(setq-local electric-indent-chars nil)
+ (setq-local sesman-system 'CIDER)
(when cider-special-mode-truncate-lines
(setq-local truncate-lines t)))
@@ -92,7 +92,7 @@
(interactive)
(cider-ensure-connected)
(cider-ensure-op-supported "classpath")
- (with-current-buffer (cider-popup-buffer cider-classpath-buffer t)
+ (with-current-buffer (cider-popup-buffer cider-classpath-buffer 'select nil 'ancillary)
(cider-classpath-list (current-buffer)
(mapcar (lambda (name)
(cider-classpath-properties name))
diff --git a/cider-client.el b/cider-client.el
index df5b1431..1e09bae2 100644
--- a/cider-client.el
+++ b/cider-client.el
@@ -26,8 +26,8 @@
;;; Code:
(require 'spinner)
-(require 'ewoc)
(require 'nrepl-client)
+(require 'cider-connection)
(require 'cider-common)
(require 'cider-util)
(require 'clojure-mode)
@@ -36,535 +36,6 @@
(require 'cider-compat)
(require 'seq)
-;;; Connection Buffer Management
-
-(defcustom cider-request-dispatch 'dynamic
- "Controls the request dispatch mechanism when several connections are present.
-Dynamic dispatch tries to infer the connection based on the current project
-& currently visited file, while static dispatch simply uses the default
-connection.
-
-Project metadata is attached to connections when they are created with commands
-like `cider-jack-in' and `cider-connect'."
- :type '(choice (const :tag "dynamic" dynamic)
- (const :tag "static" static))
- :group 'cider
- :package-version '(cider . "0.10.0"))
-
-(defcustom cider-connection-message-fn #'cider-random-words-of-inspiration
- "The function to use to generate the message displayed on connect.
-When set to nil no additional message will be displayed.
-
-A good alternative to the default is `cider-random-tip'."
- :type 'function
- :group 'cider
- :package-version '(cider . "0.11.0"))
-
-(defvar cider-connections nil
- "A list of connections.")
-
-(defun cider-connected-p ()
- "Return t if CIDER is currently connected, nil otherwise."
- (not (null (cider-connections))))
-
-(defun cider-ensure-connected ()
- "Ensure there is a cider connection present.
-An error is signaled in the absence of a connection."
- (unless (cider-connected-p)
- (user-error "`%s' needs an active nREPL connection" this-command)))
-
-(defsubst cider--in-connection-buffer-p ()
- "Return non-nil if current buffer is connected to a server."
- (and (derived-mode-p 'cider-repl-mode)
- (process-live-p
- (get-buffer-process (current-buffer)))))
-
-(defun cider-default-connection (&optional no-error)
- "The default (fallback) connection to use for nREPL interaction.
-When NO-ERROR is non-nil, don't throw an error when no connection has been
-found."
- (or (car (cider-connections))
- (unless no-error
- (error "No nREPL connection buffer"))))
-
-(defun cider-connections ()
- "Return the list of connection buffers.
-If the list is empty and buffer-local, return the global value."
- (or (setq cider-connections
- (seq-filter #'buffer-live-p cider-connections))
- (when (local-variable-p 'cider-connect)
- (kill-local-variable 'cider-connections)
- (seq-filter #'buffer-live-p cider-connections))))
-
-(defun cider-repl-buffers ()
- "Return the list of REPL buffers."
- (seq-filter
- (lambda (buffer)
- (with-current-buffer buffer (derived-mode-p 'cider-repl-mode)))
- (buffer-list)))
-
-(defun cider-make-connection-default (connection-buffer)
- "Make the nREPL CONNECTION-BUFFER the default connection.
-Moves CONNECTION-BUFFER to the front of variable `cider-connections'."
- (interactive (list (if (cider--in-connection-buffer-p)
- (current-buffer)
- (user-error "Not in a REPL buffer"))))
- ;; maintain the connection list in most recently used order
- (let ((buf (get-buffer connection-buffer)))
- (setq cider-connections
- (cons buf (delq buf cider-connections))))
- (cider--connections-refresh))
-
-(declare-function cider--close-buffer "cider-interaction")
-(defun cider--close-connection-buffer (conn-buffer)
- "Close CONN-BUFFER, removing it from variable `cider-connections'.
-Also close associated REPL and server buffers."
- (let ((buffer (get-buffer conn-buffer))
- (nrepl-messages-buffer (and nrepl-log-messages
- (nrepl-messages-buffer conn-buffer))))
- (setq cider-connections
- (delq buffer cider-connections))
- (when (buffer-live-p buffer)
- (with-current-buffer buffer
- (when spinner-current (spinner-stop))
- (when nrepl-tunnel-buffer
- (cider--close-buffer nrepl-tunnel-buffer)))
- ;; If this is the only (or last) REPL connected to its server, the
- ;; kill-process hook will kill the server.
- (cider--close-buffer buffer)
- (when nrepl-messages-buffer
- (kill-buffer nrepl-messages-buffer)))))
-
-
-;;; Current connection logic
-(defvar-local cider-repl-type nil
- "The type of this REPL buffer, usually either \"clj\" or \"cljs\".")
-
-(defun cider-find-connection-buffer-for-project-directory (&optional project-directory all-connections)
- "Return the most appropriate connection-buffer for the current project.
-
-By order of preference, this is any connection whose directory matches
-`clojure-project-dir', followed by any connection whose directory is nil,
-followed by any connection at all.
-
-If PROJECT-DIRECTORY is provided act on that project instead.
-
-Only return nil if variable `cider-connections' is empty,
-i.e there are no connections.
-
-If more than one connection satisfy a given level of preference, return the
-connection buffer closer to the start of variable `cider-connections'. This is
-usally the connection that was more recently created, but the order can be
-changed. For instance, the function `cider-make-connection-default' can be
-used to move a connection to the head of the list, so that it will take
-precedence over other connections associated with the same project.
-
-If ALL-CONNECTIONS is non-nil, the return value is a list and all matching
-connections are returned, instead of just the most recent."
- (when-let* ((project-directory (or project-directory
- (clojure-project-dir (cider-current-dir))))
- (fn (if all-connections #'seq-filter #'seq-find)))
- (or (funcall fn (lambda (conn)
- (when-let* ((conn-proj-dir (with-current-buffer conn
- nrepl-project-dir)))
- (equal (file-truename project-directory)
- (file-truename conn-proj-dir))))
- cider-connections)
- (funcall fn (lambda (conn)
- (with-current-buffer conn
- (not nrepl-project-dir)))
- cider-connections)
- (if all-connections
- cider-connections
- (car cider-connections)))))
-
-(defun cider-connection-type-for-buffer (&optional buffer)
- "Return the matching connection type (clj or cljs) for BUFFER.
-In cljc buffers return \"multi\". This function infers connection
-type based on the major mode. See `cider-project-connections-types' for a
-list of types of actual connections within a project. BUFFER defaults to
-the `current-buffer'."
- (with-current-buffer (or buffer (current-buffer))
- (cond
- ((derived-mode-p 'clojurescript-mode) "cljs")
- ((derived-mode-p 'clojurec-mode) "multi")
- ((derived-mode-p 'clojure-mode) "clj")
- (cider-repl-type))))
-
-(defun cider-project-connections-types ()
- "Return a list of types of connections within current project."
- (let ((connections (cider-find-connection-buffer-for-project-directory nil :all-connections)))
- (seq-uniq (seq-map #'cider--connection-type connections))))
-
-(defun cider-read-connection (prompt)
- "Completing read for connections using PROMPT."
- (get-buffer (completing-read prompt (mapcar #'buffer-name (cider-connections)))))
-
-(defun cider-assoc-project-with-connection (&optional project connection)
- "Associate a Clojure PROJECT with an nREPL CONNECTION.
-
-Useful for connections created using `cider-connect', as for them
-such a link cannot be established automatically."
- (interactive)
- (cider-ensure-connected)
- (let ((conn-buf (or connection (cider-read-connection "Connection: ")))
- (project-dir (or project (read-directory-name "Project directory: " (clojure-project-dir)))))
- (when conn-buf
- (with-current-buffer conn-buf
- (setq nrepl-project-dir project-dir)))))
-
-(defun cider-assoc-buffer-with-connection ()
- "Associate the current buffer with a connection.
-
-Useful for connections created using `cider-connect', as for them
-such a link cannot be established automatically."
- (interactive)
- (cider-ensure-connected)
- (let ((conn (cider-read-connection "Connection: ")))
- (when conn
- (setq-local cider-connections (list conn)))))
-
-(defun cider-toggle-buffer-connection (&optional restore-all)
- "Toggle the current buffer's connection between Clojure and ClojureScript.
-
-Default behavior of a cljc buffer is to send eval commands to both Clojure
-and ClojureScript. This function sets a local buffer variable to hide one
-or the other. Optional argument RESTORE-ALL undo any toggled behavior by
-using the default list of connections."
- (interactive "P")
- (cider-ensure-connected)
- (if restore-all
- (progn
- (kill-local-variable 'cider-connections)
- (let ((types (mapcar #'cider--connection-type (cider-connections))))
- (message (format "CIDER connections available: %s" types))))
- (let ((current-conn (cider-current-connection))
- (was-local (local-variable-p 'cider-connections))
- (original-connections (cider-connections)))
- ;; we set the local variable to eclipse all connections in favor of the
- ;; toggled connection. to recover the full list we must remove the
- ;; obfuscation
- (kill-local-variable 'cider-connections)
- (if-let* ((other-conn (cider-other-connection current-conn)))
- (progn
- (setq-local cider-connections (list other-conn))
- (message "Connection set to %s" (cider--connection-type other-conn)))
- (progn
- (when was-local
- (setq-local cider-connections original-connections))
- (user-error "No other connection available"))))))
-
-(defun cider-clear-buffer-local-connection ()
- "Remove association between the current buffer and a connection."
- (interactive)
- (cider-ensure-connected)
- (kill-local-variable 'cider-connections))
-
-(defun cider-toggle-request-dispatch ()
- "Toggle the value of `cider-request-dispatch' between static and dynamic.
-
-Handy when you're using dynamic dispatch, but you want to quickly force all
-evaluation commands to use a particular connection."
- (interactive)
- (let ((new-value (if (eq cider-request-dispatch 'static) 'dynamic 'static)))
- (setq cider-request-dispatch new-value)
- (message "Toggled CIDER request dispatch to %s." new-value)))
-
-(defun cider-current-connection (&optional type)
- "Return the REPL buffer relevant for the current Clojure source buffer.
-A REPL is relevant if its `nrepl-project-dir' is compatible with the
-current directory (see `cider-find-connection-buffer-for-project-directory').
-
-When there are multiple relevant connections of the same TYPE, return the
-most recently used one.
-
-If TYPE is provided, it is either \"clj\" or \"cljs\", and only a
-connection of that type is returned. If no connections of that TYPE exist,
-return nil.
-
-If TYPE is nil, then connections whose type matches the current file
-extension are given preference, but if none exist, any connection is
-returned. In this case, only return nil if there are no active connections
-at all."
- ;; If TYPE was specified, we only return that type (or nil). OW, we prefer
- ;; that TYPE, but ultimately allow any type.
- (cl-labels ((right-type-p (c type)
- (when (or (not type)
- (equal type "multi")
- (and (buffer-live-p c)
- (equal (cider--connection-type c) type)))
- c))
- (most-recent-buf (connections type)
- (when connections
- (seq-find (lambda (c)
- (and (member c connections)
- (right-type-p c type)))
- (buffer-list)))))
- (let ((connections (cider-connections)))
- (cond
- ((not connections) nil)
- ;; if you're in a REPL buffer, it's the connection buffer
- ((and (derived-mode-p 'cider-repl-mode) (right-type-p (current-buffer) type)))
- ((eq cider-request-dispatch 'static) (car connections))
- ((= 1 (length connections)) (right-type-p (car connections) type))
- (t (let ((project-connections (cider-find-connection-buffer-for-project-directory
- nil :all-connections))
- (guessed-type (or type (cider-connection-type-for-buffer))))
- (or
- ;; cljc
- (and (equal guessed-type "multi")
- (most-recent-buf project-connections nil))
- ;; clj or cljs
- (and guessed-type
- (or (most-recent-buf project-connections guessed-type)
- (most-recent-buf connections guessed-type)))
- ;; when type was not specified or guessed
- (most-recent-buf project-connections type)
- (most-recent-buf connections type))))))))
-
-(defun cider-other-connection (&optional connection)
- "Return the first connection of another type than CONNECTION.
-Only return connections in the same project or nil.
-CONNECTION defaults to `cider-current-connection'."
- (when-let* ((connection (or connection (cider-current-connection)))
- (connection-type (cider--connection-type connection)))
- (cider-current-connection (pcase connection-type
- (`"clj" "cljs")
- (_ "clj")))))
-
-(defvar cider--has-warned-about-bad-repl-type nil)
-
-(defun cider--guess-cljs-connection ()
- "Hacky way to find a ClojureScript REPL.
-DO NOT USE THIS FUNCTION.
-It was written only to be used in `cider-map-connections', as a workaround
-to a still-undetermined bug in the state-tracker backend."
- (when-let* ((project-connections (cider-find-connection-buffer-for-project-directory
- nil :all-connections))
- (cljs-conn
- ;; So we have multiple connections. Look for the connection type we
- ;; want, prioritizing the current project.
- (or (seq-find (lambda (c) (with-current-buffer c (equal cider-repl-type "cljs")))
- project-connections)
- (seq-find (lambda (c) (with-current-buffer c (equal cider-repl-type "cljs")))
- (cider-connections)))))
- (unless cider--has-warned-about-bad-repl-type
- (setq cider--has-warned-about-bad-repl-type t)
- (read-key
- (concat "The ClojureScript REPL seems to be misbehaving."
- (substitute-command-keys
- "\nWe have applied a workaround, but please also file a bug report with `\\[cider-report-bug]'.")
- "\nPress any key to continue.")))
- cljs-conn))
-
-(defun cider-map-connections (function which &optional any-mode)
- "Call FUNCTION once for each appropriate connection.
-The function is called with one argument, the connection buffer.
-The appropriate connections are found by inspecting the current buffer. If
-the buffer is associated with a .cljc file, BODY will be executed
-multiple times.
-
-WHICH is one of the following keywords identifying which connections to map
-over.
- :any - Act the connection whose type matches the current buffer.
- :clj - Like :any, but signal a `user-error' in `clojurescript-mode' or if
- there is no Clojure connection (use this for commands only
- supported in Clojure).
- :cljs - Like :clj, but demands a ClojureScript connection instead.
- :both - In `clojurec-mode' act on both connections, otherwise function
- like :any. Obviously, this option might run FUNCTION twice.
-
-If ANY-MODE is non-nil, :clj and :cljs don't signal errors due to being in
-the wrong major mode (they still signal if the desired connection type
-doesn't exist). Use this for commands that only apply to a specific
-connection but can be invoked from any buffer (like `cider-refresh')."
- (cl-labels ((err (msg) (user-error (concat "`%s' " msg) this-command)))
- ;; :both in a clj or cljs buffer just means :any.
- (let* ((which (if (and (eq which :both)
- (not (cider--cljc-buffer-p)))
- :any
- which))
- (curr
- (pcase which
- (`:any (let ((type (cider-connection-type-for-buffer)))
- (or (cider-current-connection type)
- (when (equal type "cljs")
- (cider--guess-cljs-connection))
- (err (substitute-command-keys
- (format "needs a Clojure%s REPL.\nIf you don't know what that means, you probably need to jack-in (%s)."
- (if (equal type "cljs") "Script" "")
- (if (equal type "cljs") "`\\[cider-jack-in-clojurescript]'" "`\\[cider-jack-in]'")))))))
- (`:both (or (cider-current-connection)
- (err "needs an active REPL connection")))
- (`:clj (cond ((and (not any-mode)
- (derived-mode-p 'clojurescript-mode))
- (err "doesn't support ClojureScript"))
- ((cider-current-connection "clj"))
- ((err "needs a Clojure REPL"))))
- (`:cljs (cond ((and (not any-mode)
- (eq major-mode 'clojure-mode))
- (err "doesn't support Clojure"))
- ((cider-current-connection "cljs"))
- ((err "needs a ClojureScript REPL")))))))
- (funcall function curr)
- (when (eq which :both)
- (when-let* ((other-connection (cider-other-connection curr)))
- (funcall function other-connection))))))
-
-
-;;; Connection Browser
-(defvar cider-connections-buffer-mode-map
- (let ((map (make-sparse-keymap)))
- (define-key map "d" #'cider-connections-make-default)
- (define-key map "g" #'cider-connection-browser)
- (define-key map "k" #'cider-connections-close-connection)
- (define-key map (kbd "RET") #'cider-connections-goto-connection)
- (define-key map "?" #'describe-mode)
- (define-key map "h" #'describe-mode)
- map))
-
-(declare-function cider-popup-buffer-mode "cider-popup")
-(define-derived-mode cider-connections-buffer-mode cider-popup-buffer-mode
- "CIDER Connections"
- "CIDER Connections Buffer Mode.
-\\{cider-connections-buffer-mode-map}
-\\{cider-popup-buffer-mode-map}"
- (when cider-special-mode-truncate-lines
- (setq-local truncate-lines t)))
-
-(defvar cider--connection-ewoc)
-(defconst cider--connection-browser-buffer-name "*cider-connections*")
-
-(defun cider-connection-browser ()
- "Open a browser buffer for nREPL connections."
- (interactive)
- (if-let* ((buffer (get-buffer cider--connection-browser-buffer-name)))
- (progn
- (cider--connections-refresh-buffer buffer)
- (unless (get-buffer-window buffer)
- (select-window (display-buffer buffer))))
- (cider--setup-connection-browser)))
-
-(defun cider--connections-refresh ()
- "Refresh the connections buffer, if the buffer exists.
-The connections buffer is determined by
-`cider--connection-browser-buffer-name'"
- (when-let* ((buffer (get-buffer cider--connection-browser-buffer-name)))
- (cider--connections-refresh-buffer buffer)))
-
-(add-hook 'nrepl-disconnected-hook #'cider--connections-refresh)
-
-(defun cider--connections-refresh-buffer (buffer)
- "Refresh the connections BUFFER."
- (cider--update-connections-display
- (buffer-local-value 'cider--connection-ewoc buffer)
- cider-connections))
-
-(defun cider--setup-connection-browser ()
- "Create a browser buffer for nREPL connections."
- (with-current-buffer (get-buffer-create cider--connection-browser-buffer-name)
- (let ((ewoc (ewoc-create
- 'cider--connection-pp
- " REPL Host Port Project Type\n")))
- (setq-local cider--connection-ewoc ewoc)
- (cider--update-connections-display ewoc cider-connections)
- (setq buffer-read-only t)
- (cider-connections-buffer-mode)
- (display-buffer (current-buffer)))))
-
-(defun cider-client-name-repl-type (type)
- "Return a human-readable name for a connection TYPE.
-TYPE can be any of the possible values of `cider-repl-type'."
- (pcase type
- ("clj" "Clojure")
- ("cljs" "ClojureScript")
- (_ "Unknown")))
-
-(defun cider-project-name (project-dir)
- "Extract the project name from PROJECT-DIR."
- (if (and project-dir (not (equal project-dir "")))
- (file-name-nondirectory (directory-file-name project-dir))
- "-"))
-
-(defun cider--connection-pp (connection)
- "Print an nREPL CONNECTION to the current buffer."
- (let* ((buffer-read-only nil)
- (buffer (get-buffer connection))
- (project-name (cider-project-name (buffer-local-value 'nrepl-project-dir buffer)))
- (repl-type (cider-client-name-repl-type (buffer-local-value 'cider-repl-type buffer)))
- (endpoint (buffer-local-value 'nrepl-endpoint buffer)))
- (insert
- (format "%s %-30s %-16s %5s %-16s %s"
- (if (equal connection (car cider-connections)) "*" " ")
- (buffer-name connection)
- (car endpoint)
- (prin1-to-string (cadr endpoint))
- project-name
- repl-type))))
-
-(defun cider--update-connections-display (ewoc connections)
- "Update the connections EWOC to show CONNECTIONS."
- (ewoc-filter ewoc (lambda (n) (member n connections)))
- (let ((existing))
- (ewoc-map (lambda (n) (setq existing (cons n existing))) ewoc)
- (let ((added (seq-difference connections existing)))
- (mapc (apply-partially 'ewoc-enter-last ewoc) added)
- (save-excursion (ewoc-refresh ewoc)))))
-
-(defun cider--ewoc-apply-at-point (f)
- "Apply function F to the ewoc node at point.
-F is a function of two arguments, the ewoc and the data at point."
- (let* ((ewoc cider--connection-ewoc)
- (node (and ewoc (ewoc-locate ewoc))))
- (when node
- (funcall f ewoc (ewoc-data node)))))
-
-(defun cider-connections-make-default ()
- "Make default the connection at point in the connection browser."
- (interactive)
- (save-excursion
- (cider--ewoc-apply-at-point #'cider--connections-make-default)))
-
-(defun cider--connections-make-default (ewoc data)
- "Make the connection in EWOC specified by DATA default.
-Refreshes EWOC."
- (interactive)
- (cider-make-connection-default data)
- (ewoc-refresh ewoc))
-
-(defun cider-connections-close-connection ()
- "Close connection at point in the connection browser."
- (interactive)
- (cider--ewoc-apply-at-point #'cider--connections-close-connection))
-
-(defun cider--connections-close-connection (ewoc data)
- "Close the connection in EWOC specified by DATA."
- (cider--close-connection-buffer (get-buffer data))
- (cider--update-connections-display ewoc cider-connections))
-
-(defun cider-connections-goto-connection ()
- "Goto connection at point in the connection browser."
- (interactive)
- (cider--ewoc-apply-at-point #'cider--connections-goto-connection))
-
-(defun cider--connections-goto-connection (_ewoc data)
- "Goto the REPL for the connection in _EWOC specified by DATA."
- (when (buffer-live-p data)
- (select-window (display-buffer data))))
-
-
-(defun cider-display-connected-message ()
- "Message displayed on successful connection."
- (message
- (concat "Connected."
- (if cider-connection-message-fn
- (format " %s" (funcall cider-connection-message-fn))
- ""))))
-
-;; TODO: Replace direct usage of such hooks with CIDER hooks,
-;; that are connection type independent
-(add-hook 'nrepl-connected-hook 'cider-display-connected-message)
-
;;; Eval spinner
(defcustom cider-eval-spinner-type 'progress-bar
@@ -625,34 +96,27 @@ EVAL-BUFFER is the buffer where the spinner was started."
(defvar-local cider-buffer-ns nil
"Current Clojure namespace of some buffer.
-
-Useful for special buffers (e.g. REPL, doc buffers) that have to
-keep track of a namespace.
-
-This should never be set in Clojure buffers, as there the namespace
-should be extracted from the buffer's ns form.")
+Useful for special buffers (e.g. REPL, doc buffers) that have to keep track
+of a namespace. This should never be set in Clojure buffers, as there the
+namespace should be extracted from the buffer's ns form.")
(defun cider-current-ns (&optional no-default)
"Return the current ns.
The ns is extracted from the ns form for Clojure buffers and from
`cider-buffer-ns' for all other buffers. If it's missing, use the current
-REPL's ns, otherwise fall back to \"user\".
-
-When NO-DEFAULT is non-nil, it will return nil instead of \"user\"."
+REPL's ns, otherwise fall back to \"user\". When NO-DEFAULT is non-nil, it
+will return nil instead of \"user\"."
(or cider-buffer-ns
(clojure-find-ns)
- (when-let* ((repl-buf (cider-current-connection)))
- (buffer-local-value 'cider-buffer-ns repl-buf))
+ (when-let* ((repl (cider-current-repl)))
+ (buffer-local-value 'cider-buffer-ns repl))
(if no-default nil "user")))
(defun cider-expected-ns (&optional path)
"Return the namespace string matching PATH, or nil if not found.
-
-PATH is expected to be an absolute file path.
-If PATH is nil, use the path to the file backing the current buffer.
-
-The command falls back to `clojure-expected-ns' in the absence of an
-active nREPL connection."
+PATH is expected to be an absolute file path. If PATH is nil, use the path
+to the file backing the current buffer. The command falls back to
+`clojure-expected-ns' in the absence of an active nREPL connection."
(if (cider-connected-p)
(let* ((path (or path (file-truename (buffer-file-name))))
(relpath (thread-last (cider-sync-request:classpath)
@@ -672,9 +136,9 @@ active nREPL connection."
(clojure-expected-ns path)))
(clojure-expected-ns path)))
-(defun cider-nrepl-op-supported-p (op)
- "Check whether the current connection supports the nREPL middleware OP."
- (nrepl-op-supported-p op (cider-current-connection)))
+(defun cider-nrepl-op-supported-p (op &optional connection)
+ "Check whether the CONNECTION supports the nREPL middleware OP."
+ (nrepl-op-supported-p op (or connection (cider-current-repl))))
(defvar cider-version)
(defun cider-ensure-op-supported (op)
@@ -686,12 +150,10 @@ Signal an error if it is not supported."
(defun cider-nrepl-send-request (request callback &optional connection)
"Send REQUEST and register response handler CALLBACK.
REQUEST is a pair list of the form (\"op\" \"operation\" \"par1-name\"
-\"par1\" ... ).
+ \"par1\" ... ).
If CONNECTION is provided dispatch to that connection instead of
-the current connection.
-
-Return the id of the sent message."
- (nrepl-send-request request callback (or connection (cider-current-connection))))
+the current connection. Return the id of the sent message."
+ (nrepl-send-request request callback (or connection (cider-current-repl))))
(defun cider-nrepl-send-sync-request (request &optional connection abort-on-input)
"Send REQUEST to the nREPL server synchronously using CONNECTION.
@@ -701,14 +163,13 @@ If ABORT-ON-INPUT is non-nil, the function will return nil
at the first sign of user input, so as not to hang the
interface."
(nrepl-send-sync-request request
- (or connection (cider-current-connection))
+ (or connection (cider-current-repl))
abort-on-input))
-(defun cider-nrepl-send-unhandled-request (request)
- "Send REQUEST to the nREPL server and ignore any responses.
-Immediately mark the REQUEST as done.
-Return the id of the sent message."
- (let* ((conn (cider-current-connection))
+(defun cider-nrepl-send-unhandled-request (request &optional connection)
+ "Send REQUEST to the nREPL CONNECTION and ignore any responses.
+Immediately mark the REQUEST as done. Return the id of the sent message."
+ (let* ((conn (or connection (cider-current-repl)))
(id (nrepl-send-request request #'ignore conn)))
(with-current-buffer conn
(nrepl--mark-id-completed id))
@@ -719,8 +180,8 @@ Return the id of the sent message."
If NS is non-nil, include it in the request. LINE and COLUMN, if non-nil,
define the position of INPUT in its buffer. ADDITIONAL-PARAMS is a plist
to be appended to the request message. CONNECTION is the connection
-buffer, defaults to (cider-current-connection)."
- (let ((connection (or connection (cider-current-connection))))
+buffer, defaults to (cider-current-repl)."
+ (let ((connection (or connection (cider-current-repl))))
(nrepl-request:eval input
(if cider-show-eval-spinner
(cider-eval-spinner-handler connection callback)
@@ -732,22 +193,20 @@ buffer, defaults to (cider-current-connection)."
(defun cider-nrepl-sync-request:eval (input &optional connection ns)
"Send the INPUT to the nREPL CONNECTION synchronously.
If NS is non-nil, include it in the eval request."
- (nrepl-sync-request:eval input
- (or connection (cider-current-connection))
- ns))
+ (nrepl-sync-request:eval input (or connection (cider-current-repl)) ns))
(defcustom cider-pprint-fn 'pprint
"Sets the function to use when pretty-printing evaluation results.
The value must be one of the following symbols:
- `pprint' - to use \\=`clojure.pprint/pprint\\=`
+`pprint' - to use \\=`clojure.pprint/pprint\\=`
- `fipp' - to use the Fast Idiomatic Pretty Printer, approximately 5-10x
- faster than \\=`clojure.core/pprint\\=` (this is the default)
+`fipp' - to use the Fast Idiomatic Pretty Printer, approximately 5-10x
+faster than \\=`clojure.core/pprint\\=` (this is the default)
- `puget' - to use Puget, which provides canonical serialization of data on
- top of fipp, but at a slight performance cost
+`puget' - to use Puget, which provides canonical serialization of data on
+top of fipp, but at a slight performance cost
Alternatively, can be the namespace-qualified name of a Clojure function of
one argument. If the function cannot be resolved, an exception will be
@@ -784,70 +243,82 @@ result, and is included in the request if non-nil."
"Plist to be appended to an eval request to make it use content-types."
'("content-type" "true"))
-(defun cider-tooling-eval (input callback &optional ns)
- "Send the request INPUT and register the CALLBACK as the response handler.
-NS specifies the namespace in which to evaluate the request.
-
-Requests evaluated in the tooling nREPL session don't affect the
-thread-local bindings of the primary eval nREPL session (e.g. this is not
-going to clobber *1/2/3)."
+(defun cider-tooling-eval (input callback &optional ns connection)
+ "Send the request INPUT to CONNECTION and register the CALLBACK.
+NS specifies the namespace in which to evaluate the request. Requests
+evaluated in the tooling nREPL session don't affect the thread-local
+bindings of the primary eval nREPL session (e.g. this is not going to
+clobber *1/2/3)."
;; namespace forms are always evaluated in the "user" namespace
(nrepl-request:eval input
callback
- (cider-current-connection)
- ns nil nil nil t ; tooling
- ))
-
-(defun cider-sync-tooling-eval (input &optional ns)
- "Send the request INPUT and evaluate in synchronously.
-NS specifies the namespace in which to evaluate the request.
-
-Requests evaluated in the tooling nREPL session don't affect the
-thread-local bindings of the primary eval nREPL session (e.g. this is not
-going to clobber *1/2/3)."
+ (or connection (cider-current-repl))
+ ns nil nil nil 'tooling))
+
+(defun cider-sync-tooling-eval (input &optional ns connection)
+ "Send the request INPUT to CONNECTION and evaluate in synchronously.
+NS specifies the namespace in which to evaluate the request. Requests
+evaluated in the tooling nREPL session don't affect the thread-local
+bindings of the primary eval nREPL session (e.g. this is not going to
+clobber *1/2/3)."
;; namespace forms are always evaluated in the "user" namespace
(nrepl-sync-request:eval input
- (cider-current-connection)
+ (or connection (cider-current-repl))
ns
- t ; tooling
- ))
+ 'tooling))
+
+;; TODO: Add some unit tests and pretty those two functions up.
+;; FIXME: Currently that's broken for group-id with multiple segments (e.g. org.clojure/clojure)
+(defun cider-classpath-libs ()
+ "Return a list of all libs on the classpath."
+ (let ((libs (seq-filter (lambda (cp-entry)
+ (string-suffix-p ".jar" cp-entry))
+ (cider-sync-request:classpath)))
+ (dir-sep (if (string-equal system-type "windows-nt") "\\\\" "/")))
+ (thread-last libs
+ (seq-map (lambda (s) (split-string s dir-sep)))
+ (seq-map #'reverse)
+ (seq-map (lambda (l) (reverse (seq-take l 4)))))))
(defun cider-library-present-p (lib)
- "Check whether LIB is present on the classpath."
- (seq-find (lambda (s) (string-match-p (concat lib ".*\\.jar") s)) (cider-sync-request:classpath)))
+ "Check whether LIB is present on the classpath.
+The library is a string of the format \"group-id/artifact-id\"."
+ (let* ((lib (split-string lib "/"))
+ (group-id (car lib))
+ (artifact-id (cadr lib)))
+ (seq-find (lambda (lib)
+ (let ((g (car lib))
+ (a (cadr lib)))
+ (and (equal group-id g) (equal artifact-id a))))
+ (cider-classpath-libs))))
+
+
+;;; Interrupt evaluation
-(defalias 'cider-current-repl-buffer #'cider-current-connection
- "The current REPL buffer.
-Return the REPL buffer given by `cider-current-connection'.")
+(defun cider-interrupt-handler (buffer)
+ "Create an interrupt response handler for BUFFER."
+ (nrepl-make-response-handler buffer nil nil nil nil))
-(declare-function cider-interrupt-handler "cider-interaction")
(defun cider-interrupt ()
"Interrupt any pending evaluations."
(interactive)
- (with-current-buffer (cider-current-connection)
+ ;; FIXME: does this work correctly in cljc files?
+ (with-current-buffer (cider-current-repl)
(let ((pending-request-ids (cider-util--hash-keys nrepl-pending-requests)))
(dolist (request-id pending-request-ids)
(nrepl-request:interrupt
request-id
(cider-interrupt-handler (current-buffer))
- (cider-current-connection))))))
+ (cider-current-repl))))))
-(defun cider-current-session ()
+(defun cider-nrepl-eval-session ()
"Return the eval nREPL session id of the current connection."
- (cider-session-for-connection (cider-current-connection)))
-
-(defun cider-session-for-connection (connection)
- "Create a CIDER session for CONNECTION."
- (with-current-buffer connection
+ (with-current-buffer (cider-current-repl)
nrepl-session))
-(defun cider-current-messages-buffer ()
- "The nREPL messages buffer, matching the current connection."
- (nrepl-messages-buffer (cider-current-connection)))
-
-(defun cider-current-tooling-session ()
+(defun cider-nrepl-tooling-session ()
"Return the tooling nREPL session id of the current connection."
- (with-current-buffer (cider-current-connection)
+ (with-current-buffer (cider-current-repl)
nrepl-tooling-session))
(defun cider--var-choice (var-info)
@@ -876,53 +347,15 @@ unless ALL is truthy."
(when (and class member)
(cider-sync-request:info nil class member)))
-(defun cider--find-var-other-window (var &optional line)
- "Find the definition of VAR, optionally at a specific LINE.
-
-Display the results in a different window."
- (if-let* ((info (cider-var-info var)))
- (progn
- (if line (setq info (nrepl-dict-put info "line" line)))
- (cider--jump-to-loc-from-info info t))
- (user-error "Symbol `%s' not resolved" var)))
-
-(defun cider--find-var (var &optional line)
- "Find the definition of VAR, optionally at a specific LINE."
- (if-let* ((info (cider-var-info var)))
- (progn
- (if line (setq info (nrepl-dict-put info "line" line)))
- (cider--jump-to-loc-from-info info))
- (user-error "Symbol `%s' not resolved" var)))
-
-(defun cider-find-var (&optional arg var line)
- "Find definition for VAR at LINE.
-
-Prompt according to prefix ARG and `cider-prompt-for-symbol'.
-A single or double prefix argument inverts the meaning of
-`cider-prompt-for-symbol'. A prefix of `-` or a double prefix argument causes
-the results to be displayed in a different window. The default value is
-thing at point."
- (interactive "P")
- (cider-ensure-op-supported "info")
- (if var
- (cider--find-var var line)
- (funcall (cider-prompt-for-symbol-function arg)
- "Symbol"
- (if (cider--open-other-window-p arg)
- #'cider--find-var-other-window
- #'cider--find-var))))
-
;;; Requests
-(declare-function cider-load-file-handler "cider-interaction")
+(declare-function cider-load-file-handler "cider-eval")
(defun cider-request:load-file (file-contents file-path file-name &optional connection callback)
"Perform the nREPL \"load-file\" op.
FILE-CONTENTS, FILE-PATH and FILE-NAME are details of the file to be
-loaded.
-
-If CONNECTION is nil, use `cider-current-connection'.
-If CALLBACK is nil, use `cider-load-file-handler'."
+loaded. If CONNECTION is nil, use `cider-current-repl'. If CALLBACK
+is nil, use `cider-load-file-handler'."
(cider-nrepl-send-request `("op" "load-file"
"file" ,file-contents
"file-path" ,file-path
@@ -935,14 +368,14 @@ If CALLBACK is nil, use `cider-load-file-handler'."
;;; Sync Requests
(defcustom cider-filtered-namespaces-regexps
- '("^cider.nrepl" "^refactor-nrepl" "^clojure.tools.nrepl")
+ '("^cider.nrepl" "^refactor-nrepl" "^clojure.tools.nrepl" "^nrepl")
"List of regexps used to filter out some vars/symbols/namespaces.
When nil, nothing is filtered out. Otherwise, all namespaces matching any
-regexp from this list are dropped out of the \"ns-list\" op.
-Also, \"apropos\" won't include vars from such namespaces.
-This list is passed on to the nREPL middleware without any pre-processing.
-So the regexps have to be in Clojure format (with twice the number of
-backslashes) and not Emacs Lisp."
+regexp from this list are dropped out of the \"ns-list\" op. Also,
+\"apropos\" won't include vars from such namespaces. This list is passed
+on to the nREPL middleware without any pre-processing. So the regexps have
+to be in Clojure format (with twice the number of backslashes) and not
+Emacs Lisp."
:type '(repeat string)
:safe #'listp
:group 'cider
@@ -986,7 +419,7 @@ CONTEXT represents a completion context for compliment."
(defun cider-sync-request:complete-flush-caches ()
"Send \"complete-flush-caches\" op to flush Compliment's caches."
(cider-nrepl-send-sync-request (list "op" "complete-flush-caches"
- "session" (cider-current-session))
+ "session" (cider-nrepl-eval-session))
'abort-on-input))
(defun cider-sync-request:info (symbol &optional class member)
@@ -1025,7 +458,6 @@ CONTEXT represents a completion context for compliment."
(defun cider-sync-request:spec-list (&optional filter-regex)
"Get a list of the available specs in the registry.
-
Optional argument FILTER-REGEX filters specs. By default, all specs are
returned."
(setq filter-regex (or filter-regex ""))
@@ -1091,7 +523,6 @@ returned."
(defun cider-sync-request:resources-list ()
"Return a list of all resources on the classpath.
-
The result entries are relative to the classpath."
(when-let* ((resources (thread-first '("op" "resources-list")
(cider-nrepl-send-sync-request)
@@ -1118,135 +549,28 @@ The result entries are relative to the classpath."
(error (car (split-string err "\n"))))
(nrepl-dict-get response "formatted-edn")))
-
-;;; Connection info
-(defun cider--java-version ()
- "Retrieve the underlying connection's Java version."
- (with-current-buffer (cider-current-connection "clj")
- (when nrepl-versions
- (thread-first nrepl-versions
- (nrepl-dict-get "java")
- (nrepl-dict-get "version-string")))))
-
-(defun cider--clojure-version ()
- "Retrieve the underlying connection's Clojure version."
- (with-current-buffer (cider-current-connection "clj")
- (when nrepl-versions
- (thread-first nrepl-versions
- (nrepl-dict-get "clojure")
- (nrepl-dict-get "version-string")))))
-
-(defun cider--nrepl-version ()
- "Retrieve the underlying connection's nREPL version."
- (with-current-buffer (cider-current-connection "clj")
- (when nrepl-versions
- (thread-first nrepl-versions
- (nrepl-dict-get "nrepl")
- (nrepl-dict-get "version-string")))))
-
-(defun cider--connection-info (connection-buffer)
- "Return info about CONNECTION-BUFFER.
-
-Info contains project name, current REPL namespace, host:port
-endpoint and Clojure version."
- (with-current-buffer connection-buffer
- (format "%s%s@%s:%s (Java %s, Clojure %s, nREPL %s)"
- (upcase (concat cider-repl-type " "))
- (or (cider--project-name nrepl-project-dir) "<no project>")
- (car nrepl-endpoint)
- (cadr nrepl-endpoint)
- (cider--java-version)
- (cider--clojure-version)
- (cider--nrepl-version))))
-
-(defun cider--connection-properties (conn-buffer)
- "Extract the essential properties of CONN-BUFFER."
- (with-current-buffer conn-buffer
- (list
- :type cider-repl-type
- :host (car nrepl-endpoint)
- :port (cadr nrepl-endpoint)
- :project-dir nrepl-project-dir)))
-
-(defun cider--connection-type (conn-buffer)
- "Get CONN-BUFFER's type.
-
-Return value matches `cider-repl-type'."
- (plist-get (cider--connection-properties conn-buffer) :type))
-
-(defun cider--connection-host (conn-buffer)
- "Get CONN-BUFFER's host."
- (plist-get (cider--connection-properties conn-buffer) :host))
-
-(defun cider--connection-port (conn-buffer)
- "Get CONN-BUFFER's port."
- (plist-get (cider--connection-properties conn-buffer) :port))
-
-(defun cider--connection-project-dir (conn-buffer)
- "Get CONN-BUFFER's project dir."
- (plist-get (cider--connection-properties conn-buffer) :project-dir))
-
-(defun cider-display-connection-info (&optional show-default)
- "Display information about the current connection.
-
-With a prefix argument SHOW-DEFAULT it will display info about the
-default connection."
- (interactive "P")
- (message "%s" (cider--connection-info (if show-default
- (cider-default-connection)
- (cider-current-connection)))))
-
-(defun cider-rotate-default-connection ()
- "Rotate and display the default nREPL connection."
- (interactive)
- (cider-ensure-connected)
- (if (= (length (cider-connections)) 1)
- (user-error "There's just a single active nREPL connection")
- (setq cider-connections
- (append (cdr cider-connections)
- (list (car cider-connections))))
- (message "Default nREPL connection: %s"
- (cider--connection-info (car cider-connections)))))
-
-
-(declare-function cider-connect "cider")
-(defun cider-replicate-connection (&optional conn)
- "Establish a new connection based on an existing connection.
-The new connection will use the same host and port.
-If CONN is not provided the user will be prompted to select a connection."
- (interactive)
- (let* ((conn (or conn (cider-read-connection "Select connection to replicate: ")))
- (host (cider--connection-host conn))
- (port (cider--connection-port conn))
- (project-dir (cider--connection-project-dir conn)))
- (cider-connect host port project-dir)))
-
-(defun cider-extract-designation-from-current-repl-buffer ()
- "Extract the designation from the cider repl buffer name."
- (let ((repl-buffer-name (buffer-name (cider-current-repl-buffer)))
- (template (split-string nrepl-repl-buffer-name-template "%s")))
- (string-match (format "^%s\\(.*\\)%s"
- (regexp-quote (concat (car template) nrepl-buffer-name-separator))
- (regexp-quote (cadr template)))
- repl-buffer-name)
- (or (match-string 1 repl-buffer-name) "<no designation>")))
-
-(defun cider-change-buffers-designation (designation)
- "Change the DESIGNATION in cider buffer names.
-Buffer names changed are cider-repl and nrepl-server."
- (interactive (list (read-string (format "Change CIDER buffer designation from '%s': "
- (cider-extract-designation-from-current-repl-buffer)))))
- (cider-ensure-connected)
- (let ((new-repl-buffer-name (nrepl-format-buffer-name-template
- nrepl-repl-buffer-name-template designation)))
- (with-current-buffer (cider-current-repl-buffer)
- (rename-buffer new-repl-buffer-name)
- (when nrepl-server-buffer
- (let ((new-server-buffer-name (nrepl-format-buffer-name-template
- nrepl-server-buffer-name-template designation)))
- (with-current-buffer nrepl-server-buffer
- (rename-buffer new-server-buffer-name)))))
- (message "CIDER buffer designation changed to: %s" designation)))
+;;; Dealing with input
+;; TODO: Replace this with some nil handler.
+(defun cider-stdin-handler (&optional _buffer)
+ "Make a stdin response handler for _BUFFER."
+ (nrepl-make-response-handler (current-buffer)
+ (lambda (_buffer _value))
+ (lambda (_buffer _out))
+ (lambda (_buffer _err))
+ nil))
+
+(defun cider-need-input (buffer)
+ "Handle an need-input request from BUFFER."
+ (with-current-buffer buffer
+ (let ((map (make-sparse-keymap)))
+ (set-keymap-parent map minibuffer-local-map)
+ (define-key map (kbd "C-c C-c") 'abort-recursive-edit)
+ (let ((stdin (condition-case nil
+ (concat (read-from-minibuffer "Stdin: " nil map) "\n")
+ (quit nil))))
+ (nrepl-request:stdin stdin
+ (cider-stdin-handler buffer)
+ (cider-current-repl))))))
(provide 'cider-client)
diff --git a/cider-common.el b/cider-common.el
index 657bd822..3bae267a 100644
--- a/cider-common.el
+++ b/cider-common.el
@@ -28,6 +28,7 @@
(require 'cider-compat)
(require 'nrepl-dict)
(require 'cider-util)
+(require 'etags) ; for find-tags-marker-ring
(require 'tramp)
(defcustom cider-prompt-for-symbol t
@@ -70,7 +71,45 @@ INVERT is used to invert the semantics of the function `cider--should-prompt-for
(when kw
(replace-regexp-in-string "\\`:+" "" kw)))
-(declare-function cider-read-from-minibuffer "cider-interaction")
+;;; Minibuffer
+(defvar cider-minibuffer-history '()
+ "History list of expressions read from the minibuffer.")
+
+(defvar cider-minibuffer-map
+ (let ((map (make-sparse-keymap)))
+ (set-keymap-parent map minibuffer-local-map)
+ (define-key map (kbd "TAB") #'complete-symbol)
+ (define-key map (kbd "M-TAB") #'complete-symbol)
+ map)
+ "Minibuffer keymap used for reading Clojure expressions.")
+
+(declare-function cider-complete-at-point "cider-completion")
+(declare-function cider-eldoc "cider-eldoc")
+(defun cider-read-from-minibuffer (prompt &optional value)
+ "Read a string from the minibuffer, prompting with PROMPT.
+If VALUE is non-nil, it is inserted into the minibuffer as initial-input.
+PROMPT need not end with \": \". If it doesn't, VALUE is displayed on the
+prompt as a default value (used if the user doesn't type anything) and is
+not used as initial input (input is left empty)."
+ (minibuffer-with-setup-hook
+ (lambda ()
+ (set-syntax-table clojure-mode-syntax-table)
+ (add-hook 'completion-at-point-functions
+ #'cider-complete-at-point nil t)
+ (setq-local eldoc-documentation-function #'cider-eldoc)
+ (run-hooks 'eval-expression-minibuffer-setup-hook))
+ (let* ((has-colon (string-match ": \\'" prompt))
+ (input (read-from-minibuffer (cond
+ (has-colon prompt)
+ (value (format "%s (default %s): " prompt value))
+ (t (format "%s: " prompt)))
+ (when has-colon value) ; initial-input
+ cider-minibuffer-map nil
+ 'cider-minibuffer-history
+ (unless has-colon value)))) ; default-value
+ (if (and (equal input "") value (not has-colon))
+ value
+ input))))
(defun cider-read-symbol-name (prompt callback)
"Read a symbol name using PROMPT with a default of the one at point.
@@ -86,7 +125,53 @@ On failure, read a symbol name using PROMPT and call CALLBACK with that."
(condition-case nil (funcall callback (cider--kw-to-symbol (cider-symbol-at-point 'look-back)))
('error (funcall callback (cider-read-from-minibuffer prompt)))))
-(declare-function cider-jump-to "cider-interaction")
+(declare-function cider-mode "cider-mode")
+
+(defun cider-jump-to (buffer &optional pos other-window)
+ "Push current point onto marker ring, and jump to BUFFER and POS.
+POS can be either a number, a cons, or a symbol.
+If a number, it is the character position (the point).
+If a cons, it specifies the position as (LINE . COLUMN). COLUMN can be nil.
+If a symbol, `cider-jump-to' searches for something that looks like the
+symbol's definition in the file.
+If OTHER-WINDOW is non-nil don't reuse current window."
+ (with-no-warnings
+ (ring-insert find-tag-marker-ring (point-marker)))
+ (if other-window
+ (pop-to-buffer buffer)
+ ;; like switch-to-buffer, but reuse existing window if BUFFER is visible
+ (pop-to-buffer buffer '((display-buffer-reuse-window display-buffer-same-window))))
+ (with-current-buffer buffer
+ (widen)
+ (goto-char (point-min))
+ (cider-mode +1)
+ (cond
+ ;; Line-column specification.
+ ((consp pos)
+ (forward-line (1- (or (car pos) 1)))
+ (if (cdr pos)
+ (move-to-column (cdr pos))
+ (back-to-indentation)))
+ ;; Point specification.
+ ((numberp pos)
+ (goto-char pos))
+ ;; Symbol or string.
+ (pos
+ ;; Try to find (def full-name ...).
+ (if (or (save-excursion
+ (search-forward-regexp (format "(def.*\\s-\\(%s\\)" (regexp-quote pos))
+ nil 'noerror))
+ (let ((name (replace-regexp-in-string ".*/" "" pos)))
+ ;; Try to find (def name ...).
+ (or (save-excursion
+ (search-forward-regexp (format "(def.*\\s-\\(%s\\)" (regexp-quote name))
+ nil 'noerror))
+ ;; Last resort, just find the first occurrence of `name'.
+ (save-excursion
+ (search-forward name nil 'noerror)))))
+ (goto-char (match-beginning 0))
+ (message "Can't find %s in %s" pos (buffer-file-name))))
+ (t nil))))
(defun cider--find-buffer-for-file (file)
"Return a buffer visiting FILE.
diff --git a/cider-compat.el b/cider-compat.el
index ec928dfd..e6b64b28 100644
--- a/cider-compat.el
+++ b/cider-compat.el
@@ -21,110 +21,13 @@
;;; Commentary:
-;; Pretty much everything here's copied from subr-x for compatibility with
-;; Emacs 24.4.
+;; Everything here was copied from subr-x for compatibility with
+;; Emacs 25.1.
;;; Code:
(eval-and-compile
- (unless (fboundp 'internal--thread-argument)
- (defmacro internal--thread-argument (first? &rest forms)
- "Internal implementation for `thread-first' and `thread-last'.
-When Argument FIRST? is non-nil argument is threaded first, else
-last. FORMS are the expressions to be threaded."
- (pcase forms
- (`(,x (,f . ,args) . ,rest)
- `(internal--thread-argument
- ,first? ,(if first? `(,f ,x ,@args) `(,f ,@args ,x)) ,@rest))
- (`(,x ,f . ,rest) `(internal--thread-argument ,first? (,f ,x) ,@rest))
- (_ (car forms)))))
-
- (unless (fboundp 'thread-first)
- (defmacro thread-first (&rest forms)
- "Thread FORMS elements as the first argument of their successor.
-Example:
- (thread-first
- 5
- (+ 20)
- (/ 25)
- -
- (+ 40))
-Is equivalent to:
- (+ (- (/ (+ 5 20) 25)) 40)
-Note how the single `-' got converted into a list before
-threading."
- (declare (indent 1)
- (debug (form &rest [&or symbolp (sexp &rest form)])))
- `(internal--thread-argument t ,@forms)))
-
- (unless (fboundp 'thread-last)
- (defmacro thread-last (&rest forms)
- "Thread FORMS elements as the last argument of their successor.
-Example:
- (thread-last
- 5
- (+ 20)
- (/ 25)
- -
- (+ 40))
-Is equivalent to:
- (+ 40 (- (/ 25 (+ 20 5))))
-Note how the single `-' got converted into a list before
-threading."
- (declare (indent 1) (debug thread-first))
- `(internal--thread-argument nil ,@forms))))
-
-
-(eval-and-compile
-
- (unless (fboundp 'internal--listify)
-
- (defsubst internal--listify (elt)
- "Wrap ELT in a list if it is not one."
- (if (not (listp elt))
- (list elt)
- elt)))
-
- (unless (fboundp 'internal--check-binding)
-
- (defsubst internal--check-binding (binding)
- "Check BINDING is properly formed."
- (when (> (length binding) 2)
- (signal
- 'error
- (cons "`let' bindings can have only one value-form" binding)))
- binding))
-
- (unless (fboundp 'internal--build-binding-value-form)
-
- (defsubst internal--build-binding-value-form (binding prev-var)
- "Build the conditional value form for BINDING using PREV-VAR."
- `(,(car binding) (and ,prev-var ,(cadr binding)))))
-
- (unless (fboundp 'internal--build-binding)
-
- (defun internal--build-binding (binding prev-var)
- "Check and build a single BINDING with PREV-VAR."
- (thread-first
- binding
- internal--listify
- internal--check-binding
- (internal--build-binding-value-form prev-var))))
-
- (unless (fboundp 'internal--build-bindings)
-
- (defun internal--build-bindings (bindings)
- "Check and build conditional value forms for BINDINGS."
- (let ((prev-var t))
- (mapcar (lambda (binding)
- (let ((binding (internal--build-binding binding prev-var)))
- (setq prev-var (car binding))
- binding))
- bindings)))))
-
-(eval-and-compile
-
(unless (fboundp 'if-let*)
(defmacro if-let* (bindings then &rest else)
"Process BINDINGS and if all values are non-nil eval THEN, else ELSE.
@@ -147,39 +50,5 @@ evalled to set symbol's value."
(declare (indent 1) (debug if-let*))
`(if-let* ,bindings ,(macroexp-progn body)))))
-(eval-and-compile
-
- (with-no-warnings
- (unless (fboundp 'directory-files-recursively)
- (defun directory-files-recursively (dir regexp &optional include-directories)
- "Return list of all files under DIR that have file names matching REGEXP.
-This function works recursively. Files are returned in \"depth first\"
-order, and files from each directory are sorted in alphabetical order.
-Each file name appears in the returned list in its absolute form.
-Optional argument INCLUDE-DIRECTORIES non-nil means also include in the
-output directories whose names match REGEXP."
- (let ((result nil)
- (files nil)
- ;; When DIR is "/", remote file names like "/method:" could
- ;; also be offered. We shall suppress them.
- (tramp-mode (and tramp-mode (file-remote-p (expand-file-name dir)))))
- (dolist (file (sort (file-name-all-completions "" dir)
- 'string<))
- (unless (member file '("./" "../"))
- (if (directory-name-p file)
- (let* ((leaf (substring file 0 (1- (length file))))
- (full-file (expand-file-name leaf dir)))
- ;; Don't follow symlinks to other directories.
- (unless (file-symlink-p full-file)
- (setq result
- (nconc result (directory-files-recursively
- full-file regexp include-directories))))
- (when (and include-directories
- (string-match regexp leaf))
- (setq result (nconc result (list full-file)))))
- (when (string-match regexp file)
- (push (expand-file-name file dir) files)))))
- (nconc result (nreverse files)))))))
-
(provide 'cider-compat)
;;; cider-compat.el ends here
diff --git a/cider-completion.el b/cider-completion.el
new file mode 100644
index 00000000..c52769ee
--- /dev/null
+++ b/cider-completion.el
@@ -0,0 +1,253 @@
+;;; cider-completion.el --- Smart REPL-powered code completion -*- lexical-binding: t -*-
+
+;; Copyright © 2013-2018 Bozhidar Batsov, Artur Malabarba and CIDER contributors
+;;
+;; Author: Bozhidar Batsov <bozhidar@batsov.com>
+;; Artur Malabarba <bruce.connor.am@gmail.com>
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;; This file is not part of GNU Emacs.
+
+;;; Commentary:
+
+;; Smart REPL-powered code completion and integration with company-mode.
+
+;;; Code:
+
+(require 'subr-x)
+(require 'thingatpt)
+
+(require 'cider-client)
+(require 'cider-common)
+(require 'cider-eldoc)
+(require 'nrepl-dict)
+
+(defcustom cider-completion-use-context t
+ "When true, uses context at point to improve completion suggestions."
+ :type 'boolean
+ :group 'cider
+ :package-version '(cider . "0.7.0"))
+
+(defcustom cider-annotate-completion-candidates t
+ "When true, annotate completion candidates with some extra information."
+ :type 'boolean
+ :group 'cider
+ :package-version '(cider . "0.8.0"))
+
+(defcustom cider-annotate-completion-function
+ #'cider-default-annotate-completion-function
+ "Controls how the annotations for completion candidates are formatted.
+Must be a function that takes two arguments: the abbreviation of the
+candidate type according to `cider-completion-annotations-alist' and the
+candidate's namespace."
+ :type 'function
+ :group 'cider
+ :package-version '(cider . "0.9.0"))
+
+(defcustom cider-completion-annotations-alist
+ '(("class" "c")
+ ("field" "fi")
+ ("function" "f")
+ ("import" "i")
+ ("keyword" "k")
+ ("local" "l")
+ ("macro" "m")
+ ("method" "me")
+ ("namespace" "n")
+ ("protocol" "p")
+ ("protocol-function" "pf")
+ ("record" "r")
+ ("special-form" "s")
+ ("static-field" "sf")
+ ("static-method" "sm")
+ ("type" "t")
+ ("var" "v"))
+ "Controls the abbreviations used when annotating completion candidates.
+
+Must be a list of elements with the form (TYPE . ABBREVIATION), where TYPE
+is a possible value of the candidate's type returned from the completion
+backend, and ABBREVIATION is a short form of that type."
+ :type '(alist :key-type string :value-type string)
+ :group 'cider
+ :package-version '(cider . "0.9.0"))
+
+(defcustom cider-completion-annotations-include-ns 'unqualified
+ "Controls passing of namespaces to `cider-annotate-completion-function'.
+
+When set to 'always, the candidate's namespace will always be passed if it
+is available. When set to 'unqualified, the namespace will only be passed
+if the candidate is not namespace-qualified."
+ :type '(choice (const always)
+ (const unqualified)
+ (const :tag "never" nil))
+ :group 'cider
+ :package-version '(cider . "0.9.0"))
+
+(defvar cider-completion-last-context nil)
+
+(defun cider-completion-symbol-start-pos ()
+ "Find the starting position of the symbol at point, unless inside a string."
+ (let ((sap (symbol-at-point)))
+ (when (and sap (not (nth 3 (syntax-ppss))))
+ (car (bounds-of-thing-at-point 'symbol)))))
+
+(defun cider-completion-get-context-at-point ()
+ "Extract the context at point.
+If point is not inside the list, returns nil; otherwise return \"top-level\"
+form, with symbol at point replaced by __prefix__."
+ (when (save-excursion
+ (condition-case _
+ (progn
+ (up-list)
+ (check-parens)
+ t)
+ (scan-error nil)
+ (user-error nil)))
+ (save-excursion
+ (let* ((pref-end (point))
+ (pref-start (cider-completion-symbol-start-pos))
+ (context (cider-defun-at-point))
+ (_ (beginning-of-defun))
+ (expr-start (point)))
+ (concat (when pref-start (substring context 0 (- pref-start expr-start)))
+ "__prefix__"
+ (substring context (- pref-end expr-start)))))))
+
+(defun cider-completion-get-context ()
+ "Extract context depending on `cider-completion-use-context' and major mode."
+ (let ((context (if (and cider-completion-use-context
+ ;; Important because `beginning-of-defun' and
+ ;; `ending-of-defun' work incorrectly in the REPL
+ ;; buffer, so context extraction fails there.
+ (derived-mode-p 'clojure-mode))
+ (or (cider-completion-get-context-at-point)
+ "nil")
+ "nil")))
+ (if (string= cider-completion-last-context context)
+ ":same"
+ (setq cider-completion-last-context context)
+ context)))
+
+(defun cider-completion--parse-candidate-map (candidate-map)
+ "Get \"candidate\" from CANDIDATE-MAP.
+Put type and ns properties on the candidate"
+ (let ((candidate (nrepl-dict-get candidate-map "candidate"))
+ (type (nrepl-dict-get candidate-map "type"))
+ (ns (nrepl-dict-get candidate-map "ns")))
+ (put-text-property 0 1 'type type candidate)
+ (put-text-property 0 1 'ns ns candidate)
+ candidate))
+
+(defun cider-complete (str)
+ "Complete STR with context at point."
+ (let* ((context (cider-completion-get-context))
+ (candidates (cider-sync-request:complete str context)))
+ (mapcar #'cider-completion--parse-candidate-map candidates)))
+
+(defun cider-completion--get-candidate-type (symbol)
+ "Get candidate type for SYMBOL."
+ (let ((type (get-text-property 0 'type symbol)))
+ (or (cadr (assoc type cider-completion-annotations-alist))
+ type)))
+
+(defun cider-completion--get-candidate-ns (symbol)
+ "Get candidate ns for SYMBOL."
+ (when (or (eq 'always cider-completion-annotations-include-ns)
+ (and (eq 'unqualified cider-completion-annotations-include-ns)
+ (not (cider-namespace-qualified-p symbol))))
+ (get-text-property 0 'ns symbol)))
+
+(defun cider-default-annotate-completion-function (type ns)
+ "Get completion function based on TYPE and NS."
+ (concat (when ns (format " (%s)" ns))
+ (when type (format " <%s>" type))))
+
+(defun cider-annotate-symbol (symbol)
+ "Return a string suitable for annotating SYMBOL.
+If SYMBOL has a text property `type` whose value is recognised, its
+abbreviation according to `cider-completion-annotations-alist' will be
+used. If `type` is present but not recognised, its value will be used
+unaltered. If SYMBOL has a text property `ns`, then its value will be used
+according to `cider-completion-annotations-include-ns'. The formatting is
+performed by `cider-annotate-completion-function'."
+ (when cider-annotate-completion-candidates
+ (let* ((type (cider-completion--get-candidate-type symbol))
+ (ns (cider-completion--get-candidate-ns symbol)))
+ (funcall cider-annotate-completion-function type ns))))
+
+(defun cider-complete-at-point ()
+ "Complete the symbol at point."
+ (when-let* ((bounds (bounds-of-thing-at-point 'symbol)))
+ (when (and (cider-connected-p)
+ (not (or (cider-in-string-p) (cider-in-comment-p))))
+ (list (car bounds) (cdr bounds)
+ (completion-table-dynamic #'cider-complete)
+ :annotation-function #'cider-annotate-symbol
+ :company-doc-buffer #'cider-create-doc-buffer
+ :company-location #'cider-company-location
+ :company-docsig #'cider-company-docsig))))
+
+(defun cider-completion-flush-caches ()
+ "Force Compliment to refill its caches.
+This command should be used if Compliment fails to pick up new classnames
+and methods from dependencies that were loaded dynamically after the REPL
+has started."
+ (interactive)
+ (cider-sync-request:complete-flush-caches))
+
+(defun cider-company-location (var)
+ "Open VAR's definition in a buffer.
+Returns the cons of the buffer itself and the location of VAR's definition
+in the buffer."
+ (when-let* ((info (cider-var-info var))
+ (file (nrepl-dict-get info "file"))
+ (line (nrepl-dict-get info "line"))
+ (buffer (cider-find-file file)))
+ (with-current-buffer buffer
+ (save-excursion
+ (goto-char (point-min))
+ (forward-line (1- line))
+ (cons buffer (point))))))
+
+(defun cider-company-docsig (thing)
+ "Return signature for THING."
+ (let* ((eldoc-info (cider-eldoc-info thing))
+ (ns (lax-plist-get eldoc-info "ns"))
+ (symbol (lax-plist-get eldoc-info "symbol"))
+ (arglists (lax-plist-get eldoc-info "arglists")))
+ (when eldoc-info
+ (format "%s: %s"
+ (cider-eldoc-format-thing ns symbol thing
+ (cider-eldoc-thing-type eldoc-info))
+ (cider-eldoc-format-arglist arglists 0)))))
+
+;; Fuzzy completion for company-mode
+
+(defun cider-company-unfiltered-candidates (string &rest _)
+ "Return CIDER completion candidates for STRING as is, unfiltered."
+ (cider-complete string))
+
+(add-to-list 'completion-styles-alist
+ '(cider
+ cider-company-unfiltered-candidates
+ cider-company-unfiltered-candidates
+ "CIDER backend-driven completion style."))
+
+(defun cider-company-enable-fuzzy-completion ()
+ "Enable backend-driven fuzzy completion in the current buffer."
+ (setq-local completion-styles '(cider)))
+
+(provide 'cider-completion)
+;;; cider-completion.el ends here
diff --git a/cider-connection.el b/cider-connection.el
new file mode 100644
index 00000000..959b78e5
--- /dev/null
+++ b/cider-connection.el
@@ -0,0 +1,799 @@
+;;; cider-connection.el --- Connection and session life-cycle management for CIDER -*- lexical-binding: t -*-
+;;
+;; Copyright © 2018 Artur Malabarba, Bozhidar Batsov, Vitalie Spinu and CIDER contributors
+;;
+;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
+;; Bozhidar Batsov <bozhidar@batsov.com>
+;; Vitalie Spinu <spinuvit@gmail.com>
+;;
+;; Keywords: languages, clojure, cider
+;;
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+;;
+;; This file is not part of GNU Emacs.
+;;
+;;
+;;; Commentary:
+;;
+;;
+;;; Code:
+
+(require 'nrepl-client)
+(require 'cl-lib)
+(require 'format-spec)
+(require 'sesman)
+(require 'sesman-browser)
+
+(defcustom cider-session-name-template "%J:%h:%p"
+ "Format string to use for session names.
+See `cider-format-connection-params' for available format characters."
+ :type 'string
+ :group 'cider
+ :package-version '(cider . "0.18.0"))
+
+(defcustom cider-connection-message-fn #'cider-random-words-of-inspiration
+ "The function to use to generate the message displayed on connect.
+When set to nil no additional message will be displayed. A good
+alternative to the default is `cider-random-tip'."
+ :type 'function
+ :group 'cider
+ :package-version '(cider . "0.11.0"))
+
+(defcustom cider-redirect-server-output-to-repl t
+ "Controls whether nREPL server output would be redirected to the REPL.
+When non-nil the output would end up in both the nrepl-server buffer (when
+available) and the matching REPL buffer."
+ :type 'boolean
+ :group 'cider
+ :safe #'booleanp
+ :package-version '(cider . "0.17.0"))
+
+(defcustom cider-auto-mode t
+ "When non-nil, automatically enable cider mode for all Clojure buffers."
+ :type 'boolean
+ :group 'cider
+ :safe #'booleanp
+ :package-version '(cider . "0.9.0"))
+
+(defconst cider-required-nrepl-version "0.2.12"
+ "The minimum nREPL version that's known to work properly with CIDER.")
+
+
+;;; Connect
+
+(defun cider-nrepl-connect (params)
+ "Start nrepl client and create the REPL.
+PARAMS is a plist containing :host, :port, :server and other parameters for
+`cider-repl-create'."
+ (process-buffer
+ (nrepl-start-client-process
+ (plist-get params :host)
+ (plist-get params :port)
+ (plist-get params :server)
+ (lambda (_)
+ (cider-repl-create params)))))
+
+(defun cider-connected-p ()
+ "Return t if CIDER is currently connected, nil otherwise."
+ (process-live-p (get-buffer-process (cider-current-repl))))
+
+(defun cider-ensure-connected ()
+ "Ensure there is a linked CIDER session."
+ (sesman-ensure-session 'CIDER))
+
+(defun cider--session-server (session)
+ "Return server buffer for SESSION or nil if there is no server."
+ (seq-some (lambda (r)
+ (buffer-local-value 'nrepl-server-buffer r))
+ (cdr session)))
+
+(defun cider--gather-session-params (session)
+ "Gather all params for a SESSION."
+ (let (params)
+ (dolist (repl (cdr session))
+ (setq params (cider--gather-connect-params params repl)))
+ (when-let* ((server (cider--session-server session)))
+ (setq params (cider--gather-connect-params params server)))
+ params))
+
+(defun cider--gather-connect-params (&optional params proc-buffer)
+ "Gather all relevant connection parameters into PARAMS plist.
+PROC-BUFFER is either server or client buffer, defaults to current buffer."
+ (let ((proc-buffer (or proc-buffer (current-buffer))))
+ (with-current-buffer proc-buffer
+ (unless nrepl-endpoint
+ (error "This is not a REPL or SERVER buffer; is there an active REPL?"))
+ (let ((server-buf (if (nrepl-server-p proc-buffer)
+ proc-buffer
+ nrepl-server-buffer)))
+ (cl-loop for l on nrepl-endpoint by #'cddr
+ do (setq params (plist-put params (car l) (cadr l))))
+ (setq params (thread-first params
+ (plist-put :project-dir nrepl-project-dir)))
+ (when (buffer-live-p server-buf)
+ (setq params (thread-first params
+ (plist-put :server (get-buffer-process server-buf))
+ (plist-put :server-command nrepl-server-command))))
+ ;; repl-specific parameters (do not pollute server params!)
+ (unless (nrepl-server-p proc-buffer)
+ (setq params (thread-first params
+ (plist-put :session-name cider-session-name)
+ (plist-put :repl-type cider-repl-type)
+ (plist-put :cljs-repl-type cider-cljs-repl-type)
+ (plist-put :repl-init-function cider-repl-init-function))))
+ params))))
+
+(defun cider--close-buffer (buffer)
+ "Close the BUFFER and kill its associated process (if any)."
+ (when (buffer-live-p buffer)
+ (when-let* ((proc (get-buffer-process buffer)))
+ (when (process-live-p proc)
+ (delete-process proc)))
+ (kill-buffer buffer)))
+
+(declare-function cider-repl-emit-interactive-stderr "cider-repl")
+(defun cider--close-connection (repl &optional no-kill)
+ "Close connection associated with REPL.
+When NO-KILL is non-nil stop the connection but don't kill the REPL
+buffer."
+ (when (buffer-live-p repl)
+ (with-current-buffer repl
+ (when spinner-current (spinner-stop))
+ (when nrepl-tunnel-buffer
+ (cider--close-buffer nrepl-tunnel-buffer))
+ (when no-kill
+ ;; inform sentinel not to kill the server, if any
+ (thread-first (get-buffer-process repl)
+ (process-plist)
+ (plist-put :no-server-kill t))))
+ (let ((proc (get-buffer-process repl)))
+ (when (and (process-live-p proc)
+ (or (not nrepl-server-buffer)
+ ;; Sync request will hang if the server is dead.
+ (process-live-p (get-buffer-process nrepl-server-buffer))))
+ (nrepl-sync-request:close repl)
+ (delete-process proc)))
+ (when-let* ((messages-buffer (and nrepl-log-messages
+ (nrepl-messages-buffer repl))))
+ (kill-buffer messages-buffer))
+ (if no-kill
+ (with-current-buffer repl
+ (goto-char (point-max))
+ (cider-repl-emit-interactive-stderr
+ (format "*** Closed on %s ***\n" (current-time-string))))
+ (kill-buffer repl)))
+ (when repl
+ (sesman-remove-object 'CIDER nil repl (not no-kill) t)))
+
+(defun cider-emit-manual-warning (section-id format &rest args)
+ "Emit a warning to the REPL and link to the online manual.
+SECTION-ID is the section to link to. The link is added on the last line.
+FORMAT is a format string to compile with ARGS and display on the REPL."
+ (let ((message (apply #'format format args)))
+ (cider-repl-emit-interactive-stderr
+ (concat "WARNING: " message "\n "
+ (cider--manual-button "More information" section-id)
+ "."))))
+
+(defvar cider-version)
+(defun cider--check-required-nrepl-version ()
+ "Check whether we're using a compatible nREPL version."
+ (if-let* ((nrepl-version (cider--nrepl-version)))
+ (when (version< nrepl-version cider-required-nrepl-version)
+ (cider-emit-manual-warning "troubleshooting/#warning-saying-you-have-to-use-nrepl-0212"
+ "CIDER requires nREPL %s (or newer) to work properly"
+ cider-required-nrepl-version))
+ (cider-emit-manual-warning "troubleshooting/#warning-saying-you-have-to-use-nrepl-0212"
+ "Can't determine nREPL's version.\nPlease, update nREPL to %s."
+ cider-required-nrepl-version)))
+
+(defvar cider-minimum-clojure-version)
+(defun cider--check-clojure-version-supported ()
+ "Ensure that we are meeting the minimum supported version of Clojure."
+ (if-let* ((clojure-version (cider--clojure-version)))
+ (when (version< clojure-version cider-minimum-clojure-version)
+ (cider-emit-manual-warning "installation/#prerequisites"
+ "Clojure version (%s) is not supported (minimum %s). CIDER will not work."
+ clojure-version cider-minimum-clojure-version))
+ (cider-emit-manual-warning "installation/#prerequisites"
+ "Can't determine Clojure's version. CIDER requires Clojure %s (or newer)."
+ cider-minimum-clojure-version)))
+
+(defvar cider-required-middleware-version)
+(defun cider--check-middleware-compatibility ()
+ "CIDER frontend/backend compatibility check.
+Retrieve the underlying connection's CIDER-nREPL version and checks if the
+middleware used is compatible with CIDER. If not, will display a warning
+message in the REPL area."
+ (let* ((version-dict (nrepl-aux-info "cider-version" (cider-current-repl)))
+ (middleware-version (nrepl-dict-get version-dict "version-string")))
+ (cond
+ ((null middleware-version)
+ (cider-emit-manual-warning "troubleshooting/#cider-complains-of-the-cider-nrepl-version"
+ "CIDER requires cider-nrepl to be fully functional. Many things will not work without it!"))
+ ((version< middleware-version cider-required-middleware-version)
+ (cider-emit-manual-warning "troubleshooting/#cider-complains-of-the-cider-nrepl-version"
+ "CIDER %s requires cider-nrepl %s+, but you're currently using cider-nrepl %s. Things will break!"
+ cider-version cider-required-middleware-version middleware-version)))))
+
+(declare-function cider-interactive-eval-handler "cider-eval")
+;; TODO: Use some null handler here
+(defun cider--subscribe-repl-to-server-out ()
+ "Subscribe to the nREPL server's *out*."
+ (cider-nrepl-send-request '("op" "out-subscribe")
+ (cider-interactive-eval-handler (current-buffer))))
+
+(declare-function cider-mode "cider-mode")
+(defun cider-enable-on-existing-clojure-buffers ()
+ "Enable CIDER's minor mode on existing Clojure buffers.
+See command `cider-mode'."
+ (interactive)
+ (add-hook 'clojure-mode-hook #'cider-mode)
+ (dolist (buffer (cider-util--clojure-buffers))
+ (with-current-buffer buffer
+ (cider-mode +1))))
+
+(defun cider-disable-on-existing-clojure-buffers ()
+ "Disable command `cider-mode' on existing Clojure buffers."
+ (interactive)
+ (dolist (buffer (cider-util--clojure-buffers))
+ (with-current-buffer buffer
+ (cider-mode -1))))
+
+(defun cider-possibly-disable-on-existing-clojure-buffers ()
+ "If not connected, disable command `cider-mode' on existing Clojure buffers."
+ (unless (cider-connected-p)
+ (cider-disable-on-existing-clojure-buffers)))
+
+(declare-function cider--debug-init-connection "cider-debug")
+(declare-function cider-repl-init "cider-repl")
+(defun cider--connected-handler ()
+ "Handle CIDER initialization after nREPL connection has been established.
+This function is appended to `nrepl-connected-hook' in the client process
+buffer."
+ ;; `nrepl-connected-hook' is run in the connection buffer
+ ;; `cider-enlighten-mode' changes eval to include the debugger, so we inhibit
+ ;; it here as the debugger isn't necessarily initialized yet
+ (let ((cider-enlighten-mode nil))
+ ;; after initialization, set mode-line and buffer name.
+ (cider-set-repl-type cider-repl-type)
+ (cider-repl-init (current-buffer))
+ (cider--check-required-nrepl-version)
+ (cider--check-clojure-version-supported)
+ (cider--check-middleware-compatibility)
+ (when cider-redirect-server-output-to-repl
+ (cider--subscribe-repl-to-server-out))
+ (when cider-auto-mode
+ (cider-enable-on-existing-clojure-buffers))
+ ;; Middleware on cider-nrepl's side is deferred until first usage, but
+ ;; loading middleware concurrently can lead to occasional "require" issues
+ ;; (likely a Clojure bug). Thus, we load the heavy debug middleware towards
+ ;; the end, allowing for the faster "server-out" middleware to load
+ ;; first.
+ (cider--debug-init-connection)
+ (when cider-repl-init-function
+ (funcall cider-repl-init-function))
+ (run-hooks 'cider-connected-hook)))
+
+(defun cider--disconnected-handler ()
+ "Cleanup after nREPL connection has been lost or closed.
+This function is appended to `nrepl-disconnected-hook' in the client
+process buffer."
+ ;; `nrepl-connected-hook' is run in the connection buffer
+ (cider-possibly-disable-on-existing-clojure-buffers)
+ (run-hooks 'cider-disconnected-hook))
+
+
+;;; Connection Info
+
+(defun cider--java-version ()
+ "Retrieve the underlying connection's Java version."
+ (with-current-buffer (cider-current-repl)
+ (when nrepl-versions
+ (thread-first nrepl-versions
+ (nrepl-dict-get "java")
+ (nrepl-dict-get "version-string")))))
+
+(defun cider--clojure-version ()
+ "Retrieve the underlying connection's Clojure version."
+ (with-current-buffer (cider-current-repl)
+ (when nrepl-versions
+ (thread-first nrepl-versions
+ (nrepl-dict-get "clojure")
+ (nrepl-dict-get "version-string")))))
+
+(defun cider--nrepl-version ()
+ "Retrieve the underlying connection's nREPL version."
+ (with-current-buffer (cider-current-repl)
+ (when nrepl-versions
+ (thread-first nrepl-versions
+ (nrepl-dict-get "nrepl")
+ (nrepl-dict-get "version-string")))))
+
+(defun cider--connection-info (connection-buffer &optional genericp)
+ "Return info about CONNECTION-BUFFER.
+Info contains project name, current REPL namespace, host:port endpoint and
+Clojure version. When GENERICP is non-nil, don't provide specific info
+about this buffer (like variable `cider-repl-type')."
+ (with-current-buffer connection-buffer
+ (format "%s%s@%s:%s (Java %s, Clojure %s, nREPL %s)"
+ (if genericp "" (upcase (concat cider-repl-type " ")))
+ (or (cider--project-name nrepl-project-dir) "<no project>")
+ (plist-get nrepl-endpoint :host)
+ (plist-get nrepl-endpoint :port)
+ (cider--java-version)
+ (cider--clojure-version)
+ (cider--nrepl-version))))
+
+
+;;; Cider's Connection Management UI
+
+(defun cider-quit (&optional repl)
+ "Quit the CIDER connection associated with REPL.
+REPL defaults to the current REPL."
+ (interactive)
+ (let ((repl (or repl
+ (sesman-browser-get 'object)
+ (cider-current-repl nil 'ensure))))
+ (cider--close-connection repl))
+ ;; if there are no more connections we can kill all ancillary buffers
+ (unless (cider-connected-p)
+ (cider-close-ancillary-buffers))
+ ;; need this to refresh sesman browser
+ (run-hooks 'sesman-post-command-hook))
+
+(defun cider-restart (&optional repl)
+ "Restart CIDER connection associated with REPL.
+REPL defaults to the current REPL. Don't restart the server or other
+connections within the same session. Use `sesman-restart' to restart the
+entire session."
+ (interactive)
+ (let* ((repl (or repl
+ (sesman-browser-get 'object)
+ (cider-current-repl nil 'ensure)))
+ (params (thread-first ()
+ (cider--gather-connect-params repl)
+ (plist-put :session-name (sesman-session-name-for-object 'CIDER repl))
+ (plist-put :repl-buffer repl))))
+ (cider--close-connection repl 'no-kill)
+ (cider-nrepl-connect params)
+ ;; need this to refresh sesman browser
+ (run-hooks 'sesman-post-command-hook)))
+
+(defun cider-close-ancillary-buffers ()
+ "Close buffers that are shared across connections."
+ (interactive)
+ (dolist (buf-name cider-ancillary-buffers)
+ (when (get-buffer buf-name)
+ (kill-buffer buf-name))))
+
+(defun cider-describe-connection (&optional repl)
+ "Display information about the connection associated with REPL.
+REPL defaults to the current REPL."
+ (interactive)
+ (let ((repl (or repl
+ (sesman-browser-get 'object)
+ (cider-current-repl nil 'ensure))))
+ (message "%s" (cider--connection-info repl))))
+(define-obsolete-function-alias 'cider-display-connection-info 'cider-describe-connection "0.18.0")
+
+(defconst cider-nrepl-session-buffer "*cider-nrepl-session*")
+
+(defun cider-describe-nrepl-session ()
+ "Describe an nREPL session."
+ (interactive)
+ (cider-ensure-connected)
+ (let* ((repl (cider-current-repl nil 'ensure))
+ (selected-session (completing-read "Describe nREPL session: " (nrepl-sessions repl))))
+ (when (and selected-session (not (equal selected-session "")))
+ (let* ((session-info (nrepl-sync-request:describe repl))
+ (ops (nrepl-dict-keys (nrepl-dict-get session-info "ops")))
+ (session-id (nrepl-dict-get session-info "session"))
+ (session-type (cond
+ ((equal session-id (cider-nrepl-eval-session)) "Active eval")
+ ((equal session-id (cider-nrepl-tooling-session)) "Active tooling")
+ (t "Unknown"))))
+ (with-current-buffer (cider-popup-buffer cider-nrepl-session-buffer 'select nil 'ancillary)
+ (read-only-mode -1)
+ (insert (format "Session: %s\n" session-id)
+ (format "Type: %s session\n" session-type)
+ (format "Supported ops:\n"))
+ (mapc (lambda (op) (insert (format " * %s\n" op))) ops)))
+ (display-buffer cider-nrepl-session-buffer))))
+
+
+;;; Sesman's Session-Wise Management UI
+
+(cl-defmethod sesman-project ((_system (eql CIDER)))
+ (clojure-project-dir (cider-current-dir)))
+
+(cl-defmethod sesman-more-relevant-p ((_system (eql CIDER)) session1 session2)
+ (sesman-more-recent-p (cdr session1) (cdr session2)))
+
+(defvar cider-sesman-browser-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "j q") #'cider-quit)
+ (define-key map (kbd "j k") #'cider-quit)
+ (define-key map (kbd "j r") #'cider-restart)
+ (define-key map (kbd "j d") #'cider-describe-connection)
+ (define-key map (kbd "j i") #'cider-describe-connection)
+ (define-key map (kbd "C-c C-q") #'cider-quit)
+ (define-key map (kbd "C-c C-q") #'cider-quit)
+ (define-key map (kbd "C-c C-r") #'cider-restart)
+ (define-key map (kbd "C-c M-r") #'cider-restart)
+ (define-key map (kbd "C-c C-d") #'cider-describe-connection)
+ (define-key map (kbd "C-c M-d") #'cider-describe-connection)
+ (define-key map (kbd "C-c C-i") #'cider-describe-connection)
+ map)
+ "Map active on REPL objects in sesman browser.")
+
+(cl-defmethod sesman-session-info ((_system (eql CIDER)) session)
+ (interactive "P")
+ (list :objects (cdr session)
+ :map cider-sesman-browser-map))
+
+(declare-function cider "cider")
+(cl-defmethod sesman-start-session ((_system (eql CIDER)))
+ "Start a connection of any type interactively.
+Fallback on `cider' command."
+ (call-interactively #'cider))
+
+(cl-defmethod sesman-quit-session ((_system (eql CIDER)) session)
+ (mapc #'cider--close-connection (cdr session))
+ ;; if there are no more connections we can kill all ancillary buffers
+ (unless (cider-connected-p)
+ (cider-close-ancillary-buffers)))
+
+(cl-defmethod sesman-restart-session ((_system (eql CIDER)) session)
+ (let* ((ses-name (car session))
+ (repls (cdr session))
+ (srv-buf (cider--session-server session)))
+ (if srv-buf
+ ;; session with a server
+ (let ((s-params (cider--gather-connect-params nil srv-buf)))
+ ;; 1) kill all connections, but keep the buffers
+ (mapc (lambda (conn)
+ (cider--close-connection conn 'no-kill))
+ repls)
+ ;; 2) kill the server
+ (nrepl-kill-server-buffer srv-buf)
+ ;; 3) start server
+ (nrepl-start-server-process
+ (plist-get s-params :project-dir)
+ (plist-get s-params :server-command)
+ (lambda (server-buf)
+ ;; 4) restart the repls reusing the buffer
+ (dolist (r repls)
+ (cider-nrepl-connect
+ (thread-first ()
+ (cider--gather-connect-params r)
+ ;; server params (:port, :project-dir etc) have precedence
+ (cider--gather-connect-params server-buf)
+ (plist-put :session-name ses-name)
+ (plist-put :repl-buffer r))))
+ (sesman-browser-revert-all 'CIDER)
+ (message "Restarted CIDER %s session" ses-name))))
+ ;; server-less session
+ (dolist (r repls)
+ (cider--close-connection r 'no-kill)
+ (cider-nrepl-connect
+ (thread-first ()
+ (cider--gather-connect-params r)
+ (plist-put :session-name ses-name)
+ (plist-put :repl-buffer r)))))))
+
+(defun cider-format-connection-params (template params)
+ "Format PARAMS with TEMPLATE string.
+The following formats can be used in TEMPLATE string:
+
+ %h - host
+ %H - remote host, empty for local hosts
+ %p - port
+ %j - short project name, or directory name if no project
+ %J - long project name including parent dir name
+ %r - REPL type (clj or cljs)
+ %S - type of the ClojureScript runtime (Nashorn, Node, Figwheel etc.)
+ %s - session name as defined by `cider-session-name-template'.
+
+In case some values are empty, extra separators (: and -) are automatically
+removed."
+ (let* ((dir (directory-file-name
+ (abbreviate-file-name
+ (or (plist-get params :project-dir)
+ (clojure-project-dir (cider-current-dir))
+ default-directory))))
+ (short-proj (file-name-nondirectory (directory-file-name dir)))
+ (parent-dir (ignore-errors
+ (thread-first dir file-name-directory
+ directory-file-name file-name-nondirectory
+ file-name-as-directory)))
+ (long-proj (format "%s%s" (or parent-dir "") short-proj))
+ ;; use `dir` if it is shorter than `long-proj` or `short-proj`
+ (short-proj (if (>= (length short-proj) (length dir))
+ dir
+ short-proj))
+ (long-proj (if (>= (length long-proj) (length dir))
+ dir
+ long-proj))
+ (port (or (plist-get params :port) ""))
+ (host (or (plist-get params :host) "localhost"))
+ (remote-host (if (member host '("localhost" "127.0.0.1"))
+ ""
+ host))
+ (repl-type (or (plist-get params :repl-type) "unknown"))
+ (cljs-repl-type (or (and (equal repl-type "cljs")
+ (plist-get params :cljs-repl-type))
+ ""))
+ (specs `((?h . ,host)
+ (?H . ,remote-host)
+ (?p . ,port)
+ (?j . ,short-proj)
+ (?J . ,long-proj)
+ (?r . ,repl-type)
+ (?S . ,cljs-repl-type)))
+ (ses-name (or (plist-get params :session-name)
+ (format-spec cider-session-name-template specs)))
+ (specs (append `((?s . ,ses-name)) specs)))
+ (thread-last (format-spec template specs)
+ ;; remove extraneous separators
+ (replace-regexp-in-string "\\([:-]\\)[:-]+" "\\1")
+ (replace-regexp-in-string "\\(^[:-]\\)\\|\\([:-]$\\)" "")
+ (replace-regexp-in-string "[:-]\\([])*]\\)" "\\1"))))
+
+(defun cider-make-session-name (params)
+ "Create new session name given plist of connection PARAMS.
+Session name can be customized with `cider-session-name-template'."
+ (let* ((root-name (cider-format-connection-params cider-session-name-template params))
+ (other-names (mapcar #'car (sesman-sessions 'CIDER)))
+ (name root-name)
+ (i 2))
+ (while (member name other-names)
+ (setq name (concat root-name "#" (number-to-string i))
+ i (+ i 1)))
+ name))
+
+
+;;; REPL Buffer Init
+
+(defvar-local cider-cljs-repl-type nil
+ "The type of the CLJS runtime (Nashorn, Node etc.)")
+
+(defvar-local cider-repl-type nil
+ "The type of this REPL buffer, usually either \"clj\" or \"cljs\".")
+
+(defun cider-repl-type (repl-buffer)
+ "Get REPL-BUFFER's type."
+ (buffer-local-value 'cider-repl-type repl-buffer))
+
+(defun cider-repl-type-for-buffer (&optional buffer)
+ "Return the matching connection type (clj or cljs) for BUFFER.
+BUFFER defaults to the `current-buffer'. In cljc buffers return
+\"multi\". This function infers connection type based on the major mode.
+For the REPL type use the function `cider-repl-type'."
+ (with-current-buffer (or buffer (current-buffer))
+ (cond
+ ((derived-mode-p 'clojurescript-mode) "cljs")
+ ((derived-mode-p 'clojurec-mode) "multi")
+ ((derived-mode-p 'clojure-mode) "clj")
+ (cider-repl-type))))
+
+(defun cider-set-repl-type (&optional type)
+ "Set REPL TYPE to \"clj\" or \"cljs\".
+Assume that the current buffer is a REPL."
+ (interactive)
+ (let ((type (or type (completing-read
+ (format "Set REPL type (currently `%s') to: "
+ cider-repl-type)
+ '("clj" "cljs")))))
+ (when (or (not (equal cider-repl-type type))
+ (null mode-name))
+ (setq cider-repl-type type)
+ (setq mode-name (format "REPL[%s]" type))
+ (let ((params (cider--gather-connect-params)))
+ ;; We need to set current name to something else temporarily to avoid
+ ;; false name duplication in `nrepl-repl-buffer-name`.
+ (rename-buffer (generate-new-buffer-name "*dummy-cider-repl-buffer*"))
+ (rename-buffer (nrepl-repl-buffer-name params))
+ (when (and nrepl-log-messages nrepl-messages-buffer)
+ (with-current-buffer nrepl-messages-buffer
+ (rename-buffer (nrepl-messages-buffer-name params))))))))
+
+(declare-function cider-default-err-handler "cider-eval")
+(declare-function cider-repl-mode "cider-repl")
+(declare-function cider-repl--state-handler "cider-repl")
+(declare-function cider-repl-reset-markers "cider-repl")
+(defvar-local cider-session-name nil)
+(defvar-local cider-repl-init-function nil)
+(defun cider-repl-create (params)
+ "Create new repl buffer.
+PARAMS is a plist which contains :repl-type, :host, :port, :project-dir,
+:repl-init-function and :session-name. When non-nil, :repl-init-function
+must be a function with no arguments which is called after repl creation
+function with the repl buffer set as current."
+ ;; Connection might not have been set as yet. Please don't send requests in
+ ;; this function, but use cider--connected-handler instead.
+ (let ((buffer (or (plist-get params :repl-buffer)
+ (get-buffer-create (generate-new-buffer-name "*cider-uninitialized-repl*"))))
+ (ses-name (or (plist-get params :session-name)
+ (cider-make-session-name params))))
+ (with-current-buffer buffer
+ (setq-local sesman-system 'CIDER)
+ (setq-local default-directory (or (plist-get params :project-dir) default-directory))
+ ;; creates a new session if session with ses-name doesn't already exist
+ (sesman-add-object 'CIDER ses-name buffer 'allow-new)
+ (unless (derived-mode-p 'cider-repl-mode)
+ (cider-repl-mode))
+ (setq nrepl-err-handler #'cider-default-err-handler
+ ;; used as a new-repl marker in cider-set-repl-type
+ mode-name nil
+ cider-session-name ses-name
+ nrepl-project-dir (plist-get params :project-dir)
+ ;; REPLs start with clj and then "upgrade" to a different type
+ cider-repl-type (plist-get params :repl-type)
+ ;; ran at the end of cider--connected-handler
+ cider-repl-init-function (plist-get params :repl-init-function))
+ (cider-repl-reset-markers)
+ (add-hook 'nrepl-response-handler-functions #'cider-repl--state-handler nil 'local)
+ (add-hook 'nrepl-connected-hook 'cider--connected-handler nil 'local)
+ (add-hook 'nrepl-disconnected-hook 'cider--disconnected-handler nil 'local)
+ (current-buffer))))
+
+
+;;; Current/other REPLs
+
+(defun cider--no-repls-user-error (type)
+ "Throw \"No REPL\" user error customized for TYPE."
+ (let ((type (cond
+ ((equal type "multi")
+ "clj or cljs")
+ ((listp type)
+ (mapconcat #'identity type " or "))
+ (type))))
+ (user-error "No %s REPLs in current session \"%s\""
+ type (car (sesman-current-session 'CIDER)))))
+
+(defun cider-current-repl (&optional type ensure)
+ "Get the most recent REPL of TYPE from the current session.
+TYPE is either \"clj\", \"cljs\" or \"multi\". When nil, infer the type
+from the current buffer. If ENSURE is non-nil, throw an error if either
+there is no linked session or there is no REPL of TYPE within the current
+session."
+ (if (and (derived-mode-p 'cider-repl-mode)
+ (or (null type)
+ (string= cider-repl-type type)))
+ ;; shortcut when in REPL buffer
+ (current-buffer)
+ (let* ((type (or type (cider-repl-type-for-buffer)))
+ (repls (cider-repls type ensure))
+ (repl (if (<= (length repls) 1)
+ (car repls)
+ ;; pick the most recent one
+ (seq-find (lambda (b)
+ (member b repls))
+ (buffer-list)))))
+ (if (and ensure (null repl))
+ (cider--no-repls-user-error type)
+ repl))))
+
+(defun cider--match-repl-type (type buffer)
+ "Return non-nil if TYPE matches BUFFER's REPL type."
+ (let ((buffer-repl-type (cider-repl-type buffer)))
+ (cond ((null buffer-repl-type) nil)
+ ((or (null type) (equal type "multi")) t)
+ ((listp type) (member buffer-repl-type type))
+ (t (string= type buffer-repl-type)))))
+
+(defun cider-repls (&optional type ensure)
+ "Return cider REPLs of TYPE from the current session.
+If TYPE is nil or \"multi\", return all repls. If TYPE is a list of types,
+return only REPLs of type contained in the list. If ENSURE is non-nil,
+throw an error if no linked session exists."
+ (let ((repls (cdr (if ensure
+ (sesman-ensure-session 'CIDER)
+ (sesman-current-session 'CIDER)))))
+ (or (seq-filter (lambda (b)
+ (cider--match-repl-type type b))
+ repls)
+ (when ensure
+ (cider--no-repls-user-error type)))))
+
+(defun cider-map-repls (which function)
+ "Call FUNCTION once for each appropriate REPL as indicated by WHICH.
+The function is called with one argument, the REPL buffer. The appropriate
+connections are found by inspecting the current buffer. WHICH is one of
+the following keywords:
+ :auto - Act on the connections whose type matches the current buffer. In
+ `cljc' files, mapping happens over both types of REPLs.
+ :clj (:cljs) - Map over clj (cljs)) REPLs only.
+ :clj-strict (:cljs-strict) - Map over clj (cljs) REPLs but signal a
+ `user-error' in `clojurescript-mode' (`clojure-mode'). Use this for
+ commands only supported in Clojure (ClojureScript).
+Error is signaled if no REPL buffers of specified type exist in current
+session."
+ (declare (indent 1))
+ (let ((cur-type (cider-repl-type-for-buffer)))
+ (cl-case which
+ (:clj-strict (when (equal cur-type "cljs")
+ (user-error "Clojure-only operation requested in a ClojureScript buffer")))
+ (:cljs-strict (when (equal cur-type "clj")
+ (user-error "ClojureScript-only operation requested in a Clojure buffer"))))
+ (let* ((type (cl-case which
+ ((:clj :clj-strict) "clj")
+ ((:cljs :cljs-strict) "cljs")
+ (:auto (if (equal cur-type "multi")
+ '("clj" "cljs")
+ cur-type))))
+ (repls (cider-repls type 'ensure)))
+ (mapcar function repls))))
+
+;; REPLs double as connections in CIDER, so it's useful to be able to refer to
+;; them as connections in certain contexts.
+(defalias 'cider-current-connection #'cider-current-repl)
+(defalias 'cider-connections #'cider-repls)
+(defalias 'cider-map-connections #'cider-map-repls)
+(defalias 'cider-connection-type-for-buffer #'cider-repl-type-for-buffer)
+
+
+;; Deprecation after #2324
+
+(define-obsolete-function-alias 'cider-current-repl-buffer 'cider-current-repl "0.18")
+(define-obsolete-function-alias 'cider-repl-buffers 'cider-repls "0.18")
+(define-obsolete-function-alias 'cider-current-session 'cider-nrepl-eval-session "0.18")
+(define-obsolete-function-alias 'cider-current-tooling-session 'cider-nrepl-tooling-session "0.18")
+(define-obsolete-function-alias 'cider-display-connection-info 'cider-describe-connection "0.18")
+(define-obsolete-function-alias 'nrepl-connection-buffer-name 'nrepl-repl-buffer-name "0.18")
+(define-obsolete-function-alias 'cider-repl-set-type 'cider-set-repl-type "0.18")
+
+(make-obsolete 'cider-assoc-buffer-with-connection 'sesman-link-with-buffer "0.18")
+(make-obsolete 'cider-assoc-project-with-connection 'sesman-link-with-project "0.18")
+(make-obsolete 'cider-change-buffers-designation nil "0.18")
+(make-obsolete 'cider-clear-buffer-local-connection nil "0.18")
+(make-obsolete 'cider-close-nrepl-session 'cider-quit "0.18")
+(make-obsolete 'cider-create-sibling-cljs-repl 'cider-connect-sibling-cljs "0.18")
+(make-obsolete 'cider-current-messages-buffer nil "0.18")
+(make-obsolete 'cider-default-connection "see sesman." "0.18")
+(make-obsolete 'cider-extract-designation-from-current-repl-buffer nil "0.18")
+(make-obsolete 'cider-find-connection-buffer-for-project-directory 'sesman-linked-sessions "0.18")
+(make-obsolete 'cider-find-reusable-repl-buffer nil "0.18")
+(make-obsolete 'cider-make-connection-default "see sesman." "0.18")
+(make-obsolete 'cider-other-connection nil "0.18")
+(make-obsolete 'cider-project-connections 'sesman-linked-sessions "0.18")
+(make-obsolete 'cider-project-connections-types nil "0.18")
+(make-obsolete 'cider-prompt-for-project-on-connect nil "0.18")
+(make-obsolete 'cider-read-connection `sesman-ask-for-session "0.18")
+(make-obsolete 'cider-replicate-connection nil "0.18")
+(make-obsolete 'cider-request-dispatch "see sesman." "0.18")
+(make-obsolete 'cider-rotate-default-connection "see sesman." "0.18")
+(make-obsolete 'cider-toggle-buffer-connection nil "0.18")
+(make-obsolete 'cider-toggle-request-dispatch nil "0.18")
+(make-obsolete 'nrepl-connection-buffer-name-template 'nrepl-repl-buffer-name-template "0.18")
+(make-obsolete 'nrepl-create-client-buffer-function nil "0.18")
+(make-obsolete 'nrepl-post-client-callback nil "0.18")
+(make-obsolete 'nrepl-prompt-to-kill-server-buffer-on-quit nil "0.18")
+(make-obsolete 'nrepl-use-this-as-repl-buffer nil "0.18")
+
+;; connection manager
+(make-obsolete 'cider-client-name-repl-type "see sesman." "0.18")
+(make-obsolete 'cider-connection-browser "see sesman." "0.18")
+(make-obsolete 'cider-connections-buffer-mode "see sesman." "0.18")
+(make-obsolete 'cider-connections-buffer-mode-map "see sesman." "0.18")
+(make-obsolete 'cider-connections-close-connection "see sesman." "0.18")
+(make-obsolete 'cider-connections-goto-connection "see sesman." "0.18")
+(make-obsolete 'cider-connections-make-default "see sesman." "0.18")
+(make-obsolete 'cider-display-connected-message "see sesman." "0.18")
+(make-obsolete 'cider-project-name "see sesman." "0.18")
+
+(provide 'cider-connection)
+
+;;; cider-connection.el ends here
diff --git a/cider-debug.el b/cider-debug.el
index 54481d49..7ea5b875 100644
--- a/cider-debug.el
+++ b/cider-debug.el
@@ -27,7 +27,7 @@
(require 'nrepl-dict)
(require 'nrepl-client) ; `nrepl--mark-id-completed'
-(require 'cider-interaction)
+(require 'cider-eval)
(require 'cider-client)
(require 'cider-util)
(require 'cider-inspector)
@@ -344,7 +344,7 @@ In order to work properly, this mode must be activated by
;; cider-nrepl has a chance to send the next message, and so that the user
;; doesn't accidentally hit `n' between two messages (thus editing the code).
(when-let* ((proc (unless nrepl-ongoing-sync-request
- (get-buffer-process (cider-current-connection)))))
+ (get-buffer-process (cider-current-repl)))))
(accept-process-output proc 1))
(unless cider--debug-mode
(setq buffer-read-only nil)
@@ -602,7 +602,7 @@ is a coordinate measure in sexps."
code ns original-id
(if (and line column)
"you edited the code"
- "your tools.nrepl version is older than 0.2.11"))
+ "your nREPL version is older than 0.2.11"))
(save-excursion
(cider--debug-move-point coor)
(point-marker)))))))))
diff --git a/cider-doc.el b/cider-doc.el
index 3c867ea0..5cca0505 100644
--- a/cider-doc.el
+++ b/cider-doc.el
@@ -46,6 +46,11 @@
:prefix "cider-doc-"
:group 'cider)
+(defcustom cider-doc-auto-select-buffer t
+ "Controls whether to auto-select the doc popup buffer."
+ :type 'boolean
+ :group 'cider-doc
+ :package-version '(cider . "0.15.0"))
(declare-function cider-apropos "cider-apropos")
(declare-function cider-apropos-select "cider-apropos")
@@ -102,12 +107,6 @@
:group 'cider-docview-mode
:package-version '(cider . "0.7.0"))
-(defcustom cider-doc-auto-select-buffer t
- "Controls whether to auto-select the doc popup buffer."
- :type 'boolean
- :group 'cider-doc
- :package-version '(cider . "0.15.0"))
-
;; Faces
@@ -185,6 +184,7 @@
\\{cider-docview-mode-map}"
(setq buffer-read-only t)
+ (setq-local sesman-system 'CIDER)
(when cider-special-mode-truncate-lines
(setq-local truncate-lines t))
(setq-local electric-indent-chars nil)
@@ -225,9 +225,6 @@ opposite of what that option dictates."
"Javadoc for"
#'cider-javadoc-handler))
-(declare-function cider-find-file "cider-common")
-(declare-function cider-jump-to "cider-interaction")
-
(defun cider-docview-source ()
"Open the source for the current symbol, if available."
(interactive)
@@ -264,12 +261,11 @@ opposite of what that option dictates."
(error "%s cannot be looked up on Grimoire" cider-docview-symbol)))
(defconst cider-doc-buffer "*cider-doc*")
-(add-to-list 'cider-ancillary-buffers cider-doc-buffer)
(defun cider-create-doc-buffer (symbol)
"Populates *cider-doc* with the documentation for SYMBOL."
(when-let* ((info (cider-var-info symbol)))
- (cider-docview-render (cider-make-popup-buffer cider-doc-buffer) symbol info)))
+ (cider-docview-render (cider-make-popup-buffer cider-doc-buffer nil 'ancillary) symbol info)))
(defun cider-doc-lookup (symbol)
"Look up documentation for SYMBOL."
diff --git a/cider-eval.el b/cider-eval.el
new file mode 100644
index 00000000..67f2706b
--- /dev/null
+++ b/cider-eval.el
@@ -0,0 +1,1115 @@
+;;; cider-eval.el --- Interactive evaluation (compilation) functionality -*- lexical-binding: t -*-
+
+;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov
+;; Copyright © 2013-2018 Bozhidar Batsov, Artur Malabarba and CIDER contributors
+;;
+;; Author: Tim King <kingtim@gmail.com>
+;; Phil Hagelberg <technomancy@gmail.com>
+;; Bozhidar Batsov <bozhidar@batsov.com>
+;; Artur Malabarba <bruce.connor.am@gmail.com>
+;; Hugo Duncan <hugo@hugoduncan.org>
+;; Steve Purcell <steve@sanityinc.com>
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;; This file is not part of GNU Emacs.
+
+;;; Commentary:
+
+;; This file contains CIDER's interactive evaluation (compilation) functionality.
+;; Although Clojure doesn't really have the concept of evaluation (only
+;; compilation), we're using everywhere in the code the term evaluation for
+;; brevity (and to be in line with the naming employed by other similar modes).
+;;
+;; This files also contains all the logic related to displaying errors and
+;; evaluation warnings.
+;;
+;; Pretty much all of the commands here are meant to be used mostly from
+;; `cider-mode', but some of them might make sense in other contexts as well.
+
+;;; Code:
+
+(require 'cider-client)
+(require 'cider-repl)
+(require 'cider-popup)
+(require 'cider-common)
+(require 'cider-util)
+(require 'cider-stacktrace)
+(require 'cider-overlays)
+(require 'cider-compat)
+
+(require 'clojure-mode)
+(require 'ansi-color)
+(require 'cl-lib)
+(require 'subr-x)
+(require 'compile)
+
+(defconst cider-read-eval-buffer "*cider-read-eval*")
+(defconst cider-result-buffer "*cider-result*")
+
+(defcustom cider-show-error-buffer t
+ "Control the popup behavior of cider stacktraces.
+The following values are possible t or 'always, 'except-in-repl,
+'only-in-repl. Any other value, including nil, will cause the stacktrace
+not to be automatically shown.
+
+Irespective of the value of this variable, the `cider-error-buffer' is
+always generated in the background. Use `cider-selector' to
+navigate to this buffer."
+ :type '(choice (const :tag "always" t)
+ (const except-in-repl)
+ (const only-in-repl)
+ (const :tag "never" nil))
+ :group 'cider)
+
+(defcustom cider-auto-jump-to-error t
+ "Control the cursor jump behaviour in compilation error buffer.
+When non-nil automatically jump to error location during interactive
+compilation. When set to 'errors-only, don't jump to warnings.
+When set to nil, don't jump at all."
+ :type '(choice (const :tag "always" t)
+ (const errors-only)
+ (const :tag "never" nil))
+ :group 'cider
+ :package-version '(cider . "0.7.0"))
+
+(defcustom cider-auto-select-error-buffer t
+ "Controls whether to auto-select the error popup buffer."
+ :type 'boolean
+ :group 'cider)
+
+(defcustom cider-auto-track-ns-form-changes t
+ "Controls whether to auto-evaluate a source buffer's ns form when changed.
+When non-nil CIDER will check for ns form changes before each eval command.
+When nil the users are expected to take care of the re-evaluating updated
+ns forms manually themselves."
+ :type 'boolean
+ :group 'cider
+ :package-version '(cider . "0.15.0"))
+
+(defcustom cider-save-file-on-load 'prompt
+ "Controls whether to prompt to save the file when loading a buffer.
+If nil, files are not saved.
+If 'prompt, the user is prompted to save the file if it's been modified.
+If t, save the file without confirmation."
+ :type '(choice (const prompt :tag "Prompt to save the file if it's been modified")
+ (const nil :tag "Don't save the file")
+ (const t :tag "Save the file without confirmation"))
+ :group 'cider
+ :package-version '(cider . "0.6.0"))
+
+
+(defconst cider-output-buffer "*cider-out*")
+
+(defcustom cider-interactive-eval-output-destination 'repl-buffer
+ "The destination for stdout and stderr produced from interactive evaluation."
+ :type '(choice (const output-buffer)
+ (const repl-buffer))
+ :group 'cider
+ :package-version '(cider . "0.7.0"))
+
+(defface cider-error-highlight-face
+ '((((supports :underline (:style wave)))
+ (:underline (:style wave :color "red") :inherit unspecified))
+ (t (:inherit font-lock-warning-face :underline t)))
+ "Face used to highlight compilation errors in Clojure buffers."
+ :group 'cider)
+
+(defface cider-warning-highlight-face
+ '((((supports :underline (:style wave)))
+ (:underline (:style wave :color "yellow") :inherit unspecified))
+ (t (:inherit font-lock-warning-face :underline (:color "yellow"))))
+ "Face used to highlight compilation warnings in Clojure buffers."
+ :group 'cider)
+
+(defcustom cider-comment-prefix ";; => "
+ "The prefix to insert before the first line of commented output."
+ :type 'string
+ :group 'cider
+ :package-version '(cider . "0.16.0"))
+
+(defcustom cider-comment-continued-prefix ";; "
+ "The prefix to use on the second and subsequent lines of commented output."
+ :type 'string
+ :group 'cider
+ :package-version '(cider . "0.16.0"))
+
+(defcustom cider-comment-postfix ""
+ "The postfix to be appended after the final line of commented output."
+ :type 'string
+ :group 'cider
+ :package-version '(cider . "0.16.0"))
+
+
+;;; Utilities
+
+(defun cider--clear-compilation-highlights ()
+ "Remove compilation highlights."
+ (remove-overlays (point-min) (point-max) 'cider-note-p t))
+
+(defun cider-clear-compilation-highlights (&optional arg)
+ "Remove compilation highlights.
+When invoked with a prefix ARG the command doesn't prompt for confirmation."
+ (interactive "P")
+ (when (or arg (y-or-n-p "Are you sure you want to clear the compilation highlights? "))
+ (cider--clear-compilation-highlights)))
+
+(defun cider--quit-error-window ()
+ "Buries the `cider-error-buffer' and quits its containing window."
+ (when-let* ((error-win (get-buffer-window cider-error-buffer)))
+ (quit-window nil error-win)))
+
+
+;;; Dealing with compilation (evaluation) errors and warnings
+(defun cider-find-property (property &optional backward)
+ "Find the next text region which has the specified PROPERTY.
+If BACKWARD is t, then search backward.
+Returns the position at which PROPERTY was found, or nil if not found."
+ (let ((p (if backward
+ (previous-single-char-property-change (point) property)
+ (next-single-char-property-change (point) property))))
+ (when (and (not (= p (point-min))) (not (= p (point-max))))
+ p)))
+
+(defun cider-jump-to-compilation-error (&optional _arg _reset)
+ "Jump to the line causing the current compilation error.
+_ARG and _RESET are ignored, as there is only ever one compilation error.
+They exist for compatibility with `next-error'."
+ (interactive)
+ (cl-labels ((goto-next-note-boundary
+ ()
+ (let ((p (or (cider-find-property 'cider-note-p)
+ (cider-find-property 'cider-note-p t))))
+ (when p
+ (goto-char p)
+ (message "%s" (get-char-property p 'cider-note))))))
+ ;; if we're already on a compilation error, first jump to the end of
+ ;; it, so that we find the next error.
+ (when (get-char-property (point) 'cider-note-p)
+ (goto-next-note-boundary))
+ (goto-next-note-boundary)))
+
+(defun cider--show-error-buffer-p ()
+ "Return non-nil if the error buffer must be shown on error.
+Takes into account both the value of `cider-show-error-buffer' and the
+currently selected buffer."
+ (let* ((selected-buffer (window-buffer (selected-window)))
+ (replp (with-current-buffer selected-buffer (derived-mode-p 'cider-repl-mode))))
+ (memq cider-show-error-buffer
+ (if replp
+ '(t always only-in-repl)
+ '(t always except-in-repl)))))
+
+(defun cider-new-error-buffer (&optional mode error-types)
+ "Return an empty error buffer using MODE.
+
+When deciding whether to display the buffer, takes into account not only
+the value of `cider-show-error-buffer' and the currently selected buffer
+but also the ERROR-TYPES of the error, which is checked against the
+`cider-stacktrace-suppressed-errors' set.
+
+When deciding whether to select the buffer, takes into account the value of
+`cider-auto-select-error-buffer'."
+ (if (and (cider--show-error-buffer-p)
+ (not (cider-stacktrace-some-suppressed-errors-p error-types)))
+ (cider-popup-buffer cider-error-buffer cider-auto-select-error-buffer mode 'ancillary)
+ (cider-make-popup-buffer cider-error-buffer mode 'ancillary)))
+
+(defun cider-emit-into-color-buffer (buffer value)
+ "Emit into color BUFFER the provided VALUE."
+ (with-current-buffer buffer
+ (let ((inhibit-read-only t)
+ (buffer-undo-list t))
+ (goto-char (point-max))
+ (insert (format "%s" value))
+ (ansi-color-apply-on-region (point-min) (point-max)))
+ (goto-char (point-min))))
+
+(defun cider--handle-err-eval-response (response)
+ "Render eval RESPONSE into a new error buffer.
+
+Uses the value of the `out' slot in RESPONSE."
+ (nrepl-dbind-response response (out)
+ (when out
+ (let ((error-buffer (cider-new-error-buffer)))
+ (cider-emit-into-color-buffer error-buffer out)
+ (with-current-buffer error-buffer
+ (compilation-minor-mode +1))))))
+
+(defun cider-default-err-eval-handler ()
+ "Display the last exception without middleware support."
+ (cider--handle-err-eval-response
+ (cider-nrepl-sync-request:eval
+ "(clojure.stacktrace/print-cause-trace *e)")))
+
+(defun cider--render-stacktrace-causes (causes &optional error-types)
+ "If CAUSES is non-nil, render its contents into a new error buffer.
+Optional argument ERROR-TYPES contains a list which should determine the
+op/situation that originated this error."
+ (when causes
+ (let ((error-buffer (cider-new-error-buffer #'cider-stacktrace-mode error-types)))
+ (cider-stacktrace-render error-buffer (reverse causes) error-types))))
+
+(defun cider--handle-stacktrace-response (response causes)
+ "Handle stacktrace op RESPONSE, aggregating the result into CAUSES.
+If RESPONSE contains a cause, cons it onto CAUSES and return that. If
+RESPONSE is the final message (i.e. it contains a status), render CAUSES
+into a new error buffer."
+ (nrepl-dbind-response response (class status)
+ (cond (class (cons response causes))
+ (status (cider--render-stacktrace-causes causes)))))
+
+(defun cider-default-err-op-handler ()
+ "Display the last exception, with middleware support."
+ ;; Causes are returned as a series of messages, which we aggregate in `causes'
+ (let (causes)
+ (cider-nrepl-send-request
+ (nconc '("op" "stacktrace")
+ (when (cider--pprint-fn)
+ `("pprint-fn" ,(cider--pprint-fn)))
+ (when cider-stacktrace-print-length
+ `("print-length" ,cider-stacktrace-print-length))
+ (when cider-stacktrace-print-level
+ `("print-level" ,cider-stacktrace-print-level)))
+ (lambda (response)
+ ;; While the return value of `cider--handle-stacktrace-response' is not
+ ;; meaningful for the last message, we do not need the value of `causes'
+ ;; after it has been handled, so it's fine to set it unconditionally here
+ (setq causes (cider--handle-stacktrace-response response causes))))))
+
+(defun cider-default-err-handler ()
+ "This function determines how the error buffer is shown.
+It delegates the actual error content to the eval or op handler."
+ (if (cider-nrepl-op-supported-p "stacktrace")
+ (cider-default-err-op-handler)
+ (cider-default-err-eval-handler)))
+
+(defvar cider-compilation-regexp
+ '("\\(?:.*\\(warning, \\)\\|.*?\\(, compiling\\):(\\)\\(.*?\\):\\([[:digit:]]+\\)\\(?::\\([[:digit:]]+\\)\\)?\\(\\(?: - \\(.*\\)\\)\\|)\\)" 3 4 5 (1))
+ "Specifications for matching errors and warnings in Clojure stacktraces.
+See `compilation-error-regexp-alist' for help on their format.")
+
+(add-to-list 'compilation-error-regexp-alist-alist
+ (cons 'cider cider-compilation-regexp))
+(add-to-list 'compilation-error-regexp-alist 'cider)
+
+(defun cider-extract-error-info (regexp message)
+ "Extract error information with REGEXP against MESSAGE."
+ (let ((file (nth 1 regexp))
+ (line (nth 2 regexp))
+ (col (nth 3 regexp))
+ (type (nth 4 regexp))
+ (pat (car regexp)))
+ (when (string-match pat message)
+ ;; special processing for type (1.2) style
+ (setq type (if (consp type)
+ (or (and (car type) (match-end (car type)) 1)
+ (and (cdr type) (match-end (cdr type)) 0)
+ 2)))
+ (list
+ (when file
+ (let ((val (match-string-no-properties file message)))
+ (unless (string= val "NO_SOURCE_PATH") val)))
+ (when line (string-to-number (match-string-no-properties line message)))
+ (when col
+ (let ((val (match-string-no-properties col message)))
+ (when val (string-to-number val))))
+ (aref [cider-warning-highlight-face
+ cider-warning-highlight-face
+ cider-error-highlight-face]
+ (or type 2))
+ message))))
+
+(defun cider--goto-expression-start ()
+ "Go to the beginning a list, vector, map or set outside of a string.
+We do so by starting and the current position and proceeding backwards
+until we find a delimiters that's not inside a string."
+ (if (and (looking-back "[])}]" (line-beginning-position))
+ (null (nth 3 (syntax-ppss))))
+ (backward-sexp)
+ (while (or (not (looking-at-p "[({[]"))
+ (nth 3 (syntax-ppss)))
+ (backward-char))))
+
+(defun cider--find-last-error-location (message)
+ "Return the location (begin end buffer) from the Clojure error MESSAGE.
+If location could not be found, return nil."
+ (save-excursion
+ (let ((info (cider-extract-error-info cider-compilation-regexp message)))
+ (when info
+ (let ((file (nth 0 info))
+ (line (nth 1 info))
+ (col (nth 2 info)))
+ (unless (or (not (stringp file))
+ (cider--tooling-file-p file))
+ (when-let* ((buffer (cider-find-file file)))
+ (with-current-buffer buffer
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (forward-line (1- line))
+ (move-to-column (or col 0))
+ (let ((begin (progn (if col (cider--goto-expression-start) (back-to-indentation))
+ (point)))
+ (end (progn (if col (forward-list) (move-end-of-line nil))
+ (point))))
+ (list begin end buffer))))))))))))
+
+(defun cider-handle-compilation-errors (message eval-buffer)
+ "Highlight and jump to compilation error extracted from MESSAGE.
+EVAL-BUFFER is the buffer that was current during user's interactive
+evaluation command. Honor `cider-auto-jump-to-error'."
+ (when-let* ((loc (cider--find-last-error-location message))
+ (overlay (make-overlay (nth 0 loc) (nth 1 loc) (nth 2 loc)))
+ (info (cider-extract-error-info cider-compilation-regexp message)))
+ (let* ((face (nth 3 info))
+ (note (nth 4 info))
+ (auto-jump (if (eq cider-auto-jump-to-error 'errors-only)
+ (not (eq face 'cider-warning-highlight-face))
+ cider-auto-jump-to-error)))
+ (overlay-put overlay 'cider-note-p t)
+ (overlay-put overlay 'font-lock-face face)
+ (overlay-put overlay 'cider-note note)
+ (overlay-put overlay 'help-echo note)
+ (overlay-put overlay 'modification-hooks
+ (list (lambda (o &rest _args) (delete-overlay o))))
+ (when auto-jump
+ (with-current-buffer eval-buffer
+ (push-mark)
+ ;; At this stage selected window commonly is *cider-error* and we need to
+ ;; re-select the original user window. If eval-buffer is not
+ ;; visible it was probably covered as a result of a small screen or user
+ ;; configuration (https://github.com/clojure-emacs/cider/issues/847). In
+ ;; that case we don't jump at all in order to avoid covering *cider-error*
+ ;; buffer.
+ (when-let* ((win (get-buffer-window eval-buffer)))
+ (with-selected-window win
+ (cider-jump-to (nth 2 loc) (car loc)))))))))
+
+
+;;; Interactive evaluation handlers
+(defun cider-insert-eval-handler (&optional buffer)
+ "Make an nREPL evaluation handler for the BUFFER.
+The handler simply inserts the result value in BUFFER."
+ (let ((eval-buffer (current-buffer)))
+ (nrepl-make-response-handler (or buffer eval-buffer)
+ (lambda (_buffer value)
+ (with-current-buffer buffer
+ (insert value)))
+ (lambda (_buffer out)
+ (cider-repl-emit-interactive-stdout out))
+ (lambda (_buffer err)
+ (cider-handle-compilation-errors err eval-buffer))
+ '())))
+
+(defun cider--emit-interactive-eval-output (output repl-emit-function)
+ "Emit output resulting from interactive code evaluation.
+The OUTPUT can be sent to either a dedicated output buffer or the current
+REPL buffer. This is controlled by `cider-interactive-eval-output-destination'.
+REPL-EMIT-FUNCTION emits the OUTPUT."
+ (pcase cider-interactive-eval-output-destination
+ (`output-buffer (let ((output-buffer (or (get-buffer cider-output-buffer)
+ (cider-popup-buffer cider-output-buffer t))))
+ (cider-emit-into-popup-buffer output-buffer output)
+ (pop-to-buffer output-buffer)))
+ (`repl-buffer (funcall repl-emit-function output))
+ (_ (error "Unsupported value %s for `cider-interactive-eval-output-destination'"
+ cider-interactive-eval-output-destination))))
+
+(defun cider-emit-interactive-eval-output (output)
+ "Emit OUTPUT resulting from interactive code evaluation.
+The output can be send to either a dedicated output buffer or the current
+REPL buffer. This is controlled via
+`cider-interactive-eval-output-destination'."
+ (cider--emit-interactive-eval-output output 'cider-repl-emit-interactive-stdout))
+
+(defun cider-emit-interactive-eval-err-output (output)
+ "Emit err OUTPUT resulting from interactive code evaluation.
+The output can be send to either a dedicated output buffer or the current
+REPL buffer. This is controlled via
+`cider-interactive-eval-output-destination'."
+ (cider--emit-interactive-eval-output output 'cider-repl-emit-interactive-stderr))
+
+(defun cider--make-fringe-overlays-for-region (beg end)
+ "Place eval indicators on all sexps between BEG and END."
+ (with-current-buffer (if (markerp end)
+ (marker-buffer end)
+ (current-buffer))
+ (save-excursion
+ (goto-char beg)
+ (remove-overlays beg end 'category 'cider-fringe-indicator)
+ (condition-case nil
+ (while (progn (clojure-forward-logical-sexp)
+ (and (<= (point) end)
+ (not (eobp))))
+ (cider--make-fringe-overlay (point)))
+ (scan-error nil)))))
+
+(defun cider-interactive-eval-handler (&optional buffer place)
+ "Make an interactive eval handler for BUFFER.
+PLACE is used to display the evaluation result.
+If non-nil, it can be the position where the evaluated sexp ends,
+or it can be a list with (START END) of the evaluated region."
+ (let* ((eval-buffer (current-buffer))
+ (beg (car-safe place))
+ (end (or (car-safe (cdr-safe place)) place))
+ (beg (when beg (copy-marker beg)))
+ (end (when end (copy-marker end)))
+ (fringed nil))
+ (nrepl-make-response-handler (or buffer eval-buffer)
+ (lambda (_buffer value)
+ (if beg
+ (unless fringed
+ (cider--make-fringe-overlays-for-region beg end)
+ (setq fringed t))
+ (cider--make-fringe-overlay end))
+ (cider--display-interactive-eval-result value end))
+ (lambda (_buffer out)
+ (cider-emit-interactive-eval-output out))
+ (lambda (_buffer err)
+ (cider-emit-interactive-eval-err-output err)
+ (cider-handle-compilation-errors err eval-buffer))
+ '())))
+
+(defun cider-load-file-handler (&optional buffer)
+ "Make a load file handler for BUFFER."
+ (let ((eval-buffer (current-buffer)))
+ (nrepl-make-response-handler (or buffer eval-buffer)
+ (lambda (buffer value)
+ (cider--display-interactive-eval-result value)
+ (when (buffer-live-p buffer)
+ (with-current-buffer buffer
+ (cider--make-fringe-overlays-for-region (point-min) (point-max))
+ (run-hooks 'cider-file-loaded-hook))))
+ (lambda (_buffer value)
+ (cider-emit-interactive-eval-output value))
+ (lambda (_buffer err)
+ (cider-emit-interactive-eval-err-output err)
+ (cider-handle-compilation-errors err eval-buffer))
+ '()
+ (lambda ()
+ (funcall nrepl-err-handler)))))
+
+(defun cider-eval-print-handler (&optional buffer)
+ "Make a handler for evaluating and printing result in BUFFER."
+ (nrepl-make-response-handler (or buffer (current-buffer))
+ (lambda (buffer value)
+ (with-current-buffer buffer
+ (insert
+ (if (derived-mode-p 'cider-clojure-interaction-mode)
+ (format "\n%s\n" value)
+ value))))
+ (lambda (_buffer out)
+ (cider-emit-interactive-eval-output out))
+ (lambda (_buffer err)
+ (cider-emit-interactive-eval-err-output err))
+ '()))
+
+(defun cider-eval-print-with-comment-handler (buffer location comment-prefix)
+ "Make a handler for evaluating and printing commented results in BUFFER.
+LOCATION is the location at which to insert. COMMENT-PREFIX is the comment
+prefix to use."
+ (nrepl-make-response-handler buffer
+ (lambda (buffer value)
+ (with-current-buffer buffer
+ (save-excursion
+ (goto-char location)
+ (insert (concat comment-prefix
+ value "\n")))))
+ (lambda (_buffer out)
+ (cider-emit-interactive-eval-output out))
+ (lambda (_buffer err)
+ (cider-emit-interactive-eval-err-output err))
+ '()))
+
+(defun cider-eval-pprint-with-multiline-comment-handler (buffer location comment-prefix continued-prefix comment-postfix)
+ "Make a handler for evaluating and inserting results in BUFFER.
+The inserted text is pretty-printed and region will be commented.
+LOCATION is the location at which to insert.
+COMMENT-PREFIX is the comment prefix for the first line of output.
+CONTINUED-PREFIX is the comment prefix to use for the remaining lines.
+COMMENT-POSTFIX is the text to output after the last line."
+ (cl-flet ((multiline-comment-handler (buffer value)
+ (with-current-buffer buffer
+ (save-excursion
+ (goto-char location)
+ (let ((lines (split-string value "[\n]+" t)))
+ ;; only the first line gets the normal comment-prefix
+ (insert (concat comment-prefix (pop lines)))
+ (dolist (elem lines)
+ (insert (concat "\n" continued-prefix elem)))
+ (unless (string= comment-postfix "")
+ (insert comment-postfix)))))))
+ (nrepl-make-response-handler buffer
+ '()
+ #'multiline-comment-handler
+ #'multiline-comment-handler
+ '())))
+
+(defun cider-popup-eval-out-handler (&optional buffer)
+ "Make a handler for evaluating and printing stdout/stderr in popup BUFFER.
+This is used by pretty-printing commands and intentionally discards their results."
+ (cl-flet ((popup-output-handler (buffer str)
+ (cider-emit-into-popup-buffer buffer
+ (ansi-color-apply str)
+ nil
+ t)))
+ (nrepl-make-response-handler (or buffer (current-buffer))
+ '()
+ ;; stdout handler
+ #'popup-output-handler
+ ;; stderr handler
+ #'popup-output-handler
+ '())))
+
+
+;;; Interactive valuation commands
+
+(defvar cider-to-nrepl-filename-function
+ (with-no-warnings
+ (if (eq system-type 'cygwin)
+ #'cygwin-convert-file-name-to-windows
+ #'identity))
+ "Function to translate Emacs filenames to nREPL namestrings.")
+
+(defun cider--prep-interactive-eval (form connection)
+ "Prepare the environment for an interactive eval of FORM in CONNECTION.
+Ensure the current ns declaration has been evaluated (so that the ns
+containing FORM exists). Cache ns-form in the current buffer unless FORM is
+ns declaration itself. Clear any compilation highlights and kill the error
+window."
+ (cider--clear-compilation-highlights)
+ (cider--quit-error-window)
+ (let ((cur-ns-form (cider-ns-form)))
+ (when (and cur-ns-form
+ (not (cider-ns-form-p form))
+ (cider-repl--ns-form-changed-p cur-ns-form connection))
+ (when cider-auto-track-ns-form-changes
+ ;; The first interactive eval on a file can load a lot of libs. This can
+ ;; easily lead to more than 10 sec.
+ (let ((nrepl-sync-request-timeout 30))
+ ;; TODO: check for evaluation errors
+ (cider-nrepl-sync-request:eval cur-ns-form connection)))
+ ;; cache at the end, in case of errors
+ (cider-repl--cache-ns-form cur-ns-form connection))))
+
+(defvar-local cider-interactive-eval-override nil
+ "Function to call instead of `cider-interactive-eval'.")
+
+(defun cider-interactive-eval (form &optional callback bounds additional-params)
+ "Evaluate FORM and dispatch the response to CALLBACK.
+If the code to be evaluated comes from a buffer, it is preferred to use a
+nil FORM, and specify the code via the BOUNDS argument instead.
+
+This function is the main entry point in CIDER's interactive evaluation
+API. Most other interactive eval functions should rely on this function.
+If CALLBACK is nil use `cider-interactive-eval-handler'.
+BOUNDS, if non-nil, is a list of two numbers marking the start and end
+positions of FORM in its buffer.
+ADDITIONAL-PARAMS is a plist to be appended to the request message.
+
+If `cider-interactive-eval-override' is a function, call it with the same
+arguments and only proceed with evaluation if it returns nil."
+ (let ((form (or form (apply #'buffer-substring-no-properties bounds)))
+ (start (car-safe bounds))
+ (end (car-safe (cdr-safe bounds))))
+ (when (and start end)
+ (remove-overlays start end 'cider-temporary t))
+ (unless (and cider-interactive-eval-override
+ (functionp cider-interactive-eval-override)
+ (funcall cider-interactive-eval-override form callback bounds))
+ (cider-map-repls :auto
+ (lambda (connection)
+ (cider--prep-interactive-eval form connection)
+ (cider-nrepl-request:eval
+ form
+ (or callback (cider-interactive-eval-handler nil bounds))
+ ;; always eval ns forms in the user namespace
+ ;; otherwise trying to eval ns form for the first time will produce an error
+ (if (cider-ns-form-p form) "user" (cider-current-ns))
+ (when start (line-number-at-pos start))
+ (when start (cider-column-number-at-pos start))
+ additional-params
+ connection))))))
+
+(defun cider-eval-region (start end)
+ "Evaluate the region between START and END."
+ (interactive "r")
+ (cider-interactive-eval nil nil (list start end)))
+
+(defun cider-eval-last-sexp (&optional output-to-current-buffer)
+ "Evaluate the expression preceding point.
+If invoked with OUTPUT-TO-CURRENT-BUFFER, print the result in the current
+buffer."
+ (interactive "P")
+ (cider-interactive-eval nil
+ (when output-to-current-buffer (cider-eval-print-handler))
+ (cider-last-sexp 'bounds)))
+
+(defun cider-eval-last-sexp-and-replace ()
+ "Evaluate the expression preceding point and replace it with its result."
+ (interactive)
+ (let ((last-sexp (cider-last-sexp)))
+ ;; we have to be sure the evaluation won't result in an error
+ (cider-nrepl-sync-request:eval last-sexp)
+ ;; seems like the sexp is valid, so we can safely kill it
+ (backward-kill-sexp)
+ (cider-interactive-eval last-sexp (cider-eval-print-handler))))
+
+(defun cider-eval-sexp-at-point (&optional output-to-current-buffer)
+ "Evaluate the expression around point.
+If invoked with OUTPUT-TO-CURRENT-BUFFER, output the result to current buffer."
+ (interactive "P")
+ (save-excursion
+ (goto-char (cadr (cider-sexp-at-point 'bounds)))
+ (cider-eval-last-sexp output-to-current-buffer)))
+
+(defvar-local cider-previous-eval-context nil
+ "The previous evaluation context if any.
+That's set by commands like `cider-eval-last-sexp-in-context'.")
+
+(defun cider--eval-in-context (code)
+ "Evaluate CODE in user-provided evaluation context."
+ (let* ((code (string-trim-right code))
+ (eval-context (read-string
+ (format "Evaluation context (let-style) for `%s': " code)
+ cider-previous-eval-context))
+ (code (concat "(let [" eval-context "]\n " code ")")))
+ (cider-interactive-eval code)
+ (setq-local cider-previous-eval-context eval-context)))
+
+(defun cider-eval-last-sexp-in-context ()
+ "Evaluate the preceding sexp in user-supplied context.
+The context is just a let binding vector (without the brackets).
+The context is remembered between command invocations."
+ (interactive)
+ (cider--eval-in-context (cider-last-sexp)))
+
+(defun cider-eval-sexp-at-point-in-context ()
+ "Evaluate the preceding sexp in user-supplied context.
+
+The context is just a let binding vector (without the brackets).
+The context is remembered between command invocations."
+ (interactive)
+ (cider--eval-in-context (cider-sexp-at-point)))
+
+(defun cider-eval-defun-to-comment (&optional insert-before)
+ "Evaluate the \"top-level\" form and insert result as comment.
+
+The formatting of the comment is defined in `cider-comment-prefix'
+which, by default, is \";; => \" and can be customized.
+
+With the prefix arg INSERT-BEFORE, insert before the form, otherwise afterwards."
+ (interactive "P")
+ (let* ((bounds (cider-defun-at-point 'bounds))
+ (insertion-point (nth (if insert-before 0 1) bounds)))
+ (cider-interactive-eval nil
+ (cider-eval-print-with-comment-handler
+ (current-buffer)
+ insertion-point
+ cider-comment-prefix)
+ bounds)))
+
+(defun cider-pprint-form-to-comment (form-fn insert-before)
+ "Evaluate the form selected by FORM-FN and insert result as comment.
+FORM-FN can be either `cider-last-sexp' or `cider-defun-at-point'.
+
+The formatting of the comment is controlled via three options:
+ `cider-comment-prefix' \";; => \"
+ `cider-comment-continued-prefix' \";; \"
+ `cider-comment-postfix' \"\"
+
+so that with customization you can optionally wrap the output
+in the reader macro \"#_( .. )\", or \"(comment ... )\", or any
+other desired formatting.
+
+If INSERT-BEFORE is non-nil, insert before the form, otherwise afterwards."
+ (let* ((bounds (funcall form-fn 'bounds))
+ (insertion-point (nth (if insert-before 0 1) bounds))
+ ;; when insert-before, we need a newline after the output to
+ ;; avoid commenting the first line of the form
+ (comment-postfix (concat cider-comment-postfix
+ (if insert-before "\n" ""))))
+ (cider-interactive-eval nil
+ (cider-eval-pprint-with-multiline-comment-handler
+ (current-buffer)
+ insertion-point
+ cider-comment-prefix
+ cider-comment-continued-prefix
+ comment-postfix)
+ bounds
+ (cider--nrepl-pprint-request-plist (cider--pretty-print-width)))))
+
+(defun cider-pprint-eval-last-sexp-to-comment (&optional insert-before)
+ "Evaluate the last sexp and insert result as comment.
+
+The formatting of the comment is controlled via three options:
+ `cider-comment-prefix' \";; => \"
+ `cider-comment-continued-prefix' \";; \"
+ `cider-comment-postfix' \"\"
+
+so that with customization you can optionally wrap the output
+in the reader macro \"#_( .. )\", or \"(comment ... )\", or any
+other desired formatting.
+
+If INSERT-BEFORE is non-nil, insert before the form, otherwise afterwards."
+ (interactive "P")
+ (cider-pprint-form-to-comment 'cider-last-sexp insert-before))
+
+(defun cider-pprint-eval-defun-to-comment (&optional insert-before)
+ "Evaluate the \"top-level\" form and insert result as comment.
+
+The formatting of the comment is controlled via three options:
+ `cider-comment-prefix' \";; => \"
+ `cider-comment-continued-prefix' \";; \"
+ `cider-comment-postfix' \"\"
+
+so that with customization you can optionally wrap the output
+in the reader macro \"#_( .. )\", or \"(comment ... )\", or any
+other desired formatting.
+
+If INSERT-BEFORE is non-nil, insert before the form, otherwise afterwards."
+ (interactive "P")
+ (cider-pprint-form-to-comment 'cider-defun-at-point insert-before))
+
+(declare-function cider-switch-to-repl-buffer "cider-mode")
+
+(defun cider-eval-last-sexp-to-repl (&optional prefix)
+ "Evaluate the expression preceding point and insert its result in the REPL.
+If invoked with a PREFIX argument, switch to the REPL buffer."
+ (interactive "P")
+ (cider-interactive-eval nil
+ (cider-insert-eval-handler (cider-current-repl))
+ (cider-last-sexp 'bounds))
+ (when prefix
+ (cider-switch-to-repl-buffer)))
+
+(defun cider-pprint-eval-last-sexp-to-repl (&optional prefix)
+ "Evaluate expr before point and insert its pretty-printed result in the REPL.
+If invoked with a PREFIX argument, switch to the REPL buffer."
+ (interactive "P")
+ (cider-interactive-eval nil
+ (cider-insert-eval-handler (cider-current-repl))
+ (cider-last-sexp 'bounds)
+ (cider--nrepl-pprint-request-plist (cider--pretty-print-width)))
+ (when prefix
+ (cider-switch-to-repl-buffer)))
+
+(defun cider-eval-print-last-sexp ()
+ "Evaluate the expression preceding point.
+Print its value into the current buffer."
+ (interactive)
+ (cider-interactive-eval nil
+ (cider-eval-print-handler)
+ (cider-last-sexp 'bounds)))
+
+(defun cider--pprint-eval-form (form)
+ "Pretty print FORM in popup buffer."
+ (let* ((result-buffer (cider-popup-buffer cider-result-buffer nil 'clojure-mode 'ancillary))
+ (handler (cider-popup-eval-out-handler result-buffer)))
+ (cider-interactive-eval (when (stringp form) form)
+ handler
+ (when (consp form) form)
+ (cider--nrepl-pprint-request-plist (cider--pretty-print-width)))))
+
+(defun cider-pprint-eval-last-sexp (&optional output-to-current-buffer)
+ "Evaluate the sexp preceding point and pprint its value.
+If invoked with OUTPUT-TO-CURRENT-BUFFER, insert as comment in the current
+buffer, else display in a popup buffer."
+ (interactive "P")
+ (if output-to-current-buffer
+ (cider-pprint-eval-last-sexp-to-comment)
+ (cider--pprint-eval-form (cider-last-sexp 'bounds))))
+
+(defun cider--prompt-and-insert-inline-dbg ()
+ "Insert a #dbg button at the current sexp."
+ (save-excursion
+ (let ((beg))
+ (skip-chars-forward "\r\n[:blank:]")
+ (unless (looking-at-p "(")
+ (ignore-errors (backward-up-list)))
+ (setq beg (point))
+ (let* ((cond (cider-read-from-minibuffer "Condition for debugging (leave empty for \"always\"): "))
+ (button (propertize (concat "#dbg"
+ (unless (equal cond "")
+ (format " ^{:break/when %s}" cond)))
+ 'font-lock-face 'cider-fragile-button-face)))
+ (when (> (current-column) 30)
+ (insert "\n")
+ (indent-according-to-mode))
+ (insert button)
+ (when (> (current-column) 40)
+ (insert "\n")
+ (indent-according-to-mode)))
+ (make-button beg (point)
+ 'help-echo "Breakpoint. Reevaluate this form to remove it."
+ :type 'cider-fragile))))
+
+(defun cider-eval-defun-at-point (&optional debug-it)
+ "Evaluate the current toplevel form, and print result in the minibuffer.
+With DEBUG-IT prefix argument, also debug the entire form as with the
+command `cider-debug-defun-at-point'."
+ (interactive "P")
+ (let ((inline-debug (eq 16 (car-safe debug-it))))
+ (when debug-it
+ (when (derived-mode-p 'clojurescript-mode)
+ (when (y-or-n-p (concat "The debugger doesn't support ClojureScript yet, and we need help with that."
+ " \nWould you like to read the Feature Request?"))
+ (browse-url "https://github.com/clojure-emacs/cider/issues/1416"))
+ (user-error "The debugger does not support ClojureScript"))
+ (when inline-debug
+ (cider--prompt-and-insert-inline-dbg)))
+ (cider-interactive-eval (when (and debug-it (not inline-debug))
+ (concat "#dbg\n" (cider-defun-at-point)))
+ nil (cider-defun-at-point 'bounds))))
+
+(defun cider--calculate-opening-delimiters ()
+ "Walks up the list of expressions to collect all sexp opening delimiters.
+The result is a list of the delimiters.
+
+That function is used in `cider-eval-defun-up-to-point' so it can make an
+incomplete expression complete."
+ (interactive)
+ (let ((result nil))
+ (save-excursion
+ (condition-case nil
+ (while t
+ (backward-up-list)
+ (push (char-after) result))
+ (error result)))))
+
+(defun cider--matching-delimiter (delimiter)
+ "Get the matching (opening/closing) delimiter for DELIMITER."
+ (pcase delimiter
+ (?\( ?\))
+ (?\[ ?\])
+ (?\{ ?\})
+ (?\) ?\()
+ (?\] ?\[)
+ (?\} ?\{)))
+
+(defun cider--calculate-closing-delimiters ()
+ "Compute the list of closing delimiters to make the defun before point valid."
+ (mapcar #'cider--matching-delimiter (cider--calculate-opening-delimiters)))
+
+(defun cider-eval-defun-up-to-point (&optional output-to-current-buffer)
+ "Evaluate the current toplevel form up to point.
+If invoked with OUTPUT-TO-CURRENT-BUFFER, print the result in the current
+buffer. It constructs an expression to eval in the following manner:
+
+- It find the code between the point and the start of the toplevel expression;
+- It balances this bit of code by closing all open expressions;
+- It evaluates the resulting code using `cider-interactive-eval'."
+ (interactive "P")
+ (let* ((beg-of-defun (save-excursion (beginning-of-defun) (point)))
+ (code (buffer-substring-no-properties beg-of-defun (point)))
+ (code (concat code (cider--calculate-closing-delimiters))))
+ (cider-interactive-eval
+ code
+ (when output-to-current-buffer (cider-eval-print-handler)))))
+
+(defun cider-eval-sexp-up-to-point (&optional output-to-current-buffer)
+ "Evaluate the current sexp form up to point.
+If invoked with OUTPUT-TO-CURRENT-BUFFER, print the result in the current
+buffer. It constructs an expression to eval in the following manner:
+
+- It finds the code between the point and the start of the sexp expression;
+- It balances this bit of code by closing the expression;
+- It evaluates the resulting code using `cider-interactive-eval'."
+ (interactive "P")
+ (let* ((beg-of-sexp (save-excursion (up-list) (backward-list) (point)))
+ (beg-delimiter (save-excursion (up-list) (backward-list) (char-after)))
+ (beg-set? (save-excursion (up-list) (backward-list) (char-before)))
+ (code (buffer-substring-no-properties beg-of-sexp (point)))
+ (code (if (= beg-set? ?#) (concat (list beg-set?) code) code))
+ (code (concat code (list (cider--matching-delimiter beg-delimiter)))))
+ (cider-interactive-eval code
+ (when output-to-current-buffer (cider-eval-print-handler)))))
+
+(defun cider-pprint-eval-defun-at-point (&optional output-to-current-buffer)
+ "Evaluate the \"top-level\" form at point and pprint its value.
+If invoked with OUTPUT-TO-CURRENT-BUFFER, insert as comment in the current
+buffer, else display in a popup buffer."
+ (interactive "P")
+ (if output-to-current-buffer
+ (cider-pprint-eval-defun-to-comment)
+ (cider--pprint-eval-form (cider-defun-at-point 'bounds))))
+
+(defun cider-eval-ns-form ()
+ "Evaluate the current buffer's namespace form."
+ (interactive)
+ (when (clojure-find-ns)
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (cider-eval-defun-at-point))))
+
+(defun cider-read-and-eval (&optional value)
+ "Read a sexp from the minibuffer and output its result to the echo area.
+If VALUE is non-nil, it is inserted into the minibuffer as initial input."
+ (interactive)
+ (let* ((form (cider-read-from-minibuffer "Clojure Eval: " value))
+ (override cider-interactive-eval-override)
+ (ns-form (if (cider-ns-form-p form) "" (format "(ns %s)" (cider-current-ns)))))
+ (with-current-buffer (get-buffer-create cider-read-eval-buffer)
+ (erase-buffer)
+ (clojure-mode)
+ (unless (string= "" ns-form)
+ (insert ns-form "\n\n"))
+ (insert form)
+ (let ((cider-interactive-eval-override override))
+ (cider-interactive-eval form)))))
+
+(defun cider-read-and-eval-defun-at-point ()
+ "Insert the toplevel form at point in the minibuffer and output its result.
+The point is placed next to the function name in the minibuffer to allow
+passing arguments."
+ (interactive)
+ (let* ((fn-name (cadr (split-string (cider-defun-at-point))))
+ (form (format "(%s)" fn-name)))
+ (cider-read-and-eval (cons form (length form)))))
+
+;; Eval keymaps
+(defvar cider-eval-pprint-commands-map
+ (let ((map (define-prefix-command 'cider-eval-pprint-commands-map)))
+ ;; single key bindings defined last for display in menu
+ (define-key map (kbd "e") #'cider-pprint-eval-last-sexp)
+ (define-key map (kbd "d") #'cider-pprint-eval-defun-at-point)
+ (define-key map (kbd "c e") #'cider-pprint-eval-last-sexp-to-comment)
+ (define-key map (kbd "c d") #'cider-pprint-eval-defun-to-comment)
+
+ ;; duplicates with C- for convenience
+ (define-key map (kbd "C-e") #'cider-pprint-eval-last-sexp)
+ (define-key map (kbd "C-d") #'cider-pprint-eval-defun-at-point)
+ (define-key map (kbd "C-c e") #'cider-pprint-eval-last-sexp-to-comment)
+ (define-key map (kbd "C-c C-e") #'cider-pprint-eval-last-sexp-to-comment)
+ (define-key map (kbd "C-c d") #'cider-pprint-eval-defun-to-comment)
+ (define-key map (kbd "C-c C-d") #'cider-pprint-eval-defun-to-comment)))
+
+(defvar cider-eval-commands-map
+ (let ((map (define-prefix-command 'cider-eval-commands-map)))
+ ;; single key bindings defined last for display in menu
+ (define-key map (kbd "w") #'cider-eval-last-sexp-and-replace)
+ (define-key map (kbd "r") #'cider-eval-region)
+ (define-key map (kbd "n") #'cider-eval-ns-form)
+ (define-key map (kbd "d") #'cider-eval-defun-at-point)
+ (define-key map (kbd "e") #'cider-eval-last-sexp)
+ (define-key map (kbd "v") #'cider-eval-sexp-at-point)
+ (define-key map (kbd "o") #'cider-eval-sexp-up-to-point)
+ (define-key map (kbd ".") #'cider-read-and-eval-defun-at-point)
+ (define-key map (kbd "z") #'cider-eval-defun-up-to-point)
+ (define-key map (kbd "c") #'cider-eval-last-sexp-in-context)
+ (define-key map (kbd "b") #'cider-eval-sexp-at-point-in-context)
+ (define-key map (kbd "f") 'cider-eval-pprint-commands-map)
+
+ ;; duplicates with C- for convenience
+ (define-key map (kbd "C-w") #'cider-eval-last-sexp-and-replace)
+ (define-key map (kbd "C-r") #'cider-eval-region)
+ (define-key map (kbd "C-n") #'cider-eval-ns-form)
+ (define-key map (kbd "C-d") #'cider-eval-defun-at-point)
+ (define-key map (kbd "C-f") #'cider-eval-last-sexp)
+ (define-key map (kbd "C-v") #'cider-eval-sexp-at-point)
+ (define-key map (kbd "C-o") #'cider-eval-sexp-up-to-point)
+ (define-key map (kbd "C-.") #'cider-read-and-eval-defun-at-point)
+ (define-key map (kbd "C-z") #'cider-eval-defun-up-to-point)
+ (define-key map (kbd "C-c") #'cider-eval-last-sexp-in-context)
+ (define-key map (kbd "C-b") #'cider-eval-sexp-at-point-in-context)
+ (define-key map (kbd "C-f") 'cider-eval-pprint-commands-map)))
+
+(defun cider--file-string (file)
+ "Read the contents of a FILE and return as a string."
+ (with-current-buffer (find-file-noselect file)
+ (substring-no-properties (buffer-string))))
+
+(defun cider-load-buffer (&optional buffer)
+ "Load (eval) BUFFER's file in nREPL.
+If no buffer is provided the command acts on the current buffer. If the
+buffer is for a cljc file, and both a Clojure and ClojureScript REPL exists
+for the project, it is evaluated in both REPLs."
+ (interactive)
+ (setq buffer (or buffer (current-buffer)))
+ ;; When cider-load-buffer or cider-load-file are called in programs the
+ ;; current context might not match the buffer's context. We use the caller
+ ;; context instead of the buffer's context because that's the common use
+ ;; case. For the other use case just let-bind the default-directory.
+ (let ((orig-default-directory default-directory))
+ (with-current-buffer buffer
+ (check-parens)
+ (let ((default-directory orig-default-directory))
+ (unless buffer-file-name
+ (user-error "Buffer `%s' is not associated with a file" (current-buffer)))
+ (when (and cider-save-file-on-load
+ (buffer-modified-p)
+ (or (eq cider-save-file-on-load t)
+ (y-or-n-p (format "Save file %s? " buffer-file-name))))
+ (save-buffer))
+ (remove-overlays nil nil 'cider-temporary t)
+ (cider--clear-compilation-highlights)
+ (cider--quit-error-window)
+ (let ((filename (buffer-file-name buffer))
+ (ns-form (cider-ns-form)))
+ (cider-map-repls :auto
+ (lambda (repl)
+ (when ns-form
+ (cider-repl--cache-ns-form ns-form repl))
+ (cider-request:load-file (cider--file-string filename)
+ (funcall cider-to-nrepl-filename-function
+ (cider--server-filename filename))
+ (file-name-nondirectory filename)
+ repl)))
+ (message "Loading %s..." filename))))))
+
+(defun cider-load-file (filename)
+ "Load (eval) the Clojure file FILENAME in nREPL.
+If the file is a cljc file, and both a Clojure and ClojureScript REPL
+exists for the project, it is evaluated in both REPLs. The heavy lifting
+is done by `cider-load-buffer'."
+ (interactive (list
+ (read-file-name "Load file: " nil nil nil
+ (when (buffer-file-name)
+ (file-name-nondirectory
+ (buffer-file-name))))))
+ (if-let* ((buffer (find-buffer-visiting filename)))
+ (cider-load-buffer buffer)
+ (cider-load-buffer (find-file-noselect filename))))
+
+(defun cider-load-all-files (directory)
+ "Load all files in DIRECTORY (recursively).
+Useful when the running nREPL on remote host."
+ (interactive "DLoad files beneath directory: ")
+ (mapcar #'cider-load-file
+ (directory-files-recursively directory ".clj$")))
+
+(defalias 'cider-eval-file 'cider-load-file
+ "A convenience alias as some people are confused by the load-* names.")
+
+(defalias 'cider-eval-all-files 'cider-load-all-files
+ "A convenience alias as some people are confused by the load-* names.")
+
+(defalias 'cider-eval-buffer 'cider-load-buffer
+ "A convenience alias as some people are confused by the load-* names.")
+
+(defun cider-load-all-project-ns ()
+ "Load all namespaces in the current project."
+ (interactive)
+ (cider-ensure-connected)
+ (cider-ensure-op-supported "ns-load-all")
+ (when (y-or-n-p "Are you sure you want to load all namespaces in the project? ")
+ (message "Loading all project namespaces...")
+ (let ((loaded-ns-count (length (cider-sync-request:ns-load-all))))
+ (message "Loaded %d namespaces" loaded-ns-count))))
+
+(provide 'cider-eval)
+
+;;; cider-eval.el ends here
diff --git a/cider-find.el b/cider-find.el
new file mode 100644
index 00000000..dc49ddac
--- /dev/null
+++ b/cider-find.el
@@ -0,0 +1,231 @@
+;;; cider-find.el --- Functionality for finding things -*- lexical-binding: t -*-
+
+;; Copyright © 2013-2018 Bozhidar Batsov, Artur Malabarba and CIDER contributors
+;;
+;; Author: Bozhidar Batsov <bozhidar@batsov.com>
+;; Artur Malabarba <bruce.connor.am@gmail.com>
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;; This file is not part of GNU Emacs.
+
+;;; Commentary:
+
+;; A bunch of commands for finding resources and definitions.
+
+;;; Code:
+
+(require 'cider-client)
+(require 'cider-common)
+
+(require 'thingatpt)
+
+(defun cider--find-var-other-window (var &optional line)
+ "Find the definition of VAR, optionally at a specific LINE.
+
+Display the results in a different window."
+ (if-let* ((info (cider-var-info var)))
+ (progn
+ (if line (setq info (nrepl-dict-put info "line" line)))
+ (cider--jump-to-loc-from-info info t))
+ (user-error "Symbol `%s' not resolved" var)))
+
+(defun cider--find-var (var &optional line)
+ "Find the definition of VAR, optionally at a specific LINE."
+ (if-let* ((info (cider-var-info var)))
+ (progn
+ (if line (setq info (nrepl-dict-put info "line" line)))
+ (cider--jump-to-loc-from-info info))
+ (user-error "Symbol `%s' not resolved" var)))
+
+;;;###autoload
+(defun cider-find-var (&optional arg var line)
+ "Find definition for VAR at LINE.
+Prompt according to prefix ARG and `cider-prompt-for-symbol'.
+A single or double prefix argument inverts the meaning of
+`cider-prompt-for-symbol'. A prefix of `-` or a double prefix argument causes
+the results to be displayed in a different window. The default value is
+thing at point."
+ (interactive "P")
+ (cider-ensure-op-supported "info")
+ (if var
+ (cider--find-var var line)
+ (funcall (cider-prompt-for-symbol-function arg)
+ "Symbol"
+ (if (cider--open-other-window-p arg)
+ #'cider--find-var-other-window
+ #'cider--find-var))))
+
+;;;###autoload
+(defun cider-find-dwim-at-mouse (event)
+ "Find and display variable or resource at mouse EVENT."
+ (interactive "e")
+ (cider-ensure-op-supported "info")
+ (if-let* ((symbol-file (save-excursion
+ (mouse-set-point event)
+ (cider-symbol-at-point 'look-back))))
+ (cider-find-dwim symbol-file)
+ (user-error "No variable or resource here")))
+
+(defun cider--find-dwim (symbol-file callback &optional other-window)
+ "Find the SYMBOL-FILE at point.
+CALLBACK upon failure to invoke prompt if not prompted previously.
+Show results in a different window if OTHER-WINDOW is true."
+ (if-let* ((info (cider-var-info symbol-file)))
+ (cider--jump-to-loc-from-info info other-window)
+ (progn
+ (cider-ensure-op-supported "resource")
+ (if-let* ((resource (cider-sync-request:resource symbol-file))
+ (buffer (cider-find-file resource)))
+ (cider-jump-to buffer 0 other-window)
+ (if (cider--prompt-for-symbol-p current-prefix-arg)
+ (error "Resource or var %s not resolved" symbol-file)
+ (let ((current-prefix-arg (if current-prefix-arg nil '(4))))
+ (call-interactively callback)))))))
+
+(defun cider--find-dwim-interactive (prompt)
+ "Get interactive arguments for jump-to functions using PROMPT as needed."
+ (if (cider--prompt-for-symbol-p current-prefix-arg)
+ (list
+ (cider-read-from-minibuffer prompt (thing-at-point 'filename)))
+ (list (or (thing-at-point 'filename) "")))) ; No prompt.
+
+(defun cider-find-dwim-other-window (symbol-file)
+ "Jump to SYMBOL-FILE at point, place results in other window."
+ (interactive (cider--find-dwim-interactive "Jump to: "))
+ (cider--find-dwim symbol-file 'cider-find-dwim-other-window t))
+
+;;;###autoload
+(defun cider-find-dwim (symbol-file)
+ "Find and display the SYMBOL-FILE at point.
+SYMBOL-FILE could be a var or a resource. If thing at point is empty then
+show dired on project. If var is not found, try to jump to resource of the
+same name. When called interactively, a prompt is given according to the
+variable `cider-prompt-for-symbol'. A single or double prefix argument
+inverts the meaning. A prefix of `-' or a double prefix argument causes
+the results to be displayed in a different window. A default value of thing
+at point is given when prompted."
+ (interactive (cider--find-dwim-interactive "Jump to: "))
+ (cider--find-dwim symbol-file `cider-find-dwim
+ (cider--open-other-window-p current-prefix-arg)))
+
+;;;###autoload
+(defun cider-find-resource (path)
+ "Find the resource at PATH.
+Prompt for input as indicated by the variable `cider-prompt-for-symbol'.
+A single or double prefix argument inverts the meaning of
+`cider-prompt-for-symbol'. A prefix argument of `-` or a double prefix
+argument causes the results to be displayed in other window. The default
+value is thing at point."
+ (interactive
+ (list
+ (if (cider--prompt-for-symbol-p current-prefix-arg)
+ (completing-read "Resource: "
+ (cider-sync-request:resources-list)
+ nil nil
+ (thing-at-point 'filename))
+ (or (thing-at-point 'filename) ""))))
+ (cider-ensure-op-supported "resource")
+ (when (= (length path) 0)
+ (error "Cannot find resource for empty path"))
+ (if-let* ((resource (cider-sync-request:resource path))
+ (buffer (cider-find-file resource)))
+ (cider-jump-to buffer nil (cider--open-other-window-p current-prefix-arg))
+ (if (cider--prompt-for-symbol-p current-prefix-arg)
+ (error "Cannot find resource %s" path)
+ (let ((current-prefix-arg (cider--invert-prefix-arg current-prefix-arg)))
+ (call-interactively 'cider-find-resource)))))
+
+(defun cider--invert-prefix-arg (arg)
+ "Invert the effect of prefix value ARG on `cider-prompt-for-symbol'.
+This function preserves the `other-window' meaning of ARG."
+ (let ((narg (prefix-numeric-value arg)))
+ (pcase narg
+ (16 -1) ; empty empty -> -
+ (-1 16) ; - -> empty empty
+ (4 nil) ; empty -> no-prefix
+ (_ 4)))) ; no-prefix -> empty
+
+(defun cider--prefix-invert-prompt-p (arg)
+ "Test prefix value ARG for its effect on `cider-prompt-for-symbol`."
+ (let ((narg (prefix-numeric-value arg)))
+ (pcase narg
+ (16 t) ; empty empty
+ (4 t) ; empty
+ (_ nil))))
+
+(defun cider--prompt-for-symbol-p (&optional prefix)
+ "Check if cider should prompt for symbol.
+Tests againsts PREFIX and the value of `cider-prompt-for-symbol'.
+Invert meaning of `cider-prompt-for-symbol' if PREFIX indicates it should be."
+ (if (cider--prefix-invert-prompt-p prefix)
+ (not cider-prompt-for-symbol) cider-prompt-for-symbol))
+
+(defun cider--find-ns (ns &optional other-window)
+ "Find the file containing NS's definition.
+Optionally open it in a different window if OTHER-WINDOW is truthy."
+ (if-let* ((path (cider-sync-request:ns-path ns)))
+ (cider-jump-to (cider-find-file path) nil other-window)
+ (user-error "Can't find namespace `%s'" ns)))
+
+;;;###autoload
+(defun cider-find-ns (&optional arg ns)
+ "Find the file containing NS.
+A prefix ARG of `-` or a double prefix argument causes
+the results to be displayed in a different window."
+ (interactive "P")
+ (cider-ensure-connected)
+ (cider-ensure-op-supported "ns-path")
+ (if ns
+ (cider--find-ns ns)
+ (let* ((namespaces (cider-sync-request:ns-list))
+ (ns (completing-read "Find namespace: " namespaces)))
+ (cider--find-ns ns (cider--open-other-window-p arg)))))
+
+;;;###autoload
+(defun cider-find-keyword (&optional arg)
+ "Find the namespace of the keyword at point and its first occurrence there.
+
+For instance - if the keyword at point is \":cider.demo/keyword\", this command
+would find the namespace \"cider.demo\" and afterwards find the first mention
+of \"::keyword\" there.
+
+Prompt according to prefix ARG and `cider-prompt-for-symbol'.
+A single or double prefix argument inverts the meaning of
+`cider-prompt-for-symbol'. A prefix of `-` or a double prefix argument causes
+the results to be displayed in a different window. The default value is
+thing at point."
+ (interactive "P")
+ (cider-ensure-connected)
+ (let* ((kw (let ((kw-at-point (cider-symbol-at-point 'look-back)))
+ (if (or cider-prompt-for-symbol arg)
+ (read-string
+ (format "Keyword (default %s): " kw-at-point)
+ nil nil kw-at-point)
+ kw-at-point)))
+ (ns-qualifier (and
+ (string-match "^:+\\(.+\\)/.+$" kw)
+ (match-string 1 kw)))
+ (kw-ns (if ns-qualifier
+ (cider-resolve-alias (cider-current-ns) ns-qualifier)
+ (cider-current-ns)))
+ (kw-to-find (concat "::" (replace-regexp-in-string "^:+\\(.+/\\)?" "" kw))))
+
+ (when (and ns-qualifier (string= kw-ns (cider-current-ns)))
+ (error "Could not resolve alias `%s' in `%s'" ns-qualifier (cider-current-ns)))
+ (cider--find-ns kw-ns arg)
+ (search-forward-regexp kw-to-find nil 'noerror)))
+
+(provide 'cider-find)
+;;; cider-find.el ends here
diff --git a/cider-format.el b/cider-format.el
new file mode 100644
index 00000000..0aa9e8f0
--- /dev/null
+++ b/cider-format.el
@@ -0,0 +1,150 @@
+;;; cider-format.el --- Code and EDN formatting functionality -*- lexical-binding: t -*-
+
+;; Copyright © 2013-2018 Bozhidar Batsov, Artur Malabarba and CIDER contributors
+;;
+;; Author: Bozhidar Batsov <bozhidar@batsov.com>
+;; Artur Malabarba <bruce.connor.am@gmail.com>
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;; This file is not part of GNU Emacs.
+
+;;; Commentary:
+
+;; Middleware-powered code and EDN formatting functionality.
+
+;;; Code:
+
+(require 'subr-x)
+
+(require 'cider-client)
+(require 'cider-util)
+
+
+;; Format
+
+(defun cider--format-reindent (formatted start)
+ "Reindent FORMATTED to align with buffer position START."
+ (let* ((start-column (save-excursion (goto-char start) (current-column)))
+ (indent-line (concat "\n" (make-string start-column ? ))))
+ (replace-regexp-in-string "\n" indent-line formatted)))
+
+
+;;; Format region
+
+(defun cider--format-region (start end formatter)
+ "Format the contents of the given region.
+
+START and END represent the region's boundaries.
+
+FORMATTER is a function of one argument which is used to convert
+the string contents of the region into a formatted string.
+
+Uses the following heuristic to try to maintain point position:
+
+- Take a snippet of text starting at current position, up to 64 chars.
+- Search for the snippet, with lax whitespace, in the formatted text.
+ - If snippet is less than 64 chars (point was near end of buffer), search
+ from end instead of beginning.
+- Place point at match beginning, or `point-min' if no match."
+ (let* ((original (buffer-substring-no-properties start end))
+ (formatted (funcall formatter original))
+ (indented (cider--format-reindent formatted start)))
+ (unless (equal original indented)
+ (let* ((pos (point))
+ (pos-max (1+ (buffer-size)))
+ (l 64)
+ (endp (> (+ pos l) pos-max))
+ (snippet (thread-last (buffer-substring-no-properties
+ pos (min (+ pos l) pos-max))
+ (replace-regexp-in-string "[[:space:]\t\n\r]+" "[[:space:]\t\n\r]*"))))
+ (delete-region start end)
+ (insert indented)
+ (goto-char (if endp (point-max) (point-min)))
+ (funcall (if endp #'re-search-backward #'re-search-forward) snippet nil t)
+ (goto-char (or (match-beginning 0) start))
+ (when (looking-at-p "\n") (forward-char))))))
+
+;;;###autoload
+(defun cider-format-region (start end)
+ "Format the Clojure code in the current region.
+START and END represent the region's boundaries."
+ (interactive "r")
+ (cider-ensure-connected)
+ (cider--format-region start end #'cider-sync-request:format-code))
+
+
+;;; Format defun
+
+;;;###autoload
+(defun cider-format-defun ()
+ "Format the code in the current defun."
+ (interactive)
+ (cider-ensure-connected)
+ (save-excursion
+ (mark-defun)
+ (cider-format-region (region-beginning) (region-end))))
+
+
+;;; Format buffer
+
+(defun cider--format-buffer (formatter)
+ "Format the contents of the current buffer.
+
+Uses FORMATTER, a function of one argument, to convert the string contents
+of the buffer into a formatted string."
+ (cider--format-region 1 (1+ (buffer-size)) formatter))
+
+;;;###autoload
+(defun cider-format-buffer ()
+ "Format the Clojure code in the current buffer."
+ (interactive)
+ (check-parens)
+ (cider-ensure-connected)
+ (cider--format-buffer #'cider-sync-request:format-code))
+
+
+;;; Format EDN
+
+(declare-function cider--pretty-print-width "cider-repl")
+
+;;;###autoload
+(defun cider-format-edn-buffer ()
+ "Format the EDN data in the current buffer."
+ (interactive)
+ (check-parens)
+ (cider-ensure-connected)
+ (cider--format-buffer (lambda (edn)
+ (cider-sync-request:format-edn edn (cider--pretty-print-width)))))
+
+;;;###autoload
+(defun cider-format-edn-region (start end)
+ "Format the EDN data in the current region.
+START and END represent the region's boundaries."
+ (interactive "r")
+ (cider-ensure-connected)
+ (let* ((start-column (save-excursion (goto-char start) (current-column)))
+ (right-margin (- (cider--pretty-print-width) start-column)))
+ (cider--format-region start end
+ (lambda (edn)
+ (cider-sync-request:format-edn edn right-margin)))))
+
+;;;###autoload
+(defun cider-format-edn-last-sexp ()
+ "Format the EDN data of the last sexp."
+ (interactive)
+ (apply 'cider-format-edn-region (cider-sexp-at-point 'bounds)))
+
+(provide 'cider-format)
+;;; cider-format.el ends here
diff --git a/cider-inspector.el b/cider-inspector.el
index c32d738d..61d5007d 100644
--- a/cider-inspector.el
+++ b/cider-inspector.el
@@ -29,14 +29,13 @@
(require 'cl-lib)
(require 'seq)
-(require 'cider-interaction)
+(require 'cider-eval)
;; ===================================
;; Inspector Key Map and Derived Mode
;; ===================================
(defconst cider-inspector-buffer "*cider-inspect*")
-(add-to-list 'cider-ancillary-buffers cider-inspector-buffer)
;;; Customization
(defgroup cider-inspector nil
@@ -85,6 +84,7 @@ The page size can be also changed interactively within the inspector."
\\{cider-inspector-mode-map}"
(set-syntax-table clojure-mode-syntax-table)
(setq-local electric-indent-chars nil)
+ (setq-local sesman-system 'CIDER)
(when cider-special-mode-truncate-lines
(setq-local truncate-lines t)))
@@ -244,7 +244,7 @@ Set the page size in paginated view to PAGE-SIZE."
;; Render Inspector from Structured Values
(defun cider-inspector--render-value (value)
"Render VALUE."
- (cider-make-popup-buffer cider-inspector-buffer 'cider-inspector-mode)
+ (cider-make-popup-buffer cider-inspector-buffer 'cider-inspector-mode 'ancillary)
(cider-inspector-render cider-inspector-buffer value)
(cider-popup-buffer-display cider-inspector-buffer t)
(when cider-inspector-fill-frame (delete-other-windows))
diff --git a/cider-interaction.el b/cider-interaction.el
deleted file mode 100644
index 962bcd1d..00000000
--- a/cider-interaction.el
+++ /dev/null
@@ -1,2146 +0,0 @@
-;;; cider-interaction.el --- IDE for Clojure -*- lexical-binding: t -*-
-
-;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov
-;; Copyright © 2013-2018 Bozhidar Batsov, Artur Malabarba and CIDER contributors
-;;
-;; Author: Tim King <kingtim@gmail.com>
-;; Phil Hagelberg <technomancy@gmail.com>
-;; Bozhidar Batsov <bozhidar@batsov.com>
-;; Artur Malabarba <bruce.connor.am@gmail.com>
-;; Hugo Duncan <hugo@hugoduncan.org>
-;; Steve Purcell <steve@sanityinc.com>
-
-;; This program is free software: you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;; This file is not part of GNU Emacs.
-
-;;; Commentary:
-
-;; Provides an Emacs Lisp client to connect to Clojure nREPL servers.
-
-;;; Code:
-
-(require 'cider-client)
-(require 'cider-repl)
-(require 'cider-popup)
-(require 'cider-common)
-(require 'cider-util)
-(require 'cider-stacktrace)
-(require 'cider-test)
-(require 'cider-doc)
-(require 'cider-eldoc)
-(require 'cider-overlays)
-(require 'subr-x)
-(require 'cider-compat)
-
-(require 'clojure-mode)
-(require 'thingatpt)
-(require 'arc-mode)
-(require 'ansi-color)
-(require 'cl-lib)
-(require 'compile)
-(require 'etags) ; for find-tags-marker-ring
-(require 'tramp)
-
-(defconst cider-read-eval-buffer "*cider-read-eval*")
-(defconst cider-result-buffer "*cider-result*")
-(defconst cider-nrepl-session-buffer "*cider-nrepl-session*")
-(add-to-list 'cider-ancillary-buffers cider-nrepl-session-buffer)
-
-(defcustom cider-show-error-buffer t
- "Control the popup behavior of cider stacktraces.
-The following values are possible t or 'always, 'except-in-repl,
-'only-in-repl. Any other value, including nil, will cause the stacktrace
-not to be automatically shown.
-
-Irespective of the value of this variable, the `cider-error-buffer' is
-always generated in the background. Use `cider-visit-error-buffer' to
-navigate to this buffer."
- :type '(choice (const :tag "always" t)
- (const except-in-repl)
- (const only-in-repl)
- (const :tag "never" nil))
- :group 'cider)
-
-(defcustom cider-auto-jump-to-error t
- "Control the cursor jump behaviour in compilation error buffer.
-
-When non-nil automatically jump to error location during interactive
-compilation. When set to 'errors-only, don't jump to warnings.
-When set to nil, don't jump at all."
- :type '(choice (const :tag "always" t)
- (const errors-only)
- (const :tag "never" nil))
- :group 'cider
- :package-version '(cider . "0.7.0"))
-
-(defcustom cider-auto-select-error-buffer t
- "Controls whether to auto-select the error popup buffer."
- :type 'boolean
- :group 'cider)
-
-(defcustom cider-auto-track-ns-form-changes t
- "Controls whether to auto-evaluate a source buffer's ns form when changed.
-
-When non-nil CIDER will check for ns form changes before each eval command.
-When nil the users are expected to take care of the re-evaluating updated
-ns forms manually themselves."
- :type 'boolean
- :group 'cider
- :package-version '(cider . "0.15.0"))
-
-(defcustom cider-save-file-on-load 'prompt
- "Controls whether to prompt to save the file when loading a buffer.
-If nil, files are not saved.
-If 'prompt, the user is prompted to save the file if it's been modified.
-If t, save the file without confirmation."
- :type '(choice (const prompt :tag "Prompt to save the file if it's been modified")
- (const nil :tag "Don't save the file")
- (const t :tag "Save the file without confirmation"))
- :group 'cider
- :package-version '(cider . "0.6.0"))
-
-(define-obsolete-variable-alias 'cider-prompt-save-file-on-load 'cider-save-file-on-load "0.15.0")
-
-(defcustom cider-save-files-on-cider-refresh 'prompt
- "Controls whether to prompt to save Clojure files on `cider-refresh'.
-If nil, files are not saved.
-If 'prompt, the user is prompted to save files if they have been modified.
-If t, save the files without confirmation."
- :type '(choice (const prompt :tag "Prompt to save files if they have been modified")
- (const nil :tag "Don't save the files")
- (const t :tag "Save the files without confirmation"))
- :group 'cider
- :package-version '(cider . "0.15.0"))
-
-(defcustom cider-completion-use-context t
- "When true, uses context at point to improve completion suggestions."
- :type 'boolean
- :group 'cider
- :package-version '(cider . "0.7.0"))
-
-(defcustom cider-annotate-completion-candidates t
- "When true, annotate completion candidates with some extra information."
- :type 'boolean
- :group 'cider
- :package-version '(cider . "0.8.0"))
-
-(defcustom cider-annotate-completion-function
- #'cider-default-annotate-completion-function
- "Controls how the annotations for completion candidates are formatted.
-
-Must be a function that takes two arguments: the abbreviation of the
-candidate type according to `cider-completion-annotations-alist' and the
-candidate's namespace."
- :type 'function
- :group 'cider
- :package-version '(cider . "0.9.0"))
-
-(defcustom cider-completion-annotations-alist
- '(("class" "c")
- ("field" "fi")
- ("function" "f")
- ("import" "i")
- ("keyword" "k")
- ("local" "l")
- ("macro" "m")
- ("method" "me")
- ("namespace" "n")
- ("protocol" "p")
- ("protocol-function" "pf")
- ("record" "r")
- ("special-form" "s")
- ("static-field" "sf")
- ("static-method" "sm")
- ("type" "t")
- ("var" "v"))
- "Controls the abbreviations used when annotating completion candidates.
-
-Must be a list of elements with the form (TYPE . ABBREVIATION), where TYPE
-is a possible value of the candidate's type returned from the completion
-backend, and ABBREVIATION is a short form of that type."
- :type '(alist :key-type string :value-type string)
- :group 'cider
- :package-version '(cider . "0.9.0"))
-
-(defcustom cider-completion-annotations-include-ns 'unqualified
- "Controls passing of namespaces to `cider-annotate-completion-function'.
-
-When set to 'always, the candidate's namespace will always be passed if it
-is available. When set to 'unqualified, the namespace will only be passed
-if the candidate is not namespace-qualified."
- :type '(choice (const always)
- (const unqualified)
- (const :tag "never" nil))
- :group 'cider
- :package-version '(cider . "0.9.0"))
-
-(defconst cider-refresh-log-buffer "*cider-refresh-log*")
-
-(defcustom cider-refresh-show-log-buffer nil
- "Controls when to display the refresh log buffer.
-
-If non-nil, the log buffer will be displayed every time `cider-refresh' is
-called.
-
-If nil, the log buffer will still be written to, but will never be
-displayed automatically. Instead, the most relevant information will be
-displayed in the echo area."
- :type '(choice (const :tag "always" t)
- (const :tag "never" nil))
- :group 'cider
- :package-version '(cider . "0.10.0"))
-
-(defcustom cider-refresh-before-fn nil
- "Clojure function for `cider-refresh' to call before reloading.
-
-If nil, nothing will be invoked before reloading. Must be a
-namespace-qualified function of zero arity. Any thrown exception will
-prevent reloading from occurring."
- :type 'string
- :group 'cider
- :package-version '(cider . "0.10.0"))
-
-(defcustom cider-refresh-after-fn nil
- "Clojure function for `cider-refresh' to call after reloading.
-
-If nil, nothing will be invoked after reloading. Must be a
-namespace-qualified function of zero arity."
- :type 'string
- :group 'cider
- :package-version '(cider . "0.10.0"))
-
-(defconst cider-output-buffer "*cider-out*")
-
-(defcustom cider-interactive-eval-output-destination 'repl-buffer
- "The destination for stdout and stderr produced from interactive evaluation."
- :type '(choice (const output-buffer)
- (const repl-buffer))
- :group 'cider
- :package-version '(cider . "0.7.0"))
-
-(defface cider-error-highlight-face
- '((((supports :underline (:style wave)))
- (:underline (:style wave :color "red") :inherit unspecified))
- (t (:inherit font-lock-warning-face :underline t)))
- "Face used to highlight compilation errors in Clojure buffers."
- :group 'cider)
-
-(defface cider-warning-highlight-face
- '((((supports :underline (:style wave)))
- (:underline (:style wave :color "yellow") :inherit unspecified))
- (t (:inherit font-lock-warning-face :underline (:color "yellow"))))
- "Face used to highlight compilation warnings in Clojure buffers."
- :group 'cider)
-
-(defcustom cider-comment-prefix ";; => "
- "The prefix to insert before the first line of commented output."
- :type 'string
- :group 'cider
- :package-version '(cider . "0.16.0"))
-
-(defcustom cider-comment-continued-prefix ";; "
- "The prefix to use on the second and subsequent lines of commented output."
- :type 'string
- :group 'cider
- :package-version '(cider . "0.16.0"))
-
-(defcustom cider-comment-postfix ""
- "The postfix to be appended after the final line of commented output."
- :type 'string
- :group 'cider
- :package-version '(cider . "0.16.0"))
-
-(defconst cider-clojure-artifact-id "org.clojure/clojure"
- "Artifact identifier for Clojure.")
-
-(defconst cider-minimum-clojure-version "1.7.0"
- "Minimum supported version of Clojure.")
-
-(defconst cider-latest-clojure-version "1.8.0"
- "Latest supported version of Clojure.")
-
-(defconst cider-required-nrepl-version "0.2.12"
- "The minimum nREPL version that's known to work properly with CIDER.")
-
-;;; Minibuffer
-(defvar cider-minibuffer-history '()
- "History list of expressions read from the minibuffer.")
-
-(defvar cider-minibuffer-map
- (let ((map (make-sparse-keymap)))
- (set-keymap-parent map minibuffer-local-map)
- (define-key map (kbd "TAB") #'complete-symbol)
- (define-key map (kbd "M-TAB") #'complete-symbol)
- map)
- "Minibuffer keymap used for reading Clojure expressions.")
-
-(defvar-local cider-connection-created-with 'jack-in
- "Save how the connection was created.
-Its value can be either 'jack-in or 'connect.")
-
-(defun cider-read-from-minibuffer (prompt &optional value)
- "Read a string from the minibuffer, prompting with PROMPT.
-If VALUE is non-nil, it is inserted into the minibuffer as initial-input.
-
-PROMPT need not end with \": \". If it doesn't, VALUE is displayed on the
-prompt as a default value (used if the user doesn't type anything) and is
-not used as initial input (input is left empty)."
- (minibuffer-with-setup-hook
- (lambda ()
- (set-syntax-table clojure-mode-syntax-table)
- (add-hook 'completion-at-point-functions
- #'cider-complete-at-point nil t)
- (setq-local eldoc-documentation-function #'cider-eldoc)
- (run-hooks 'eval-expression-minibuffer-setup-hook))
- (let* ((has-colon (string-match ": \\'" prompt))
- (input (read-from-minibuffer (cond
- (has-colon prompt)
- (value (format "%s (default %s): " prompt value))
- (t (format "%s: " prompt)))
- (when has-colon value) ; initial-input
- cider-minibuffer-map nil
- 'cider-minibuffer-history
- (unless has-colon value)))) ; default-value
- (if (and (equal input "") value (not has-colon))
- value
- input))))
-
-
-;;; Utilities
-
-(defun cider--clear-compilation-highlights ()
- "Remove compilation highlights."
- (remove-overlays (point-min) (point-max) 'cider-note-p t))
-
-(defun cider-clear-compilation-highlights (&optional arg)
- "Remove compilation highlights.
-
-When invoked with a prefix ARG the command doesn't prompt for confirmation."
- (interactive "P")
- (when (or arg (y-or-n-p "Are you sure you want to clear the compilation highlights? "))
- (cider--clear-compilation-highlights)))
-
-(defun cider--quit-error-window ()
- "Buries the `cider-error-buffer' and quits its containing window."
- (when-let* ((error-win (get-buffer-window cider-error-buffer)))
- (quit-window nil error-win)))
-
-;;;
-(declare-function cider-mode "cider-mode")
-
-(defun cider-jump-to (buffer &optional pos other-window)
- "Push current point onto marker ring, and jump to BUFFER and POS.
-POS can be either a number, a cons, or a symbol.
-If a number, it is the character position (the point).
-If a cons, it specifies the position as (LINE . COLUMN). COLUMN can be nil.
-If a symbol, `cider-jump-to' searches for something that looks like the
-symbol's definition in the file.
-If OTHER-WINDOW is non-nil don't reuse current window."
- (with-no-warnings
- (ring-insert find-tag-marker-ring (point-marker)))
- (if other-window
- (pop-to-buffer buffer)
- ;; like switch-to-buffer, but reuse existing window if BUFFER is visible
- (pop-to-buffer buffer '((display-buffer-reuse-window display-buffer-same-window))))
- (with-current-buffer buffer
- (widen)
- (goto-char (point-min))
- (cider-mode +1)
- (cond
- ;; Line-column specification.
- ((consp pos)
- (forward-line (1- (or (car pos) 1)))
- (if (cdr pos)
- (move-to-column (cdr pos))
- (back-to-indentation)))
- ;; Point specification.
- ((numberp pos)
- (goto-char pos))
- ;; Symbol or string.
- (pos
- ;; Try to find (def full-name ...).
- (if (or (save-excursion
- (search-forward-regexp (format "(def.*\\s-\\(%s\\)" (regexp-quote pos))
- nil 'noerror))
- (let ((name (replace-regexp-in-string ".*/" "" pos)))
- ;; Try to find (def name ...).
- (or (save-excursion
- (search-forward-regexp (format "(def.*\\s-\\(%s\\)" (regexp-quote name))
- nil 'noerror))
- ;; Last resort, just find the first occurrence of `name'.
- (save-excursion
- (search-forward name nil 'noerror)))))
- (goto-char (match-beginning 0))
- (message "Can't find %s in %s" pos (buffer-file-name))))
- (t nil))))
-
-(defun cider-find-dwim-other-window (symbol-file)
- "Jump to SYMBOL-FILE at point, place results in other window."
- (interactive (cider--find-dwim-interactive "Jump to: "))
- (cider--find-dwim symbol-file 'cider-find-dwim-other-window t))
-
-(defun cider-find-dwim (symbol-file)
- "Find and display the SYMBOL-FILE at point.
-
-SYMBOL-FILE could be a var or a resource. If thing at point is empty
-then show dired on project. If var is not found, try to jump to resource
-of the same name. When called interactively, a prompt is given according
-to the variable `cider-prompt-for-symbol'. A single or double prefix argument
-inverts the meaning. A prefix of `-' or a double prefix argument causes the
-results to be displayed in a different window.
-A default value of thing at point is given when prompted."
- (interactive (cider--find-dwim-interactive "Jump to: "))
- (cider--find-dwim symbol-file `cider-find-dwim
- (cider--open-other-window-p current-prefix-arg)))
-
-(defun cider--find-dwim (symbol-file callback &optional other-window)
- "Find the SYMBOL-FILE at point.
-
-CALLBACK upon failure to invoke prompt if not prompted previously.
-Show results in a different window if OTHER-WINDOW is true."
- (if-let* ((info (cider-var-info symbol-file)))
- (cider--jump-to-loc-from-info info other-window)
- (progn
- (cider-ensure-op-supported "resource")
- (if-let* ((resource (cider-sync-request:resource symbol-file))
- (buffer (cider-find-file resource)))
- (cider-jump-to buffer 0 other-window)
- (if (cider--prompt-for-symbol-p current-prefix-arg)
- (error "Resource or var %s not resolved" symbol-file)
- (let ((current-prefix-arg (if current-prefix-arg nil '(4))))
- (call-interactively callback)))))))
-
-(defun cider--find-dwim-interactive (prompt)
- "Get interactive arguments for jump-to functions using PROMPT as needed."
- (if (cider--prompt-for-symbol-p current-prefix-arg)
- (list
- (cider-read-from-minibuffer prompt (thing-at-point 'filename)))
- (list (or (thing-at-point 'filename) "")))) ; No prompt.
-
-(defun cider-find-resource (path)
- "Find the resource at PATH.
-
-Prompt for input as indicated by the variable `cider-prompt-for-symbol'.
-A single or double prefix argument inverts the meaning of
-`cider-prompt-for-symbol'. A prefix argument of `-` or a double prefix
-argument causes the results to be displayed in other window. The default
-value is thing at point."
- (interactive
- (list
- (if (cider--prompt-for-symbol-p current-prefix-arg)
- (completing-read "Resource: "
- (cider-sync-request:resources-list)
- nil nil
- (thing-at-point 'filename))
- (or (thing-at-point 'filename) ""))))
- (cider-ensure-op-supported "resource")
- (when (= (length path) 0)
- (error "Cannot find resource for empty path"))
- (if-let* ((resource (cider-sync-request:resource path))
- (buffer (cider-find-file resource)))
- (cider-jump-to buffer nil (cider--open-other-window-p current-prefix-arg))
- (if (cider--prompt-for-symbol-p current-prefix-arg)
- (error "Cannot find resource %s" path)
- (let ((current-prefix-arg (cider--invert-prefix-arg current-prefix-arg)))
- (call-interactively `cider-find-resource)))))
-
-(defun cider--invert-prefix-arg (arg)
- "Invert the effect of prefix value ARG on `cider-prompt-for-symbol'.
-
-This function preserves the `other-window' meaning of ARG."
- (let ((narg (prefix-numeric-value arg)))
- (pcase narg
- (16 -1) ; empty empty -> -
- (-1 16) ; - -> empty empty
- (4 nil) ; empty -> no-prefix
- (_ 4)))) ; no-prefix -> empty
-
-(defun cider--prefix-invert-prompt-p (arg)
- "Test prefix value ARG for its effect on `cider-prompt-for-symbol`."
- (let ((narg (prefix-numeric-value arg)))
- (pcase narg
- (16 t) ; empty empty
- (4 t) ; empty
- (_ nil))))
-
-(defun cider--prompt-for-symbol-p (&optional prefix)
- "Check if cider should prompt for symbol.
-
-Tests againsts PREFIX and the value of `cider-prompt-for-symbol'.
-Invert meaning of `cider-prompt-for-symbol' if PREFIX indicates it should be."
- (if (cider--prefix-invert-prompt-p prefix)
- (not cider-prompt-for-symbol) cider-prompt-for-symbol))
-
-(defun cider--find-ns (ns &optional other-window)
- "Find the file containing NS's definition.
-Optionally open it in a different window if OTHER-WINDOW is truthy."
- (if-let* ((path (cider-sync-request:ns-path ns)))
- (cider-jump-to (cider-find-file path) nil other-window)
- (user-error "Can't find namespace `%s'" ns)))
-
-(defun cider-find-ns (&optional arg ns)
- "Find the file containing NS.
-
-A prefix ARG of `-` or a double prefix argument causes
-the results to be displayed in a different window."
- (interactive "P")
- (cider-ensure-connected)
- (cider-ensure-op-supported "ns-path")
- (if ns
- (cider--find-ns ns)
- (let* ((namespaces (cider-sync-request:ns-list))
- (ns (completing-read "Find namespace: " namespaces)))
- (cider--find-ns ns (cider--open-other-window-p arg)))))
-
-(defun cider-find-keyword (&optional arg)
- "Find the namespace of the keyword at point and its first occurrence there.
-
-For instance - if the keyword at point is \":cider.demo/keyword\", this command
-would find the namespace \"cider.demo\" and afterwards find the first mention
-of \"::keyword\" there.
-
-Prompt according to prefix ARG and `cider-prompt-for-symbol'.
-A single or double prefix argument inverts the meaning of
-`cider-prompt-for-symbol'. A prefix of `-` or a double prefix argument causes
-the results to be displayed in a different window. The default value is
-thing at point."
- (interactive "P")
- (cider-ensure-connected)
- (let* ((kw (let ((kw-at-point (cider-symbol-at-point 'look-back)))
- (if (or cider-prompt-for-symbol arg)
- (read-string
- (format "Keyword (default %s): " kw-at-point)
- nil nil kw-at-point)
- kw-at-point)))
- (ns-qualifier (and
- (string-match "^:+\\(.+\\)/.+$" kw)
- (match-string 1 kw)))
- (kw-ns (if ns-qualifier
- (cider-resolve-alias (cider-current-ns) ns-qualifier)
- (cider-current-ns)))
- (kw-to-find (concat "::" (replace-regexp-in-string "^:+\\(.+/\\)?" "" kw))))
-
- (when (and ns-qualifier (string= kw-ns (cider-current-ns)))
- (error "Could not resolve alias `%s' in `%s'" ns-qualifier (cider-current-ns)))
- (cider--find-ns kw-ns arg)
- (search-forward-regexp kw-to-find nil 'noerror)))
-
-(defvar cider-completion-last-context nil)
-
-(defun cider-completion-symbol-start-pos ()
- "Find the starting position of the symbol at point, unless inside a string."
- (let ((sap (symbol-at-point)))
- (when (and sap (not (nth 3 (syntax-ppss))))
- (car (bounds-of-thing-at-point 'symbol)))))
-
-(defun cider-completion-get-context-at-point ()
- "Extract the context at point.
-If point is not inside the list, returns nil; otherwise return \"top-level\"
-form, with symbol at point replaced by __prefix__."
- (when (save-excursion
- (condition-case _
- (progn
- (up-list)
- (check-parens)
- t)
- (scan-error nil)
- (user-error nil)))
- (save-excursion
- (let* ((pref-end (point))
- (pref-start (cider-completion-symbol-start-pos))
- (context (cider-defun-at-point))
- (_ (beginning-of-defun))
- (expr-start (point)))
- (concat (when pref-start (substring context 0 (- pref-start expr-start)))
- "__prefix__"
- (substring context (- pref-end expr-start)))))))
-
-(defun cider-completion-get-context ()
- "Extract context depending on `cider-completion-use-context' and major mode."
- (let ((context (if (and cider-completion-use-context
- ;; Important because `beginning-of-defun' and
- ;; `ending-of-defun' work incorrectly in the REPL
- ;; buffer, so context extraction fails there.
- (derived-mode-p 'clojure-mode))
- (or (cider-completion-get-context-at-point)
- "nil")
- "nil")))
- (if (string= cider-completion-last-context context)
- ":same"
- (setq cider-completion-last-context context)
- context)))
-
-(defun cider-completion--parse-candidate-map (candidate-map)
- "Get \"candidate\" from CANDIDATE-MAP.
-Put type and ns properties on the candidate"
- (let ((candidate (nrepl-dict-get candidate-map "candidate"))
- (type (nrepl-dict-get candidate-map "type"))
- (ns (nrepl-dict-get candidate-map "ns")))
- (put-text-property 0 1 'type type candidate)
- (put-text-property 0 1 'ns ns candidate)
- candidate))
-
-(defun cider-complete (str)
- "Complete STR with context at point."
- (let* ((context (cider-completion-get-context))
- (candidates (cider-sync-request:complete str context)))
- (mapcar #'cider-completion--parse-candidate-map candidates)))
-
-(defun cider-completion--get-candidate-type (symbol)
- "Get candidate type for SYMBOL."
- (let ((type (get-text-property 0 'type symbol)))
- (or (cadr (assoc type cider-completion-annotations-alist))
- type)))
-
-(defun cider-completion--get-candidate-ns (symbol)
- "Get candidate ns for SYMBOL."
- (when (or (eq 'always cider-completion-annotations-include-ns)
- (and (eq 'unqualified cider-completion-annotations-include-ns)
- (not (cider-namespace-qualified-p symbol))))
- (get-text-property 0 'ns symbol)))
-
-(defun cider-default-annotate-completion-function (type ns)
- "Get completion function based on TYPE and NS."
- (concat (when ns (format " (%s)" ns))
- (when type (format " <%s>" type))))
-
-(defun cider-annotate-symbol (symbol)
- "Return a string suitable for annotating SYMBOL.
-
-If SYMBOL has a text property `type` whose value is recognised, its
-abbreviation according to `cider-completion-annotations-alist' will be
-used. If `type` is present but not recognised, its value will be used
-unaltered.
-
-If SYMBOL has a text property `ns`, then its value will be used according
-to `cider-completion-annotations-include-ns'.
-
-The formatting is performed by `cider-annotate-completion-function'."
- (when cider-annotate-completion-candidates
- (let* ((type (cider-completion--get-candidate-type symbol))
- (ns (cider-completion--get-candidate-ns symbol)))
- (funcall cider-annotate-completion-function type ns))))
-
-(defun cider-complete-at-point ()
- "Complete the symbol at point."
- (when-let* ((bounds (bounds-of-thing-at-point 'symbol)))
- (when (and (cider-connected-p)
- (not (or (cider-in-string-p) (cider-in-comment-p))))
- (list (car bounds) (cdr bounds)
- (completion-table-dynamic #'cider-complete)
- :annotation-function #'cider-annotate-symbol
- :company-doc-buffer #'cider-create-doc-buffer
- :company-location #'cider-company-location
- :company-docsig #'cider-company-docsig))))
-
-(defun cider-completion-flush-caches ()
- "Force Compliment to refill its caches.
-
-This command should be used if Compliment fails to pick up new classnames
-and methods from dependencies that were loaded dynamically after the REPL
-has started."
- (interactive)
- (cider-sync-request:complete-flush-caches))
-
-(defun cider-company-location (var)
- "Open VAR's definition in a buffer.
-
-Returns the cons of the buffer itself and the location of VAR's definition
-in the buffer."
- (when-let* ((info (cider-var-info var))
- (file (nrepl-dict-get info "file"))
- (line (nrepl-dict-get info "line"))
- (buffer (cider-find-file file)))
- (with-current-buffer buffer
- (save-excursion
- (goto-char (point-min))
- (forward-line (1- line))
- (cons buffer (point))))))
-
-(defun cider-company-docsig (thing)
- "Return signature for THING."
- (let* ((eldoc-info (cider-eldoc-info thing))
- (ns (lax-plist-get eldoc-info "ns"))
- (symbol (lax-plist-get eldoc-info "symbol"))
- (arglists (lax-plist-get eldoc-info "arglists")))
- (when eldoc-info
- (format "%s: %s"
- (cider-eldoc-format-thing ns symbol thing
- (cider-eldoc-thing-type eldoc-info))
- (cider-eldoc-format-arglist arglists 0)))))
-
-;; Fuzzy completion for company-mode
-
-(defun cider-company-unfiltered-candidates (string &rest _)
- "Return CIDER completion candidates for STRING as is, unfiltered."
- (cider-complete string))
-
-(add-to-list 'completion-styles-alist
- '(cider
- cider-company-unfiltered-candidates
- cider-company-unfiltered-candidates
- "CIDER backend-driven completion style."))
-
-(defun cider-company-enable-fuzzy-completion ()
- "Enable backend-driven fuzzy completion in the current buffer."
- (setq-local completion-styles '(cider)))
-
-(defun cider-stdin-handler (&optional buffer)
- "Make a stdin response handler for BUFFER."
- (nrepl-make-response-handler (or buffer (current-buffer))
- (let (after-first-result-chunk)
- (lambda (buffer value)
- (cider-repl-emit-result buffer value (not after-first-result-chunk) t)
- (setq after-first-result-chunk t)))
- (lambda (buffer out)
- (cider-repl-emit-stdout buffer out))
- (lambda (buffer err)
- (cider-repl-emit-stderr buffer err))
- nil))
-
-(defun cider-insert-eval-handler (&optional buffer)
- "Make an nREPL evaluation handler for the BUFFER.
-The handler simply inserts the result value in BUFFER."
- (let ((eval-buffer (current-buffer)))
- (nrepl-make-response-handler (or buffer eval-buffer)
- (lambda (_buffer value)
- (with-current-buffer buffer
- (insert value)))
- (lambda (_buffer out)
- (cider-repl-emit-interactive-stdout out))
- (lambda (_buffer err)
- (cider-handle-compilation-errors err eval-buffer))
- '())))
-
-(defun cider--emit-interactive-eval-output (output repl-emit-function)
- "Emit output resulting from interactive code evaluation.
-
-The OUTPUT can be sent to either a dedicated output buffer or the current
-REPL buffer. This is controlled by `cider-interactive-eval-output-destination'.
-REPL-EMIT-FUNCTION emits the OUTPUT."
- (pcase cider-interactive-eval-output-destination
- (`output-buffer (let ((output-buffer (or (get-buffer cider-output-buffer)
- (cider-popup-buffer cider-output-buffer t))))
- (cider-emit-into-popup-buffer output-buffer output)
- (pop-to-buffer output-buffer)))
- (`repl-buffer (funcall repl-emit-function output))
- (_ (error "Unsupported value %s for `cider-interactive-eval-output-destination'"
- cider-interactive-eval-output-destination))))
-
-(defun cider-emit-interactive-eval-output (output)
- "Emit OUTPUT resulting from interactive code evaluation.
-
-The output can be send to either a dedicated output buffer or the current
-REPL buffer. This is controlled via
-`cider-interactive-eval-output-destination'."
- (cider--emit-interactive-eval-output output 'cider-repl-emit-interactive-stdout))
-
-(defun cider-emit-interactive-eval-err-output (output)
- "Emit err OUTPUT resulting from interactive code evaluation.
-
-The output can be send to either a dedicated output buffer or the current
-REPL buffer. This is controlled via
-`cider-interactive-eval-output-destination'."
- (cider--emit-interactive-eval-output output 'cider-repl-emit-interactive-stderr))
-
-(defun cider--make-fringe-overlays-for-region (beg end)
- "Place eval indicators on all sexps between BEG and END."
- (with-current-buffer (if (markerp end)
- (marker-buffer end)
- (current-buffer))
- (save-excursion
- (goto-char beg)
- (remove-overlays beg end 'category 'cider-fringe-indicator)
- (condition-case nil
- (while (progn (clojure-forward-logical-sexp)
- (and (<= (point) end)
- (not (eobp))))
- (cider--make-fringe-overlay (point)))
- (scan-error nil)))))
-
-(defun cider-interactive-eval-handler (&optional buffer place)
- "Make an interactive eval handler for BUFFER.
-PLACE is used to display the evaluation result.
-If non-nil, it can be the position where the evaluated sexp ends,
-or it can be a list with (START END) of the evaluated region."
- (let* ((eval-buffer (current-buffer))
- (beg (car-safe place))
- (end (or (car-safe (cdr-safe place)) place))
- (beg (when beg (copy-marker beg)))
- (end (when end (copy-marker end)))
- (fringed nil))
- (nrepl-make-response-handler (or buffer eval-buffer)
- (lambda (_buffer value)
- (if beg
- (unless fringed
- (cider--make-fringe-overlays-for-region beg end)
- (setq fringed t))
- (cider--make-fringe-overlay end))
- (cider--display-interactive-eval-result value end))
- (lambda (_buffer out)
- (cider-emit-interactive-eval-output out))
- (lambda (_buffer err)
- (cider-emit-interactive-eval-err-output err)
- (cider-handle-compilation-errors err eval-buffer))
- '())))
-
-(defun cider-load-file-handler (&optional buffer)
- "Make a load file handler for BUFFER."
- (let ((eval-buffer (current-buffer)))
- (nrepl-make-response-handler (or buffer eval-buffer)
- (lambda (buffer value)
- (cider--display-interactive-eval-result value)
- (when (buffer-live-p buffer)
- (with-current-buffer buffer
- (cider--make-fringe-overlays-for-region (point-min) (point-max))
- (run-hooks 'cider-file-loaded-hook))))
- (lambda (_buffer value)
- (cider-emit-interactive-eval-output value))
- (lambda (_buffer err)
- (cider-emit-interactive-eval-err-output err)
- (cider-handle-compilation-errors err eval-buffer))
- '()
- (lambda ()
- (funcall nrepl-err-handler)))))
-
-(defun cider-eval-print-handler (&optional buffer)
- "Make a handler for evaluating and printing result in BUFFER."
- (nrepl-make-response-handler (or buffer (current-buffer))
- (lambda (buffer value)
- (with-current-buffer buffer
- (insert
- (if (derived-mode-p 'cider-clojure-interaction-mode)
- (format "\n%s\n" value)
- value))))
- (lambda (_buffer out)
- (cider-emit-interactive-eval-output out))
- (lambda (_buffer err)
- (cider-emit-interactive-eval-err-output err))
- '()))
-
-(defun cider-eval-print-with-comment-handler (buffer location comment-prefix)
- "Make a handler for evaluating and printing commented results in BUFFER.
-
-LOCATION is the location at which to insert.
-COMMENT-PREFIX is the comment prefix to use."
- (nrepl-make-response-handler buffer
- (lambda (buffer value)
- (with-current-buffer buffer
- (save-excursion
- (goto-char location)
- (insert (concat comment-prefix
- value "\n")))))
- (lambda (_buffer out)
- (cider-emit-interactive-eval-output out))
- (lambda (_buffer err)
- (cider-emit-interactive-eval-err-output err))
- '()))
-
-(defun cider-eval-pprint-with-multiline-comment-handler (buffer location comment-prefix continued-prefix comment-postfix)
- "Make a handler for evaluating and inserting results in BUFFER.
-The inserted text is pretty-printed and region will be commented.
-LOCATION is the location at which to insert.
-COMMENT-PREFIX is the comment prefix for the first line of output.
-CONTINUED-PREFIX is the comment prefix to use for the remaining lines.
-COMMENT-POSTFIX is the text to output after the last line."
- (cl-flet ((multiline-comment-handler (buffer value)
- (with-current-buffer buffer
- (save-excursion
- (goto-char location)
- (let ((lines (split-string value "[\n]+" t)))
- ;; only the first line gets the normal comment-prefix
- (insert (concat comment-prefix (pop lines)))
- (dolist (elem lines)
- (insert (concat "\n" continued-prefix elem)))
- (unless (string= comment-postfix "")
- (insert comment-postfix)))))))
- (nrepl-make-response-handler buffer
- '()
- #'multiline-comment-handler
- #'multiline-comment-handler
- '())))
-
-(defun cider-popup-eval-out-handler (&optional buffer)
- "Make a handler for evaluating and printing stdout/stderr in popup BUFFER.
-
-This is used by pretty-printing commands and intentionally discards their results."
- (cl-flet ((popup-output-handler (buffer str)
- (cider-emit-into-popup-buffer buffer
- (ansi-color-apply str)
- nil
- t)))
- (nrepl-make-response-handler (or buffer (current-buffer))
- '()
- ;; stdout handler
- #'popup-output-handler
- ;; stderr handler
- #'popup-output-handler
- '())))
-
-(defun cider-visit-error-buffer ()
- "Visit the `cider-error-buffer' (usually *cider-error*) if it exists."
- (interactive)
- (if-let* ((buffer (get-buffer cider-error-buffer)))
- (cider-popup-buffer-display buffer cider-auto-select-error-buffer)
- (user-error "No %s buffer" cider-error-buffer)))
-
-(defun cider-find-property (property &optional backward)
- "Find the next text region which has the specified PROPERTY.
-If BACKWARD is t, then search backward.
-Returns the position at which PROPERTY was found, or nil if not found."
- (let ((p (if backward
- (previous-single-char-property-change (point) property)
- (next-single-char-property-change (point) property))))
- (when (and (not (= p (point-min))) (not (= p (point-max))))
- p)))
-
-(defun cider-jump-to-compilation-error (&optional _arg _reset)
- "Jump to the line causing the current compilation error.
-_ARG and _RESET are ignored, as there is only ever one compilation error.
-They exist for compatibility with `next-error'."
- (interactive)
- (cl-labels ((goto-next-note-boundary
- ()
- (let ((p (or (cider-find-property 'cider-note-p)
- (cider-find-property 'cider-note-p t))))
- (when p
- (goto-char p)
- (message "%s" (get-char-property p 'cider-note))))))
- ;; if we're already on a compilation error, first jump to the end of
- ;; it, so that we find the next error.
- (when (get-char-property (point) 'cider-note-p)
- (goto-next-note-boundary))
- (goto-next-note-boundary)))
-
-(defun cider--show-error-buffer-p ()
- "Return non-nil if the error buffer must be shown on error.
-
-Takes into account both the value of `cider-show-error-buffer' and the
-currently selected buffer."
- (let* ((selected-buffer (window-buffer (selected-window)))
- (replp (with-current-buffer selected-buffer (derived-mode-p 'cider-repl-mode))))
- (memq cider-show-error-buffer
- (if replp
- '(t always only-in-repl)
- '(t always except-in-repl)))))
-
-(defun cider-new-error-buffer (&optional mode error-types)
- "Return an empty error buffer using MODE.
-
-When deciding whether to display the buffer, takes into account not only
-the value of `cider-show-error-buffer' and the currently selected buffer
-but also the ERROR-TYPES of the error, which is checked against the
-`cider-stacktrace-suppressed-errors' set.
-
-When deciding whether to select the buffer, takes into account the value of
-`cider-auto-select-error-buffer'."
- (if (and (cider--show-error-buffer-p)
- (not (cider-stacktrace-some-suppressed-errors-p error-types)))
- (cider-popup-buffer cider-error-buffer cider-auto-select-error-buffer mode)
- (cider-make-popup-buffer cider-error-buffer mode)))
-
-(defun cider--handle-err-eval-response (response)
- "Render eval RESPONSE into a new error buffer.
-
-Uses the value of the `out' slot in RESPONSE."
- (nrepl-dbind-response response (out)
- (when out
- (let ((error-buffer (cider-new-error-buffer)))
- (cider-emit-into-color-buffer error-buffer out)
- (with-current-buffer error-buffer
- (compilation-minor-mode +1))))))
-
-(defun cider-default-err-eval-handler ()
- "Display the last exception without middleware support."
- (cider--handle-err-eval-response
- (cider-nrepl-sync-request:eval
- "(clojure.stacktrace/print-cause-trace *e)")))
-
-(defun cider--render-stacktrace-causes (causes &optional error-types)
- "If CAUSES is non-nil, render its contents into a new error buffer.
-Optional argument ERROR-TYPES contains a list which should determine the
-op/situation that originated this error."
- (when causes
- (let ((error-buffer (cider-new-error-buffer #'cider-stacktrace-mode error-types)))
- (cider-stacktrace-render error-buffer (reverse causes) error-types))))
-
-(defun cider--handle-stacktrace-response (response causes)
- "Handle stacktrace op RESPONSE, aggregating the result into CAUSES.
-
-If RESPONSE contains a cause, cons it onto CAUSES and return that. If
-RESPONSE is the final message (i.e. it contains a status), render CAUSES
-into a new error buffer."
- (nrepl-dbind-response response (class status)
- (cond (class (cons response causes))
- (status (cider--render-stacktrace-causes causes)))))
-
-(defun cider-default-err-op-handler ()
- "Display the last exception, with middleware support."
- ;; Causes are returned as a series of messages, which we aggregate in `causes'
- (let (causes)
- (cider-nrepl-send-request
- (nconc '("op" "stacktrace")
- (when (cider--pprint-fn)
- `("pprint-fn" ,(cider--pprint-fn)))
- (when cider-stacktrace-print-length
- `("print-length" ,cider-stacktrace-print-length))
- (when cider-stacktrace-print-level
- `("print-level" ,cider-stacktrace-print-level)))
- (lambda (response)
- ;; While the return value of `cider--handle-stacktrace-response' is not
- ;; meaningful for the last message, we do not need the value of `causes'
- ;; after it has been handled, so it's fine to set it unconditionally here
- (setq causes (cider--handle-stacktrace-response response causes))))))
-
-(defun cider-default-err-handler ()
- "This function determines how the error buffer is shown.
-It delegates the actual error content to the eval or op handler."
- (if (cider-nrepl-op-supported-p "stacktrace")
- (cider-default-err-op-handler)
- (cider-default-err-eval-handler)))
-
-(defvar cider-compilation-regexp
- '("\\(?:.*\\(warning, \\)\\|.*?\\(, compiling\\):(\\)\\(.*?\\):\\([[:digit:]]+\\)\\(?::\\([[:digit:]]+\\)\\)?\\(\\(?: - \\(.*\\)\\)\\|)\\)" 3 4 5 (1))
- "Specifications for matching errors and warnings in Clojure stacktraces.
-See `compilation-error-regexp-alist' for help on their format.")
-
-(add-to-list 'compilation-error-regexp-alist-alist
- (cons 'cider cider-compilation-regexp))
-(add-to-list 'compilation-error-regexp-alist 'cider)
-
-(defun cider-extract-error-info (regexp message)
- "Extract error information with REGEXP against MESSAGE."
- (let ((file (nth 1 regexp))
- (line (nth 2 regexp))
- (col (nth 3 regexp))
- (type (nth 4 regexp))
- (pat (car regexp)))
- (when (string-match pat message)
- ;; special processing for type (1.2) style
- (setq type (if (consp type)
- (or (and (car type) (match-end (car type)) 1)
- (and (cdr type) (match-end (cdr type)) 0)
- 2)))
- (list
- (when file
- (let ((val (match-string-no-properties file message)))
- (unless (string= val "NO_SOURCE_PATH") val)))
- (when line (string-to-number (match-string-no-properties line message)))
- (when col
- (let ((val (match-string-no-properties col message)))
- (when val (string-to-number val))))
- (aref [cider-warning-highlight-face
- cider-warning-highlight-face
- cider-error-highlight-face]
- (or type 2))
- message))))
-
-(defun cider--goto-expression-start ()
- "Go to the beginning a list, vector, map or set outside of a string.
-
-We do so by starting and the current position and proceeding backwards
-until we find a delimiters that's not inside a string."
- (if (and (looking-back "[])}]" (line-beginning-position))
- (null (nth 3 (syntax-ppss))))
- (backward-sexp)
- (while (or (not (looking-at-p "[({[]"))
- (nth 3 (syntax-ppss)))
- (backward-char))))
-
-(defun cider--find-last-error-location (message)
- "Return the location (begin end buffer) from the Clojure error MESSAGE.
-If location could not be found, return nil."
- (save-excursion
- (let ((info (cider-extract-error-info cider-compilation-regexp message)))
- (when info
- (let ((file (nth 0 info))
- (line (nth 1 info))
- (col (nth 2 info)))
- (unless (or (not (stringp file))
- (cider--tooling-file-p file))
- (when-let* ((buffer (cider-find-file file)))
- (with-current-buffer buffer
- (save-excursion
- (save-restriction
- (widen)
- (goto-char (point-min))
- (forward-line (1- line))
- (move-to-column (or col 0))
- (let ((begin (progn (if col (cider--goto-expression-start) (back-to-indentation))
- (point)))
- (end (progn (if col (forward-list) (move-end-of-line nil))
- (point))))
- (list begin end buffer))))))))))))
-
-(defun cider-handle-compilation-errors (message eval-buffer)
- "Highlight and jump to compilation error extracted from MESSAGE.
-EVAL-BUFFER is the buffer that was current during user's interactive
-evaluation command. Honor `cider-auto-jump-to-error'."
- (when-let* ((loc (cider--find-last-error-location message))
- (overlay (make-overlay (nth 0 loc) (nth 1 loc) (nth 2 loc)))
- (info (cider-extract-error-info cider-compilation-regexp message)))
- (let* ((face (nth 3 info))
- (note (nth 4 info))
- (auto-jump (if (eq cider-auto-jump-to-error 'errors-only)
- (not (eq face 'cider-warning-highlight-face))
- cider-auto-jump-to-error)))
- (overlay-put overlay 'cider-note-p t)
- (overlay-put overlay 'font-lock-face face)
- (overlay-put overlay 'cider-note note)
- (overlay-put overlay 'help-echo note)
- (overlay-put overlay 'modification-hooks
- (list (lambda (o &rest _args) (delete-overlay o))))
- (when auto-jump
- (with-current-buffer eval-buffer
- (push-mark)
- ;; At this stage selected window commonly is *cider-error* and we need to
- ;; re-select the original user window. If eval-buffer is not
- ;; visible it was probably covered as a result of a small screen or user
- ;; configuration (https://github.com/clojure-emacs/cider/issues/847). In
- ;; that case we don't jump at all in order to avoid covering *cider-error*
- ;; buffer.
- (when-let* ((win (get-buffer-window eval-buffer)))
- (with-selected-window win
- (cider-jump-to (nth 2 loc) (car loc)))))))))
-
-(defun cider-need-input (buffer)
- "Handle an need-input request from BUFFER."
- (with-current-buffer buffer
- (nrepl-request:stdin (concat (read-from-minibuffer "Stdin: ") "\n")
- (cider-stdin-handler buffer)
- (cider-current-connection))))
-
-(defun cider-emit-into-color-buffer (buffer value)
- "Emit into color BUFFER the provided VALUE."
- (with-current-buffer buffer
- (let ((inhibit-read-only t)
- (buffer-undo-list t))
- (goto-char (point-max))
- (insert (format "%s" value))
- (ansi-color-apply-on-region (point-min) (point-max)))
- (goto-char (point-min))))
-
-
-;;; Evaluation
-
-(defvar cider-to-nrepl-filename-function
- (with-no-warnings
- (if (eq system-type 'cygwin)
- #'cygwin-convert-file-name-to-windows
- #'identity))
- "Function to translate Emacs filenames to nREPL namestrings.")
-
-(defun cider--prep-interactive-eval (form connection)
- "Prepare the environment for an interactive eval of FORM in CONNECTION.
-Ensure the current ns declaration has been evaluated (so that the ns
-containing FORM exists). Cache ns-form in the current buffer unless FORM is
-ns declaration itself. Clear any compilation highlights and kill the error
-window."
- (cider--clear-compilation-highlights)
- (cider--quit-error-window)
- (let ((cur-ns-form (cider-ns-form)))
- (when (and cur-ns-form
- (not (cider-ns-form-p form))
- (cider-repl--ns-form-changed-p cur-ns-form connection))
- (when cider-auto-track-ns-form-changes
- ;; The first interactive eval on a file can load a lot of libs. This can
- ;; easily lead to more than 10 sec.
- (let ((nrepl-sync-request-timeout 30))
- ;; TODO: check for evaluation errors
- (cider-nrepl-sync-request:eval cur-ns-form connection)))
- ;; cache at the end, in case of errors
- (cider-repl--cache-ns-form cur-ns-form connection))))
-
-(defvar-local cider-interactive-eval-override nil
- "Function to call instead of `cider-interactive-eval'.")
-
-(defun cider-interactive-eval (form &optional callback bounds additional-params)
- "Evaluate FORM and dispatch the response to CALLBACK.
-If the code to be evaluated comes from a buffer, it is preferred to use a
-nil FORM, and specify the code via the BOUNDS argument instead.
-
-This function is the main entry point in CIDER's interactive evaluation
-API. Most other interactive eval functions should rely on this function.
-If CALLBACK is nil use `cider-interactive-eval-handler'.
-BOUNDS, if non-nil, is a list of two numbers marking the start and end
-positions of FORM in its buffer.
-ADDITIONAL-PARAMS is a plist to be appended to the request message.
-
-If `cider-interactive-eval-override' is a function, call it with the same
-arguments and only proceed with evaluation if it returns nil."
- (let ((form (or form (apply #'buffer-substring-no-properties bounds)))
- (start (car-safe bounds))
- (end (car-safe (cdr-safe bounds))))
- (when (and start end)
- (remove-overlays start end 'cider-temporary t))
- (unless (and cider-interactive-eval-override
- (functionp cider-interactive-eval-override)
- (funcall cider-interactive-eval-override form callback bounds))
- (cider-map-connections #'ignore :any)
- (cider-map-connections
- (lambda (connection)
- (cider--prep-interactive-eval form connection)
- (cider-nrepl-request:eval
- form
- (or callback (cider-interactive-eval-handler nil bounds))
- ;; always eval ns forms in the user namespace
- ;; otherwise trying to eval ns form for the first time will produce an error
- (if (cider-ns-form-p form) "user" (cider-current-ns))
- (when start (line-number-at-pos start))
- (when start (cider-column-number-at-pos start))
- additional-params
- connection))
- :both))))
-
-(defun cider-eval-region (start end)
- "Evaluate the region between START and END."
- (interactive "r")
- (cider-interactive-eval nil nil (list start end)))
-
-(defun cider-eval-last-sexp (&optional output-to-current-buffer)
- "Evaluate the expression preceding point.
-If invoked with OUTPUT-TO-CURRENT-BUFFER, print the result in the current buffer."
- (interactive "P")
- (cider-interactive-eval nil
- (when output-to-current-buffer (cider-eval-print-handler))
- (cider-last-sexp 'bounds)))
-
-(defun cider-eval-last-sexp-and-replace ()
- "Evaluate the expression preceding point and replace it with its result."
- (interactive)
- (let ((last-sexp (cider-last-sexp)))
- ;; we have to be sure the evaluation won't result in an error
- (cider-nrepl-sync-request:eval last-sexp)
- ;; seems like the sexp is valid, so we can safely kill it
- (backward-kill-sexp)
- (cider-interactive-eval last-sexp (cider-eval-print-handler))))
-
-(defun cider-eval-sexp-at-point (&optional output-to-current-buffer)
- "Evaluate the expression around point.
-
-If invoked with OUTPUT-TO-CURRENT-BUFFER, output the result to current buffer."
- (interactive "P")
- (save-excursion
- (goto-char (cadr (cider-sexp-at-point 'bounds)))
- (cider-eval-last-sexp output-to-current-buffer)))
-
-(defvar-local cider-previous-eval-context nil
- "The previous evaluation context if any.
-
-That's set by commands like `cider-eval-last-sexp-in-context'.")
-
-(defun cider--eval-in-context (code)
- "Evaluate CODE in user-provided evaluation context."
- (let* ((code (string-trim-right code))
- (eval-context (read-string
- (format "Evaluation context (let-style) for `%s': " code)
- cider-previous-eval-context))
- (code (concat "(let [" eval-context "]\n " code ")")))
- (cider-interactive-eval code)
- (setq-local cider-previous-eval-context eval-context)))
-
-(defun cider-eval-last-sexp-in-context ()
- "Evaluate the preceding sexp in user-supplied context.
-
-The context is just a let binding vector (without the brackets).
-The context is remembered between command invocations."
- (interactive)
- (cider--eval-in-context (cider-last-sexp)))
-
-(defun cider-eval-sexp-at-point-in-context ()
- "Evaluate the preceding sexp in user-supplied context.
-
-The context is just a let binding vector (without the brackets).
-The context is remembered between command invocations."
- (interactive)
- (cider--eval-in-context (cider-sexp-at-point)))
-
-(defun cider-eval-defun-to-comment (&optional insert-before)
- "Evaluate the \"top-level\" form and insert result as comment.
-
-The formatting of the comment is defined in `cider-comment-prefix`
-which, by default, is \";; => \" and can be customized.
-
-With the prefix arg INSERT-BEFORE, insert before the form, otherwise afterwards."
- (interactive "P")
- (let* ((bounds (cider-defun-at-point 'bounds))
- (insertion-point (nth (if insert-before 0 1) bounds)))
- (cider-interactive-eval nil
- (cider-eval-print-with-comment-handler
- (current-buffer)
- insertion-point
- cider-comment-prefix)
- bounds)))
-
-(defun cider-pprint-form-to-comment (form-fn insert-before)
- "Evaluate the form selected by FORM-FN and insert result as comment.
-FORM-FN can be either `cider-last-sexp` or `cider-defun-at-point`.
-
-The formatting of the comment is controlled via three options:
- `cider-comment-prefix` \";; => \"
- `cider-comment-continued-prefix` \";; \"
- `cider-comment-postfix` \"\"
-
-so that with customization you can optionally wrap the output
-in the reader macro \"#_( .. )\", or \"(comment ... )\", or any
-other desired formatting.
-
-If INSERT-BEFORE is non-nil, insert before the form, otherwise afterwards."
- (let* ((bounds (funcall form-fn 'bounds))
- (insertion-point (nth (if insert-before 0 1) bounds))
- ;; when insert-before, we need a newline after the output to
- ;; avoid commenting the first line of the form
- (comment-postfix (concat cider-comment-postfix
- (if insert-before "\n" ""))))
- (cider-interactive-eval nil
- (cider-eval-pprint-with-multiline-comment-handler
- (current-buffer)
- insertion-point
- cider-comment-prefix
- cider-comment-continued-prefix
- comment-postfix)
- bounds
- (cider--nrepl-pprint-request-plist (cider--pretty-print-width)))))
-
-(defun cider-pprint-eval-last-sexp-to-comment (&optional insert-before)
- "Evaluate the last sexp and insert result as comment.
-
-The formatting of the comment is controlled via three options:
- `cider-comment-prefix` \";; => \"
- `cider-comment-continued-prefix` \";; \"
- `cider-comment-postfix` \"\"
-
-so that with customization you can optionally wrap the output
-in the reader macro \"#_( .. )\", or \"(comment ... )\", or any
-other desired formatting.
-
-If INSERT-BEFORE is non-nil, insert before the form, otherwise afterwards."
- (interactive "P")
- (cider-pprint-form-to-comment 'cider-last-sexp insert-before))
-
-(defun cider-pprint-eval-defun-to-comment (&optional insert-before)
- "Evaluate the \"top-level\" form and insert result as comment.
-
-The formatting of the comment is controlled via three options:
- `cider-comment-prefix` \";; => \"
- `cider-comment-continued-prefix` \";; \"
- `cider-comment-postfix` \"\"
-
-so that with customization you can optionally wrap the output
-in the reader macro \"#_( .. )\", or \"(comment ... )\", or any
-other desired formatting.
-
-If INSERT-BEFORE is non-nil, insert before the form, otherwise afterwards."
- (interactive "P")
- (cider-pprint-form-to-comment 'cider-defun-at-point insert-before))
-
-(declare-function cider-switch-to-repl-buffer "cider-mode")
-
-(defun cider-eval-last-sexp-to-repl (&optional prefix)
- "Evaluate the expression preceding point and insert its result in the REPL.
-If invoked with a PREFIX argument, switch to the REPL buffer."
- (interactive "P")
- (cider-interactive-eval nil
- (cider-insert-eval-handler (cider-current-connection))
- (cider-last-sexp 'bounds))
- (when prefix
- (cider-switch-to-repl-buffer)))
-
-(defun cider-pprint-eval-last-sexp-to-repl (&optional prefix)
- "Evaluate expr before point and insert its pretty-printed result in the REPL.
-If invoked with a PREFIX argument, switch to the REPL buffer."
- (interactive "P")
- (cider-interactive-eval nil
- (cider-insert-eval-handler (cider-current-connection))
- (cider-last-sexp 'bounds)
- (cider--nrepl-pprint-request-plist (cider--pretty-print-width)))
- (when prefix
- (cider-switch-to-repl-buffer)))
-
-(defun cider-eval-print-last-sexp ()
- "Evaluate the expression preceding point.
-Print its value into the current buffer."
- (interactive)
- (cider-interactive-eval nil
- (cider-eval-print-handler)
- (cider-last-sexp 'bounds)))
-
-(defun cider--pprint-eval-form (form)
- "Pretty print FORM in popup buffer."
- (let* ((result-buffer (cider-popup-buffer cider-result-buffer nil 'clojure-mode))
- (handler (cider-popup-eval-out-handler result-buffer)))
- (cider-interactive-eval (when (stringp form) form)
- handler
- (when (consp form) form)
- (cider--nrepl-pprint-request-plist (cider--pretty-print-width)))))
-
-(defun cider-pprint-eval-last-sexp (&optional output-to-current-buffer)
- "Evaluate the sexp preceding point and pprint its value.
-If invoked with OUTPUT-TO-CURRENT-BUFFER, insert as comment in the current
-buffer, else display in a popup buffer."
- (interactive "P")
- (if output-to-current-buffer
- (cider-pprint-eval-last-sexp-to-comment)
- (cider--pprint-eval-form (cider-last-sexp 'bounds))))
-
-(defun cider--prompt-and-insert-inline-dbg ()
- "Insert a #dbg button at the current sexp."
- (save-excursion
- (let ((beg))
- (skip-chars-forward "\r\n[:blank:]")
- (unless (looking-at-p "(")
- (ignore-errors (backward-up-list)))
- (setq beg (point))
- (let* ((cond (cider-read-from-minibuffer "Condition for debugging (leave empty for \"always\"): "))
- (button (propertize (concat "#dbg"
- (unless (equal cond "")
- (format " ^{:break/when %s}" cond)))
- 'font-lock-face 'cider-fragile-button-face)))
- (when (> (current-column) 30)
- (insert "\n")
- (indent-according-to-mode))
- (insert button)
- (when (> (current-column) 40)
- (insert "\n")
- (indent-according-to-mode)))
- (make-button beg (point)
- 'help-echo "Breakpoint. Reevaluate this form to remove it."
- :type 'cider-fragile))))
-
-(defun cider-eval-defun-at-point (&optional debug-it)
- "Evaluate the current toplevel form, and print result in the minibuffer.
-With DEBUG-IT prefix argument, also debug the entire form as with the
-command `cider-debug-defun-at-point'."
- (interactive "P")
- (let ((inline-debug (eq 16 (car-safe debug-it))))
- (when debug-it
- (when (derived-mode-p 'clojurescript-mode)
- (when (y-or-n-p (concat "The debugger doesn't support ClojureScript yet, and we need help with that."
- " \nWould you like to read the Feature Request?"))
- (browse-url "https://github.com/clojure-emacs/cider/issues/1416"))
- (user-error "The debugger does not support ClojureScript"))
- (when inline-debug
- (cider--prompt-and-insert-inline-dbg)))
- (cider-interactive-eval (when (and debug-it (not inline-debug))
- (concat "#dbg\n" (cider-defun-at-point)))
- nil (cider-defun-at-point 'bounds))))
-
-(defun cider--calculate-opening-delimiters ()
- "Walks up the list of expressions to collect all sexp opening delimiters.
-
-The result is a list of the delimiters.
-
-That function is used in `cider-eval-defun-to-point' so it can make an
-incomplete expression complete."
- (interactive)
- (let ((result nil))
- (save-excursion
- (condition-case nil
- (while t
- (backward-up-list)
- (push (char-after) result))
- (error result)))))
-
-(defun cider--matching-delimiter (delimiter)
- "Get the matching (opening/closing) delimiter for DELIMITER."
- (pcase delimiter
- (?\( ?\))
- (?\[ ?\])
- (?\{ ?\})
- (?\) ?\()
- (?\] ?\[)
- (?\} ?\{)))
-
-(defun cider--calculate-closing-delimiters ()
- "Compute the list of closing delimiters to make the defun before point valid."
- (mapcar #'cider--matching-delimiter (cider--calculate-opening-delimiters)))
-
-(defun cider-eval-defun-to-point ()
- "Evaluate the current toplevel form up to point.
-
-It constructs an expression to eval in the following manner:
-
-- It find the code between the point and the start of the toplevel expression;
-- It balances this bit of code by closing all open expressions;
-- It evaluates the resulting code using `cider-interactive-eval'."
- (interactive)
- (let* ((beg-of-defun (save-excursion (beginning-of-defun) (point)))
- (code (buffer-substring-no-properties beg-of-defun (point)))
- (code (concat code (cider--calculate-closing-delimiters))))
- (cider-interactive-eval code)))
-
-(defun cider-pprint-eval-defun-at-point (&optional output-to-current-buffer)
- "Evaluate the \"top-level\" form at point and pprint its value.
-If invoked with OUTPUT-TO-CURRENT-BUFFER, insert as comment in the current
-buffer, else display in a popup buffer."
- (interactive "P")
- (if output-to-current-buffer
- (cider-pprint-eval-defun-to-comment)
- (cider--pprint-eval-form (cider-defun-at-point 'bounds))))
-
-(defun cider-eval-ns-form ()
- "Evaluate the current buffer's namespace form."
- (interactive)
- (when (clojure-find-ns)
- (save-excursion
- (goto-char (match-beginning 0))
- (cider-eval-defun-at-point))))
-
-(defun cider-read-and-eval (&optional value)
- "Read a sexp from the minibuffer and output its result to the echo area.
-If VALUE is non-nil, it is inserted into the minibuffer as initial input."
- (interactive)
- (let* ((form (cider-read-from-minibuffer "Clojure Eval: " value))
- (override cider-interactive-eval-override)
- (ns-form (if (cider-ns-form-p form) "" (format "(ns %s)" (cider-current-ns)))))
- (with-current-buffer (get-buffer-create cider-read-eval-buffer)
- (erase-buffer)
- (clojure-mode)
- (unless (string= "" ns-form)
- (insert ns-form "\n\n"))
- (insert form)
- (let ((cider-interactive-eval-override override))
- (cider-interactive-eval form)))))
-
-(defun cider-read-and-eval-defun-at-point ()
- "Insert the toplevel form at point in the minibuffer and output its result.
-The point is placed next to the function name in the minibuffer to allow
-passing arguments."
- (interactive)
- (let* ((fn-name (cadr (split-string (cider-defun-at-point))))
- (form (format "(%s)" fn-name)))
- (cider-read-and-eval (cons form (length form)))))
-
-;; Eval keymap
-
-(defvar cider-eval-commands-map
- (let ((map (define-prefix-command 'cider-eval-commands-map)))
- ;; single key bindings defined last for display in menu
- (define-key map (kbd "w") #'cider-eval-last-sexp-and-replace)
- (define-key map (kbd "r") #'cider-eval-region)
- (define-key map (kbd "n") #'cider-eval-ns-form)
- (define-key map (kbd "v") #'cider-eval-sexp-at-point)
- (define-key map (kbd ".") #'cider-read-and-eval-defun-at-point)
- (define-key map (kbd "z") #'cider-eval-defun-to-point)
- (define-key map (kbd "c") #'cider-eval-last-sexp-in-context)
- (define-key map (kbd "b") #'cider-eval-sexp-at-point-in-context)
- ;; duplicates with C- for convenience
- (define-key map (kbd "C-w") #'cider-eval-last-sexp-and-replace)
- (define-key map (kbd "C-r") #'cider-eval-region)
- (define-key map (kbd "C-n") #'cider-eval-ns-form)
- (define-key map (kbd "C-v") #'cider-eval-sexp-at-point)
- (define-key map (kbd "C-.") #'cider-read-and-eval-defun-at-point)
- (define-key map (kbd "C-z") #'cider-eval-defun-to-point)
- (define-key map (kbd "C-c") #'cider-eval-last-sexp-in-context)
- (define-key map (kbd "C-b") #'cider-eval-sexp-at-point-in-context)))
-
-
-;; Connection and REPL
-
-(defun cider-insert-in-repl (form eval)
- "Insert FORM in the REPL buffer and switch to it.
-If EVAL is non-nil the form will also be evaluated."
- (while (string-match "\\`[ \t\n\r]+\\|[ \t\n\r]+\\'" form)
- (setq form (replace-match "" t t form)))
- (with-current-buffer (cider-current-connection)
- (goto-char (point-max))
- (let ((beg (point)))
- (insert form)
- (indent-region beg (point)))
- (when eval
- (cider-repl-return)))
- (cider-switch-to-repl-buffer))
-
-(defun cider-insert-last-sexp-in-repl (&optional arg)
- "Insert the expression preceding point in the REPL buffer.
-If invoked with a prefix ARG eval the expression after inserting it."
- (interactive "P")
- (cider-insert-in-repl (cider-last-sexp) arg))
-
-(defun cider-insert-defun-in-repl (&optional arg)
- "Insert the top level form at point in the REPL buffer.
-If invoked with a prefix ARG eval the expression after inserting it."
- (interactive "P")
- (cider-insert-in-repl (cider-defun-at-point) arg))
-
-(defun cider-insert-region-in-repl (start end &optional arg)
- "Insert the curent region in the REPL buffer.
-START and END represent the region's boundaries.
-If invoked with a prefix ARG eval the expression after inserting it."
- (interactive "rP")
- (cider-insert-in-repl
- (buffer-substring-no-properties start end) arg))
-
-(defun cider-insert-ns-form-in-repl (&optional arg)
- "Insert the current buffer's ns form in the REPL buffer.
-If invoked with a prefix ARG eval the expression after inserting it."
- (interactive "P")
- (cider-insert-in-repl (cider-ns-form) arg))
-
-(defun cider-ping ()
- "Check that communication with the nREPL server works."
- (interactive)
- (thread-first (cider-nrepl-sync-request:eval "\"PONG\"")
- (nrepl-dict-get "value")
- (read)
- (message)))
-
-(defun cider-enable-on-existing-clojure-buffers ()
- "Enable CIDER's minor mode on existing Clojure buffers.
-See command `cider-mode'."
- (interactive)
- (add-hook 'clojure-mode-hook #'cider-mode)
- (dolist (buffer (cider-util--clojure-buffers))
- (with-current-buffer buffer
- (cider-mode +1))))
-
-(defun cider-disable-on-existing-clojure-buffers ()
- "Disable command `cider-mode' on existing Clojure buffers."
- (interactive)
- (dolist (buffer (cider-util--clojure-buffers))
- (with-current-buffer buffer
- (cider-mode -1))))
-
-(defun cider-possibly-disable-on-existing-clojure-buffers ()
- "If not connected, disable command `cider-mode' on existing Clojure buffers."
- (unless (cider-connected-p)
- (cider-disable-on-existing-clojure-buffers)))
-
-
-;;; Completion
-
-(defun cider-sync-request:toggle-trace-var (symbol)
- "Toggle var tracing for SYMBOL."
- (thread-first `("op" "toggle-trace-var"
- "ns" ,(cider-current-ns)
- "sym" ,symbol)
- (cider-nrepl-send-sync-request)))
-
-(defun cider--toggle-trace-var (sym)
- "Toggle var tracing for SYM."
- (let* ((trace-response (cider-sync-request:toggle-trace-var sym))
- (var-name (nrepl-dict-get trace-response "var-name"))
- (var-status (nrepl-dict-get trace-response "var-status")))
- (pcase var-status
- ("not-found" (error "Var %s not found" (cider-propertize sym 'fn)))
- ("not-traceable" (error "Var %s can't be traced because it's not bound to a function" (cider-propertize var-name 'fn)))
- (_ (message "Var %s %s" (cider-propertize var-name 'fn) var-status)))))
-
-(defun cider-toggle-trace-var (arg)
- "Toggle var tracing.
-
-Prompts for the symbol to use, or uses the symbol at point, depending on
-the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the
-opposite of what that option dictates."
- (interactive "P")
- (cider-ensure-op-supported "toggle-trace-var")
- (funcall (cider-prompt-for-symbol-function arg)
- "Toggle trace for var"
- #'cider--toggle-trace-var))
-
-(defun cider-sync-request:toggle-trace-ns (ns)
- "Toggle namespace tracing for NS."
- (thread-first `("op" "toggle-trace-ns"
- "ns" ,ns)
- (cider-nrepl-send-sync-request)))
-
-(defun cider-toggle-trace-ns (query)
- "Toggle ns tracing.
-Defaults to the current ns. With prefix arg QUERY, prompts for a ns."
- (interactive "P")
- (cider-map-connections
- (lambda (conn)
- (with-current-buffer conn
- (cider-ensure-op-supported "toggle-trace-ns")
- (let ((ns (if query
- (completing-read "Toggle trace for ns: "
- (cider-sync-request:ns-list))
- (cider-current-ns))))
- (let* ((trace-response (cider-sync-request:toggle-trace-ns ns))
- (ns-status (nrepl-dict-get trace-response "ns-status")))
- (pcase ns-status
- ("not-found" (error "Namespace %s not found" (cider-propertize ns 'ns)))
- (_ (message "Namespace %s %s" (cider-propertize ns 'ns) ns-status)))))))
- :clj))
-
-(defun cider-undef ()
- "Undefine a symbol from the current ns."
- (interactive)
- (cider-ensure-op-supported "undef")
- (cider-read-symbol-name
- "Undefine symbol: "
- (lambda (sym)
- (cider-nrepl-send-request
- `("op" "undef"
- "ns" ,(cider-current-ns)
- "symbol" ,sym)
- (cider-interactive-eval-handler (current-buffer))))))
-
-(defun cider-refresh--handle-response (response log-buffer)
- "Refresh LOG-BUFFER with RESPONSE."
- (nrepl-dbind-response response (out err reloading status error error-ns after before)
- (cl-flet* ((log (message &optional face)
- (cider-emit-into-popup-buffer log-buffer message face t))
-
- (log-echo (message &optional face)
- (log message face)
- (unless cider-refresh-show-log-buffer
- (let ((message-truncate-lines t))
- (message "cider-refresh: %s" message)))))
- (cond
- (out
- (log out))
-
- (err
- (log err 'font-lock-warning-face))
-
- ((member "invoking-before" status)
- (log-echo (format "Calling %s\n" before) 'font-lock-string-face))
-
- ((member "invoked-before" status)
- (log-echo (format "Successfully called %s\n" before) 'font-lock-string-face))
-
- ((member "invoked-not-resolved" status)
- (log-echo "Could not resolve refresh function\n" 'font-lock-string-face))
-
- (reloading
- (log-echo (format "Reloading %s\n" reloading) 'font-lock-string-face))
-
- ((member "reloading" (nrepl-dict-keys response))
- (log-echo "Nothing to reload\n" 'font-lock-string-face))
-
- ((member "ok" status)
- (log-echo "Reloading successful\n" 'font-lock-string-face))
-
- (error-ns
- (log-echo (format "Error reloading %s\n" error-ns) 'font-lock-warning-face))
-
- ((member "invoking-after" status)
- (log-echo (format "Calling %s\n" after) 'font-lock-string-face))
-
- ((member "invoked-after" status)
- (log-echo (format "Successfully called %s\n" after) 'font-lock-string-face))))
-
- (with-selected-window (or (get-buffer-window cider-refresh-log-buffer)
- (selected-window))
- (with-current-buffer cider-refresh-log-buffer
- (goto-char (point-max))))
-
- (when (member "error" status)
- (cider--render-stacktrace-causes error))))
-
-(defun cider-save-project-buffers ()
- "Ensure modified project buffers are saved before certain operations.
-Its behavior is controlled by `cider-save-files-on-cider-refresh'."
- (when-let* ((project-root (clojure-project-dir)))
- (when cider-save-files-on-cider-refresh
- (save-some-buffers
- (eq cider-save-files-on-cider-refresh t)
- (lambda ()
- (and
- (derived-mode-p 'clojure-mode)
- (string-prefix-p project-root
- (file-truename default-directory)
- (eq system-type 'windows-nt))))))))
-
-(defun cider-refresh (&optional mode)
- "Reload modified and unloaded namespaces on the classpath.
-
-With a single prefix argument, or if MODE is `refresh-all', reload all
-namespaces on the classpath unconditionally.
-
-With a double prefix argument, or if MODE is `clear', clear the state of
-the namespace tracker before reloading. This is useful for recovering from
-some classes of error (for example, those caused by circular dependencies)
-that a normal reload would not otherwise recover from. The trade-off of
-clearing is that stale code from any deleted files may not be completely
-unloaded.
-
-With a negative prefix argument, or if MODE is `inhibit-fns', prevent any
-refresh functions (defined in `cider-refresh-before-fn' and
-`cider-refresh-after-fn') from being invoked."
- (interactive "p")
- (cider-ensure-connected)
- (cider-ensure-op-supported "refresh")
- (cider-save-project-buffers)
- (let ((clear? (member mode '(clear 16)))
- (refresh-all? (member mode '(refresh-all 4)))
- (inhibit-refresh-fns (member mode '(inhibit-fns -1))))
- (cider-map-connections
- (lambda (conn)
- ;; Inside the lambda, so the buffer is not created if we error out.
- (let ((log-buffer (or (get-buffer cider-refresh-log-buffer)
- (cider-make-popup-buffer cider-refresh-log-buffer))))
- (when cider-refresh-show-log-buffer
- (cider-popup-buffer-display log-buffer))
- (when inhibit-refresh-fns
- (cider-emit-into-popup-buffer log-buffer
- "inhibiting refresh functions\n"
- nil
- t))
- (when clear?
- (cider-nrepl-send-sync-request '("op" "refresh-clear") conn))
- (cider-nrepl-send-request
- (nconc `("op" ,(if refresh-all? "refresh-all" "refresh")
- "print-length" ,cider-stacktrace-print-length
- "print-level" ,cider-stacktrace-print-level)
- (when (cider--pprint-fn)
- `("pprint-fn" ,(cider--pprint-fn)))
- (when (and (not inhibit-refresh-fns) cider-refresh-before-fn)
- `("before" ,cider-refresh-before-fn))
- (when (and (not inhibit-refresh-fns) cider-refresh-after-fn)
- `("after" ,cider-refresh-after-fn)))
- (lambda (response)
- (cider-refresh--handle-response response log-buffer))
- conn)))
- :clj 'any-mode)))
-
-(defun cider-file-string (file)
- "Read the contents of a FILE and return as a string."
- (with-current-buffer (find-file-noselect file)
- (substring-no-properties (buffer-string))))
-
-(defun cider-load-buffer (&optional buffer)
- "Load (eval) BUFFER's file in nREPL.
-If no buffer is provided the command acts on the current buffer.
-
-If the buffer is for a cljc file, and both a Clojure and
-ClojureScript REPL exists for the project, it is evaluated in both REPLs."
- (interactive)
- (check-parens)
- (cider-ensure-connected)
- (setq buffer (or buffer (current-buffer)))
- (with-current-buffer buffer
- (unless buffer-file-name
- (user-error "Buffer `%s' is not associated with a file" (current-buffer)))
- (when (and cider-save-file-on-load
- (buffer-modified-p)
- (or (eq cider-save-file-on-load t)
- (y-or-n-p (format "Save file %s? " buffer-file-name))))
- (save-buffer))
- (remove-overlays nil nil 'cider-temporary t)
- (cider--clear-compilation-highlights)
- (cider--quit-error-window)
- (let ((filename (buffer-file-name buffer))
- (ns-form (cider-ns-form)))
- (cider-map-connections
- (lambda (connection)
- (when ns-form
- (cider-repl--cache-ns-form ns-form connection))
- (cider-request:load-file (cider-file-string filename)
- (funcall cider-to-nrepl-filename-function
- (cider--server-filename filename))
- (file-name-nondirectory filename)
- connection))
- :both)
- (message "Loading %s..." filename))))
-
-(defun cider-load-file (filename)
- "Load (eval) the Clojure file FILENAME in nREPL.
-
-If the file is a cljc file, and both a Clojure and ClojureScript
-REPL exists for the project, it is evaluated in both REPLs.
-
-The heavy lifting is done by `cider-load-buffer'."
- (interactive (list
- (read-file-name "Load file: " nil nil nil
- (when (buffer-file-name)
- (file-name-nondirectory
- (buffer-file-name))))))
- (if-let* ((buffer (find-buffer-visiting filename)))
- (cider-load-buffer buffer)
- (find-file filename)
- (cider-load-buffer (current-buffer))))
-
-(defun cider-load-all-files (directory)
- "Load all files in DIRECTORY (recursively). Useful when the running nREPL on remote host."
- (interactive "DLoad files beneath directory: ")
- (mapcar #'cider-load-file
- (directory-files-recursively directory ".clj$")))
-
-(defalias 'cider-eval-file 'cider-load-file
- "A convenience alias as some people are confused by the load-* names.")
-
-(defalias 'cider-eval-all-files 'cider-load-all-files
- "A convenience alias as some people are confused by the load-* names.")
-
-(defalias 'cider-eval-buffer 'cider-load-buffer
- "A convenience alias as some people are confused by the load-* names.")
-
-
-;; Format
-
-(defun cider--format-reindent (formatted start)
- "Reindent FORMATTED to align with buffer position START."
- (let* ((start-column (save-excursion (goto-char start) (current-column)))
- (indent-line (concat "\n" (make-string start-column ? ))))
- (replace-regexp-in-string "\n" indent-line formatted)))
-
-
-;;; Format region
-
-(defun cider--format-region (start end formatter)
- "Format the contents of the given region.
-
-START and END represent the region's boundaries.
-
-FORMATTER is a function of one argument which is used to convert
-the string contents of the region into a formatted string.
-
-Uses the following heuristic to try to maintain point position:
-
-- Take a snippet of text starting at current position, up to 64 chars.
-- Search for the snippet, with lax whitespace, in the formatted text.
- - If snippet is less than 64 chars (point was near end of buffer), search
- from end instead of beginning.
-- Place point at match beginning, or `point-min' if no match."
- (let* ((original (buffer-substring-no-properties start end))
- (formatted (funcall formatter original))
- (indented (cider--format-reindent formatted start)))
- (unless (equal original indented)
- (let* ((pos (point))
- (pos-max (1+ (buffer-size)))
- (l 64)
- (endp (> (+ pos l) pos-max))
- (snippet (thread-last (buffer-substring-no-properties
- pos (min (+ pos l) pos-max))
- (replace-regexp-in-string "[[:space:]\t\n\r]+" "[[:space:]\t\n\r]*"))))
- (delete-region start end)
- (insert indented)
- (goto-char (if endp (point-max) (point-min)))
- (funcall (if endp #'re-search-backward #'re-search-forward) snippet nil t)
- (goto-char (or (match-beginning 0) start))
- (when (looking-at-p "\n") (forward-char))))))
-
-(defun cider-format-region (start end)
- "Format the Clojure code in the current region.
-START and END represent the region's boundaries."
- (interactive "r")
- (cider-ensure-connected)
- (cider--format-region start end #'cider-sync-request:format-code))
-
-
-;;; Format defun
-
-(defun cider-format-defun ()
- "Format the code in the current defun."
- (interactive)
- (cider-ensure-connected)
- (save-excursion
- (mark-defun)
- (cider-format-region (region-beginning) (region-end))))
-
-
-;;; Format buffer
-
-(defun cider--format-buffer (formatter)
- "Format the contents of the current buffer.
-
-Uses FORMATTER, a function of one argument, to convert the string contents
-of the buffer into a formatted string."
- (cider--format-region 1 (1+ (buffer-size)) formatter))
-
-(defun cider-format-buffer ()
- "Format the Clojure code in the current buffer."
- (interactive)
- (check-parens)
- (cider-ensure-connected)
- (cider--format-buffer #'cider-sync-request:format-code))
-
-
-;;; Format EDN
-
-(defun cider-format-edn-buffer ()
- "Format the EDN data in the current buffer."
- (interactive)
- (check-parens)
- (cider-ensure-connected)
- (cider--format-buffer (lambda (edn)
- (cider-sync-request:format-edn edn (cider--pretty-print-width)))))
-
-(defun cider-format-edn-region (start end)
- "Format the EDN data in the current region.
-START and END represent the region's boundaries."
- (interactive "r")
- (cider-ensure-connected)
- (let* ((start-column (save-excursion (goto-char start) (current-column)))
- (right-margin (- (cider--pretty-print-width) start-column)))
- (cider--format-region start end
- (lambda (edn)
- (cider-sync-request:format-edn edn right-margin)))))
-
-
-;;; Interrupt evaluation
-
-(defun cider-interrupt-handler (buffer)
- "Create an interrupt response handler for BUFFER."
- (nrepl-make-response-handler buffer nil nil nil nil))
-
-(defun cider-describe-nrepl-session ()
- "Describe an nREPL session."
- (interactive)
- (cider-ensure-connected)
- (let ((selected-session (completing-read "Describe nREPL session: " (nrepl-sessions (cider-current-connection)))))
- (when (and selected-session (not (equal selected-session "")))
- (let* ((session-info (nrepl-sync-request:describe (cider-current-connection)))
- (ops (nrepl-dict-keys (nrepl-dict-get session-info "ops")))
- (session-id (nrepl-dict-get session-info "session"))
- (session-type (cond
- ((equal session-id (cider-current-session)) "Active eval")
- ((equal session-id (cider-current-tooling-session)) "Active tooling")
- (t "Unknown"))))
- (with-current-buffer (cider-popup-buffer cider-nrepl-session-buffer)
- (read-only-mode -1)
- (insert (format "Session: %s\n" session-id)
- (format "Type: %s session\n" session-type)
- (format "Supported ops:\n"))
- (mapc (lambda (op) (insert (format " * %s\n" op))) ops)))
- (display-buffer cider-nrepl-session-buffer))))
-
-(defun cider-close-nrepl-session ()
- "Close an nREPL session for the current connection."
- (interactive)
- (cider-ensure-connected)
- (nrepl-sync-request:close (cider-current-connection))
- (message "Closed nREPL session"))
-
-;;; quiting
-(defun cider--close-buffer (buffer)
- "Close the BUFFER and kill its associated process (if any)."
- (when (buffer-live-p buffer)
- (with-current-buffer buffer
- (when-let* ((proc (get-buffer-process buffer)))
- (when (process-live-p proc)
- (when (or (not nrepl-server-buffer)
- ;; Sync request will hang if the server is dead.
- (process-live-p (get-buffer-process nrepl-server-buffer)))
- (when (or nrepl-session nrepl-tooling-session)
- (nrepl-sync-request:close buffer)))
- (when proc (delete-process proc)))))
- (kill-buffer buffer)))
-
-(defun cider-close-ancillary-buffers ()
- "Close buffers that are shared across connections."
- (interactive)
- (dolist (buf-name cider-ancillary-buffers)
- (when (get-buffer buf-name)
- (kill-buffer buf-name))))
-
-(defun cider--quit-connection (conn)
- "Quit the connection CONN."
- (when conn
- (cider--close-connection-buffer conn)))
-
-(defvar cider-scratch-buffer-name)
-(defun cider-quit (&optional quit-all)
- "Quit the currently active CIDER connection.
-With a prefix argument QUIT-ALL the command will kill all connections
-and all ancillary CIDER buffers."
- (interactive "P")
- (cider-ensure-connected)
- (if (and quit-all (y-or-n-p "Are you sure you want to quit all CIDER connections? "))
- (progn
- (when-let* ((scratch (get-buffer cider-scratch-buffer-name)))
- (when (y-or-n-p (format "Kill %s buffer? " cider-scratch-buffer-name))
- (kill-buffer cider-scratch-buffer-name)))
- (dolist (connection cider-connections)
- (cider--quit-connection connection))
- (message "All active nREPL connections were closed"))
- (let ((connection (cider-current-connection)))
- (when (y-or-n-p (format "Are you sure you want to quit the current CIDER connection %s? "
- (cider-propertize (buffer-name connection) 'bold)))
- (cider--quit-connection connection))))
- ;; if there are no more connections we can kill all ancillary buffers
- (unless (cider-connected-p)
- (cider-close-ancillary-buffers)))
-
-(declare-function cider-connect "cider")
-(declare-function cider-jack-in "cider")
-(defun cider--restart-connection (conn)
- "Restart the connection CONN."
- (let ((project-dir (with-current-buffer conn nrepl-project-dir))
- (buf-name (buffer-name conn))
- ;; save these variables before we kill the connection
- (conn-creation-method (with-current-buffer conn cider-connection-created-with))
- (conn-endpoint (with-current-buffer conn nrepl-endpoint)))
- (cider--quit-connection conn)
- ;; Workaround for a nasty race condition https://github.com/clojure-emacs/cider/issues/439
- ;; TODO: Find a better way to ensure `cider-quit' has finished
- (message "Waiting for CIDER connection %s to quit..."
- (cider-propertize buf-name 'bold))
- (sleep-for 2)
- (pcase conn-creation-method
- (`connect (apply #'cider-connect conn-endpoint))
- (`jack-in (if project-dir
- (let ((default-directory project-dir))
- (cider-jack-in))
- (error "Can't restart CIDER connection for unknown project")))
- (_ (error "Unexpected value %S for `cider-connection-created-with'"
- conn-creation-method)))))
-
-(defun cider-restart (&optional restart-all)
- "Restart the currently active CIDER connection.
-If RESTART-ALL is t, then restarts all connections."
- (interactive "P")
- (cider-ensure-connected)
- (if restart-all
- (dolist (conn cider-connections)
- (cider--restart-connection conn))
- (cider--restart-connection (cider-current-connection))))
-
-(defvar cider--namespace-history nil
- "History of user input for namespace prompts.")
-
-(defun cider--var-namespace (var)
- "Return the namespace of VAR.
-VAR is a fully qualified Clojure variable name as a string."
- (replace-regexp-in-string "\\(?:#'\\)?\\(.*\\)/.*" "\\1" var))
-
-(defun cider-load-all-project-ns ()
- "Load all namespaces in the current project."
- (interactive)
- (cider-ensure-connected)
- (cider-ensure-op-supported "ns-load-all")
- (when (y-or-n-p "Are you sure you want to load all namespaces in the project? ")
- (message "Loading all project namespaces...")
- (let ((loaded-ns-count (length (cider-sync-request:ns-load-all))))
- (message "Loaded %d namespaces" loaded-ns-count))))
-
-(defun cider-run (&optional function)
- "Run -main or FUNCTION, prompting for its namespace if necessary.
-With a prefix argument, prompt for function to run instead of -main."
- (interactive (list (when current-prefix-arg (read-string "Function name: "))))
- (cider-ensure-connected)
- (let ((name (or function "-main")))
- (when-let* ((response (cider-nrepl-send-sync-request
- `("op" "ns-list-vars-by-name"
- "name" ,name))))
- (if-let* ((vars (split-string (substring (nrepl-dict-get response "var-list") 1 -1))))
- (cider-interactive-eval
- (if (= (length vars) 1)
- (concat "(" (car vars) ")")
- (let* ((completions (mapcar #'cider--var-namespace vars))
- (def (or (car cider--namespace-history)
- (car completions))))
- (format "(#'%s/%s)"
- (completing-read (format "Namespace (%s): " def)
- completions nil t nil
- 'cider--namespace-history def)
- name))))
- (user-error "No %s var defined in any namespace" (cider-propertize name 'fn))))))
-
-(provide 'cider-interaction)
-
-;;; cider-interaction.el ends here
diff --git a/cider-macroexpansion.el b/cider-macroexpansion.el
index e0d42d7d..8123932a 100644
--- a/cider-macroexpansion.el
+++ b/cider-macroexpansion.el
@@ -36,7 +36,6 @@
(require 'cider-compat)
(defconst cider-macroexpansion-buffer "*cider-macroexpansion*")
-(add-to-list 'cider-ancillary-buffers cider-macroexpansion-buffer)
(defcustom cider-macroexpansion-display-namespaces 'tidy
"Determines if namespaces are displayed in the macroexpansion buffer.
@@ -163,12 +162,13 @@ and point is placed after the expanded form."
(defun cider-create-macroexpansion-buffer ()
"Create a new macroexpansion buffer."
- (with-current-buffer (cider-popup-buffer cider-macroexpansion-buffer t)
- (clojure-mode)
+ (with-current-buffer (cider-popup-buffer cider-macroexpansion-buffer 'select 'clojure-mode 'ancillary)
(cider-mode -1)
(cider-macroexpansion-mode 1)
(current-buffer)))
+(declare-function cider-find-var "cider-find")
+
(defvar cider-macroexpansion-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "g") #'cider-macroexpand-again)
diff --git a/cider-mode.el b/cider-mode.el
index 10471619..8258f05c 100644
--- a/cider-mode.el
+++ b/cider-mode.el
@@ -32,12 +32,13 @@
;;; Code:
(require 'clojure-mode)
-(require 'cider-interaction)
-(require 'cider-profile)
-(require 'cider-test)
+(require 'cider-eval)
+(require 'cider-test) ; required only for the menu
(require 'cider-eldoc)
(require 'cider-resolve)
-(require 'cider-doc)
+(require 'cider-doc) ; required only for the menu
+(require 'cider-profile) ; required only for the menu
+(require 'cider-completion)
(require 'subr-x)
(require 'cider-compat)
@@ -49,9 +50,8 @@
(defun cider--modeline-info ()
"Return info for the cider mode modeline.
-
Info contains the connection type, project name and host:port endpoint."
- (if-let* ((current-connection (ignore-errors (cider-current-connection))))
+ (if-let* ((current-connection (ignore-errors (cider-current-repl))))
(with-current-buffer current-connection
(concat
cider-repl-type
@@ -86,14 +86,8 @@ variable to nil to disable the mode line entirely."
(defun cider--switch-to-repl-buffer (repl-buffer &optional set-namespace)
"Select the REPL-BUFFER, when possible in an existing window.
-
-Hint: You can use `display-buffer-reuse-frames' and
-`special-display-buffer-names' to customize the frame in which
-the buffer should appear.
-
When SET-NAMESPACE is t, sets the namespace in the REPL buffer to
that of the namespace in the Clojure source buffer."
- (cider-ensure-connected)
(let ((buffer (current-buffer)))
;; first we switch to the REPL buffer
(if cider-repl-display-in-current-window
@@ -105,36 +99,16 @@ that of the namespace in the Clojure source buffer."
(goto-char (point-max))))
(defun cider-switch-to-repl-buffer (&optional set-namespace)
- "Select the REPL buffer, when possible in an existing window.
-The buffer chosen is based on the file open in the current buffer. If
-multiple REPL buffers are associated with current connection the most
-recent is used.
-
-If the REPL buffer cannot be unambiguously determined, the REPL
-buffer is chosen based on the current connection buffer and a
-message raised informing the user.
-
-Hint: You can use `display-buffer-reuse-frames' and
-`special-display-buffer-names' to customize the frame in which
-the buffer should appear.
-
-With a prefix arg SET-NAMESPACE sets the namespace in the REPL buffer to that
-of the namespace in the Clojure source buffer."
+ "Switch to current REPL buffer, when possible in an existing window.
+The type of the REPL is inferred from the mode of current buffer. With a
+prefix arg SET-NAMESPACE sets the namespace in the REPL buffer to that of
+the namespace in the Clojure source buffer"
(interactive "P")
- (let* ((connections (cider-connections))
- (type (cider-connection-type-for-buffer))
- (a-repl)
- (the-repl (seq-find (lambda (b)
- (when (member b connections)
- (unless a-repl
- (setq a-repl b))
- (equal type (cider-connection-type-for-buffer b))))
- (buffer-list))))
- (if-let* ((repl (or the-repl a-repl)))
- (cider--switch-to-repl-buffer repl set-namespace)
- (user-error "No REPL found"))))
-
-(declare-function cider-load-buffer "cider-interaction")
+ (cider--switch-to-repl-buffer
+ (cider-current-repl nil 'ensure)
+ set-namespace))
+
+(declare-function cider-load-buffer "cider-eval")
(defun cider-load-buffer-and-switch-to-repl-buffer (&optional set-namespace)
"Load the current buffer into the matching REPL buffer and switch to it.
@@ -153,10 +127,10 @@ Clojure buffer and the REPL buffer."
(interactive)
(if (derived-mode-p 'cider-repl-mode)
(let* ((a-buf)
- (the-buf (let ((repl-type (cider-connection-type-for-buffer)))
+ (the-buf (let ((repl-type (cider-repl-type-for-buffer)))
(seq-find (lambda (b)
(unless (with-current-buffer b (derived-mode-p 'cider-repl-mode))
- (when-let* ((type (cider-connection-type-for-buffer b)))
+ (when-let* ((type (cider-repl-type-for-buffer b)))
(unless a-buf
(setq a-buf b))
(or (equal type "multi")
@@ -171,43 +145,160 @@ Clojure buffer and the REPL buffer."
(defun cider-find-and-clear-repl-output (&optional clear-repl)
"Find the current REPL buffer and clear it.
-With a prefix argument CLEAR-REPL the command clears the entire REPL buffer.
-Returns to the buffer in which the command was invoked.
-
-See also the related commands `cider-repl-clear-buffer' and
+With a prefix argument CLEAR-REPL the command clears the entire REPL
+buffer. Returns to the buffer in which the command was invoked. See also
+the related commands `cider-repl-clear-buffer' and
`cider-repl-clear-output'."
(interactive "P")
(let ((origin-buffer (current-buffer)))
- (switch-to-buffer (cider-current-repl-buffer))
+ (switch-to-buffer (cider-current-repl))
(if clear-repl
(cider-repl-clear-buffer)
(cider-repl-clear-output))
(switch-to-buffer origin-buffer)))
+(defun cider-undef ()
+ "Undefine a symbol from the current ns."
+ (interactive)
+ (cider-ensure-op-supported "undef")
+ (cider-read-symbol-name
+ "Undefine symbol: "
+ (lambda (sym)
+ (cider-nrepl-send-request
+ `("op" "undef"
+ "ns" ,(cider-current-ns)
+ "symbol" ,sym)
+ (cider-interactive-eval-handler (current-buffer))))))
+
+;;; cider-run
+(defvar cider--namespace-history nil
+ "History of user input for namespace prompts.")
+
+(defun cider--var-namespace (var)
+ "Return the namespace of VAR.
+VAR is a fully qualified Clojure variable name as a string."
+ (replace-regexp-in-string "\\(?:#'\\)?\\(.*\\)/.*" "\\1" var))
+
+(defun cider-run (&optional function)
+ "Run -main or FUNCTION, prompting for its namespace if necessary.
+With a prefix argument, prompt for function to run instead of -main."
+ (interactive (list (when current-prefix-arg (read-string "Function name: "))))
+ (cider-ensure-connected)
+ (let ((name (or function "-main")))
+ (when-let* ((response (cider-nrepl-send-sync-request
+ `("op" "ns-list-vars-by-name"
+ "name" ,name))))
+ (if-let* ((vars (split-string (substring (nrepl-dict-get response "var-list") 1 -1))))
+ (cider-interactive-eval
+ (if (= (length vars) 1)
+ (concat "(" (car vars) ")")
+ (let* ((completions (mapcar #'cider--var-namespace vars))
+ (def (or (car cider--namespace-history)
+ (car completions))))
+ (format "(#'%s/%s)"
+ (completing-read (format "Namespace (%s): " def)
+ completions nil t nil
+ 'cider--namespace-history def)
+ name))))
+ (user-error "No %s var defined in any namespace" (cider-propertize name 'fn))))))
+
+;;; Insert (and eval) in REPL functionality
+(defvar cider-insert-commands-map
+ (let ((map (define-prefix-command 'cider-insert-commands-map)))
+ ;; single key bindings defined last for display in menu
+ (define-key map (kbd "e") #'cider-insert-last-sexp-in-repl)
+ (define-key map (kbd "d") #'cider-insert-defun-in-repl)
+ (define-key map (kbd "r") #'cider-insert-region-in-repl)
+ (define-key map (kbd "n") #'cider-insert-ns-form-in-repl)
+
+ ;; duplicates with C- for convenience
+ (define-key map (kbd "C-e") #'cider-insert-last-sexp-in-repl)
+ (define-key map (kbd "C-d") #'cider-insert-defun-in-repl)
+ (define-key map (kbd "C-r") #'cider-insert-region-in-repl)
+ (define-key map (kbd "C-n") #'cider-insert-ns-form-in-repl)))
+
+(defcustom cider-switch-to-repl-after-insert-p t
+ "Whether to switch to the repl after inserting a form into the repl."
+ :type 'boolean
+ :group 'cider
+ :package-version '(cider . "0.18.0"))
+
+(defcustom cider-invert-insert-eval-p nil
+ "Whether to invert the behavior of evaling.
+Default behavior when inserting is to NOT eval the form and only eval with
+a prefix. This allows to invert this so that default behavior is to insert
+and eval and the prefix is required to prevent evaluation."
+ :type 'boolean
+ :group 'cider
+ :package-version '(cider . "0.18.0"))
+
+(defun cider-insert-in-repl (form eval)
+ "Insert FORM in the REPL buffer and switch to it.
+If EVAL is non-nil the form will also be evaluated."
+ (while (string-match "\\`[ \t\n\r]+\\|[ \t\n\r]+\\'" form)
+ (setq form (replace-match "" t t form)))
+ (with-current-buffer (cider-current-repl)
+ (goto-char (point-max))
+ (let ((beg (point)))
+ (insert form)
+ (indent-region beg (point)))
+ (when (if cider-invert-insert-eval-p
+ (not eval)
+ eval)
+ (cider-repl-return)))
+ (when cider-switch-to-repl-after-insert-p
+ (cider-switch-to-repl-buffer)))
+
+(defun cider-insert-last-sexp-in-repl (&optional arg)
+ "Insert the expression preceding point in the REPL buffer.
+If invoked with a prefix ARG eval the expression after inserting it."
+ (interactive "P")
+ (cider-insert-in-repl (cider-last-sexp) arg))
+
+(defun cider-insert-defun-in-repl (&optional arg)
+ "Insert the top level form at point in the REPL buffer.
+If invoked with a prefix ARG eval the expression after inserting it."
+ (interactive "P")
+ (cider-insert-in-repl (cider-defun-at-point) arg))
+
+(defun cider-insert-region-in-repl (start end &optional arg)
+ "Insert the curent region in the REPL buffer.
+START and END represent the region's boundaries.
+If invoked with a prefix ARG eval the expression after inserting it."
+ (interactive "rP")
+ (cider-insert-in-repl
+ (buffer-substring-no-properties start end) arg))
+
+(defun cider-insert-ns-form-in-repl (&optional arg)
+ "Insert the current buffer's ns form in the REPL buffer.
+If invoked with a prefix ARG eval the expression after inserting it."
+ (interactive "P")
+ (cider-insert-in-repl (cider-ns-form) arg))
+
+
;;; The menu-bar
(defconst cider-mode-menu
`("CIDER"
- ["Start a REPL" cider-jack-in
- :help "Starts an nREPL server (with Leiningen, Boot, or Gradle) and connects a REPL to it."]
- ["Connect to a REPL" cider-connect
- :help "Connects to a REPL that's already running."]
- ["Replicate connection" cider-replicate-connection
- :help "Opens another connection based on a existing one. The new connection uses the same host and port as the base connection."]
- ["Quit" cider-quit :active cider-connections]
- ["Restart" cider-restart :active cider-connections]
+ ["Start or connect to any REPL" cider
+ :help "A simple wrapper around all commands for starting/connecting to a REPL."]
+ ("Clojure"
+ ["Start a Clojure REPL" cider-jack-in
+ :help "Starts an nREPL server and connects a Clojure REPL to it."]
+ ["Connect to a Clojure REPL" cider-connect
+ :help "Connects to a REPL that's already running."])
("ClojureScript"
- ["Start a Clojure REPL, and a ClojureScript REPL" cider-jack-in-clojurescript
- :help "Starts an nREPL server, connects a Clojure REPL to it, and then a ClojureScript REPL.
-Configure `cider-cljs-repl-types' to change the ClojureScript REPL to use for your build tool."]
+ ["Start a ClojureScript REPL" cider-jack-in-cljs
+ :help "Starts an nREPL server and connects a ClojureScript REPL to it."]
["Connect to a ClojureScript REPL" cider-connect-clojurescript
:help "Connects to a ClojureScript REPL that's already running."]
- ["Create a ClojureScript REPL from a Clojure REPL" cider-create-sibling-cljs-repl])
+ ["Create a ClojureScript REPL from a Clojure REPL" cider-jack-in-sibling-clojurescript])
+ "--"
+ ["Quit" cider-quit :active (cider-connected-p)]
+ ["Restart" cider-restart :active (cider-connected-p)]
"--"
- ["Connection info" cider-display-connection-info
- :active cider-connections]
- ["Rotate default connection" cider-rotate-default-connection
- :active (cdr cider-connections)]
+ ["Connection info" cider-describe-connection
+ :active (cider-connected-p)]
["Select any CIDER buffer" cider-selector]
"--"
["Configure CIDER" (customize-group 'cider)]
@@ -220,25 +311,24 @@ Configure `cider-cljs-repl-types' to change the ClojureScript REPL to use for yo
"--"
["Close ancillary buffers" cider-close-ancillary-buffers
:active (seq-remove #'null cider-ancillary-buffers)]
- ("nREPL" :active cider-connections
- ["Describe session" cider-describe-nrepl-session]
- ["Close session" cider-close-nrepl-session]
- ["Toggle message logging" nrepl-toggle-message-logging]))
- "Menu for CIDER mode.")
+ ("nREPL" :active (cider-connected-p)
+ ["Describe nrepl session" cider-describe-nrepl-session]
+ ["Toggle message logging" nrepl-toggle-message-logging])
+ "Menu for CIDER mode."))
(defconst cider-mode-eval-menu
- '("CIDER Eval" :visible cider-connections
+ '("CIDER Eval" :visible (cider-connected-p)
["Eval top-level sexp" cider-eval-defun-at-point]
- ["Eval top-level sexp to point" cider-eval-defun-to-point]
+ ["Eval top-level sexp to point" cider-eval-defun-up-to-point]
+ ["Eval top-level sexp to comment" cider-eval-defun-to-comment]
+ ["Eval top-level sexp and pretty-print to comment" cider-pprint-eval-defun-to-comment]
+ "--"
["Eval current sexp" cider-eval-sexp-at-point]
+ ["Eval current sexp to point" cider-eval-sexp-up-to-point]
["Eval current sexp in context" cider-eval-sexp-at-point-in-context]
+ "--"
["Eval last sexp" cider-eval-last-sexp]
["Eval last sexp in context" cider-eval-last-sexp-in-context]
- ["Eval selected region" cider-eval-region]
- ["Eval ns form" cider-eval-ns-form]
- "--"
- ["Interrupt evaluation" cider-interrupt]
- "--"
["Eval last sexp and insert" cider-eval-print-last-sexp
:keys "\\[universal-argument] \\[cider-eval-last-sexp]"]
["Eval last sexp in popup buffer" cider-pprint-eval-last-sexp]
@@ -246,23 +336,30 @@ Configure `cider-cljs-repl-types' to change the ClojureScript REPL to use for yo
["Eval last sexp to REPL" cider-eval-last-sexp-to-repl]
["Eval last sexp and pretty-print to REPL" cider-pprint-eval-last-sexp-to-repl]
["Eval last sexp and pretty-print to comment" cider-pprint-eval-last-sexp-to-comment]
+ "--"
+ ["Eval selected region" cider-eval-region]
+ ["Eval ns form" cider-eval-ns-form]
+ "--"
+ ["Interrupt evaluation" cider-interrupt]
+ "--"
["Insert last sexp in REPL" cider-insert-last-sexp-in-repl]
["Insert top-level sexp in REPL" cider-insert-defun-in-repl]
["Insert region in REPL" cider-insert-region-in-repl]
["Insert ns form in REPL" cider-insert-ns-form-in-repl]
- ["Eval top-level sexp to comment" cider-eval-defun-to-comment]
- ["Eval top-level sexp and pretty-print to comment" cider-pprint-eval-defun-to-comment]
"--"
["Load this buffer" cider-load-buffer]
+ ["Load this buffer and switch to REPL" cider-load-buffer-and-switch-to-repl-buffer]
["Load another file" cider-load-file]
["Recursively load all files in directory" cider-load-all-files]
["Load all project files" cider-load-all-project-ns]
- ["Refresh loaded code" cider-refresh]
+ ["Refresh loaded code" cider-ns-refresh]
+ ["Require and reload" cider-ns-reload]
+ ["Require and reload all" cider-ns-reload-all]
["Run project (-main function)" cider-run])
"Menu for CIDER mode eval commands.")
(defconst cider-mode-interactions-menu
- `("CIDER Interactions" :visible cider-connections
+ `("CIDER Interactions" :visible (cider-connected-p)
["Complete symbol" complete-symbol]
"--"
("REPL"
@@ -290,6 +387,10 @@ Configure `cider-cljs-repl-types' to change the ClojureScript REPL to use for yo
["Browse REPL input history" cider-repl-history]
["Browse classpath" cider-classpath]
["Browse classpath entry" cider-open-classpath-entry])
+ ("Format"
+ ["Format EDN last sexp" cider-format-edn-last-sexp]
+ ["Format EDN region" cider-format-edn-region]
+ ["Format EDN buffer" cider-format-edn-buffer])
("Macroexpand"
["Macroexpand-1" cider-macroexpand-1]
["Macroexpand-all" cider-macroexpand-all])
@@ -310,13 +411,56 @@ Configure `cider-cljs-repl-types' to change the ClojureScript REPL to use for yo
["Flush completion cache" cider-completion-flush-caches]))
"Menu for CIDER interactions.")
+
+(declare-function cider-ns-refresh "cider-ns")
+(declare-function cider-ns-reload "cider-ns")
+(declare-function cider-ns-reload-all "cider-ns")
+(declare-function cider-browse-ns "cider-browse-ns")
+(declare-function cider-eval-ns-form "cider-eval")
+(declare-function cider-repl-set-ns "cider-repl")
+(declare-function cider-find-ns "cider-find")
+
+(defvar cider-ns-map
+ (let ((map (define-prefix-command 'cider-ns-map)))
+ (define-key map (kbd "b") #'cider-browse-ns)
+ (define-key map (kbd "M-b") #'cider-browse-ns)
+ (define-key map (kbd "e") #'cider-eval-ns-form)
+ (define-key map (kbd "M-e") #'cider-eval-ns-form)
+ (define-key map (kbd "f") #'cider-find-ns)
+ (define-key map (kbd "M-f") #'cider-find-ns)
+ (define-key map (kbd "n") #'cider-repl-set-ns)
+ (define-key map (kbd "M-n") #'cider-repl-set-ns)
+ (define-key map (kbd "r") #'cider-ns-refresh)
+ (define-key map (kbd "M-r") #'cider-ns-refresh)
+ (define-key map (kbd "l") #'cider-ns-reload)
+ (define-key map (kbd "M-l") #'cider-ns-reload-all)
+ map)
+ "CIDER NS keymap.")
+
+;; Those declares are needed, because we autoload all those commands when first
+;; used. That optimizes CIDER's initial load time.
(declare-function cider-macroexpand-1 "cider-macroexpansion")
(declare-function cider-macroexpand-all "cider-macroexpansion")
(declare-function cider-selector "cider-selector")
+(declare-function cider-toggle-trace-ns "cider-tracing")
+(declare-function cider-toggle-trace-var "cider-tracing")
+(declare-function cider-find-resource "cider-find")
+(declare-function cider-find-keyword "cider-find")
+(declare-function cider-find-var "cider-find")
+(declare-function cider-find-dwim-at-mouse "cider-find")
+
+(defconst cider--has-many-mouse-buttons (not (memq window-system '(mac ns)))
+ "Non-nil if system binds forward and back buttons to <mouse-8> and <mouse-9>.
+
+As it stands Emacs fires these events on <mouse-8> and <mouse-9> on 'x' and
+'w32'systems while on macOS it presents them on <mouse-4> and <mouse-5>.")
+
(defconst cider-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c C-d") 'cider-doc-map)
(define-key map (kbd "M-.") #'cider-find-var)
+ (define-key map (kbd (if cider--has-many-mouse-buttons "<mouse-8>" "<mouse-4>")) #'xref-pop-marker-stack)
+ (define-key map (kbd (if cider--has-many-mouse-buttons "<mouse-9>" "<mouse-5>")) #'cider-find-dwim-at-mouse)
(define-key map (kbd "C-c C-.") #'cider-find-ns)
(define-key map (kbd "C-c C-:") #'cider-find-keyword)
(define-key map (kbd "M-,") #'cider-pop-back)
@@ -326,17 +470,18 @@ Configure `cider-cljs-repl-types' to change the ClojureScript REPL to use for yo
(define-key map (kbd "C-c C-c") #'cider-eval-defun-at-point)
(define-key map (kbd "C-x C-e") #'cider-eval-last-sexp)
(define-key map (kbd "C-c C-e") #'cider-eval-last-sexp)
+ (define-key map (kbd "C-c C-p") #'cider-pprint-eval-last-sexp)
+ (define-key map (kbd "C-c C-f") #'cider-pprint-eval-defun-at-point)
(define-key map (kbd "C-c C-v") 'cider-eval-commands-map)
+ (define-key map (kbd "C-c C-j") 'cider-insert-commands-map)
(define-key map (kbd "C-c M-;") #'cider-eval-defun-to-comment)
(define-key map (kbd "C-c M-e") #'cider-eval-last-sexp-to-repl)
(define-key map (kbd "C-c M-p") #'cider-insert-last-sexp-in-repl)
- (define-key map (kbd "C-c C-p") #'cider-pprint-eval-last-sexp)
- (define-key map (kbd "C-c C-f") #'cider-pprint-eval-defun-at-point)
(define-key map (kbd "C-c M-:") #'cider-read-and-eval)
(define-key map (kbd "C-c C-u") #'cider-undef)
(define-key map (kbd "C-c C-m") #'cider-macroexpand-1)
(define-key map (kbd "C-c M-m") #'cider-macroexpand-all)
- (define-key map (kbd "C-c M-n") #'cider-repl-set-ns)
+ (define-key map (kbd "C-c M-n") 'cider-ns-map)
(define-key map (kbd "C-c M-i") #'cider-inspect)
(define-key map (kbd "C-c M-t v") #'cider-toggle-trace-var)
(define-key map (kbd "C-c M-t n") #'cider-toggle-trace-ns)
@@ -350,11 +495,10 @@ Configure `cider-cljs-repl-types' to change the ClojureScript REPL to use for yo
(define-key map (kbd "C-c ,") 'cider-test-commands-map)
(define-key map (kbd "C-c C-t") 'cider-test-commands-map)
(define-key map (kbd "C-c M-s") #'cider-selector)
- (define-key map (kbd "C-c M-r") #'cider-rotate-default-connection)
- (define-key map (kbd "C-c M-d") #'cider-display-connection-info)
- (define-key map (kbd "C-c C-=") #'cider-profile-map)
- (define-key map (kbd "C-c C-x") #'cider-refresh)
+ (define-key map (kbd "C-c M-d") #'cider-describe-connection)
+ (define-key map (kbd "C-c C-=") 'cider-profile-map)
(define-key map (kbd "C-c C-q") #'cider-quit)
+ (define-key map (kbd "C-c M-r") #'cider-restart)
(dolist (variable '(cider-mode-interactions-menu
cider-mode-eval-menu
cider-mode-menu))
@@ -379,7 +523,7 @@ Configure `cider-cljs-repl-types' to change the ClojureScript REPL to use for yo
:help "Connects to a REPL that's already running."]
["Connect to a ClojureScript REPL" cider-connect-clojurescript
:help "Connects to a ClojureScript REPL that's already running."]
- ["Start a Clojure REPL, and a ClojureScript REPL" cider-jack-in-clojurescript
+ ["Start a Clojure REPL, and a ClojureScript REPL" cider-jack-in-cljs
:help "Starts an nREPL server, connects a Clojure REPL to it, and then a ClojureScript REPL."]
"--"
["View manual online" cider-view-manual])))
@@ -399,7 +543,8 @@ re-visited."
(defun cider--get-symbol-indent (symbol-name)
"Return the indent metadata for SYMBOL-NAME in the current namespace."
- (let* ((ns (cider-current-ns)))
+ (let* ((ns (let ((clojure-cache-ns t)) ; we force ns caching here for performance reasons
+ (cider-current-ns))))
(if-let* ((meta (cider-resolve-var ns symbol-name))
(indent (or (nrepl-dict-get meta "style/indent")
(nrepl-dict-get meta "indent"))))
@@ -502,35 +647,38 @@ Search is done with the given LIMIT."
(set-match-data md)
t))))))))
-(defun cider--anchored-search-suppressed-forms-internal (limit)
+(defun cider--anchored-search-suppressed-forms-internal (repl-types limit)
"Helper function for `cider--anchored-search-suppressed-forms`.
-LIMIT is the same as the LIMIT in `cider--anchored-search-suppressed-forms`"
- (let ((types (cider-project-connections-types)))
- (when (= (length types) 1)
- (let ((type (car types))
- (expr (read (current-buffer)))
- (start (save-excursion (backward-sexp) (point))))
- (when (<= (point) limit)
- (forward-sexp)
- (if (not (string-equal (symbol-name expr) (concat ":" type)))
- (ignore-errors
- (cl-assert (<= (point) limit))
- (let ((md (match-data nil cider--reader-conditionals-match-data)))
- (setf (nth 0 md) start)
- (setf (nth 1 md) (point))
- (set-match-data md)
- t))
- (cider--anchored-search-suppressed-forms-internal limit)))))))
+REPL-TYPES is a list of strings repl-type strings. LIMIT is the same as
+the LIMIT in `cider--anchored-search-suppressed-forms`"
+ (when (= (length repl-types) 1)
+ (let ((type (car repl-types))
+ (expr (read (current-buffer)))
+ (start (save-excursion (backward-sexp) (point))))
+ (when (<= (point) limit)
+ (forward-sexp)
+ (if (not (string-equal (symbol-name expr) (concat ":" type)))
+ (ignore-errors
+ (cl-assert (<= (point) limit))
+ (let ((md (match-data nil cider--reader-conditionals-match-data)))
+ (setf (nth 0 md) start)
+ (setf (nth 1 md) (point))
+ (set-match-data md)
+ t))
+ (cider--anchored-search-suppressed-forms-internal repl-types limit))))))
(defun cider--anchored-search-suppressed-forms (limit)
"Matcher for finding unused reader conditional expressions.
An unused reader conditional expression is an expression for a platform
that does not match the CIDER connection for the buffer. Search is done
with the given LIMIT."
- (let ((result 'retry))
+ (let ((repl-types (seq-uniq (seq-map #'cider-repl-type (cider-repls))))
+ (result 'retry))
(while (and (eq result 'retry) (<= (point) limit))
(condition-case condition
- (setq result (cider--anchored-search-suppressed-forms-internal limit))
+ (setq result
+ (cider--anchored-search-suppressed-forms-internal
+ repl-types limit))
(invalid-read-syntax
(setq result 'retry))
(wrong-type-argument
@@ -541,12 +689,9 @@ with the given LIMIT."
(setq result nil))
(error
(setq result nil)
- (display-warning
- '(cider warning)
- (format
- (concat "Caught error during fontification while searching for forms\n"
- "that are suppressed by reader-conditionals. The error was: %S.")
- condition)))))
+ (message
+ "Error during fontification while searching for forms: %S"
+ condition))))
(if (eq result 'retry) (setq result nil))
result))
@@ -807,7 +952,8 @@ SYM and INFO is passed to `cider-docview-render'"
"Return the help-echo string for OBJ at POS.
See \(info \"(elisp) Special Properties\")"
(while-no-input
- (when (and (bufferp obj) (cider-connected-p)
+ (when (and (bufferp obj)
+ (cider-connected-p)
cider-use-tooltips (not help-at-pt-display-when-idle))
(with-current-buffer obj
(ignore-errors
@@ -855,6 +1001,7 @@ property."
cider-mode-map
(if cider-mode
(progn
+ (setq-local sesman-system 'CIDER)
(cider-eldoc-setup)
(make-local-variable 'completion-at-point-functions)
(add-to-list 'completion-at-point-functions
diff --git a/cider-ns.el b/cider-ns.el
new file mode 100644
index 00000000..bcb843ee
--- /dev/null
+++ b/cider-ns.el
@@ -0,0 +1,265 @@
+;;; cider-ns.el --- Namespace manipulation functionality -*- lexical-binding: t -*-
+
+;; Copyright © 2013-2018 Bozhidar Batsov, Artur Malabarba and CIDER contributors
+;;
+;; Author: Bozhidar Batsov <bozhidar@batsov.com>
+;; Artur Malabarba <bruce.connor.am@gmail.com>
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;; This file is not part of GNU Emacs.
+
+;;; Commentary:
+
+;; Smart code refresh functionality based on ideas from:
+;; http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded
+;;
+;; Note that refresh with clojure.tools.namespace.repl is a smarter way to
+;; reload code: the traditional way to reload Clojure code without restarting
+;; the JVM is (require ... :reload) or an editor/IDE feature that does the same
+;; thing.
+;;
+;; This has several problems:
+;;
+;; If you modify two namespaces which depend on each other, you must remember to
+;; reload them in the correct order to avoid compilation errors.
+;;
+;; If you remove definitions from a source file and then reload it, those
+;; definitions are still available in memory. If other code depends on those
+;; definitions, it will continue to work but will break the next time you
+;; restart the JVM.
+;;
+;; If the reloaded namespace contains defmulti, you must also reload all of the
+;; associated defmethod expressions.
+;;
+;; If the reloaded namespace contains defprotocol, you must also reload any
+;; records or types implementing that protocol and replace any existing
+;; instances of those records/types with new instances.
+;;
+;; If the reloaded namespace contains macros, you must also reload any
+;; namespaces which use those macros.
+;;
+;; If the running program contains functions which close over values in the
+;; reloaded namespace, those closed-over values are not updated (This is common
+;; in web applications which construct the "handler stack" as a composition of
+;; functions.)
+
+;;; Code:
+
+(require 'subr-x)
+
+(require 'cider-client)
+(require 'cider-popup)
+(require 'cider-stacktrace)
+
+(define-obsolete-variable-alias 'cider-save-files-on-cider-ns-refresh 'cider-ns-save-files-on-refresh "0.18")
+(defcustom cider-ns-save-files-on-refresh 'prompt
+ "Controls whether to prompt to save Clojure files on `cider-ns-refresh'.
+If nil, files are not saved.
+If 'prompt, the user is prompted to save files if they have been modified.
+If t, save the files without confirmation."
+ :type '(choice (const prompt :tag "Prompt to save files if they have been modified")
+ (const nil :tag "Don't save the files")
+ (const t :tag "Save the files without confirmation"))
+ :group 'cider
+ :package-version '(cider . "0.15.0"))
+
+(defconst cider-ns-refresh-log-buffer "*cider-ns-refresh-log*")
+
+(define-obsolete-variable-alias 'cider-refresh-show-log-buffer 'cider-ns-refresh-show-log-buffer "0.18")
+(defcustom cider-ns-refresh-show-log-buffer nil
+ "Controls when to display the refresh log buffer.
+If non-nil, the log buffer will be displayed every time `cider-ns-refresh' is
+called. If nil, the log buffer will still be written to, but will never be
+displayed automatically. Instead, the most relevant information will be
+displayed in the echo area."
+ :type '(choice (const :tag "always" t)
+ (const :tag "never" nil))
+ :group 'cider
+ :package-version '(cider . "0.10.0"))
+
+(define-obsolete-variable-alias 'cider-refresh-before-fn 'cider-ns-refresh-before-fn "0.18")
+(defcustom cider-ns-refresh-before-fn nil
+ "Clojure function for `cider-ns-refresh' to call before reloading.
+If nil, nothing will be invoked before reloading. Must be a
+namespace-qualified function of zero arity. Any thrown exception will
+prevent reloading from occurring."
+ :type 'string
+ :group 'cider
+ :package-version '(cider . "0.10.0"))
+
+(define-obsolete-variable-alias 'cider-refresh-after-fn 'cider-ns-refresh-after-fn "0.18")
+(defcustom cider-ns-refresh-after-fn nil
+ "Clojure function for `cider-ns-refresh' to call after reloading.
+If nil, nothing will be invoked after reloading. Must be a
+namespace-qualified function of zero arity."
+ :type 'string
+ :group 'cider
+ :package-version '(cider . "0.10.0"))
+
+(defun cider-ns-refresh--handle-response (response log-buffer)
+ "Refresh LOG-BUFFER with RESPONSE."
+ (nrepl-dbind-response response (out err reloading status error error-ns after before)
+ (cl-flet* ((log (message &optional face)
+ (cider-emit-into-popup-buffer log-buffer message face t))
+
+ (log-echo (message &optional face)
+ (log message face)
+ (unless cider-ns-refresh-show-log-buffer
+ (let ((message-truncate-lines t))
+ (message "cider-ns-refresh: %s" message)))))
+ (cond
+ (out
+ (log out))
+
+ (err
+ (log err 'font-lock-warning-face))
+
+ ((member "invoking-before" status)
+ (log-echo (format "Calling %s\n" before) 'font-lock-string-face))
+
+ ((member "invoked-before" status)
+ (log-echo (format "Successfully called %s\n" before) 'font-lock-string-face))
+
+ ((member "invoked-not-resolved" status)
+ (log-echo "Could not resolve refresh function\n" 'font-lock-string-face))
+
+ (reloading
+ (log-echo (format "Reloading %s\n" reloading) 'font-lock-string-face))
+
+ ((member "reloading" (nrepl-dict-keys response))
+ (log-echo "Nothing to reload\n" 'font-lock-string-face))
+
+ ((member "ok" status)
+ (log-echo "Reloading successful\n" 'font-lock-string-face))
+
+ (error-ns
+ (log-echo (format "Error reloading %s\n" error-ns) 'font-lock-warning-face))
+
+ ((member "invoking-after" status)
+ (log-echo (format "Calling %s\n" after) 'font-lock-string-face))
+
+ ((member "invoked-after" status)
+ (log-echo (format "Successfully called %s\n" after) 'font-lock-string-face))))
+
+ (with-selected-window (or (get-buffer-window cider-ns-refresh-log-buffer)
+ (selected-window))
+ (with-current-buffer cider-ns-refresh-log-buffer
+ (goto-char (point-max))))
+
+ (when (member "error" status)
+ (cider--render-stacktrace-causes error))))
+
+(defun cider-ns-refresh--save-project-buffers ()
+ "Ensure modified project buffers are saved before certain operations.
+Its behavior is controlled by `cider-save-files-on-cider-ns-refresh'."
+ (when-let* ((project-root (clojure-project-dir)))
+ (when cider-save-files-on-cider-ns-refresh
+ (save-some-buffers
+ (eq cider-save-files-on-cider-ns-refresh t)
+ (lambda ()
+ (and
+ (derived-mode-p 'clojure-mode)
+ (string-prefix-p project-root
+ (file-truename default-directory)
+ (eq system-type 'windows-nt))))))))
+
+;;;###autoload
+(defun cider-ns-reload (&optional prompt)
+ "Send a (require 'ns :reload) to the REPL.
+
+With an argument PROMPT, it prompts for a namespace name. This is the
+Clojure out of the box reloading experience and does not rely on
+org.clojure/tools.namespace. See Commentary of this file for a longer list
+of differences. From the Clojure doc: \":reload forces loading of all the
+identified libs even if they are already loaded\"."
+ (interactive "P")
+ (let ((ns (if prompt
+ (string-remove-prefix "'" (read-from-minibuffer "Namespace: " (clojure-find-ns)))
+ (clojure-find-ns))))
+ (cider-interactive-eval (format "(require '%s :reload)" ns))))
+
+;;;###autoload
+(defun cider-ns-reload-all (&optional prompt)
+ "Send a (require 'ns :reload-all) to the REPL.
+
+With an argument PROMPT, it prompts for a namespace name. This is the
+Clojure out of the box reloading experience and does not rely on
+org.clojure/tools.namespace. See Commentary of this file for a longer list
+of differences. From the Clojure doc: \":reload-all implies :reload and
+also forces loading of all libs that the identified libs directly or
+indirectly load via require\"."
+ (interactive "P")
+ (let ((ns (if prompt
+ (string-remove-prefix "'" (read-from-minibuffer "Namespace: " (clojure-find-ns)))
+ (clojure-find-ns))))
+ (cider-interactive-eval (format "(require '%s :reload-all)" ns))))
+
+;;;###autoload
+(defun cider-ns-refresh (&optional mode)
+ "Reload modified and unloaded namespaces on the classpath.
+
+With a single prefix argument, or if MODE is `refresh-all', reload all
+namespaces on the classpath unconditionally.
+
+With a double prefix argument, or if MODE is `clear', clear the state of
+the namespace tracker before reloading. This is useful for recovering from
+some classes of error (for example, those caused by circular dependencies)
+that a normal reload would not otherwise recover from. The trade-off of
+clearing is that stale code from any deleted files may not be completely
+unloaded.
+
+With a negative prefix argument, or if MODE is `inhibit-fns', prevent any
+refresh functions (defined in `cider-ns-refresh-before-fn' and
+`cider-ns-refresh-after-fn') from being invoked."
+ (interactive "p")
+ (cider-ensure-connected)
+ (cider-ensure-op-supported "refresh")
+ (cider-ns-refresh--save-project-buffers)
+ (let ((clear? (member mode '(clear 16)))
+ (refresh-all? (member mode '(refresh-all 4)))
+ (inhibit-refresh-fns (member mode '(inhibit-fns -1))))
+ (cider-map-repls :clj
+ (lambda (conn)
+ ;; Inside the lambda, so the buffer is not created if we error out.
+ (let ((log-buffer (or (get-buffer cider-ns-refresh-log-buffer)
+ (cider-make-popup-buffer cider-ns-refresh-log-buffer))))
+ (when cider-ns-refresh-show-log-buffer
+ (cider-popup-buffer-display log-buffer))
+ (when inhibit-refresh-fns
+ (cider-emit-into-popup-buffer log-buffer
+ "inhibiting refresh functions\n"
+ nil
+ t))
+ (when clear?
+ (cider-nrepl-send-sync-request '("op" "refresh-clear") conn))
+ (cider-nrepl-send-request
+ (nconc `("op" ,(if refresh-all? "refresh-all" "refresh")
+ "print-length" ,cider-stacktrace-print-length
+ "print-level" ,cider-stacktrace-print-level)
+ (when (cider--pprint-fn)
+ `("pprint-fn" ,(cider--pprint-fn)))
+ (when (and (not inhibit-refresh-fns) cider-ns-refresh-before-fn)
+ `("before" ,cider-ns-refresh-before-fn))
+ (when (and (not inhibit-refresh-fns) cider-ns-refresh-after-fn)
+ `("after" ,cider-ns-refresh-after-fn)))
+ (lambda (response)
+ (cider-ns-refresh--handle-response response log-buffer))
+ conn))))))
+
+;;;###autoload
+(define-obsolete-function-alias 'cider-refresh 'cider-ns-refresh "0.18")
+
+(provide 'cider-ns)
+;;; cider-ns.el ends here
diff --git a/cider-popup.el b/cider-popup.el
index 411acebe..274a0666 100644
--- a/cider-popup.el
+++ b/cider-popup.el
@@ -81,7 +81,9 @@ If prefix argument KILL is non-nil, kill the buffer instead of burying it."
(defvar-local cider-popup-output-marker nil)
-(defvar cider-ancillary-buffers nil)
+(defvar cider-ancillary-buffers nil
+ "A list ancillary buffers created by the various CIDER commands.
+We track them mostly to be able to clean them up on quit.")
(defun cider-make-popup-buffer (name &optional mode ancillary)
"Create a temporary buffer called NAME using major MODE (if specified).
diff --git a/cider-profile.el b/cider-profile.el
index 0ccd1a63..79577910 100644
--- a/cider-profile.el
+++ b/cider-profile.el
@@ -26,6 +26,8 @@
;;; Code:
(require 'cider-client)
+(require 'cider-popup)
+(require 'cider-eval)
(defconst cider-profile-buffer "*cider-profile*")
@@ -175,7 +177,6 @@ With prefix arg or no symbol at point, prompts for a var."
;;;###autoload
(defun cider-profile-var-summary (query)
"Display profile data for var under point QUERY.
-
Defaults to the symbol at point. With prefix arg or no symbol at point,
prompts for a var."
(interactive "P")
diff --git a/cider-repl-history.el b/cider-repl-history.el
index c4d66dbc..f6cd4c86 100644
--- a/cider-repl-history.el
+++ b/cider-repl-history.el
@@ -37,7 +37,6 @@
(require 'pulse)
(defconst cider-repl-history-buffer "*cider-repl-history*")
-(add-to-list 'cider-ancillary-buffers cider-repl-history-buffer)
(defgroup cider-repl-history nil
"A package for browsing and inserting the items in the CIDER command history."
@@ -258,18 +257,14 @@ call `cider-repl-history' again.")
(defun cider-repl-history-read-regexp (msg use-default-p)
"Get a regular expression from the user, prompting with MSG; previous entry is default if USE-DEFAULT-P."
(let* ((default (car regexp-history))
+ (prompt (if (and default use-default-p)
+ (format "%s for regexp (default `%s'): "
+ msg
+ default)
+ (format "%s (regexp): " msg)))
(input
- (read-from-minibuffer
- (if (and default use-default-p)
- (format "%s for regexp (default `%s'): "
- msg
- default)
- (format "%s (regexp): " msg))
- nil
- nil
- nil
- 'regexp-history
- (if use-default-p nil default))))
+ (read-from-minibuffer prompt nil nil nil 'regexp-history
+ (if use-default-p nil default))))
(if (equal input "")
(if use-default-p default nil)
input)))
@@ -697,6 +692,7 @@ text from the *cider-repl-history* buffer."
"Major mode for browsing the entries in the command input history.
\\{cider-repl-history-mode-map}"
+ (setq-local sesman-system 'CIDER)
(define-key cider-repl-history-mode-map (kbd "n") 'cider-repl-history-forward)
(define-key cider-repl-history-mode-map (kbd "p") 'cider-repl-history-previous)
(define-key cider-repl-history-mode-map (kbd "SPC") 'cider-repl-history-insert-and-quit)
diff --git a/cider-repl.el b/cider-repl.el
index bb659215..14eaeb84 100644
--- a/cider-repl.el
+++ b/cider-repl.el
@@ -1,4 +1,4 @@
-;;; cider-repl.el --- REPL interactions -*- lexical-binding: t -*-
+;;; cider-repl.el --- CIDER REPL mode interactions -*- lexical-binding: t -*-
;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov
;; Copyright © 2013-2018 Bozhidar Batsov, Artur Malabarba and CIDER contributors
@@ -28,7 +28,8 @@
;;; Commentary:
-;; REPL interactions.
+;; This functionality concerns `cider-repl-mode' and REPL interaction. For
+;; REPL/connection life-cycle management see cider-connection.el.
;;; Code:
@@ -45,6 +46,7 @@
(require 'clojure-mode)
(require 'easymenu)
(require 'cl-lib)
+(require 'sesman)
(eval-when-compile
(defvar paredit-version)
@@ -136,6 +138,16 @@ change the setting's value."
:group 'cider-repl
:package-version '(cider . "0.17.0"))
+(defcustom cider-repl-auto-detect-type t
+ "Control whether to auto-detect the REPL type using track-state information.
+If you disable this you'll have to manually change the REPL type between
+Clojure and ClojureScript when invoking REPL type changing forms.
+Use `cider-set-repl-type' to manually change the REPL type."
+ :type 'boolean
+ :group 'cider-repl
+ :safe #'booleanp
+ :package-version '(cider . "0.18.0"))
+
(defcustom cider-repl-use-clojure-font-lock t
"Non-nil means to use Clojure mode font-locking for input and result.
Nil means that `cider-repl-input-face' and `cider-repl-result-face'
@@ -222,8 +234,7 @@ Currently its only purpose is to facilitate `cider-repl-clear-buffer'.")
(defvar-local cider-repl-ns-cache nil
"A dict holding information about all currently loaded namespaces.
-This cache is stored in the connection buffer. Other buffer's access it
-via `cider-current-connection'.")
+This cache is stored in the connection buffer.")
(defvar cider-mode)
(declare-function cider-refresh-dynamic-font-lock "cider-mode")
@@ -233,8 +244,8 @@ via `cider-current-connection'.")
(with-demoted-errors "Error in `cider-repl--state-handler': %s"
(when (member "state" (nrepl-dict-get response "status"))
(nrepl-dbind-response response (repl-type changed-namespaces)
- (when repl-type
- (cider-repl-set-type repl-type))
+ (when (and repl-type cider-repl-auto-detect-type)
+ (cider-set-repl-type repl-type))
(unless (nrepl-dict-empty-p changed-namespaces)
(setq cider-repl-ns-cache (nrepl-dict-merge cider-repl-ns-cache changed-namespaces))
(dolist (b (buffer-list))
@@ -249,35 +260,6 @@ via `cider-current-connection'.")
ns-dict)))))
(cider-refresh-dynamic-font-lock ns-dict))))))))))
-(declare-function cider-default-err-handler "cider-interaction")
-
-(defun cider-repl-create (endpoint)
- "Create a REPL buffer and install `cider-repl-mode'.
-ENDPOINT is a plist as returned by `nrepl-connect'."
- ;; Connection might not have been set as yet. Please don't send requests here.
- (let* ((reuse-buff (not (eq 'new nrepl-use-this-as-repl-buffer)))
- (buff-name (nrepl-make-buffer-name nrepl-repl-buffer-name-template nil
- (plist-get endpoint :host)
- (plist-get endpoint :port)
- reuse-buff)))
- ;; when reusing, rename the buffer accordingly
- (when (and reuse-buff
- (not (equal buff-name nrepl-use-this-as-repl-buffer)))
- ;; uniquify as it might be Nth connection to the same endpoint
- (setq buff-name (generate-new-buffer-name buff-name))
- (with-current-buffer nrepl-use-this-as-repl-buffer
- (rename-buffer buff-name)))
- (with-current-buffer (get-buffer-create buff-name)
- (unless (derived-mode-p 'cider-repl-mode)
- (cider-repl-mode)
- (cider-repl-set-type "clj"))
- (setq nrepl-err-handler #'cider-default-err-handler)
- (cider-repl-reset-markers)
- (add-hook 'nrepl-response-handler-functions #'cider-repl--state-handler nil 'local)
- (add-hook 'nrepl-connected-hook 'cider--connected-handler nil 'local)
- (add-hook 'nrepl-disconnected-hook 'cider--disconnected-handler nil 'local)
- (current-buffer))))
-
(declare-function cider-set-buffer-ns "cider-mode")
(defun cider-repl-set-initial-ns (buffer)
"Require standard REPL util functions and set the ns of the REPL's BUFFER.
@@ -291,7 +273,7 @@ efficiency."
(let* ((response (nrepl-send-sync-request
(lax-plist-put (nrepl--eval-request "(str *ns*)")
"inhibit-cider-middleware" "true")
- (cider-current-connection)))
+ (cider-current-repl)))
(initial-ns (or (read (nrepl-dict-get response "value"))
"user")))
(cider-set-buffer-ns initial-ns)))))
@@ -305,7 +287,7 @@ efficiency."
"(when (clojure.core/resolve 'clojure.main/repl-requires)
(clojure.core/map clojure.core/require clojure.main/repl-requires))")
"inhibit-cider-middleware" "true")
- (cider-current-connection)))
+ (cider-current-repl)))
(defun cider-repl--build-config-expression ()
"Build the initial config expression."
@@ -324,7 +306,7 @@ efficiency."
(lax-plist-put
(nrepl--eval-request config-expression)
"inhibit-cider-middleware" "true")
- (cider-current-connection))))
+ (cider-current-repl))))
(defun cider-repl-init (buffer &optional no-banner)
"Initialize the REPL in BUFFER.
@@ -356,9 +338,7 @@ client process connection. Unless NO-BANNER is non-nil, insert a banner."
(defun cider-repl--banner ()
"Generate the welcome REPL buffer banner."
- (let ((host (cider--connection-host (current-buffer)))
- (port (cider--connection-port (current-buffer))))
- (format ";; Connected to nREPL server - nrepl://%s:%s
+ (format ";; Connected to nREPL server - nrepl://%s:%s
;; CIDER %s, nREPL %s
;; Clojure %s, Java %s
;; Docs: (doc function-name)
@@ -367,12 +347,12 @@ client process connection. Unless NO-BANNER is non-nil, insert a banner."
;; Javadoc: (javadoc java-object-or-class)
;; Exit: <C-c C-q>
;; Results: Stored in vars *1, *2, *3, an exception in *e;"
- host
- port
- (cider--version)
- (cider--nrepl-version)
- (cider--clojure-version)
- (cider--java-version))))
+ (plist-get nrepl-endpoint :host)
+ (plist-get nrepl-endpoint :port)
+ (cider--version)
+ (cider--nrepl-version)
+ (cider--clojure-version)
+ (cider--java-version)))
(defun cider-repl--help-banner ()
"Generate the help banner."
@@ -697,7 +677,7 @@ If BOL is non-nil insert at the beginning of line. Run
(defun cider-repl--emit-interactive-output (string face)
"Emit STRING as interactive output using FACE."
- (with-current-buffer (cider-current-repl-buffer)
+ (with-current-buffer (cider-current-repl)
(let ((pos (cider-repl--end-of-line-before-input-start))
(string (replace-regexp-in-string "\n\\'" "" string)))
(cider-repl--emit-output-at-pos (current-buffer) string face pos t))))
@@ -710,16 +690,6 @@ If BOL is non-nil insert at the beginning of line. Run
"Emit STRING as interactive err output."
(cider-repl--emit-interactive-output string 'cider-repl-stderr-face))
-(defun cider-repl-manual-warning (section-id format &rest args)
- "Emit a warning to the REPL and link to the online manual.
-SECTION-ID is the section to link to. The link is added on the last line.
-FORMAT is a format string to compile with ARGS and display on the REPL."
- (let ((message (apply #'format format args)))
- (cider-repl-emit-interactive-stderr
- (concat "WARNING: " message "\n "
- (cider--manual-button "More information" section-id)
- "."))))
-
(defun cider-repl--emit-output (buffer string face &optional bol)
"Using BUFFER, emit STRING font-locked with FACE.
If BOL is non-nil, emit at the beginning of the line."
@@ -833,7 +803,6 @@ SHOW-PREFIX and BOL."
(defcustom cider-repl-image-margin 10
"Specifies the margin to be applied to images displayed in the REPL.
-
Either a single number of pixels - interpreted as a symmetric margin, or
pair of numbers `(x . y)' encoding an arbitrary margin."
:type '(choice integer (vector integer integer))
@@ -842,12 +811,10 @@ pair of numbers `(x . y)' encoding an arbitrary margin."
(defun cider-repl--image (data type datap)
"A helper for creating images with CIDER's image options.
-
-DATA is either the path to an image or its base64 coded data. TYPE
-is a symbol indicating the image type. DATAP indicates whether the image is
-the raw image data or a filename.
-
-Returns an image instance with a margin per `cider-repl-image-margin'."
+DATA is either the path to an image or its base64 coded data. TYPE is a
+symbol indicating the image type. DATAP indicates whether the image is the
+raw image data or a filename. Returns an image instance with a margin per
+`cider-repl-image-margin'."
(create-image data type datap
:margin cider-repl-image-margin))
@@ -856,14 +823,14 @@ Returns an image instance with a margin per `cider-repl-image-margin'."
Part of the default `cider-repl-content-type-handler-alist'."
(cider-repl--display-image buffer
(cider-repl--image image 'jpeg t)
- show-prefix bol image))
+ show-prefix bol " "))
(defun cider-repl-handle-png (_type buffer image &optional show-prefix bol)
"A handler for inserting a png IMAGE into a repl BUFFER.
Part of the default `cider-repl-content-type-handler-alist'."
(cider-repl--display-image buffer
(cider-repl--image image 'png t)
- show-prefix bol image))
+ show-prefix bol " "))
(defun cider-repl-handle-external-body (type buffer _ &optional _show-prefix _bol)
"Handler for slurping external content into BUFFER.
@@ -871,9 +838,9 @@ Handles an external-body TYPE by issuing a slurp request to fetch the content."
(if-let* ((args (cadr type))
(access-type (nrepl-dict-get args "access-type")))
(nrepl-send-request
- (list "op" "slurp" "url" (nrepl-dict-get args "URL"))
+ (list "op" "slurp" "url" (nrepl-dict-get args access-type))
(cider-repl-handler buffer)
- (cider-current-connection)))
+ (cider-current-repl)))
nil)
(defvar cider-repl-content-type-handler-alist
@@ -881,7 +848,6 @@ Handles an external-body TYPE by issuing a slurp request to fetch the content."
("image/jpeg" . ,#'cider-repl-handle-jpeg)
("image/png" . ,#'cider-repl-handle-png))
"Association list from content-types to handlers.
-
Handlers must be functions of two required and two optional arguments - the
REPL buffer to insert into, the value of the given content type as a raw
string, the REPL's show prefix as any and an `end-of-line' flag.
@@ -1062,9 +1028,13 @@ text property `cider-old-input'."
(defun cider-repl-switch-to-other ()
"Switch between the Clojure and ClojureScript REPLs for the current project."
(interactive)
- (if-let* ((other-connection (cider-other-connection)))
- (switch-to-buffer other-connection)
- (message "There's no other REPL for the current project")))
+ ;; FIXME: implement cycling as session can hold more than two REPLs
+ (let* ((this-repl (cider-current-repl nil 'ensure))
+ (other-repl (car (seq-remove (lambda (r) (eq r this-repl)) (cider-repls nil t)))))
+ (if other-repl
+ (switch-to-buffer other-repl)
+ (user-error "No other REPL in current session (%s)"
+ (car (sesman-current-session 'CIDER))))))
(defvar cider-repl-clear-buffer-hook)
@@ -1075,7 +1045,6 @@ text property `cider-old-input'."
(defun cider-repl-clear-buffer ()
"Clear the currently visited REPL buffer completely.
-
See also the related commands `cider-repl-clear-output' and
`cider-find-and-clear-repl-output'."
(interactive)
@@ -1162,12 +1131,9 @@ With a prefix argument CLEAR-REPL it will clear the entire REPL buffer instead."
(defun cider-repl-set-ns (ns)
"Switch the namespace of the REPL buffer to NS.
-
-If called from a cljc buffer act on both the Clojure and
-ClojureScript REPL if there are more than one REPL present.
-
-If invoked in a REPL buffer the command will prompt for the name of the
-namespace to switch to."
+If called from a cljc buffer act on both the Clojure and ClojureScript REPL
+if there are more than one REPL present. If invoked in a REPL buffer the
+command will prompt for the name of the namespace to switch to."
(interactive (list (if (or (derived-mode-p 'cider-repl-mode)
(null (cider-ns-form)))
(completing-read "Switch to namespace: "
@@ -1175,23 +1141,10 @@ namespace to switch to."
(cider-current-ns))))
(when (or (not ns) (equal ns ""))
(user-error "No namespace selected"))
- (cider-map-connections
- (lambda (connection)
- (cider-nrepl-request:eval (format "(in-ns '%s)" ns)
- (cider-repl-switch-ns-handler connection)))
- :both))
-
-(defun cider-repl-set-type (&optional type)
- "Set REPL TYPE to \"clj\" or \"cljs\"."
- (interactive)
- (let ((type (or type (completing-read
- (format "Set REPL type (currently `%s') to: "
- cider-repl-type)
- '("clj" "cljs")))))
- (setq cider-repl-type type)
- (if (equal type "cljs")
- (setq mode-name "REPL[cljs]")
- (setq mode-name "REPL[clj]"))))
+ (cider-map-repls :auto
+ (lambda (connection)
+ (cider-nrepl-request:eval (format "(in-ns '%s)" ns)
+ (cider-repl-switch-ns-handler connection)))))
;;; Location References
@@ -1200,7 +1153,9 @@ namespace to switch to."
'((stdout-stacktrace "[ \t]\\(at \\([^$(]+\\).*(\\([^:()]+\\):\\([0-9]+\\))\\)" 1 2 3 4)
(aviso-stacktrace "^[ \t]*\\(\\([^$/ \t]+\\).*? +\\([^:]+\\): +\\([0-9]+\\)\\)" 1 2 3 4)
(print-stacktrace "\\[\\([^][$ \t]+\\).* +\\([^ \t]+\\) +\\([0-9]+\\)\\]" 0 1 2 3)
- (timbre-log "\\(TRACE\\|INFO\\|DEBUG\\|WARN\\|ERROR\\) +\\(\\[\\([^:]+\\):\\([0-9]+\\)\\]\\)" 2 3 nil 4))
+ (timbre-log "\\(TRACE\\|INFO\\|DEBUG\\|WARN\\|ERROR\\) +\\(\\[\\([^:]+\\):\\([0-9]+\\)\\]\\)" 2 3 nil 4)
+ (cljs-message "at line \\([0-9]+\\) +\\(.*\\)$" 0 nil 2 1)
+ (reflection "Reflection warning, +\\(\\([^\n:]+\\):\\([0-9]+\\):[0-9]+\\)" 1 nil 2 3))
"Alist holding regular expressions for inline location references.
Each element in the alist has the form (NAME REGEXP HIGHLIGHT VAR FILE
LINE), where NAME is the identifier of the regexp, REGEXP - regexp matching
@@ -1258,7 +1213,12 @@ regexes from `cider-locref-regexp-alist' to infer locations at point."
(or (cider-sync-request:ns-path var)
(nrepl-dict-get (cider-sync-request:info var) "file")))
;; when not found, return the file detected by regexp
- (plist-get loc :file))))
+ (when-let* ((file (plist-get loc :file)))
+ (if (file-name-absolute-p file)
+ file
+ ;; when not absolute, expand within the current project
+ (when-let* ((proj (clojure-project-dir)))
+ (expand-file-name file proj)))))))
(if file
(cider--jump-to-loc-from-info (nrepl-dict "file" file "line" line) t)
(error "No source location for %s" var)))
@@ -1522,15 +1482,13 @@ constructs."
"Add a REPL shortcut command, defined by NAME and HANDLER."
(puthash name handler cider-repl-shortcuts))
-(declare-function cider-restart "cider-interaction")
-(declare-function cider-quit "cider-interaction")
-(declare-function cider-toggle-trace-ns "cider-interaction")
-(declare-function cider-undef "cider-interaction")
+(declare-function cider-toggle-trace-ns "cider-tracing")
+(declare-function cider-undef "cider-mode")
(declare-function cider-browse-ns "cider-browse-ns")
(declare-function cider-classpath "cider-classpath")
(declare-function cider-repl-history "cider-repl-history")
-(declare-function cider-run "cider-interaction")
-(declare-function cider-refresh "cider-interaction")
+(declare-function cider-run "cider-mode")
+(declare-function cider-ns-refresh "cider-ns")
(declare-function cider-version "cider")
(declare-function cider-test-run-loaded-tests "cider-test")
(declare-function cider-test-run-project-tests "cider-test")
@@ -1545,7 +1503,7 @@ constructs."
(cider-repl-add-shortcut "history" #'cider-repl-history)
(cider-repl-add-shortcut "trace-ns" #'cider-toggle-trace-ns)
(cider-repl-add-shortcut "undef" #'cider-undef)
-(cider-repl-add-shortcut "refresh" #'cider-refresh)
+(cider-repl-add-shortcut "refresh" #'cider-ns-refresh)
(cider-repl-add-shortcut "help" #'cider-repl-shortcuts-help)
(cider-repl-add-shortcut "test-ns" #'cider-test-run-ns-tests)
(cider-repl-add-shortcut "test-all" #'cider-test-run-loaded-tests)
@@ -1555,8 +1513,7 @@ constructs."
(cider-repl-add-shortcut "test-project-with-filters" (lambda () (interactive) (cider-test-run-project-tests 'prompt-for-filters)))
(cider-repl-add-shortcut "test-report" #'cider-test-show-report)
(cider-repl-add-shortcut "run" #'cider-run)
-(cider-repl-add-shortcut "conn-info" #'cider-display-connection-info)
-(cider-repl-add-shortcut "conn-rotate" #'cider-rotate-default-connection)
+(cider-repl-add-shortcut "conn-info" #'cider-describe-connection)
(cider-repl-add-shortcut "hasta la vista" #'cider-quit)
(cider-repl-add-shortcut "adios" #'cider-quit)
(cider-repl-add-shortcut "sayonara" #'cider-quit)
@@ -1608,18 +1565,21 @@ constructs."
(defvar cider-repl-mode-syntax-table
(copy-syntax-table clojure-mode-syntax-table))
-(declare-function cider-eval-last-sexp "cider-interaction")
-(declare-function cider-refresh "cider-interaction")
-(declare-function cider-toggle-trace-ns "cider-interaction")
-(declare-function cider-toggle-trace-var "cider-interaction")
-(declare-function cider-find-resource "cider-interaction")
-(declare-function cider-restart "cider-interaction")
-(declare-function cider-find-ns "cider-interaction")
-(declare-function cider-find-keyword "cider-interaction")
+(declare-function cider-eval-last-sexp "cider-eval")
+(declare-function cider-toggle-trace-ns "cider-tracing")
+(declare-function cider-toggle-trace-var "cider-tracing")
+(declare-function cider-find-resource "cider-find")
+(declare-function cider-find-ns "cider-find")
+(declare-function cider-find-keyword "cider-find")
+(declare-function cider-find-var "cider-find")
(declare-function cider-switch-to-last-clojure-buffer "cider-mode")
(declare-function cider-macroexpand-1 "cider-macroexpansion")
(declare-function cider-macroexpand-all "cider-macroexpansion")
(declare-function cider-selector "cider-selector")
+(declare-function cider-jack-in-clj "cider")
+(declare-function cider-jack-in-cljs "cider")
+(declare-function cider-connect-clj "cider")
+(declare-function cider-connect-cljs "cider")
(defvar cider-repl-mode-map
(let ((map (make-sparse-keymap)))
@@ -1652,19 +1612,26 @@ constructs."
(define-key map (kbd "C-c C-c") #'cider-interrupt)
(define-key map (kbd "C-c C-m") #'cider-macroexpand-1)
(define-key map (kbd "C-c M-m") #'cider-macroexpand-all)
+ (define-key map (kbd "C-c C-s") #'sesman-map)
(define-key map (kbd "C-c C-z") #'cider-switch-to-last-clojure-buffer)
(define-key map (kbd "C-c M-o") #'cider-repl-switch-to-other)
(define-key map (kbd "C-c M-s") #'cider-selector)
- (define-key map (kbd "C-c M-d") #'cider-display-connection-info)
+ (define-key map (kbd "C-c M-d") #'cider-describe-connection)
(define-key map (kbd "C-c C-q") #'cider-quit)
+ (define-key map (kbd "C-c M-r") #'cider-restart)
(define-key map (kbd "C-c M-i") #'cider-inspect)
(define-key map (kbd "C-c M-p") #'cider-repl-history)
(define-key map (kbd "C-c M-t v") #'cider-toggle-trace-var)
(define-key map (kbd "C-c M-t n") #'cider-toggle-trace-ns)
- (define-key map (kbd "C-c C-x") #'cider-refresh)
+ (define-key map (kbd "C-c C-x") 'cider-start-map)
(define-key map (kbd "C-x C-e") #'cider-eval-last-sexp)
(define-key map (kbd "C-c C-r") 'clojure-refactor-map)
(define-key map (kbd "C-c C-v") 'cider-eval-commands-map)
+ (define-key map (kbd "C-c M-j") #'cider-jack-in-clj)
+ (define-key map (kbd "C-c M-J") #'cider-jack-in-cljs)
+ (define-key map (kbd "C-c M-c") #'cider-connect-clj)
+ (define-key map (kbd "C-c M-C") #'cider-connect-cljs)
+
(define-key map (string cider-repl-shortcut-dispatch-char) #'cider-repl-handle-shortcut)
(easy-menu-define cider-repl-mode-menu map
"Menu for CIDER's REPL mode"
@@ -1693,10 +1660,9 @@ constructs."
["Inspect" cider-inspect]
["Toggle var tracing" cider-toggle-trace-var]
["Toggle ns tracing" cider-toggle-trace-ns]
- ["Refresh loaded code" cider-refresh]
+ ["Refresh loaded code" cider-ns-refresh]
"--"
["Set REPL ns" cider-repl-set-ns]
- ["Set REPL type" cider-repl-set-type]
["Toggle pretty printing" cider-repl-toggle-pretty-printing]
["Require REPL utils" cider-repl-require-repl-utils]
"--"
@@ -1717,7 +1683,7 @@ constructs."
"--"
["Interrupt evaluation" cider-interrupt]
"--"
- ["Connection info" cider-display-connection-info]
+ ["Connection info" cider-describe-connection]
"--"
["Close ancillary buffers" cider-close-ancillary-buffers]
["Quit" cider-quit]
@@ -1732,6 +1698,8 @@ constructs."
["Version info" cider-version]))
map))
+(sesman-install-menu cider-repl-mode-map)
+
(defun cider-repl-wrap-fontify-function (func)
"Return a function that will call FUNC narrowed to input region."
(lambda (beg end &rest rest)
@@ -1742,7 +1710,7 @@ constructs."
(let ((font-lock-dont-widen t))
(apply func (max beg cider-repl-input-start-mark) end rest))))))
-(declare-function cider-complete-at-point "cider-interaction")
+(declare-function cider-complete-at-point "cider-completion")
(defvar cider--static-font-lock-keywords)
(define-derived-mode cider-repl-mode fundamental-mode "REPL"
@@ -1752,6 +1720,7 @@ constructs."
(clojure-mode-variables)
(clojure-font-lock-setup)
(font-lock-add-keywords nil cider--static-font-lock-keywords)
+ (setq-local sesman-system 'CIDER)
(setq-local font-lock-fontify-region-function
(cider-repl-wrap-fontify-function font-lock-fontify-region-function))
(setq-local font-lock-unfontify-region-function
diff --git a/cider-resolve.el b/cider-resolve.el
index 454663b8..3c2dc6fd 100644
--- a/cider-resolve.el
+++ b/cider-resolve.el
@@ -60,7 +60,7 @@
;; (("m" "f")))
;; "wrap-tracker" (dict "arglists"
;; (("handler"))))
-;; "refers" (dict "set-descriptor!" "#'clojure.tools.nrepl.middleware/set-descriptor!"))
+;; "refers" (dict "set-descriptor!" "#'nrepl.middleware/set-descriptor!"))
;;; Code:
@@ -72,8 +72,8 @@
(defun cider-resolve--get-in (&rest keys)
"Return (nrepl-dict-get-in cider-repl-ns-cache KEYS)."
- (when cider-connections
- (with-current-buffer (cider-current-connection)
+ (when-let* ((conn (cider-current-repl)))
+ (with-current-buffer conn
(nrepl-dict-get-in cider-repl-ns-cache keys))))
(defun cider-resolve-alias (ns alias)
@@ -103,9 +103,10 @@ Return nil only if VAR cannot be resolved."
(defun cider-resolve-core-ns ()
"Return a dict of the core namespace for current connection.
-This will be clojure.core or cljs.core depending on `cider-repl-type'."
- (when (cider-connected-p)
- (with-current-buffer (cider-current-connection)
+This will be clojure.core or cljs.core depending on the return value of the
+function `cider-repl-type'."
+ (when-let* ((repl (cider-current-repl)))
+ (with-current-buffer repl
(cider-resolve--get-in (if (equal cider-repl-type "cljs")
"cljs.core"
"clojure.core")))))
diff --git a/cider-scratch.el b/cider-scratch.el
index 2dded9fb..f1c3e93d 100644
--- a/cider-scratch.el
+++ b/cider-scratch.el
@@ -30,10 +30,18 @@
;;; Code:
-(require 'cider-interaction)
+(require 'cider-eval)
(require 'clojure-mode)
(require 'easymenu)
+(defcustom cider-scratch-initial-message
+ ";; This buffer is for Clojure experiments and evaluation.\n
+;; Press C-j to evaluate the last expression.\n\n"
+ "The initial message displayed in new scratch buffers."
+ :type 'string
+ :group 'cider
+ :package-version '(cider . "0.18.0"))
+
(defvar cider-clojure-interaction-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map clojure-mode-map)
@@ -44,11 +52,7 @@
'("Clojure Interaction"
(["Eval and print last sexp" #'cider-eval-print-last-sexp]
"--"
- ["Reset" #'cider-scratch-reset]
- "--"
- ["Set buffer connection" #'cider-assoc-buffer-with-connection]
- ["Toggle buffer connection" #'cider-toggle-buffer-connection]
- ["Reset buffer connection" #'cider-clear-buffer-local-connection])))
+ ["Reset" #'cider-scratch-reset])))
map))
(defconst cider-scratch-buffer-name "*cider-scratch*")
@@ -57,37 +61,37 @@
(defun cider-scratch ()
"Go to the scratch buffer named `cider-scratch-buffer-name'."
(interactive)
- (pop-to-buffer (cider-find-or-create-scratch-buffer)))
+ (pop-to-buffer (cider-scratch-find-or-create-buffer)))
-(defun cider-find-or-create-scratch-buffer ()
+(defun cider-scratch-find-or-create-buffer ()
"Find or create the scratch buffer."
(or (get-buffer cider-scratch-buffer-name)
- (cider-create-scratch-buffer)))
+ (cider-scratch--create-buffer)))
(define-derived-mode cider-clojure-interaction-mode clojure-mode "Clojure Interaction"
"Major mode for typing and evaluating Clojure forms.
Like clojure-mode except that \\[cider-eval-print-last-sexp] evals the Lisp expression
before point, and prints its value into the buffer, advancing point.
-\\{cider-clojure-interaction-mode-map}")
+\\{cider-clojure-interaction-mode-map}"
+ (setq-local sesman-system 'CIDER))
-(defun cider--scratch-insert-welcome-message ()
+(defun cider-scratch--insert-welcome-message ()
"Insert the welcome message for the scratch buffer."
- (insert ";; This buffer is for Clojure experiments and evaluation.\n"
- ";; Press C-j to evaluate the last expression.\n\n"))
+ (insert cider-scratch-initial-message))
-(defun cider-create-scratch-buffer ()
+(defun cider-scratch--create-buffer ()
"Create a new scratch buffer."
(with-current-buffer (get-buffer-create cider-scratch-buffer-name)
(cider-clojure-interaction-mode)
- (cider--scratch-insert-welcome-message)
+ (cider-scratch--insert-welcome-message)
(current-buffer)))
(defun cider-scratch-reset ()
"Reset the current scratch buffer."
(interactive)
(erase-buffer)
- (cider--scratch-insert-welcome-message))
+ (cider-scratch--insert-welcome-message))
(provide 'cider-scratch)
diff --git a/cider-selector.el b/cider-selector.el
index dc6b4ea2..a21032db 100644
--- a/cider-selector.el
+++ b/cider-selector.el
@@ -32,8 +32,9 @@
;;; Code:
(require 'cider-client)
-(require 'cider-interaction)
+(require 'cider-eval)
(require 'cider-scratch)
+(require 'cider-profile)
(defconst cider-selector-help-buffer "*CIDER Selector Help*"
"The name of the selector's help buffer.")
@@ -44,9 +45,11 @@ Each element is a list (KEY DESCRIPTION FUNCTION).
DESCRIPTION is a one-line description of what the key selects.")
(defvar cider-selector-other-window nil
- "If non-nil use `switch-to-buffer-other-window'.")
+ "If non-nil use `switch-to-buffer-other-window'.
+Not meant to be set by users. It's used internally
+by `cider-selector'.")
-(defun cider--recently-visited-buffer (mode)
+(defun cider-selector--recently-visited-buffer (mode)
"Return the most recently visited buffer, deriving its `major-mode' from MODE.
Only considers buffers that are not already visible."
(cl-loop for buffer in (buffer-list)
@@ -64,7 +67,6 @@ Only considers buffers that are not already visible."
The user is prompted for a single character indicating the method by
which to choose a new buffer. The `?' character describes then
available methods. OTHER-WINDOW provides an optional target.
-
See `def-cider-selector-method' for defining new methods."
(interactive)
(message "Select [%s]: "
@@ -85,7 +87,6 @@ See `def-cider-selector-method' for defining new methods."
(defmacro def-cider-selector-method (key description &rest body)
"Define a new `cider-select' buffer selection method.
-
KEY is the key the user will enter to choose this method.
DESCRIPTION is a one-line sentence describing how the method
@@ -127,40 +128,38 @@ is chosen. The returned buffer is selected with
(def-cider-selector-method ?c
"Most recently visited clojure-mode buffer."
- (cider--recently-visited-buffer 'clojure-mode))
+ (cider-selector--recently-visited-buffer 'clojure-mode))
(def-cider-selector-method ?e
"Most recently visited emacs-lisp-mode buffer."
- (cider--recently-visited-buffer 'emacs-lisp-mode))
+ (cider-selector--recently-visited-buffer 'emacs-lisp-mode))
(def-cider-selector-method ?q "Abort."
(top-level))
(def-cider-selector-method ?r
"Current REPL buffer."
- (cider-current-repl-buffer))
-
-(def-cider-selector-method ?n
- "Connections browser buffer."
- (cider-connection-browser)
- cider--connection-browser-buffer-name)
+ (cider-current-repl))
(def-cider-selector-method ?m
"Current connection's *nrepl-messages* buffer."
- (cider-current-messages-buffer))
+ (nrepl-messages-buffer (cider-current-repl)))
(def-cider-selector-method ?x
"*cider-error* buffer."
cider-error-buffer)
+(def-cider-selector-method ?p
+ "CIDER profiler buffer."
+ cider-profile-buffer)
+
(def-cider-selector-method ?d
"*cider-doc* buffer."
cider-doc-buffer)
-(declare-function cider-find-or-create-scratch-buffer "cider-scratch")
(def-cider-selector-method ?s
"*cider-scratch* buffer."
- (cider-find-or-create-scratch-buffer))
+ (cider-scratch-find-or-create-buffer))
(provide 'cider-selector)
diff --git a/cider-stacktrace.el b/cider-stacktrace.el
index c8ebb7ee..321d4bbd 100644
--- a/cider-stacktrace.el
+++ b/cider-stacktrace.el
@@ -89,12 +89,12 @@ cyclical data structures."
(defvar-local cider-stacktrace-hidden-frame-count 0)
(defvar-local cider-stacktrace-filters nil)
-(defvar-local cider-stacktrace-prior-filters nil)
(defvar-local cider-stacktrace-cause-visibility nil)
(defvar-local cider-stacktrace-positive-filters nil)
(defconst cider-error-buffer "*cider-error*")
-(add-to-list 'cider-ancillary-buffers cider-error-buffer)
+
+(make-obsolete 'cider-visit-error-buffer 'cider-selector "0.18")
(defcustom cider-stacktrace-suppressed-errors '()
"Errors that won't make the stacktrace buffer 'pop-over' your active window.
@@ -225,7 +225,7 @@ The error types are represented as strings."
["Show/hide REPL frames" cider-stacktrace-toggle-repl]
["Show/hide tooling frames" cider-stacktrace-toggle-tooling]
["Show/hide duplicate frames" cider-stacktrace-toggle-duplicates]
- ["Show only project frames" cider-stacktrace-show-only-project]
+ ["Toggle only project frames" cider-stacktrace-show-only-project]
["Show/hide all frames" cider-stacktrace-toggle-all]))
map))
@@ -235,8 +235,8 @@ The error types are represented as strings."
\\{cider-stacktrace-mode-map}"
(when cider-special-mode-truncate-lines
(setq-local truncate-lines t))
+ (setq-local sesman-system 'CIDER)
(setq-local electric-indent-chars nil)
- (setq-local cider-stacktrace-prior-filters nil)
(setq-local cider-stacktrace-hidden-frame-count 0)
(setq-local cider-stacktrace-filters cider-stacktrace-default-filters)
(setq-local cider-stacktrace-cause-visibility (make-vector 10 0)))
@@ -249,7 +249,7 @@ The error types are represented as strings."
"Filters that remove stackframes.")
(defvar cider-stacktrace--all-positive-filters
- '(project)
+ '(project all)
"Filters that ensure stackframes are shown.")
(defun cider-stacktrace--face-for-filter (filter neg-filters pos-filters)
@@ -266,10 +266,6 @@ override this and ensure that those frames are shown."
((member filter cider-stacktrace--all-positive-filters)
(if (member filter pos-filters)
'cider-stacktrace-filter-active-face
- 'cider-stacktrace-filter-inactive-face))
- ((null filter) ; "all" filter
- (if (null neg-filters)
- 'cider-stacktrace-filter-active-face
'cider-stacktrace-filter-inactive-face))))
(defun cider-stacktrace-indicate-filters (filters pos-filters)
@@ -315,18 +311,22 @@ NEG-FILTERS dictate which frames should be hidden while POS-FILTERS can
override this and ensure that those frames are shown.
Argument FLAGS are the flags set on the stackframe, ie: clj dup, etc."
(let ((neg (seq-intersection neg-filters flags))
- (pos (seq-intersection pos-filters flags)))
- (cond ((and pos neg) nil)
+ (pos (seq-intersection pos-filters flags))
+ (all (memq 'all pos-filters)))
+ (cond (all nil) ;; if all filter is on then we should not hide
+ ((and pos neg) nil) ;; if hidden and "resurrected" we should not hide
(pos nil)
(neg t)
(t nil))))
-(defun cider-stacktrace-apply-filters (neg-filters pos-filters)
+(defun cider-stacktrace--apply-filters (neg-filters pos-filters)
"Set visibility on stack frames.
-Update `cider-stacktrace-hidden-frame-count' and indicate filters applied.
-Currently collapsed stacktraces are ignored, and do not contribute to the
-hidden count. NEG-FILTERS remove frames with the flag in that list and
-POS-FILTERS ensure that frames with flag is shown."
+Should be called by `cider-stacktrace-apply-filters' which has the logic of
+how to interpret the combinations of the positive and negative filters.
+For instance, the presence of the positive filter `project' requires all of
+the other negative filters to be applied so that only project frames are
+shown. NEG-FILTERS are the tags that should be hidden. POS-FILTERS are
+the tags that must be shown."
(with-current-buffer cider-error-buffer
(save-excursion
(goto-char (point-min))
@@ -346,6 +346,29 @@ POS-FILTERS ensure that frames with flag is shown."
(setq cider-stacktrace-hidden-frame-count hidden)))
(cider-stacktrace-indicate-filters neg-filters pos-filters)))
+(defun cider-stacktrace-apply-filters (filters)
+ "Takes a single list of filters and applies them.
+Update `cider-stacktrace-hidden-frame-count' and indicate
+filters applied. Currently collapsed stacktraces are ignored, and do not
+contribute to the hidden count. FILTERS is the list of filters to be
+applied, positive and negative all together. This function defines how
+those choices interact and separates them into positive and negative
+filters for the resulting machinery."
+ (let ((neg-filters (seq-intersection filters cider-stacktrace--all-negative-filters))
+ (pos-filters (seq-intersection filters cider-stacktrace--all-positive-filters)))
+ ;; project and all are mutually exclusive. when both are present we check to
+ ;; see the most recent one (as cons onto the list would put it) and use that
+ ;; interaction.
+ (cond
+ ((memq 'all (memq 'project pos-filters)) ;; project is most recent
+ (cider-stacktrace--apply-filters cider-stacktrace--all-negative-filters '(project)))
+ ((memq 'project (memq 'all pos-filters)) ;; all is most recent
+ (cider-stacktrace--apply-filters nil '(all)))
+ ((memq 'all pos-filters) (cider-stacktrace--apply-filters nil '(all)))
+ ((memq 'project pos-filters) (cider-stacktrace--apply-filters cider-stacktrace--all-negative-filters
+ pos-filters))
+ (t (cider-stacktrace--apply-filters neg-filters pos-filters)))))
+
(defun cider-stacktrace-apply-cause-visibility ()
"Apply `cider-stacktrace-cause-visibility' to causes and reapply filters."
(with-current-buffer cider-error-buffer
@@ -369,8 +392,7 @@ POS-FILTERS ensure that frames with flag is shown."
(add-text-properties (point) detail-end
(list 'invisible hide
'collapsed hide))))))))
- (cider-stacktrace-apply-filters cider-stacktrace-filters
- cider-stacktrace-positive-filters))))
+ (cider-stacktrace-apply-filters cider-stacktrace-filters))))
;;; Internal/Middleware error suppression
@@ -469,42 +491,23 @@ When it reaches 3, it wraps to 0."
(interactive)
(cider-stacktrace-cycle-cause 5))
-(defun cider-stacktrace-toggle-all ()
- "Reset `cider-stacktrace-filters' if present; otherwise restore prior filters."
- (interactive)
- (when cider-stacktrace-filters
- (setq-local cider-stacktrace-prior-filters
- cider-stacktrace-filters))
- (cider-stacktrace-apply-filters
- (setq cider-stacktrace-filters
- (unless cider-stacktrace-filters ; when current filters are nil,
- cider-stacktrace-prior-filters)) ; reenable prior filter set
- cider-stacktrace-positive-filters))
-
(defun cider-stacktrace-toggle (flag)
"Update `cider-stacktrace-filters' to add or remove FLAG, and apply filters."
(cider-stacktrace-apply-filters
(setq cider-stacktrace-filters
(if (memq flag cider-stacktrace-filters)
(remq flag cider-stacktrace-filters)
- (cons flag cider-stacktrace-filters)))
- cider-stacktrace-positive-filters))
+ (cons flag cider-stacktrace-filters)))))
+
+(defun cider-stacktrace-toggle-all ()
+ "Toggle `all' in filter list."
+ (interactive)
+ (cider-stacktrace-toggle 'all))
(defun cider-stacktrace-show-only-project ()
- "Display only the stackframes from the project.
-BUTTON is the button at the top of the error buffer as the button calls
-with the button."
+ "Display only the stackframes from the project."
(interactive)
- (if (null cider-stacktrace-positive-filters)
- (progn
- (setq-local cider-stacktrace-prior-filters cider-stacktrace-filters)
- (setq-local cider-stacktrace-filters cider-stacktrace--all-negative-filters)
- (setq-local cider-stacktrace-positive-filters '(project)))
- (progn
- (setq-local cider-stacktrace-filters cider-stacktrace-prior-filters)
- (setq-local cider-stacktrace-positive-filters nil)))
- (cider-stacktrace-apply-filters cider-stacktrace-filters
- cider-stacktrace-positive-filters))
+ (cider-stacktrace-toggle 'project))
(defun cider-stacktrace-toggle-java ()
"Toggle display of Java stack frames."
@@ -584,6 +587,8 @@ Achieved by destructively manipulating the `cider-stacktrace-suppressed-errors'
(forward-line line-shift)
(back-to-indentation)))
+(declare-function cider-find-var "cider-find")
+
(defun cider-stacktrace-jump (&optional arg)
"Find definition for stack frame at point, if available.
The prefix ARG and `cider-prompt-for-symbol' decide whether to
@@ -596,6 +601,10 @@ prompt and whether to use a new window. Similar to `cider-find-var'."
;; Rendering
+(defvar cider-use-tooltips)
+(defun cider-stacktrace-tooltip (tooltip)
+ "Return TOOLTIP if `cider-use-tooltips' is set to true, nil otherwise."
+ (when cider-use-tooltips tooltip))
(defun cider-stacktrace-emit-indented (text &optional indent fill fontify)
"Insert TEXT, and optionally FILL and FONTIFY as clojure the entire block.
@@ -633,8 +642,9 @@ others."
'filter (cadr filter)
'follow-link t
'action 'cider-stacktrace-filter
- 'help-echo (format "Toggle %s stack frames"
- (car filter)))
+ 'help-echo (cider-stacktrace-tooltip
+ (format "Toggle %s stack frames"
+ (car filter))))
(insert " "))
(insert "\n")
(insert " Hide: ")
@@ -643,8 +653,9 @@ others."
'filter (cadr filter)
'follow-link t
'action 'cider-stacktrace-filter
- 'help-echo (format "Toggle %s stack frames"
- (car filter)))
+ 'help-echo (cider-stacktrace-tooltip
+ (format "Toggle %s stack frames"
+ (car filter))))
(insert " "))
(let ((hidden "(0 frames hidden)"))
@@ -659,7 +670,8 @@ others."
(insert-text-button "M-x cider-report-bug"
'follow-link t
'action (lambda (_button) (cider-report-bug))
- 'help-echo "Report bug to the CIDER team.")
+ 'help-echo (cider-stacktrace-tooltip
+ "Report bug to the CIDER team."))
(insert "`.\n\n")
(insert "\
If these stacktraces are occuring frequently, consider using the
@@ -677,8 +689,9 @@ others."
'face (if suppressed
'cider-stacktrace-suppressed-button-face
'cider-stacktrace-promoted-button-face)
- 'help-echo (format "Click to %s these stacktraces."
- (if suppressed "promote" "suppress"))))
+ 'help-echo (cider-stacktrace-tooltip
+ (format "Click to %s these stacktraces."
+ (if suppressed "promote" "suppress")))))
(insert " ")))))
(defun cider-stacktrace-render-frame (buffer frame)
@@ -695,7 +708,8 @@ This associates text properties to enable filtering and source navigation."
'name name 'file file 'line line
'flags flags 'follow-link t
'action 'cider-stacktrace-navigate
- 'help-echo "View source at this location"
+ 'help-echo (cider-stacktrace-tooltip
+ "View source at this location")
'font-lock-face 'cider-stacktrace-face
'type 'cider-plain-button)
(save-excursion
@@ -709,8 +723,6 @@ This associates text properties to enable filtering and source navigation."
'cider-stacktrace-frame t)))
(insert "\n")))))
-(declare-function cider-jump-to "cider-interaction")
-
(defun cider-stacktrace-render-compile-error (buffer cause)
"Emit into BUFFER the compile error CAUSE, and enable jumping to it."
(with-current-buffer buffer
@@ -723,7 +735,9 @@ This associates text properties to enable filtering and source navigation."
'file file 'line line 'column column 'follow-link t
'action (lambda (_button)
(cider-jump-to (cider-find-file file)
- (cons line column))))
+ (cons line column)))
+ 'help-echo (cider-stacktrace-tooltip
+ "Jump to the line that caused the error"))
(insert (propertize (format " at (%d:%d)" line column)
'font-lock-face message-face))))))
@@ -874,7 +888,7 @@ through the `cider-stacktrace-suppressed-errors' variable."
;; Stacktrace filters
(cider-stacktrace-render-filters
buffer
- `(("Project-Only" project) ("All" ,nil))
+ `(("Project-Only" project) ("All" all))
`(("Clojure" clj) ("Java" java) ("REPL" repl)
("Tooling" tooling) ("Duplicates" dup)))
(insert "\n")
diff --git a/cider-test.el b/cider-test.el
index 239124b0..bce6b4c0 100644
--- a/cider-test.el
+++ b/cider-test.el
@@ -77,8 +77,6 @@ Add to this list to have CIDER recognize additional test defining macros."
(defconst cider-test-report-buffer "*cider-test-report*"
"Buffer name in which to display test reports.")
-(add-to-list 'cider-ancillary-buffers cider-test-report-buffer)
-
;;; Faces
@@ -226,6 +224,7 @@ Add to this list to have CIDER recognize additional test defining macros."
(setq buffer-read-only t)
(when cider-special-mode-truncate-lines
(setq-local truncate-lines t))
+ (setq-local sesman-system 'CIDER)
(setq-local electric-indent-chars nil))
;; Report navigation
@@ -257,6 +256,8 @@ Add to this list to have CIDER recognize additional test defining macros."
(when-let* ((pos (next-single-property-change pos 'type)))
(goto-char pos))))))
+(declare-function cider-find-var "cider-find")
+
(defun cider-test-jump (&optional arg)
"Find definition for test at point, if available.
The prefix ARG and `cider-prompt-for-symbol' decide whether to
@@ -294,7 +295,8 @@ prompt and whether to use a new window. Similar to `cider-find-var'."
(cider-stacktrace-render
(cider-popup-buffer cider-error-buffer
cider-auto-select-error-buffer
- #'cider-stacktrace-mode)
+ #'cider-stacktrace-mode
+ 'ancillary)
(reverse causes))))))))))
(defun cider-test-stacktrace ()
@@ -617,12 +619,19 @@ This uses the Leiningen convention of appending '-test' to the namespace name."
;;; Test execution
-(declare-function cider-emit-interactive-eval-output "cider-interaction")
-(declare-function cider-emit-interactive-eval-err-output "cider-interaction")
+(declare-function cider-emit-interactive-eval-output "cider-eval")
+(declare-function cider-emit-interactive-eval-err-output "cider-eval")
+
+(defun cider-test--prompt-for-selectors (message)
+ "Prompt for test selectors with MESSAGE.
+The selectors can be either keywords or strings."
+ (mapcar
+ (lambda (string) (replace-regexp-in-string "^:+" "" string))
+ (split-string
+ (cider-read-from-minibuffer message))))
(defun cider-test-execute (ns &optional tests silent prompt-for-filters)
"Run tests for NS, which may be a keyword, optionally specifying TESTS.
-
This tests a single NS, or multiple namespaces when using keywords `:project',
`:loaded' or `:non-passing'. Optional TESTS are only honored when a single
namespace is specified. Upon test completion, results are echoed and a test
@@ -635,59 +644,63 @@ The include/exclude selectors will be used to filter the tests before
(cider-test-clear-highlights)
(let ((include-selectors
(when prompt-for-filters
- (split-string
- (cider-read-from-minibuffer "Test selectors to include (space separated): "))))
+ (cider-test--prompt-for-selectors "Test selectors to include (space separated): ")))
(exclude-selectors
(when prompt-for-filters
- (split-string
- (cider-read-from-minibuffer "Test selectors to exclude (space separated): ")))))
- (cider-map-connections
- (lambda (conn)
- (unless silent
- (if (and tests (= (length tests) 1))
- ;; we generate a different message when running individual tests
- (cider-test-echo-running ns (car tests))
- (cider-test-echo-running ns)))
- (cider-nrepl-send-request
- `("op" ,(cond ((stringp ns) "test")
- ((eq :project ns) "test-all")
- ((eq :loaded ns) "test-all")
- ((eq :non-passing ns) "retest"))
- "includes" ,(when (listp include-selectors) include-selectors)
- "excludes" ,(when (listp exclude-selectors) exclude-selectors)
- "ns" ,(when (stringp ns) ns)
- "tests" ,(when (stringp ns) tests)
- "load?" ,(when (or (stringp ns) (eq :project ns)) "true"))
- (lambda (response)
- (nrepl-dbind-response response (summary results status out err)
- (cond ((member "namespace-not-found" status)
- (unless silent
- (message "No test namespace: %s" (cider-propertize ns 'ns))))
- (out (cider-emit-interactive-eval-output out))
- (err (cider-emit-interactive-eval-err-output err))
- (results
- (nrepl-dbind-response summary (error fail)
- (setq cider-test-last-summary summary)
- (setq cider-test-last-results results)
- (cider-test-highlight-problems results)
- (cider-test-echo-summary summary results)
- (if (or (not (zerop (+ error fail)))
- cider-test-show-report-on-success)
- (cider-test-render-report
- (cider-popup-buffer
- cider-test-report-buffer
- cider-auto-select-test-report-buffer)
- summary
- results)
- (when (get-buffer cider-test-report-buffer)
- (with-current-buffer cider-test-report-buffer
- (let ((inhibit-read-only t))
- (erase-buffer)))
- (cider-test-render-report
- cider-test-report-buffer
- summary results))))))))
- conn))
- :clj)))
+ (cider-test--prompt-for-selectors "Test selectors to exclude (space separated): "))))
+ (cider-map-repls :clj-strict
+ (lambda (conn)
+ (unless silent
+ (if (and tests (= (length tests) 1))
+ ;; we generate a different message when running individual tests
+ (cider-test-echo-running ns (car tests))
+ (cider-test-echo-running ns)))
+ (let ((request `("op" ,(cond ((stringp ns) "test")
+ ((eq :project ns) "test-all")
+ ((eq :loaded ns) "test-all")
+ ((eq :non-passing ns) "retest")))))
+ ;; we add optional parts of the request only when relevant
+ (when (and (listp include-selectors) include-selectors)
+ (setq request (append request `("include" ,include-selectors))))
+ (when (and (listp exclude-selectors) exclude-selectors)
+ (setq request (append request `("exclude" ,exclude-selectors))))
+ (when (stringp ns)
+ (setq request (append request `("ns" ,ns))))
+ (when (stringp ns)
+ (setq request (append request `("tests" ,tests))))
+ (when (or (stringp ns) (eq :project ns))
+ (setq request (append request `("load?" ,"true"))))
+ (cider-nrepl-send-request
+ request
+ (lambda (response)
+ (nrepl-dbind-response response (summary results status out err)
+ (cond ((member "namespace-not-found" status)
+ (unless silent
+ (message "No test namespace: %s" (cider-propertize ns 'ns))))
+ (out (cider-emit-interactive-eval-output out))
+ (err (cider-emit-interactive-eval-err-output err))
+ (results
+ (nrepl-dbind-response summary (error fail)
+ (setq cider-test-last-summary summary)
+ (setq cider-test-last-results results)
+ (cider-test-highlight-problems results)
+ (cider-test-echo-summary summary results)
+ (if (or (not (zerop (+ error fail)))
+ cider-test-show-report-on-success)
+ (cider-test-render-report
+ (cider-popup-buffer
+ cider-test-report-buffer
+ cider-auto-select-test-report-buffer)
+ summary
+ results)
+ (when (get-buffer cider-test-report-buffer)
+ (with-current-buffer cider-test-report-buffer
+ (let ((inhibit-read-only t))
+ (erase-buffer)))
+ (cider-test-render-report
+ cider-test-report-buffer
+ summary results))))))))
+ conn))))))
(defun cider-test-rerun-failed-tests ()
"Rerun failed and erring tests from the last test run."
diff --git a/cider-tracing.el b/cider-tracing.el
new file mode 100644
index 00000000..c00e7b7f
--- /dev/null
+++ b/cider-tracing.el
@@ -0,0 +1,90 @@
+;;; cider-tracing.el --- Executing tracing functionality -*- lexical-binding: t -*-
+
+;; Copyright © 2013-2018 Bozhidar Batsov, Artur Malabarba and CIDER contributors
+;;
+;; Author: Bozhidar Batsov <bozhidar@batsov.com>
+;; Artur Malabarba <bruce.connor.am@gmail.com>
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;; This file is not part of GNU Emacs.
+
+;;; Commentary:
+
+;; A couple of commands for tracing the execution of functions.
+
+;;; Code:
+
+(require 'cider-client)
+(require 'cider-common) ; for `cider-prompt-for-symbol-function'
+(require 'cider-util) ; for `cider-propertize'
+(require 'cider-connection) ; for `cider-map-repls'
+(require 'nrepl-dict)
+
+(defun cider-sync-request:toggle-trace-var (symbol)
+ "Toggle var tracing for SYMBOL."
+ (thread-first `("op" "toggle-trace-var"
+ "ns" ,(cider-current-ns)
+ "sym" ,symbol)
+ (cider-nrepl-send-sync-request)))
+
+(defun cider--toggle-trace-var (sym)
+ "Toggle var tracing for SYM."
+ (let* ((trace-response (cider-sync-request:toggle-trace-var sym))
+ (var-name (nrepl-dict-get trace-response "var-name"))
+ (var-status (nrepl-dict-get trace-response "var-status")))
+ (pcase var-status
+ ("not-found" (error "Var %s not found" (cider-propertize sym 'fn)))
+ ("not-traceable" (error "Var %s can't be traced because it's not bound to a function" (cider-propertize var-name 'fn)))
+ (_ (message "Var %s %s" (cider-propertize var-name 'fn) var-status)))))
+
+;;;###autoload
+(defun cider-toggle-trace-var (arg)
+ "Toggle var tracing.
+Prompts for the symbol to use, or uses the symbol at point, depending on
+the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the
+opposite of what that option dictates."
+ (interactive "P")
+ (cider-ensure-op-supported "toggle-trace-var")
+ (funcall (cider-prompt-for-symbol-function arg)
+ "Toggle trace for var"
+ #'cider--toggle-trace-var))
+
+(defun cider-sync-request:toggle-trace-ns (ns)
+ "Toggle namespace tracing for NS."
+ (thread-first `("op" "toggle-trace-ns"
+ "ns" ,ns)
+ (cider-nrepl-send-sync-request)))
+
+;;;###autoload
+(defun cider-toggle-trace-ns (query)
+ "Toggle ns tracing.
+Defaults to the current ns. With prefix arg QUERY, prompts for a ns."
+ (interactive "P")
+ (cider-map-repls :clj-strict
+ (lambda (conn)
+ (with-current-buffer conn
+ (cider-ensure-op-supported "toggle-trace-ns")
+ (let ((ns (if query
+ (completing-read "Toggle trace for ns: "
+ (cider-sync-request:ns-list))
+ (cider-current-ns))))
+ (let* ((trace-response (cider-sync-request:toggle-trace-ns ns))
+ (ns-status (nrepl-dict-get trace-response "ns-status")))
+ (pcase ns-status
+ ("not-found" (error "Namespace %s not found" (cider-propertize ns 'ns)))
+ (_ (message "Namespace %s %s" (cider-propertize ns 'ns) ns-status)))))))))
+
+(provide 'cider-tracing)
+;;; cider-tracing.el ends here
diff --git a/cider-util.el b/cider-util.el
index fe7e275d..f09fb9ab 100644
--- a/cider-util.el
+++ b/cider-util.el
@@ -36,6 +36,7 @@
(require 'color)
(require 'seq)
(require 'subr-x)
+(require 'thingatpt)
;; clojure-mode and CIDER
(require 'cider-compat)
@@ -96,6 +97,14 @@ If BUFFER is provided act on that buffer instead."
;;; Thing at point
+
+(defun cider--text-or-limits (bounds start end)
+ "Returns the substring or the bounds of text.
+If BOUNDS is non-nil, returns the list (START END) of character
+positions. Else returns the substring from START to END."
+ (funcall (if bounds #'list #'buffer-substring-no-properties)
+ start end))
+
(defun cider-defun-at-point (&optional bounds)
"Return the text of the top level sexp at point.
If BOUNDS is non-nil, return a list of its starting and ending position
@@ -105,8 +114,7 @@ instead."
(end-of-defun)
(let ((end (point)))
(clojure-backward-logical-sexp 1)
- (funcall (if bounds #'list #'buffer-substring-no-properties)
- (point) end)))))
+ (cider--text-or-limits bounds (point) end)))))
(defun cider-ns-form ()
"Retrieve the ns form."
@@ -666,9 +674,9 @@ through a stack of help buffers. Variables `help-back-label' and
"Press <\\[describe-mode]> to see a list of the keybindings available (this will work in every Emacs buffer)."
"Press <\\[cider-repl-handle-shortcut]> to quickly invoke some REPL command."
"Press <\\[cider-switch-to-last-clojure-buffer]> to switch between the REPL and a Clojure source buffer."
- "Press <\\[cider-find-var]> to jump to the source of something (e.g. a var, a Java method)."
"Press <\\[cider-doc]> to view the documentation for something (e.g. a var, a Java method)."
"Press <\\[cider-find-resource]> to find a resource on the classpath."
+ "Press <\\[cider-find-var]> to jump to the source of something (e.g. a var, a Java method)."
"Press <\\[cider-selector]> to quickly select a CIDER buffer."
"Press <\\[cider-test-run-ns-tests]> to run the tests for the current namespace."
"Press <\\[cider-test-run-loaded-tests]> to run all loaded tests."
@@ -676,6 +684,10 @@ through a stack of help buffers. Variables `help-back-label' and
"Press <\\[cider-apropos]> to look for a symbol by some search string."
"Press <\\[cider-apropos-documentation]> to look for a symbol that has some string in its docstring."
"Press <\\[cider-eval-defun-at-point]> to eval the top-level form at point."
+ "Press <\\[cider-eval-defun-up-to-point]> to eval the top-level form up to the point."
+ "Press <\\[cider-eval-sexp-up-to-point]> to eval the current form up to the point."
+ "Press <\\[cider-eval-sexp-at-point]> to eval the current form around the point."
+ "Press <\\[cider-eval-sexp-at-point-in-context]> to eval the current form around the point in a user-provided context."
"Press <\\[cider-eval-buffer]> to eval the entire source buffer."
"Press <\\[cider-scratch]> to create a Clojure scratchpad. Pretty handy for prototyping."
"Press <\\[cider-read-and-eval]> to evaluate some Clojure expression directly in the minibuffer."
@@ -687,9 +699,9 @@ through a stack of help buffers. Variables `help-back-label' and
"Press <\\[cider-inspect]> to inspect the preceding expression's result."
"Press <C-u \\[cider-inspect]> to inspect the defun at point's result."
"Press <C-u C-u \\[cider-inspect]> to read Clojure code from the minibuffer and inspect its result."
- "Press <\\[cider-refresh]> to reload modified and unloaded namespaces."
- "You can define Clojure functions to be called before and after `cider-refresh' (see `cider-refresh-before-fn' and `cider-refresh-after-fn'."
- "Press <\\[cider-display-connection-info]> to view information about the connection."
+ "Press <\\[cider-ns-refresh]> to reload modified and unloaded namespaces."
+ "You can define Clojure functions to be called before and after `cider-ns-refresh' (see `cider-ns-refresh-before-fn' and `cider-ns-refresh-after-fn'."
+ "Press <\\[cider-describe-connection]> to view information about the connection."
"Press <\\[cider-undef]> to undefine a symbol in the current namespace."
"Press <\\[cider-interrupt]> to interrupt an ongoing evaluation."
"Use <M-x customize-group RET cider RET> to see every possible setting you can customize."
@@ -700,7 +712,9 @@ through a stack of help buffers. Variables `help-back-label' and
"Exploring CIDER's menu-bar entries is a great way to discover features."
"Keep in mind that some commands don't have a keybinding by default. Explore CIDER!"
"Tweak `cider-repl-prompt-function' to customize your REPL prompt."
- "Tweak `cider-eldoc-ns-function' to customize the way namespaces are displayed by eldoc.")
+ "Tweak `cider-eldoc-ns-function' to customize the way namespaces are displayed by eldoc."
+ "For no middleware, low-tech and reliable namespace reloading use <\\[cider-ns-reload]>."
+ "Press <\\[cider-load-buffer-and-switch-to-repl-buffer]> to load the current buffer and switch to the REPL buffer afterwards.")
"Some handy CIDER tips."
)
diff --git a/cider.el b/cider.el
index 226058ca..2129767f 100644
--- a/cider.el
+++ b/cider.el
@@ -11,8 +11,8 @@
;; Steve Purcell <steve@sanityinc.com>
;; Maintainer: Bozhidar Batsov <bozhidar@batsov.com>
;; URL: http://www.github.com/clojure-emacs/cider
-;; Version: 0.17.0
-;; Package-Requires: ((emacs "24.4") (clojure-mode "5.6.0") (pkg-info "0.4") (queue "0.1.1") (spinner "1.7") (seq "2.16"))
+;; Version: 0.18.0
+;; Package-Requires: ((emacs "25") (clojure-mode "5.9") (pkg-info "0.4") (queue "0.2") (spinner "1.7") (seq "2.16") (sesman "0.3"))
;; Keywords: languages, clojure, cider
;; This program is free software: you can redistribute it and/or modify
@@ -51,7 +51,14 @@
;;; Usage:
-;; M-x cider-jack-in
+;; M-x cider-jack-in-clj
+;; M-x cider-jack-in-cljs
+;;
+;; M-x cider-connect-sibling-clj
+;; M-x cider-connect-sibling-cljs
+;;
+;; M-x cider-connect-clj
+;; M-x cider-connect-cljs
;;; Code:
@@ -60,42 +67,31 @@
:prefix "cider-"
:group 'applications
:link '(url-link :tag "GitHub" "https://github.com/clojure-emacs/cider")
- :link '(url-link :tag "Online Manual" "https://cider.readthedocs.io")
+ :link '(url-link :tag "Online Manual" "http://docs.cider.mx")
:link '(emacs-commentary-link :tag "Commentary" "cider"))
-(defcustom cider-prompt-for-project-on-connect 'when-needed
- "Controls whether to prompt for associated project on `cider-connect'.
-
-When set to when-needed, the project will be derived from the buffer you're
-visiting, when invoking `cider-connect'.
-When set to t, you'll always to prompted to select the matching project.
-When set to nil, you'll never be prompted to select a project and no
-project inference will take place."
- :type '(choice (const :tag "always" t)
- (const when-needed)
- (const :tag "never" nil))
- :group 'cider
- :package-version '(cider . "0.10.0"))
-
(require 'cider-client)
(require 'cider-eldoc)
(require 'cider-repl)
+(require 'cider-repl-history)
+(require 'cider-connection)
(require 'cider-mode)
(require 'cider-common)
-(require 'subr-x)
(require 'cider-compat)
(require 'cider-debug)
-(require 'tramp-sh)
-(require 'cider-repl-history)
+(require 'cider-util)
+(require 'tramp-sh)
+(require 'subr-x)
(require 'seq)
+(require 'sesman)
-(defconst cider-version "0.17.0"
+(defconst cider-version "0.18.0"
"Fallback version used when it cannot be extracted automatically.
Normally it won't be used, unless `pkg-info' fails to extract the
version from the CIDER package or library.")
-(defconst cider-codename "Andalucía"
+(defconst cider-codename "Saigon"
"Codename used to denote stable releases.")
(defcustom cider-lein-command
@@ -134,7 +130,7 @@ version from the CIDER package or library.")
:package-version '(cider . "0.14.0"))
(defcustom cider-boot-parameters
- "repl -s -H :: wait"
+ "cider.tasks/nrepl-server -b :: wait"
"Params passed to boot to start an nREPL server via `cider-jack-in'."
:type 'string
:group 'cider
@@ -143,9 +139,12 @@ version from the CIDER package or library.")
(defcustom cider-clojure-cli-command
"clojure"
- "The command used to execute clojure with tools.deps (requires Clojure 1.9+)."
+ "The command used to execute clojure with tools.deps (requires Clojure 1.9+).
+Don't use clj here, as it doesn't work when spawned from Emacs due to
+it using rlwrap."
:type 'string
:group 'cider
+ :safe #'stringp
:package-version '(cider . "0.17.0"))
(defcustom cider-clojure-cli-global-options
@@ -173,6 +172,7 @@ vector of middleware variables as a string."
By default we favor the project-specific shadow-cljs over the system-wide."
:type 'string
:group 'cider
+ :safe #'stringp
:package-version '(cider . "0.17.0"))
(defcustom cider-shadow-cljs-global-options
@@ -196,6 +196,7 @@ By default we favor the project-specific shadow-cljs over the system-wide."
"The command used to execute Gradle."
:type 'string
:group 'cider
+ :safe #'stringp
:package-version '(cider . "0.10.0"))
(defcustom cider-gradle-global-options
@@ -214,34 +215,41 @@ By default we favor the project-specific shadow-cljs over the system-wide."
:safe #'stringp
:package-version '(cider . "0.10.0"))
-(defcustom cider-default-repl-command "clojure-cli"
- "The default command and parameters to use when connecting to nREPL.
+(define-obsolete-variable-alias 'cider-default-repl-command 'cider-jack-in-default)
+(defcustom cider-jack-in-default (if (executable-find "clojure") 'clojure-cli 'lein)
+ "The default tool to use when doing `cider-jack-in' outside a project.
This value will only be consulted when no identifying file types, i.e.
project.clj for leiningen or build.boot for boot, could be found.
-As tools.deps is bundled with Clojure itself, it's the default REPL command."
- :type 'string
+As the Clojure CLI is bundled with Clojure itself, it's the default.
+In the absence of the Clojure CLI (e.g. on Windows), we fallback
+to Leiningen."
+ :type '(choice (const 'lein)
+ (const 'boot)
+ (const 'clojure-cli)
+ (const 'shadow-cljs)
+ (const 'gradle))
:group 'cider
- :safe #'stringp
+ :safe #'symbolp
:package-version '(cider . "0.9.0"))
(defcustom cider-preferred-build-tool
nil
"Allow choosing a build system when there are many.
-When there are artifacts from multiple build systems (\"lein\", \"boot\",
-\"gradle\") the user is prompted to select one of them. When non-nil, this
+When there are project markers from multiple build systems (e.g. lein and
+boot) the user is prompted to select one of them. When non-nil, this
variable will suppress this behavior and will select whatever build system
is indicated by the variable if present. Note, this is only when CIDER
cannot decide which of many build systems to use and will never override a
command when there is no ambiguity."
- :type '(choice (const "lein")
- (const "boot")
- (const "clojure-cli")
- (const "shadow-cljs")
- (const "gradle")
+ :type '(choice (const 'lein)
+ (const 'boot)
+ (const 'clojure-cli)
+ (const 'shadow-cljs)
+ (const 'gradle)
(const :tag "Always ask" nil))
:group 'cider
- :safe #'stringp
+ :safe #'symbolp
:package-version '(cider . "0.13.0"))
(defcustom cider-allow-jack-in-without-project 'warn
@@ -278,11 +286,6 @@ This variable is used by `cider-connect'."
:group 'cider
:package-version '(cider . "0.9.0"))
-(defcustom cider-auto-mode t
- "When non-nil, automatically enable cider mode for all Clojure buffers."
- :type 'boolean
- :package-version '(cider . "0.9.0"))
-
(defcustom cider-inject-dependencies-at-jack-in nil
"When nil, do not inject repl dependencies (most likely nREPL middlewares) at `cider-jack-in' time."
:type 'boolean
@@ -292,6 +295,7 @@ This variable is used by `cider-connect'."
(defcustom cider-offer-to-open-cljs-app-in-browser t
"When nil, do not offer to open ClojureScript apps in a browser on connect."
:type 'boolean
+ :safe #'booleanp
:version '(cider . "0.15.0"))
(defvar cider-ps-running-nrepls-command "ps u | grep leiningen"
@@ -316,53 +320,54 @@ Sub-match 1 must be the project path.")
(defun cider-jack-in-command (project-type)
"Determine the command `cider-jack-in' needs to invoke for the PROJECT-TYPE."
(pcase project-type
- ("lein" cider-lein-command)
- ("boot" cider-boot-command)
- ("clojure-cli" cider-clojure-cli-command)
- ("shadow-cljs" cider-shadow-cljs-command)
- ("gradle" cider-gradle-command)
- (_ (user-error "Unsupported project type `%s'" project-type))))
+ ('lein cider-lein-command)
+ ('boot cider-boot-command)
+ ('clojure-cli cider-clojure-cli-command)
+ ('shadow-cljs cider-shadow-cljs-command)
+ ('gradle cider-gradle-command)
+ (_ (user-error "Unsupported project type `%S'" project-type))))
(defun cider-jack-in-resolve-command (project-type)
"Determine the resolved file path to `cider-jack-in-command'.
-Throws an error if PROJECT-TYPE is unknown. Known types are
-\"lein\", \"boot\", and \"gradle\"."
+Throws an error if PROJECT-TYPE is unknown."
(pcase project-type
- ("lein" (cider--resolve-command cider-lein-command))
- ("boot" (cider--resolve-command cider-boot-command))
- ("clojure-cli" (cider--resolve-command cider-clojure-cli-command))
+ ('lein (cider--resolve-command cider-lein-command))
+ ('boot (cider--resolve-command cider-boot-command))
+ ('clojure-cli (cider--resolve-command cider-clojure-cli-command))
;; here we have to account for the possibility that the command is either
;; "npx shadow-cljs" or just "shadow-cljs"
- ("shadow-cljs" (cider--resolve-command (car (split-string cider-shadow-cljs-command))))
- ("gradle" (cider--resolve-command cider-gradle-command))
- (_ (user-error "Unsupported project type `%s'" project-type))))
+ ('shadow-cljs (let ((parts (split-string cider-shadow-cljs-command)))
+ (when-let* ((command (cider--resolve-command (car parts))))
+ (mapconcat #'identity (cons command (cdr parts)) " "))))
+ ('gradle (cider--resolve-command cider-gradle-command))
+ (_ (user-error "Unsupported project type `%S'" project-type))))
(defun cider-jack-in-global-options (project-type)
"Determine the command line options for `cider-jack-in' for the PROJECT-TYPE."
(pcase project-type
- ("lein" cider-lein-global-options)
- ("boot" cider-boot-global-options)
- ("clojure-cli" cider-clojure-cli-global-options)
- ("shadow-cljs" cider-shadow-cljs-global-options)
- ("gradle" cider-gradle-global-options)
- (_ (user-error "Unsupported project type `%s'" project-type))))
+ ('lein cider-lein-global-options)
+ ('boot cider-boot-global-options)
+ ('clojure-cli cider-clojure-cli-global-options)
+ ('shadow-cljs cider-shadow-cljs-global-options)
+ ('gradle cider-gradle-global-options)
+ (_ (user-error "Unsupported project type `%S'" project-type))))
(defun cider-jack-in-params (project-type)
"Determine the commands params for `cider-jack-in' for the PROJECT-TYPE."
(pcase project-type
- ("lein" cider-lein-parameters)
- ("boot" cider-boot-parameters)
- ("clojure-cli" (format cider-clojure-cli-parameters
- (concat
- "["
- (mapconcat
- (apply-partially #'format "\"%s\"")
- (cider-jack-in-normalized-nrepl-middlewares)
- ", ")
- "]")))
- ("shadow-cljs" cider-shadow-cljs-parameters)
- ("gradle" cider-gradle-parameters)
- (_ (user-error "Unsupported project type `%s'" project-type))))
+ ('lein cider-lein-parameters)
+ ('boot cider-boot-parameters)
+ ('clojure-cli (format cider-clojure-cli-parameters
+ (concat
+ "["
+ (mapconcat
+ (apply-partially #'format "\"%s\"")
+ (cider-jack-in-normalized-nrepl-middlewares)
+ ", ")
+ "]")))
+ ('shadow-cljs cider-shadow-cljs-parameters)
+ ('gradle cider-gradle-parameters)
+ (_ (user-error "Unsupported project type `%S'" project-type))))
;;; Jack-in dependencies injection
@@ -372,17 +377,36 @@ Throws an error if PROJECT-TYPE is unknown. Known types are
(cider-add-to-alist 'cider-jack-in-dependencies
"org.clojure/tools.nrepl" "0.2.13")
+(defvar cider-jack-in-cljs-dependencies nil
+ "List of dependencies where elements are lists of artifact name and version.
+Added to `cider-jack-in-dependencies' when doing `cider-jack-in-cljs'.")
+(put 'cider-jack-in-cljs-dependencies 'risky-local-variable t)
+(cider-add-to-alist 'cider-jack-in-cljs-dependencies "cider/piggieback" "0.3.9")
+
(defvar cider-jack-in-dependencies-exclusions nil
"List of exclusions for jack in dependencies.
-
Elements of the list are artifact name and list of exclusions to apply for the artifact.")
(put 'cider-jack-in-dependencies-exclusions 'risky-local-variable t)
(cider-add-to-alist 'cider-jack-in-dependencies-exclusions
"org.clojure/tools.nrepl" '("org.clojure/clojure"))
+(defconst cider-clojure-artifact-id "org.clojure/clojure"
+ "Artifact identifier for Clojure.")
+
+(defconst cider-minimum-clojure-version "1.8.0"
+ "Minimum supported version of Clojure.")
+
+(defconst cider-latest-clojure-version "1.10.0"
+ "Latest supported version of Clojure.")
+
+(defconst cider-required-middleware-version "0.18.0"
+ "The minimum CIDER nREPL version that's known to work properly with CIDER.")
+
+(defconst cider-latest-middleware-version "0.18.0"
+ "The latest CIDER nREPL version that's known to work properly with CIDER.")
+
(defcustom cider-jack-in-auto-inject-clojure nil
"Version of clojure to auto-inject into REPL.
-
If nil, do not inject Clojure into the REPL. If `latest', inject
`cider-latest-clojure-version', which should approximate to the most recent
version of Clojure. If `minimal', inject `cider-minimum-clojure-version',
@@ -408,7 +432,13 @@ that extend CIDER, not for users. For example, a refactoring package might
want to inject some middleware only when within a project context.)")
(put 'cider-jack-in-lein-plugins 'risky-local-variable t)
(cider-add-to-alist 'cider-jack-in-lein-plugins
- "cider/cider-nrepl" (upcase cider-version))
+ "cider/cider-nrepl" cider-latest-middleware-version)
+
+(defvar cider-jack-in-cljs-lein-plugins nil
+ "List of Leiningen plugins to be injected at jack-in.
+Added to `cider-jack-in-lein-plugins' (which see) when doing
+`cider-jack-in-cljs'.")
+(put 'cider-jack-in-cljs-lein-plugins 'risky-local-variable t)
(defun cider-jack-in-normalized-lein-plugins ()
"Return a normalized list of Leiningen plugins to be injected.
@@ -435,6 +465,13 @@ the middlewares should actually be injected.")
(put 'cider-jack-in-nrepl-middlewares 'risky-local-variable t)
(add-to-list 'cider-jack-in-nrepl-middlewares "cider.nrepl/cider-middleware")
+(defvar cider-jack-in-cljs-nrepl-middlewares nil
+ "List of Clojure variable names.
+Added to `cider-jack-in-nrepl-middlewares' (which see) when doing
+`cider-jack-in-cljs'.")
+(put 'cider-jack-in-cljs-nrepl-middlewares 'risky-local-variable t)
+(add-to-list 'cider-jack-in-cljs-nrepl-middlewares "cider.piggieback/wrap-cljs-repl")
+
(defun cider-jack-in-normalized-nrepl-middlewares ()
"Return a normalized list of middleware variable names.
See `cider-jack-in-nrepl-middlewares' for the format, except that the list
@@ -527,7 +564,7 @@ Does so by concatenating GLOBAL-OPTS, DEPENDENCIES finally PARAMS."
(unless (seq-empty-p global-opts) " ")
"-Sdeps '{:deps {"
(mapconcat #'identity
- (seq-map (lambda (dep) (format "%s {:mvn/version \"%s\"}" (car dep) (cadr dep))) dependencies)
+ (seq-map (lambda (dep) (format "%s {:mvn/version \"%s\"}" (car dep) (cadr dep))) dependencies)
" ")
"}}' "
params)))
@@ -540,14 +577,13 @@ Does so by concatenating GLOBAL-OPTS, DEPENDENCIES finally PARAMS."
global-opts
(unless (seq-empty-p global-opts) " ")
(mapconcat #'identity
- (seq-map (lambda (dep) (format "-d %s:%s" (car dep) (cadr dep))) dependencies)
+ (seq-map (lambda (dep) (format "-d %s:%s" (car dep) (cadr dep))) dependencies)
" ")
" "
params)))
(defun cider-add-clojure-dependencies-maybe (dependencies)
"Return DEPENDENCIES with an added Clojure dependency if requested.
-
See also `cider-jack-in-auto-inject-clojure'."
(if cider-jack-in-auto-inject-clojure
(if (consp cider-jack-in-auto-inject-clojure)
@@ -571,62 +607,59 @@ the used PROJECT-TYPE. Eliminates the need for hacking profiles.clj or the
boot script for supporting cider with its nREPL middleware and
dependencies."
(pcase project-type
- ("lein" (cider-lein-jack-in-dependencies
- global-opts
- params
- (cider-add-clojure-dependencies-maybe
- cider-jack-in-dependencies)
- cider-jack-in-dependencies-exclusions
- (cider-jack-in-normalized-lein-plugins)))
- ("boot" (cider-boot-jack-in-dependencies
- global-opts
- params
- (cider-add-clojure-dependencies-maybe
- cider-jack-in-dependencies)
- (cider-jack-in-normalized-lein-plugins)
- (cider-jack-in-normalized-nrepl-middlewares)))
- ("clojure-cli" (cider-clojure-cli-jack-in-dependencies
- global-opts
- params
- (cider-add-clojure-dependencies-maybe
- cider-jack-in-dependencies)))
- ("shadow-cljs" (cider-shadow-cljs-jack-in-dependencies
- global-opts
- params
- (cider-add-clojure-dependencies-maybe
- cider-jack-in-dependencies)))
- ("gradle" (concat
- global-opts
- (unless (seq-empty-p global-opts) " ")
- params))
- (_ (error "Unsupported project type `%s'" project-type))))
+ ('lein (cider-lein-jack-in-dependencies
+ global-opts
+ params
+ (cider-add-clojure-dependencies-maybe
+ cider-jack-in-dependencies)
+ cider-jack-in-dependencies-exclusions
+ (cider-jack-in-normalized-lein-plugins)))
+ ('boot (cider-boot-jack-in-dependencies
+ global-opts
+ params
+ (cider-add-clojure-dependencies-maybe
+ cider-jack-in-dependencies)
+ (cider-jack-in-normalized-lein-plugins)
+ (cider-jack-in-normalized-nrepl-middlewares)))
+ ('clojure-cli (cider-clojure-cli-jack-in-dependencies
+ global-opts
+ params
+ (cider-add-clojure-dependencies-maybe
+ cider-jack-in-dependencies)))
+ ('shadow-cljs (cider-shadow-cljs-jack-in-dependencies
+ global-opts
+ params
+ (cider-add-clojure-dependencies-maybe
+ cider-jack-in-dependencies)))
+ ('gradle (concat
+ global-opts
+ (unless (seq-empty-p global-opts) " ")
+ params))
+ (_ (error "Unsupported project type `%S'" project-type))))
;;; ClojureScript REPL creation
(defcustom cider-check-cljs-repl-requirements t
"When non-nil will run the requirement checks for the different cljs repls.
-
Generally you should not disable this unless you run into some faulty check."
:type 'boolean
:safe #'booleanp
- :version '(cider . "0.17.0"))
+ :package-version '(cider . "0.17.0"))
(defun cider-verify-clojurescript-is-present ()
"Check whether ClojureScript is present."
- (unless (cider-library-present-p "clojurescript")
- (user-error "ClojureScript is not available. See http://cider.readthedocs.io/en/latest/clojurescript for details")))
+ (unless (cider-library-present-p "clojure/clojurescript")
+ (user-error "ClojureScript is not available. See http://docs.cider.mx/en/latest/clojurescript for details")))
(defun cider-verify-piggieback-is-present ()
"Check whether the piggieback middleware is present."
- (unless (cider-library-present-p "piggieback")
- (user-error "Piggieback is not available. See http://cider.readthedocs.io/en/latest/clojurescript for details")))
+ (unless (cider-library-present-p "cider/piggieback")
+ (user-error "Piggieback is not available. See http://docs.cider.mx/en/latest/clojurescript for details")))
(defun cider-check-nashorn-requirements ()
"Check whether we can start a Nashorn ClojureScript REPL."
- (cider-verify-piggieback-is-present)
- (when (string-prefix-p "1.7" (cider--java-version))
- (user-error "Nashorn is supported only on Java 8 or newer")))
+ (cider-verify-piggieback-is-present))
(defun cider-check-node-requirements ()
"Check whether we can start a Node ClojureScript REPL."
@@ -637,38 +670,85 @@ Generally you should not disable this unless you run into some faulty check."
(defun cider-check-figwheel-requirements ()
"Check whether we can start a Figwheel ClojureScript REPL."
(cider-verify-piggieback-is-present)
- (unless (cider-library-present-p "figwheel-sidecar")
- (user-error "Figwheel-sidecar is not available. Please check http://cider.readthedocs.io/en/latest/clojurescript")))
+ (unless (cider-library-present-p "figwheel-sidecar/figwheel-sidecar")
+ (user-error "Figwheel-sidecar is not available. Please check http://docs.cider.mx/en/latest/clojurescript")))
+
+(defun cider-check-figwheel-main-requirements ()
+ "Check whether we can start a Figwheel ClojureScript REPL."
+ (cider-verify-piggieback-is-present)
+ (unless (cider-library-present-p "bhauman/figwheel-main")
+ (user-error "Figwheel-main is not available. Please check http://docs.cider.mx/en/latest/clojurescript")))
(defun cider-check-weasel-requirements ()
"Check whether we can start a Weasel ClojureScript REPL."
(cider-verify-piggieback-is-present)
- (unless (cider-library-present-p "weasel")
- (user-error "Weasel in not available. Please check http://cider.readthedocs.io/en/latest/clojurescript/#browser-connected-clojurescript-repl")))
+ (unless (cider-library-present-p "weasel/weasel")
+ (user-error "Weasel in not available. Please check http://docs.cider.mx/en/latest/clojurescript/#browser-connected-clojurescript-repl")))
(defun cider-check-boot-requirements ()
"Check whether we can start a Boot ClojureScript REPL."
(cider-verify-piggieback-is-present)
- (unless (cider-library-present-p "boot-cljs-repl")
+ (unless (cider-library-present-p "adzerk/boot-cljs-repl")
(user-error "The Boot ClojureScript REPL is not available. Please check https://github.com/adzerk-oss/boot-cljs-repl/blob/master/README.md")))
(defun cider-check-shadow-cljs-requirements ()
"Check whether we can start a shadow-cljs REPL."
- (unless (cider-library-present-p "shadow-cljs")
+ (unless (cider-library-present-p "thheller/shadow-cljs")
(user-error "The shadow-cljs ClojureScript REPL is not available")))
+(defun cider-normalize-cljs-init-options (options)
+ "Normalize the OPTIONS string used for initializing a CLJS REPL."
+ (if (or (string-prefix-p "{" options)
+ (string-prefix-p "(" options)
+ (string-prefix-p "[" options)
+ (string-prefix-p ":" options))
+ options
+ (concat ":" options)))
+
+(defcustom cider-shadow-default-options nil
+ "Defines default `shadow-cljs' options."
+ :type 'string
+ :safe (lambda (s) (or (null s) (stringp s)))
+ :package-version '(cider . "0.18.0"))
+
+(defun cider-shadow-select-cljs-init-form ()
+ "Generate the init form for a shadow-cljs select-only REPL.
+We have to prompt the user to select a build, that's why this is a command,
+not just a string."
+ (let ((form "(do (require '[shadow.cljs.devtools.api :as shadow]) (shadow/nrepl-select %s))")
+ (options (or cider-shadow-default-options
+ (read-from-minibuffer "Select shadow-cljs build (e.g. dev): "))))
+ (format form (cider-normalize-cljs-init-options options))))
+
(defun cider-shadow-cljs-init-form ()
"Generate the init form for a shadow-cljs REPL.
-
We have to prompt the user to select a build, that's why
this is a command, not just a string."
- (let ((form "(do (require '[shadow.cljs.devtools.api :as shadow]) (shadow/watch :%s) (shadow/nrepl-select :%s))")
- (build (string-remove-prefix ":" (read-from-minibuffer "Select shadow-cljs build: "))))
+ (let* ((form "(do (require '[shadow.cljs.devtools.api :as shadow]) (shadow/watch %s) (shadow/nrepl-select %s))")
+ (options (or cider-shadow-default-options
+ (read-from-minibuffer "Select shadow-cljs build (e.g. dev): ")))
+ (build (cider-normalize-cljs-init-options options)))
(format form build build)))
+(defcustom cider-figwheel-main-default-options nil
+ "Defines the `figwheel.main/start' options.
+
+Note that figwheel-main/start can also accept a map of options, refer to
+Figwheel for details."
+ :type 'string
+ :safe (lambda (s) (or (null s) (stringp s)))
+ :package-version '(cider . "0.18.0"))
+
+(defun cider-figwheel-main-init-form ()
+ "Produce the figwheel-main ClojureScript init form."
+ (let ((form "(do (require 'figwheel.main) (figwheel.main/start %s))")
+ (options (string-trim
+ (or cider-figwheel-main-default-options
+ (read-from-minibuffer "Select figwheel-main build (e.g. :dev): ")))))
+ (format form (cider-normalize-cljs-init-options options))))
+
(defun cider-custom-cljs-repl-init-form ()
"Prompt for a form that would start a ClojureScript REPL.
-
The supplied string will be wrapped in a do form if needed."
(let ((form (read-from-minibuffer "Please, provide a form to start a ClojureScript REPL: ")))
;; TODO: We should probably make this more robust (e.g. by using a regexp or
@@ -678,10 +758,11 @@ The supplied string will be wrapped in a do form if needed."
(format "(do %s)" form))))
(defvar cider-cljs-repl-types
- '((nashorn "(cider.piggieback/cljs-repl (cljs.repl.nashorn/repl-env))"
+ '((nashorn "(do (require 'cljs.repl.nashorn) (cider.piggieback/cljs-repl (cljs.repl.nashorn/repl-env)))"
cider-check-nashorn-requirements)
(figwheel "(do (require 'figwheel-sidecar.repl-api) (figwheel-sidecar.repl-api/start-figwheel!) (figwheel-sidecar.repl-api/cljs-repl))"
cider-check-figwheel-requirements)
+ (figwheel-main cider-figwheel-main-init-form cider-check-figwheel-main-requirements)
(node "(do (require 'cljs.repl.node) (cider.piggieback/cljs-repl (cljs.repl.node/repl-env)))"
cider-check-node-requirements)
(weasel "(do (require 'weasel.repl.websocket) (cider.piggieback/cljs-repl (weasel.repl.websocket/repl-env :ip \"127.0.0.1\" :port 9001)))"
@@ -689,6 +770,7 @@ The supplied string will be wrapped in a do form if needed."
(boot "(do (require 'adzerk.boot-cljs-repl) (adzerk.boot-cljs-repl/start-repl))"
cider-check-boot-requirements)
(shadow cider-shadow-cljs-init-form cider-check-shadow-cljs-requirements)
+ (shadow-select cider-shadow-select-cljs-init-form cider-check-shadow-cljs-requirements)
(custom cider-custom-cljs-repl-init-form nil))
"A list of supported ClojureScript REPLs.
@@ -719,8 +801,7 @@ It's intended to be used in your Emacs config."
(defcustom cider-default-cljs-repl nil
"The default ClojureScript REPL to start.
-
-This affects commands like `cider-jack-in-clojurescript'. Generally it's
+This affects commands like `cider-jack-in-cljs'. Generally it's
intended to be set via .dir-locals.el for individual projects, as its
relatively unlikely you'd like to use the same type of REPL in each project
you're working on."
@@ -739,29 +820,55 @@ you're working on."
(make-obsolete-variable 'cider-cljs-boot-repl 'cider-default-cljs-repl "0.17")
(make-obsolete-variable 'cider-cljs-gradle-repl 'cider-default-cljs-repl "0.17")
-(defun cider-select-cljs-repl ()
- "Select the ClojureScript REPL to use with `cider-jack-in-clojurescript'."
+(defvar cider--select-cljs-repl-history nil)
+(defun cider-select-cljs-repl (&optional default)
+ "Select the ClojureScript REPL to use with `cider-jack-in-cljs'.
+DEFAULT is the default CLJS REPL to offer in completion."
(let ((repl-types (mapcar #'car cider-cljs-repl-types)))
- (intern (completing-read "Select ClojureScript REPL type: " repl-types))))
+ (intern (completing-read "Select ClojureScript REPL type: " repl-types
+ nil nil nil 'cider--select-cljs-repl-history
+ (or default (car cider--select-cljs-repl-history))))))
(defun cider-cljs-repl-form (repl-type)
"Get the cljs REPL form for REPL-TYPE."
- (let ((repl-form (cadr (seq-find
- (lambda (entry)
- (eq (car entry) repl-type))
- cider-cljs-repl-types))))
- ;; repl-form can be either a string or a function producing a string
- (if (symbolp repl-form)
- (funcall repl-form)
- repl-form)))
-
-(defun cider-verify-cljs-repl-requirements (repl-type)
- "Verify that the requirements for REPL-TYPE are met."
- (when-let* ((fun (nth 2 (seq-find
- (lambda (entry)
- (eq (car entry) repl-type))
- cider-cljs-repl-types))))
- (funcall fun)))
+ (if-let* ((repl-form (cadr (seq-find
+ (lambda (entry)
+ (eq (car entry) repl-type))
+ cider-cljs-repl-types))))
+ ;; repl-form can be either a string or a function producing a string
+ (if (symbolp repl-form)
+ (funcall repl-form)
+ repl-form)
+ (user-error "No ClojureScript REPL type %s found. Please make sure that `cider-cljs-repl-types' has an entry for it" repl-type)))
+
+(defun cider-verify-cljs-repl-requirements (&optional repl-type)
+ "Verify that the requirements for REPL-TYPE are met.
+Return REPL-TYPE if requirements are met."
+ (let ((repl-type (or repl-type
+ cider-default-cljs-repl
+ (cider-select-cljs-repl))))
+ (when-let* ((fun (nth 2 (seq-find
+ (lambda (entry)
+ (eq (car entry) repl-type))
+ cider-cljs-repl-types))))
+ (funcall fun))
+ repl-type))
+
+(defun cider--check-cljs (&optional cljs-type no-error)
+ "Verify that all cljs requirements are met for CLJS-TYPE connection.
+Return REPL-TYPE of requirement are met, and throw an ‘user-error’ otherwise.
+When NO-ERROR is non-nil, don't throw an error, issue a message and return
+nil."
+ (if no-error
+ (condition-case ex
+ (progn
+ (cider-verify-clojurescript-is-present)
+ (cider-verify-cljs-repl-requirements cljs-type))
+ (error
+ (message "Invalid CLJS dependency: %S" ex)
+ nil))
+ (cider-verify-clojurescript-is-present)
+ (cider-verify-cljs-repl-requirements cljs-type)))
(defun cider--offer-to-open-app-in-browser (server-buffer)
"Look for a server address in SERVER-BUFFER and offer to open it."
@@ -774,200 +881,394 @@ you're working on."
(when (y-or-n-p (format "Visit ‘%s’ in a browser? " url))
(browse-url url)))))))
-(defun cider-create-sibling-cljs-repl (client-buffer)
- "Create a ClojureScript REPL with the same server as CLIENT-BUFFER.
-The new buffer will correspond to the same project as CLIENT-BUFFER, which
-should be the regular Clojure REPL started by the server process filter.
-
-Normally this would prompt for the ClojureScript REPL to start (e.g. Node,
-Figwheel, etc), unless you've set `cider-default-cljs-repl'."
- (interactive (list (cider-current-connection)))
- ;; We can't start a ClojureScript REPL without ClojureScript
- (when cider-check-cljs-repl-requirements
- (cider-verify-clojurescript-is-present))
- ;; Load variables in .dir-locals.el into the server process buffer, so
- ;; cider-default-cljs-repl can be set for each project individually.
- (hack-local-variables)
- (let* ((cljs-repl-type (or cider-default-cljs-repl
- (cider-select-cljs-repl)))
- (cljs-repl-form (cider-cljs-repl-form cljs-repl-type)))
- (when cider-check-cljs-repl-requirements
- (cider-verify-cljs-repl-requirements cljs-repl-type))
- ;; if all the requirements are met we can finally proceed with starting
- ;; the ClojureScript REPL for `cljs-repl-type'
- (let* ((nrepl-repl-buffer-name-template "*cider-repl%s(cljs)*")
- (nrepl-create-client-buffer-function #'cider-repl-create)
- (nrepl-use-this-as-repl-buffer 'new)
- (client-process-args (with-current-buffer client-buffer
- (unless (or nrepl-server-buffer nrepl-endpoint)
- (error "This is not a REPL buffer, is there a REPL active?"))
- (list (car nrepl-endpoint)
- (elt nrepl-endpoint 1)
- (when (buffer-live-p nrepl-server-buffer)
- (get-buffer-process nrepl-server-buffer)))))
- (cljs-proc (apply #'nrepl-start-client-process client-process-args))
- (cljs-buffer (process-buffer cljs-proc)))
- (with-current-buffer cljs-buffer
- ;; The new connection has now been bumped to the top, but it's still a
- ;; Clojure REPL! Additionally, some ClojureScript REPLs can actually take
- ;; a while to start (some even depend on the user opening a browser).
- ;; Meanwhile, this REPL will gladly receive requests in place of the
- ;; original Clojure REPL. Our solution is to bump the original REPL back
- ;; up the list, so it takes priority on Clojure requests.
- (cider-make-connection-default client-buffer)
- (cider-repl-set-type "cljs")
- (pcase cider-cljs-repl-types
- (`(,name ,_ ,info)
- (message "Starting a %s REPL%s" name (or info ""))))
- ;; So far we have just another Clojure REPL. It's time to convert it
- ;; to a ClojureScript REPL with a magic incantation.
- (cider-nrepl-send-request
- `("op" "eval"
- "ns" ,(cider-current-ns)
- "code" ,cljs-repl-form)
- (cider-repl-handler (current-buffer)))
- (when cider-offer-to-open-cljs-app-in-browser
- (cider--offer-to-open-app-in-browser nrepl-server-buffer))))))
-
-(defun cider--select-zombie-buffer (repl-buffers)
- "Return a zombie buffer from REPL-BUFFERS, or nil if none exists."
- (when-let* ((zombie-buffs (seq-remove #'get-buffer-process repl-buffers)))
- (when (y-or-n-p
- (format "Zombie REPL buffers exist (%s). Reuse? "
- (mapconcat #'buffer-name zombie-buffs ", ")))
- (if (= (length zombie-buffs) 1)
- (car zombie-buffs)
- (completing-read "Choose REPL buffer: "
- (mapcar #'buffer-name zombie-buffs)
- nil t)))))
-
-(defun cider-find-reusable-repl-buffer (endpoint project-directory)
- "Check whether a reusable connection buffer already exists.
-Looks for buffers where `nrepl-endpoint' matches ENDPOINT, or
-`nrepl-project-dir' matches PROJECT-DIRECTORY. If such a buffer was found,
-and has no process, return it. If the process is alive, ask the user for
-confirmation and return 'new/nil for y/n answer respectively. If other
-REPL buffers with dead process exist, ask the user if any of those should
-be reused."
- (if-let* ((repl-buffers (cider-repl-buffers))
- (exact-buff (seq-find
- (lambda (buff)
- (with-current-buffer buff
- (or (and endpoint
- (equal endpoint nrepl-endpoint))
- (and project-directory
- (equal project-directory nrepl-project-dir)))))
- repl-buffers)))
- (if (get-buffer-process exact-buff)
- (when (y-or-n-p (format "REPL buffer already exists (%s). \
-Do you really want to create a new one? "
- exact-buff))
- 'new)
- exact-buff)
- (or (cider--select-zombie-buffer repl-buffers) 'new)))
+
+;;; User Level Connectors
+
+(defvar cider-start-map
+ (let ((map (define-prefix-command 'cider-start-map)))
+ (define-key map (kbd "x") #'cider)
+ (define-key map (kbd "C-x") #'cider)
+ (define-key map (kbd "j j") #'cider-jack-in-clj)
+ (define-key map (kbd "j s") #'cider-jack-in-cljs)
+ (define-key map (kbd "j m") #'cider-jack-in-clj&cljs)
+ (define-key map (kbd "C-j j") #'cider-jack-in-clj)
+ (define-key map (kbd "C-j s") #'cider-jack-in-cljs)
+ (define-key map (kbd "C-j m") #'cider-jack-in-clj&cljs)
+ (define-key map (kbd "C-j C-j") #'cider-jack-in-clj)
+ (define-key map (kbd "C-j C-s") #'cider-jack-in-cljs)
+ (define-key map (kbd "C-j C-m") #'cider-jack-in-clj&cljs)
+ (define-key map (kbd "c j") #'cider-connect-clj)
+ (define-key map (kbd "c s") #'cider-connect-cljs)
+ (define-key map (kbd "c m") #'cider-connect-clj&cljs)
+ (define-key map (kbd "C-c j") #'cider-connect-clj)
+ (define-key map (kbd "C-c s") #'cider-connect-cljs)
+ (define-key map (kbd "C-c m") #'cider-connect-clj&cljs)
+ (define-key map (kbd "C-c C-j") #'cider-connect-clj)
+ (define-key map (kbd "C-c C-s") #'cider-connect-cljs)
+ (define-key map (kbd "C-c C-m") #'cider-connect-clj&cljs)
+ (define-key map (kbd "s j") #'cider-connect-sibling-clj)
+ (define-key map (kbd "s s") #'cider-connect-sibling-cljs)
+ (define-key map (kbd "C-s j") #'cider-connect-sibling-clj)
+ (define-key map (kbd "C-s s") #'cider-connect-sibling-cljs)
+ (define-key map (kbd "C-s C-j") #'cider-connect-sibling-clj)
+ (define-key map (kbd "C-s C-s") #'cider-connect-sibling-cljs)
+ map)
+ "CIDER jack-in and connect keymap.")
;;;###autoload
-(defun cider-jack-in (&optional prompt-project cljs-too)
+(defun cider-jack-in-clj (params)
"Start an nREPL server for the current project and connect to it.
-If PROMPT-PROJECT is t, then prompt for the project for which to
-start the server.
-If CLJS-TOO is non-nil, also start a ClojureScript REPL session with its
-own buffer."
+PARAMS is a plist optionally containing :project-dir and :jack-in-cmd.
+With the prefix argument, prompt for all these parameters."
+ (interactive "P")
+ (let ((params (thread-first params
+ (cider--update-project-dir)
+ (cider--check-existing-session)
+ (cider--update-jack-in-cmd))))
+ (nrepl-start-server-process
+ (plist-get params :project-dir)
+ (plist-get params :jack-in-cmd)
+ (lambda (server-buffer)
+ (cider-connect-sibling-clj params server-buffer)))))
+
+;;;###autoload
+(defun cider-jack-in-cljs (params)
+ "Start an nREPL server for the current project and connect to it.
+PARAMS is a plist optionally containing :project-dir, :jack-in-cmd and
+:cljs-repl-type (e.g. Node, Figwheel, etc). With the prefix argument,
+prompt for all these parameters."
+ (interactive "P")
+ (let ((cider-jack-in-dependencies (append cider-jack-in-dependencies cider-jack-in-cljs-dependencies))
+ (cider-jack-in-lein-plugins (append cider-jack-in-lein-plugins cider-jack-in-cljs-lein-plugins))
+ (cider-jack-in-nrepl-middlewares (append cider-jack-in-nrepl-middlewares cider-jack-in-cljs-nrepl-middlewares))
+ (orig-buffer (current-buffer)))
+ ;; cider--update-jack-in-cmd relies indirectly on the above dynamic vars
+ (let ((params (thread-first params
+ (cider--update-project-dir)
+ (cider--check-existing-session)
+ (cider--update-jack-in-cmd))))
+ (nrepl-start-server-process
+ (plist-get params :project-dir)
+ (plist-get params :jack-in-cmd)
+ (lambda (server-buffer)
+ (with-current-buffer orig-buffer
+ (cider-connect-sibling-cljs params server-buffer)))))))
+
+;;;###autoload
+(defun cider-jack-in-clj&cljs (&optional params soft-cljs-start)
+ "Start an nREPL server and connect with clj and cljs REPLs.
+PARAMS is a plist optionally containing :project-dir, :jack-in-cmd and
+:cljs-repl-type (e.g. Node, Figwheel, etc). With the prefix argument,
+prompt for all these parameters. When SOFT-CLJS-START is non-nil, start
+cljs REPL only when the ClojureScript dependencies are met."
+ (interactive "P")
+ (let ((cider-jack-in-dependencies (append cider-jack-in-dependencies cider-jack-in-cljs-dependencies))
+ (cider-jack-in-lein-plugins (append cider-jack-in-lein-plugins cider-jack-in-cljs-lein-plugins))
+ (cider-jack-in-nrepl-middlewares (append cider-jack-in-nrepl-middlewares cider-jack-in-cljs-nrepl-middlewares))
+ (orig-buffer (current-buffer)))
+ ;; cider--update-jack-in-cmd relies indirectly on the above dynamic vars
+ (let ((params (thread-first params
+ (cider--update-project-dir)
+ (cider--check-existing-session)
+ (cider--update-jack-in-cmd)
+ (cider--update-cljs-type)
+ ;; already asked, don't ask on sibling connect
+ (plist-put :do-prompt nil))))
+ (nrepl-start-server-process
+ (plist-get params :project-dir)
+ (plist-get params :jack-in-cmd)
+ (lambda (server-buffer)
+ (with-current-buffer orig-buffer
+ (let ((clj-repl (cider-connect-sibling-clj params server-buffer)))
+ (if soft-cljs-start
+ (when (cider--check-cljs (plist-get params :cljs-repl-type) 'no-error)
+ (cider-connect-sibling-cljs params clj-repl))
+ (cider-connect-sibling-cljs params clj-repl)))))))))
+
+;;;###autoload
+(defun cider-connect-sibling-clj (params &optional other-repl)
+ "Create a Clojure REPL with the same server as OTHER-REPL.
+PARAMS is for consistency with other connection commands and is currently
+ignored. OTHER-REPL defaults to `cider-current-repl' and in programs can
+also be a server buffer, in which case a new session with a REPL for that
+server is created."
+ (interactive "P")
+ (cider-nrepl-connect
+ (let* ((other-repl (or other-repl (cider-current-repl nil 'ensure)))
+ (other-params (cider--gather-connect-params nil other-repl))
+ (ses-name (unless (nrepl-server-p other-repl)
+ (sesman-session-name-for-object 'CIDER other-repl))))
+ (thread-first params
+ (cider--update-do-prompt)
+ (append other-params)
+ (plist-put :repl-init-function nil)
+ (plist-put :repl-type "clj")
+ (plist-put :session-name ses-name)))))
+
+;;;###autoload
+(defun cider-connect-sibling-cljs (params &optional other-repl)
+ "Create a ClojureScript REPL with the same server as OTHER-REPL.
+PARAMS is a plist optionally containing :cljs-repl-type (e.g. Node,
+Figwheel, etc). All other parameters are inferred from the OTHER-REPL.
+OTHER-REPL defaults to `cider-current-repl' but in programs can also be a
+server buffer, in which case a new session for that server is created."
+ (interactive "P")
+ (let* ((other-repl (or other-repl (cider-current-repl nil 'ensure)))
+ (other-params (cider--gather-connect-params nil other-repl))
+ (ses-name (unless (nrepl-server-p other-repl)
+ (sesman-session-name-for-object 'CIDER other-repl))))
+ (cider-nrepl-connect
+ (thread-first params
+ (cider--update-do-prompt)
+ (append other-params)
+ (cider--update-cljs-type)
+ (cider--update-cljs-init-function)
+ (plist-put :session-name ses-name)
+ (plist-put :repl-type "pending-cljs")))))
+
+;;;###autoload
+(defun cider-connect-clj (&optional params)
+ "Initialize a CLJ connection to an nREPL server.
+PARAMS is a plist optionally containing :host, :port and :project-dir. On
+prefix argument, prompt for all the parameters."
+ (interactive "P")
+ (cider-nrepl-connect
+ (thread-first params
+ (cider--update-project-dir)
+ (cider--update-host-port)
+ (cider--check-existing-session)
+ (plist-put :repl-init-function nil)
+ (plist-put :session-name nil)
+ (plist-put :repl-type "clj"))))
+
+;;;###autoload
+(defun cider-connect-cljs (&optional params)
+ "Initialize a CLJS connection to an nREPL server.
+PARAMS is a plist optionally containing :host, :port, :project-dir and
+:cljs-repl-type (e.g. Node, Figwheel, etc). On prefix, prompt for all the
+parameters regardless of their supplied or default values."
+ (interactive "P")
+ (cider-nrepl-connect
+ (thread-first params
+ (cider--update-project-dir)
+ (cider--update-host-port)
+ (cider--check-existing-session)
+ (cider--update-cljs-type)
+ (cider--update-cljs-init-function)
+ (plist-put :session-name nil)
+ (plist-put :repl-type "pending-cljs"))))
+
+;;;###autoload
+(defun cider-connect-clj&cljs (params &optional soft-cljs-start)
+ "Initialize a CLJ and CLJS connection to an nREPL server..
+PARAMS is a plist optionally containing :host, :port, :project-dir and
+:cljs-repl-type (e.g. Node, Figwheel, etc). When SOFT-CLJS-START is
+non-nil, don't start if ClojureScript requirements are not met."
(interactive "P")
- (let* ((project-type (cider-project-type))
+ (let* ((params (thread-first params
+ (cider--update-project-dir)
+ (cider--update-host-port)
+ (cider--check-existing-session)
+ (cider--update-cljs-type)))
+ (clj-repl (cider-connect-clj params)))
+ (if soft-cljs-start
+ (when (cider--check-cljs (plist-get params :cljs-repl-type) 'no-error)
+ (cider-connect-sibling-cljs params clj-repl))
+ (cider-connect-sibling-cljs params clj-repl))))
+
+(defvar cider-connection-init-commands
+ '(cider-jack-in-clj
+ cider-jack-in-cljs
+ cider-jack-in-clj&cljs
+ cider-connect-clj
+ cider-connect-cljs
+ cider-connect-clj&cljs
+ cider-connect-sibling-clj
+ cider-connect-sibling-cljs)
+ "A list of all user-level connection init commands in CIDER.")
+
+;;;###autoload
+(defun cider ()
+ "Start a connection of any type interactively."
+ (interactive)
+ (when-let* ((command (intern (completing-read "Select command: " cider-connection-init-commands))))
+ (call-interactively command)))
+
+
+;;; PARAMS updating
+
+(defun cider--update-do-prompt (params)
+ "Update :do-prompt in PARAMS."
+ (if (equal params '(4))
+ (list :do-prompt t)
+ params))
+
+(defun cider--update-project-dir (params)
+ "Update :project-dir in PARAMS."
+ (let* ((params (cider--update-do-prompt params))
+ (proj-dir (if (plist-get params :do-prompt)
+ (read-directory-name "Project: "
+ (clojure-project-dir (cider-current-dir)))
+ (plist-get params :project-dir)))
+ (orig-buffer (current-buffer)))
+ (if (or (null proj-dir)
+ (file-in-directory-p default-directory proj-dir))
+ (plist-put params :project-dir
+ (or proj-dir
+ (clojure-project-dir (cider-current-dir))))
+ ;; If proj-dir is not a parent of default-directory, transfer all local
+ ;; variables and hack dir-local variables into a temporary buffer and keep
+ ;; that buffer within `params` for the later use by other --update-
+ ;; functions. The context buffer should not be used outside of the param
+ ;; initialization pipeline. Therefore, we don't bother with making it
+ ;; unique or killing it anywhere.
+ (let ((context-buf-name " *cider-context-buffer*"))
+ (when (get-buffer context-buf-name)
+ (kill-buffer context-buf-name))
+ (with-current-buffer (get-buffer-create context-buf-name)
+ (dolist (pair (buffer-local-variables orig-buffer))
+ (pcase pair
+ (`(,name . ,value) ;ignore unbound variables
+ (ignore-errors (set (make-local-variable name) value))))
+ (setq-local buffer-file-name nil))
+ (let ((default-directory proj-dir))
+ (hack-dir-local-variables-non-file-buffer)
+ (thread-first params
+ (plist-put :project-dir proj-dir)
+ (plist-put :--context-buffer (current-buffer)))))))))
+
+(defun cider--update-cljs-type (params)
+ "Update :cljs-repl-type in PARAMS."
+ (with-current-buffer (or (plist-get params :--context-buffer)
+ (current-buffer))
+ (let ((params (cider--update-do-prompt params))
+ (inferred-type (or (plist-get params :cljs-repl-type)
+ cider-default-cljs-repl)))
+ (plist-put params :cljs-repl-type
+ (if (plist-get params :do-prompt)
+ (cider-select-cljs-repl inferred-type)
+ (or inferred-type
+ (cider-select-cljs-repl)))))))
+
+(defun cider--update-jack-in-cmd (params)
+ "Update :jack-in-cmd key in PARAMS."
+ (let* ((params (cider--update-do-prompt params))
+ (project-dir (plist-get params :project-dir))
+ (project-type (cider-project-type project-dir))
(command (cider-jack-in-command project-type))
(command-resolved (cider-jack-in-resolve-command project-type))
(command-global-opts (cider-jack-in-global-options project-type))
(command-params (cider-jack-in-params project-type)))
(if command-resolved
- (let* ((project (when prompt-project
- (read-directory-name "Project: ")))
- (project-dir (clojure-project-dir
- (or project (cider-current-dir))))
- (params (if prompt-project
- (read-string (format "nREPL server command: %s "
- command-params)
- command-params)
- command-params))
- (params (if cider-inject-dependencies-at-jack-in
- (cider-inject-jack-in-dependencies command-global-opts params project-type)
- params))
-
- (cmd (format "%s %s" command params)))
- (if (or project-dir cider-allow-jack-in-without-project)
- (progn
+ (with-current-buffer (or (plist-get params :--context-buffer)
+ (current-buffer))
+ (let* ((command-params (if (plist-get params :do-prompt)
+ (read-string (format "nREPL server command: %s " command-params)
+ command-params)
+ command-params))
+ (cmd-params (if cider-inject-dependencies-at-jack-in
+ (cider-inject-jack-in-dependencies command-global-opts command-params project-type)
+ command-params)))
+ (if (or project-dir cider-allow-jack-in-without-project)
(when (or project-dir
(eq cider-allow-jack-in-without-project t)
(and (null project-dir)
(eq cider-allow-jack-in-without-project 'warn)
(y-or-n-p "Are you sure you want to run `cider-jack-in' without a Clojure project? ")))
- (when-let* ((repl-buff (cider-find-reusable-repl-buffer nil project-dir)))
- (let ((nrepl-create-client-buffer-function #'cider-repl-create)
- (nrepl-use-this-as-repl-buffer repl-buff))
- (nrepl-start-server-process
- project-dir cmd
- (when cljs-too #'cider-create-sibling-cljs-repl))))))
- (user-error "`cider-jack-in' is not allowed without a Clojure project")))
+ (let* ((cmd (format "%s %s" command-resolved cmd-params)))
+ (plist-put params :jack-in-cmd cmd)))
+ (user-error "`cider-jack-in' is not allowed without a Clojure project"))))
(user-error "The %s executable isn't on your `exec-path'" command))))
-;;;###autoload
-(defun cider-jack-in-clojurescript (&optional prompt-project)
- "Start an nREPL server and connect to it both Clojure and ClojureScript REPLs.
-If PROMPT-PROJECT is t, then prompt for the project for which to
-start the server."
- (interactive "P")
- (cider-jack-in prompt-project 'cljs-too))
+(defun cider--update-host-port (params)
+ "Update :host and :port in PARAMS."
+ (with-current-buffer (or (plist-get params :--context-buffer)
+ (current-buffer))
+ (let* ((params (cider--update-do-prompt params))
+ (host (plist-get params :host))
+ (port (plist-get params :port))
+ (endpoint (if (plist-get params :do-prompt)
+ (cider-select-endpoint)
+ (if (and host port)
+ (cons host port)
+ (cider-select-endpoint)))))
+ (thread-first params
+ (plist-put :host (car endpoint))
+ (plist-put :port (cdr endpoint))))))
+
+(defun cider--update-cljs-init-function (params)
+ "Update PARAMS :repl-init-function for cljs connections."
+ (with-current-buffer (or (plist-get params :--context-buffer)
+ (current-buffer))
+ (let ((cljs-type (plist-get params :cljs-repl-type)))
+ (plist-put params :repl-init-function
+ (lambda ()
+ (cider--check-cljs cljs-type)
+ ;; FIXME: ideally this should be done in the state handler
+ (setq-local cider-cljs-repl-type cljs-type)
+ (cider-nrepl-send-request
+ (list "op" "eval"
+ "ns" (cider-current-ns)
+ "code" (cider-cljs-repl-form cljs-type))
+ (cider-repl-handler (current-buffer)))
+ (when (and (buffer-live-p nrepl-server-buffer)
+ cider-offer-to-open-cljs-app-in-browser)
+ (cider--offer-to-open-app-in-browser nrepl-server-buffer)))))))
+
+(defun cider--check-existing-session (params)
+ "Ask for confirmation if a session with similar PARAMS already exists.
+If no session exists or user chose to proceed, return PARAMS. If the user
+canceled the action, signal quit."
+ (let* ((proj-dir (plist-get params :project-dir))
+ (host (plist-get params :host))
+ (port (plist-get params :port))
+ (session (seq-find (lambda (ses)
+ (let ((ses-params (cider--gather-session-params ses)))
+ (and (equal proj-dir (plist-get ses-params :project-dir))
+ (or (null port)
+ (equal port (plist-get ses-params :port)))
+ (or (null host)
+ (equal host (plist-get ses-params :host))))))
+ (sesman-linked-sessions 'CIDER '(project)))))
+ (when session
+ (unless (y-or-n-p
+ (concat
+ "A session with the same parameters exists (" (car session) "). "
+ "You can connect a sibling instead. Proceed? "))
+ (let ((debug-on-quit nil))
+ (signal 'quit nil)))))
+ params)
-;;;###autoload
-(defalias 'cider-jack-in-cljs #'cider-jack-in-clojurescript)
+
+;;; Aliases
+ ;;;###autoload
+(defalias 'cider-jack-in #'cider-jack-in-clj)
+ ;;;###autoload
+(defalias 'cider-jack-in-clojure #'cider-jack-in-clj)
;;;###autoload
-(defun cider-connect (host port &optional project-dir)
- "Connect to an nREPL server identified by HOST and PORT.
-Create REPL buffer and start an nREPL client connection.
-
-When the optional param PROJECT-DIR is present, the connection
-gets associated with it."
- (interactive (cider-select-endpoint))
- (when-let* ((repl-buff (cider-find-reusable-repl-buffer `(,host ,port) nil)))
- (let* ((nrepl-create-client-buffer-function #'cider-repl-create)
- (nrepl-use-this-as-repl-buffer repl-buff)
- (conn (process-buffer (nrepl-start-client-process host port))))
- (with-current-buffer conn
- (setq cider-connection-created-with 'connect))
- (if project-dir
- (cider-assoc-project-with-connection project-dir conn)
- (let ((project-dir (clojure-project-dir)))
- (cond
- ;; associate only if we're in a project
- ((and project-dir (null cider-prompt-for-project-on-connect)) (cider-assoc-project-with-connection project-dir conn))
- ;; associate if we're in a project, prompt otherwise
- ((eq cider-prompt-for-project-on-connect 'when-needed) (cider-assoc-project-with-connection project-dir conn))
- ;; always prompt
- (t (cider-assoc-project-with-connection nil conn)))))
- conn)))
+(defalias 'cider-jack-in-clojurescript #'cider-jack-in-cljs)
;;;###autoload
-(defun cider-connect-clojurescript ()
- "Connect to a ClojureScript REPL.
-
-It just delegates pretty much everything to `cider-connect' and just sets
-the appropriate REPL type in the end."
- (interactive)
- (when-let* ((conn (call-interactively #'cider-connect)))
- (with-current-buffer conn
- (cider-repl-set-type "cljs"))))
+(defalias 'cider-connect #'cider-connect-clj)
+;;;###autoload
+(defalias 'cider-connect-clojure #'cider-connect-clj)
+;;;###autoload
+(defalias 'cider-connect-clojurescript #'cider-connect-cljs)
;;;###autoload
-(defalias 'cider-connect-cljs #'cider-connect-clojurescript)
+(defalias 'cider-connect-sibling-clojure #'cider-connect-sibling-clj)
+;;;###autoload
+(defalias 'cider-connect-sibling-clojurescript #'cider-connect-sibling-cljs)
+
+
+;;; Helpers
(defun cider-current-host ()
"Retrieve the current host."
- (if (and (stringp buffer-file-name)
- (file-remote-p buffer-file-name)
- (boundp 'tramp-current-host))
- tramp-current-host
- "localhost"))
+ (or (when (stringp buffer-file-name)
+ (file-remote-p buffer-file-name 'host))
+ "localhost"))
(defun cider-select-endpoint ()
"Interactively select the host and port to connect to."
@@ -983,14 +1284,13 @@ the appropriate REPL type in the end."
(list (list (cider-current-host)))
cider-known-endpoints
ssh-hosts
- (when (file-remote-p default-directory)
- ;; add localhost even in remote buffers
- '(("localhost"))))))
+ ;; always add localhost
+ '(("localhost")))))
(sel-host (cider--completing-read-host hosts))
(host (car sel-host))
(port (or (cadr sel-host)
(cider--completing-read-port host (cider--infer-ports host ssh-hosts)))))
- (list host port)))
+ (cons host port)))
(defun cider--ssh-hosts ()
"Retrieve all ssh host from local configuration files."
@@ -1012,6 +1312,15 @@ Return a list of the form (HOST PORT), where PORT can be nil."
;; remove the label
(if (= 3 (length host)) (cdr host) host)))
+(defun cider--tramp-file-name (vec)
+ "A simple compatibility wrapper around `make-tramp-file-name'.
+Tramp version starting 26.1 is using a `cl-defstruct' rather than vanilla VEC."
+ (if (version< emacs-version "26.1")
+ vec
+ (with-no-warnings
+ (make-tramp-file-name :method (elt vec 0)
+ :host (elt vec 2)))))
+
(defun cider--infer-ports (host ssh-hosts)
"Infer nREPL ports on HOST.
Return a list of elements of the form (directory port). SSH-HOSTS is a list
@@ -1028,8 +1337,8 @@ of remote SSH hosts."
(dir (when (file-remote-p default-directory)
(with-parsed-tramp-file-name default-directory cur
(when (string= cur-host host) default-directory)))))
- (tramp-maybe-open-connection vec)
- (with-current-buffer (tramp-get-connection-buffer vec)
+ (tramp-maybe-open-connection (cider--tramp-file-name vec))
+ (with-current-buffer (tramp-get-connection-buffer (cider--tramp-file-name vec))
(cider-locate-running-nrepl-ports dir))))))
(defun cider--completing-read-port (host ports)
@@ -1064,142 +1373,74 @@ Use `cider-ps-running-nrepls-command' and `cider-ps-running-nrepl-path-regexp-li
(setq paths (cons (match-string 1) paths)))))
(seq-uniq paths)))
-(defun cider--identify-buildtools-present ()
- "Identify build systems present by their build files."
- (let* ((default-directory (clojure-project-dir (cider-current-dir)))
- (build-files '(("lein" . "project.clj")
- ("boot" . "build.boot")
- ("clojure-cli" . "deps.edn")
- ("shadow-cljs" . "shadow-cljs.edn")
- ("gradle" . "build.gradle"))))
+(defun cider--identify-buildtools-present (&optional project-dir)
+ "Identify build systems present by their build files in PROJECT-DIR.
+PROJECT-DIR defaults to current project."
+ (let* ((default-directory (or project-dir (clojure-project-dir (cider-current-dir))))
+ (build-files '((lein . "project.clj")
+ (boot . "build.boot")
+ (clojure-cli . "deps.edn")
+ (shadow-cljs . "shadow-cljs.edn")
+ (gradle . "build.gradle"))))
(delq nil
(mapcar (lambda (candidate)
(when (file-exists-p (cdr candidate))
(car candidate)))
build-files))))
-(defun cider-project-type ()
- "Determine the type of the current project.
-When multiple project file markers are present, check for a preferred
-build tool in `cider-preferred-build-tool', otherwise prompt the user to
-choose."
- (let* ((choices (cider--identify-buildtools-present))
+(defun cider-project-type (&optional project-dir)
+ "Determine the type of the project in PROJECT-DIR.
+When multiple project file markers are present, check for a preferred build
+tool in `cider-preferred-build-tool', otherwise prompt the user to choose.
+PROJECT-DIR defaults to the current project."
+ (let* ((choices (cider--identify-buildtools-present project-dir))
(multiple-project-choices (> (length choices) 1))
- (default (car choices)))
+ ;; this needs to be a string to be used in `completing-read'
+ (default (symbol-name (car choices)))
+ ;; `cider-preferred-build-tool' used to be a string prior to CIDER
+ ;; 0.18, therefore the need for `cider-maybe-intern'
+ (preferred-build-tool (cider-maybe-intern cider-preferred-build-tool)))
(cond ((and multiple-project-choices
- (member cider-preferred-build-tool choices))
- cider-preferred-build-tool)
+ (member preferred-build-tool choices))
+ preferred-build-tool)
(multiple-project-choices
- (completing-read (format "Which command should be used (default %s): " default)
- choices nil t nil nil default))
+ (intern
+ (completing-read
+ (format "Which command should be used (default %s): " default)
+ choices nil t nil nil default)))
(choices
(car choices))
- (t cider-default-repl-command))))
+ ;; TODO: Move this fallback outside the project-type check
+ ;; if we're outside a project we fallback to whatever tool
+ ;; is specified in `cider-jack-in-default' (normally clojure-cli)
+ ;; `cider-jack-in-default' used to be a string prior to CIDER
+ ;; 0.18, therefore the need for `cider-maybe-intern'
+ (t (cider-maybe-intern cider-jack-in-default)))))
;; TODO: Implement a check for command presence over tramp
(defun cider--resolve-command (command)
- "Find COMMAND on `exec-path' if possible, or return nil.
-
-In case `default-directory' is non-local we assume the command is available."
+ "Find COMMAND in exec path (see variable `exec-path').
+Return nil if not found. In case `default-directory' is non-local we
+assume the command is available."
(when-let* ((command (or (and (file-remote-p default-directory) command)
(executable-find command)
(executable-find (concat command ".bat")))))
(shell-quote-argument command)))
-
-;;; Check that the connection is working well
-;; TODO: This is nrepl specific. It should eventually go into some cider-nrepl-client
-;; file.
-(defun cider--check-required-nrepl-version ()
- "Check whether we're using a compatible nREPL version."
- (if-let* ((nrepl-version (cider--nrepl-version)))
- (when (version< nrepl-version cider-required-nrepl-version)
- (cider-repl-manual-warning "troubleshooting/#warning-saying-you-have-to-use-nrepl-0212"
- "CIDER requires nREPL %s (or newer) to work properly"
- cider-required-nrepl-version))
- (cider-repl-manual-warning "troubleshooting/#warning-saying-you-have-to-use-nrepl-0212"
- "Can't determine nREPL's version.\nPlease, update nREPL to %s."
- cider-required-nrepl-version)))
-
-(defun cider--check-clojure-version-supported ()
- "Ensure that we are meeting the minimum supported version of Clojure."
- (if-let* ((clojure-version (cider--clojure-version)))
- (when (version< clojure-version cider-minimum-clojure-version)
- (cider-repl-manual-warning "installation/#prerequisites"
- "Clojure version (%s) is not supported (minimum %s). CIDER will not work."
- clojure-version cider-minimum-clojure-version))
- (cider-repl-manual-warning "installation/#prerequisites"
- "Can't determine Clojure's version. CIDER requires Clojure %s (or newer)."
- cider-minimum-clojure-version)))
-
-(defun cider--check-middleware-compatibility ()
- "CIDER frontend/backend compatibility check.
-Retrieve the underlying connection's CIDER-nREPL version and checks if the
-middleware used is compatible with CIDER. If not, will display a warning
-message in the REPL area."
- (let* ((version-dict (nrepl-aux-info "cider-version" (cider-current-connection)))
- (middleware-version (nrepl-dict-get version-dict "version-string" "not installed")))
- (unless (equal cider-version middleware-version)
- (cider-repl-manual-warning "troubleshooting/#cider-complains-of-the-cider-nrepl-version"
- "CIDER's version (%s) does not match cider-nrepl's version (%s). Things will break!"
- cider-version middleware-version))))
-
-(defcustom cider-redirect-server-output-to-repl t
- "Controls whether nREPL server output would be redirected to the REPL.
-When non-nil the output would end up in both the nrepl-server buffer (when
-available) and the matching REPL buffer."
- :type 'boolean
- :group 'cider
- :safe #'booleanp
- :package-version '(cider . "0.17.0"))
-
-(defun cider--subscribe-repl-to-server-out ()
- "Subscribe to the nREPL server's *out*."
- (cider-nrepl-send-request '("op" "out-subscribe")
- (cider-interactive-eval-handler (current-buffer))))
-
-(defun cider--connected-handler ()
- "Handle CIDER initialization after nREPL connection has been established.
-This function is appended to `nrepl-connected-hook' in the client process
-buffer."
- ;; `nrepl-connected-hook' is run in the connection buffer
-
- ;; `cider-enlighten-mode' changes eval to include the debugger, so we inhibit
- ;; it here as the debugger isn't necessarily initialized yet
- (let ((cider-enlighten-mode nil))
- (cider-make-connection-default (current-buffer))
- (cider-repl-init (current-buffer))
- (cider--check-required-nrepl-version)
- (cider--check-clojure-version-supported)
- (cider--check-middleware-compatibility)
- (when cider-redirect-server-output-to-repl
- (cider--subscribe-repl-to-server-out))
- (when cider-auto-mode
- (cider-enable-on-existing-clojure-buffers))
- ;; Middleware on cider-nrepl's side is deferred until first usage, but
- ;; loading middleware concurrently can lead to occasional "require" issues
- ;; (likely a Clojure bug). Thus, we load the heavy debug middleware towards
- ;; the end, allowing for the faster "server-out" middleware to load
- ;; first.
- (cider--debug-init-connection)
- (run-hooks 'cider-connected-hook)))
-
-(defun cider--disconnected-handler ()
- "Cleanup after nREPL connection has been lost or closed.
-This function is appended to `nrepl-disconnected-hook' in the client
-process buffer."
- ;; `nrepl-connected-hook' is run in the connection buffer
- (cider-possibly-disable-on-existing-clojure-buffers)
- (run-hooks 'cider-disconnected-hook))
-
;;;###autoload
(eval-after-load 'clojure-mode
'(progn
- (define-key clojure-mode-map (kbd "C-c M-j") #'cider-jack-in)
- (define-key clojure-mode-map (kbd "C-c M-J") #'cider-jack-in-clojurescript)
- (define-key clojure-mode-map (kbd "C-c M-c") #'cider-connect)
- (define-key clojure-mode-map (kbd "C-c M-C") #'cider-connect-clojurescript)))
+ (define-key clojure-mode-map (kbd "C-c M-x") #'cider)
+ (define-key clojure-mode-map (kbd "C-c M-j") #'cider-jack-in-clj)
+ (define-key clojure-mode-map (kbd "C-c M-J") #'cider-jack-in-cljs)
+ (define-key clojure-mode-map (kbd "C-c M-c") #'cider-connect-clj)
+ (define-key clojure-mode-map (kbd "C-c M-C") #'cider-connect-cljs)
+ (define-key clojure-mode-map (kbd "C-c C-x") 'cider-start-map)
+ (define-key clojure-mode-map (kbd "C-c C-s") 'sesman-map)
+ (require 'sesman)
+ (sesman-install-menu clojure-mode-map)
+ (add-hook 'clojure-mode-hook (lambda () (setq-local sesman-system 'CIDER)))))
(provide 'cider)
diff --git a/doc/about/logo.md b/doc/about/logo.md
index a4232760..9ccc726f 100644
--- a/doc/about/logo.md
+++ b/doc/about/logo.md
@@ -1,4 +1,4 @@
-CIDER's logo was created by [@ndr-qef](https://github.com/ndr-qef). You can find
+CIDER's logo was created by [@tapeinosyne](https://github.com/tapeinosyne). You can find
the logo in various formats
[here](https://github.com/clojure-emacs/cider/tree/master/logo).
diff --git a/doc/about/team.md b/doc/about/team.md
index 7592a0a9..a26733b1 100644
--- a/doc/about/team.md
+++ b/doc/about/team.md
@@ -5,9 +5,7 @@ group of long-term contributors manage releases, evaluate pull-requests, and
does a lot of the groundwork on major new features.
* [Bozhidar Batsov](https://github.com/bbatsov) (author & head maintainer)
-* [Artur Malabarba](https://github.com/malabarba)
-* [Michael Griffiths](https://github.com/cichli)
-* [Jeff Valk](https://github.com/jeffvalk)
+* [Vitalie Spinu](https://github.com/vspinu)
* [Lars Andersen](https://github.com/expez)
## CIDER Alumni
@@ -19,3 +17,6 @@ core team members. Lovingly known as The Alumni:
* [Phil Hagelberg](https://github.com/technomancy)
* [Hugo Duncan](https://github.com/hugoduncan)
* [Steve Purcell](https://github.com/purcell)
+* [Artur Malabarba](https://github.com/malabarba)
+* [Michael Griffiths](https://github.com/cichli)
+* [Jeff Valk](https://github.com/jeffvalk)
diff --git a/doc/cider-refcard.tex b/doc/cider-refcard.tex
index 6d877bd5..6885b438 100644
--- a/doc/cider-refcard.tex
+++ b/doc/cider-refcard.tex
@@ -125,11 +125,10 @@
\item[C-c M-p] cider-insert-last-sexp-in-repl
\item[C-c C-z] cider-switch-to-repl-buffer
\item[C-c M-o] cider-find-and-clear-repl-buffer
- \item[C-c M-d] cider-display-connection-info
- \item[C-c M-r] cider-rotate-default-connection
- \item[C-c M-n] cider-repl-set-ns
+ \item[C-c M-d] cider-describe-connection
+ \item[C-c M-n M-n] cider-repl-set-ns
\item[C-c C-b] cider-interrupt
- \item[C-c C-x] cider-refresh
+ \item[C-c M-n M-r] cider-ns-refresh
\item[C-c C-q] cider-quit
\end{keylist}
\end{multicols}
diff --git a/doc/clojurescript.md b/doc/clojurescript.md
index 17393e09..ade0222a 100644
--- a/doc/clojurescript.md
+++ b/doc/clojurescript.md
@@ -5,18 +5,31 @@ the functionality available with Clojure exists for ClojureScript (at least
not yet). To give you a concrete example - things like running tests and
the debugger are currently Clojure-only features.
+
+### Piggieback
+
ClojureScript support relies on the [piggieback][] nREPL middleware
being present in your REPL session. There's one exception to this,
though - [shadow-cljs][]. It has its own nREPL middleware and doesn't rely
on piggieback at all.
+If `cider-inject-dependencies-at-jack-in` is enabled (which is the default) then
+piggieback will be automatically added and configured for your project when
+doing `cider-jack-in-cljs`.
+
+If this configuration option is disabled or you're going to connect to
+an already running nREPL server using `cider-connect-cljs` - continue
+reading ahead.
+
+#### Manual Piggieback Setup
+
To setup piggieback add the following dependencies to your project
(`project.clj` in Leiningen based project or `build.boot` in Boot
project):
```clojure
;; use whatever are the most recent versions here
-[cider/piggieback "0.3.2"]
+[cider/piggieback "0.3.9"]
[org.clojure/clojure "1.9.0"]
```
@@ -35,19 +48,28 @@ or in `build.boot`:
repl {:middleware '[cider.piggieback/wrap-cljs-repl]})
```
+### Starting a ClojureScript REPL
+
There are many ClojureScript REPLs out there and it's often hard to wrap your
head around them and the differences between them. You'd do well to read [this
awesome article](https://lambdaisland.com/guides/clojure-repls/clojurescript-repls)
before proceeding with the rest of the instructions listed here.
Open a file in your project and issue <kbd>M-x</kbd>
-`cider-jack-in-clojurescript` <kbd>RET</kbd>. This will start up the nREPL
-server, and then create two REPL buffers for you, one in Clojure and one in
-ClojureScript. All usual CIDER commands will be automatically directed to the
-appropriate REPL, depending on whether you're visiting a `.clj` or a `.cljs`
-file.
+`cider-jack-in-cljs` <kbd>RET</kbd>. This will start up the nREPL
+server, and then create a ClojureScript REPL buffer for you, one.
+
+!!! Note
+
+ Prior to CIDER 0.18, `cider-jack-in-cljs` would create both a Clojure and
+ a ClojureScript REPL. In CIDER 0.18+ if you want to create both REPLs
+ you'll have to use `cider-jack-in-clj&cljs` instead.
+
+When you have a combination of Clojure and ClojureScript REPLs all
+usual CIDER commands will be automatically directed to the appropriate
+REPL, depending on whether you're visiting a `.clj` or a `.cljs` file.
-`cider-jack-in-clojurescript` will prompt you about the type of
+`cider-jack-in-cljs` will prompt you about the type of
ClojureScript to start. Keep in mind that some of the REPLs will
require some additional setup, before you can make use of them (e.g. you'll
need to have Node.js installed to be able to start a node REPL).
@@ -67,8 +89,25 @@ All supported ClojureScript REPLs are stored in
(cider-register-cljs-repl-type 'super-cljs "(do (...))" optional-requirements-function)
```
+You can also modify the known ClojureScript REPLs on a per-project basis using
+`.dir-locals.el`:
+
+```el
+;; replace the list of REPLs types and set some default
+((nil
+ (cider-default-cljs-repl . super-cljs)
+ (cider-cljs-repl-types . ((super-cljs "(do (foo) (bar))")))))
+```
+
+```el
+;; modify the list of known REPLs and set some default
+((nil
+ (eval . (cider-register-cljs-repl-type 'super-cljs "(do (foo) (bar))"))
+ (cider-default-cljs-repl . super-cljs)))
+```
+
You can also create a ClojureScript REPL with the command
-`cider-create-sibling-cljs-repl` in cases where you already have a
+`cider-jack-in-sibling-clojurescript` in cases where you already have a
Clojure REPL running.
Continue reading for the additional setup needed for the various ClojureScript
@@ -80,7 +119,7 @@ Using Weasel, you can also have a browser-connected REPL.
1. Add `[weasel "0.7.0"]` to your project's `:dependencies`.
-2. Issue <kbd>M-x</kbd> `cider-jack-in-clojurescript` <kbd>RET</kbd> and choose
+2. Issue <kbd>M-x</kbd> `cider-jack-in-cljs` <kbd>RET</kbd> and choose
the `Weasel` option when prompted about the ClojureScript REPL type you want
to use.
@@ -92,7 +131,7 @@ Using Weasel, you can also have a browser-connected REPL.
(repl/connect "ws://localhost:9001")
```
-4. Open a file in your project and issue <kbd>M-x</kbd> `cider-jack-in-clojurescript`.
+4. Open a file in your project and issue <kbd>M-x</kbd> `cider-jack-in-cljs`.
Provided that a Piggieback-enabled ClojureScript environment is active in your
REPL session, code loading and evaluation will work seamlessly regardless of the
@@ -109,7 +148,7 @@ documentation lookup, the namespace browser, and macroexpansion).
[adzerk/boot-cljs-repl "X.Y.Z" :scope "test"]
[pandeiro/boot-http "X.Y.Z" :scope "test"]
[weasel "0.7.0" :scope "test"]
-[cider/piggieback "0.3.2" :scope "test"]
+[cider/piggieback "0.3.9" :scope "test"] ; not needed for cider-jack-in-cljs
```
and this at the end of `build.boot`:
@@ -130,13 +169,18 @@ and this at the end of `build.boot`:
2. Issue <kbd>M-x</kbd> `customize-variable` <kbd>RET</kbd> `cider-boot-parameters`
and insert `dev`.
-3. Open a file in your project and issue <kbd>M-x</kbd> `cider-jack-in-clojurescript`.
+3. Open a file in your project and issue <kbd>M-x</kbd> `cider-jack-in-cljs`.
5. Connect to the running server with your browser. The address is printed on the terminal, but it's probably `http://localhost:3000`.
For more information visit [boot-cljs-repl](https://github.com/adzerk-oss/boot-cljs-repl).
-### Using the Figwheel REPL (Leiningen-only)
+### Using Figwheel (Leiningen-only)
+
+!!! Warning
+
+ This has been deprecated in favour of using `figwheel-main`. Check out
+ the instructions in the next section.
You can also use [Figwheel](https://github.com/bhauman/lein-figwheel) with CIDER.
@@ -145,35 +189,59 @@ You can also use [Figwheel](https://github.com/bhauman/lein-figwheel) with CIDER
2. Add these to your dev `:dependencies`:
-```clojure
-[cider/piggieback "0.3.2"]
-[figwheel-sidecar "0.5.16"] ; use here whatever the current version of figwheel is
-```
+ ```clojure
+ [cider/piggieback "0.3.9"] ; not needed for cider-jack-in-cljs
+ [figwheel-sidecar "0.5.16"] ; use here whatever the current version of figwheel is
+ ```
Keep in mind that figwheel 0.5.16 is the first to support piggieback
0.3. If you're using an older figwheel you should stick to piggieback
0.2.2 (which uses the old `com.cemerick/piggieback` package coordinates).
-3. Add this to your dev `:repl-options`:
+3. Add this to your dev `:repl-options` (not needed for `cider-jack-in-cljs`):
-```clojure
-:nrepl-middleware [cider.piggieback/wrap-cljs-repl]
-```
+ ```clojure
+ :nrepl-middleware [cider.piggieback/wrap-cljs-repl]
+ ```
-4. Start the REPL with `cider-jack-in-clojurescript` (<kbd>C-c M-J</kbd>)
+4. Start the REPL with `cider-jack-in-cljs` (<kbd>C-c C-x (C-)j (C-)s</kbd>). Select
+`figwheel` when prompted about the ClojureScript REPL type.
5. Open a browser to the Figwheel URL so that it can connect to your application.
-You now have two nREPL connections, one for Clojure and one for ClojureScript.
-CIDER will determine which to use based on the type of file you're editing.
-
You should also check out
[Figwheel's wiki](https://github.com/bhauman/lein-figwheel/wiki/Using-the-Figwheel-REPL-within-NRepl).
+### Using Figwheel-main
+
+!!! Note
+
+ The instructions here assume you're using Leiningen. Adapting them to your
+ favourite build tool is up to you.
+
+You can also use [Figwheel-main](https://github.com/bhauman/figwheel-main) with CIDER.
+
+1. Add this to your dev `:dependencies`:
+
+ ```clojure
+ [cider/piggieback "0.3.9"] ; not needed for cider-jack-in-cljs
+ ```
+
+2. Add this to your dev `:repl-options` (not needed for `cider-jack-in-cljs`):
+
+ ```clojure
+ :nrepl-middleware [cider.piggieback/wrap-cljs-repl]
+ ```
+
+3. Start the REPL with `cider-jack-in-cljs` (<kbd>C-c C-x (C-)j (C-)s</kbd>). Select
+`figwheel-main` when prompted about the ClojureScript REPL type.
+
+4. Select the Figwheel build to run when prompted for it. (e.g. `:dev`).
+
### Using shadow-cljs
Provided you've configured your project correctly you can simply use
-`cider-jack-in-clojurescript` to use `shadow-cljs`.
+`cider-jack-in-cljs` to use `shadow-cljs`.
This will automatically start the shadow-cljs server and connect to it. You'll also
be prompted for the build to use.
@@ -186,9 +254,13 @@ npx shadow-cljs server
And connect to it with `cider-connect`.
+Lastly, if you already have a running server watching a build, for instance you
+have already run `npx shadow-cljs watch :dev`, you can use the `shadow-select`
+CLJS REPL and specify `:dev` when prompted.
+
[leiningen]: http://leiningen.org/
[boot]: http://boot-clj.com/
-[piggieback]: https://github.com/clojure-emacs/piggieback
+[piggieback]: https://github.com/nrepl/piggieback
[shadow-cljs]: https://github.com/thheller/shadow-cljs
## Working with `.cljc` files
diff --git a/doc/code_completion.md b/doc/code_completion.md
index a42c08ef..243a8ac8 100644
--- a/doc/code_completion.md
+++ b/doc/code_completion.md
@@ -1,10 +1,12 @@
CIDER provides intelligent code completion for both source buffers (powered by
`cider-mode`) and REPL buffers.
-Internally CIDER leverages
-[compliment](https://github.com/alexander-yakushev/compliment) for Clojure and
-[cljs-tooling](https://github.com/clojure-emacs/cljs-tooling) for ClojureScript.
-Improvements to the two libraries automatically translate to improvements in CIDER.
+!!! Note
+
+ Internally CIDER leverages
+ [compliment](https://github.com/alexander-yakushev/compliment) for Clojure and
+ [cljs-tooling](https://github.com/clojure-emacs/cljs-tooling) for ClojureScript.
+ Improvements to the two libraries automatically translate to improvements in CIDER.
## Standard completion
@@ -80,11 +82,13 @@ used to format the annotation can be configured by
`cider-completion-annotations-alist` and the context in which their namespace is
included is configured by `cider-completion-annotations-include-ns.`
-Completion annotations can be disabled by setting
-`cider-annotate-completion-candidates` to `nil`.
-
![Completion Annotations](images/completion-annotations.png)
+!!! Tip
+
+ Completion annotations can be disabled by setting
+ `cider-annotate-completion-candidates` to `nil`.
+
### Updating stale classes and methods cache
Sometimes, the completion fails to recognize new classes that came with
diff --git a/doc/configuration.md b/doc/configuration.md
index 4cad2a1d..62ffcacc 100644
--- a/doc/configuration.md
+++ b/doc/configuration.md
@@ -153,7 +153,7 @@ You can hide all nREPL middleware details from `cider-browse-ns*` and `cider-apr
commands by customizing the variable `cider-filter-regexps`. It should be a list of
regexps matching the pattern of namespaces you want to filter out.
-Its default value is `'("^cider.nrepl" "^refactor-nrepl" "^clojure.tools.nrepl")`,
+Its default value is `'("^cider.nrepl" "^refactor-nrepl" "^clojure.tools.nrepl" "^nrepl")`,
the most commonly used middleware collections/packages.
An important thing to note is that this list of regexps is passed on to the middleware
diff --git a/doc/debugging.md b/doc/debugging.md
index 964ea419..11c83109 100644
--- a/doc/debugging.md
+++ b/doc/debugging.md
@@ -3,6 +3,12 @@ CIDER ships with a powerful interactive Clojure debugger inspired by Emacs's own
![CIDER Debugger](images/cider_debugger.gif)
+!!! Warning "ClojureScript Support"
+
+ The debugger currently **does not** support ClojureScript. See
+ [this issue](https://github.com/clojure-emacs/cider/issues/1416)
+ for more details.
+
## Debugging
The debugger can be invoked in several ways, the simplest one is to type
@@ -140,9 +146,11 @@ C-M-x` or `C-u C-u C-c C-c`.
## Internal Details
-*This section explains a bit of the inner workings of the debugger. It is
-intended mostly to help those who are interested in contributing, and doesn't
-teach anything about the debugger's usage.*
+!!! Note
+
+ This section explains a bit of the inner workings of the debugger. It is
+ intended mostly to help those who are interested in contributing, and doesn't
+ teach anything about the debugger's usage.
The CIDER debugger works in several steps:
diff --git a/doc/faq.md b/doc/faq.md
index 04f1f303..ac130e87 100644
--- a/doc/faq.md
+++ b/doc/faq.md
@@ -8,8 +8,12 @@ Yes.
## What are CIDER's installation prerequisites?
-CIDER officially supports Emacs 24.4+, Java 7+ and Clojure(Script) 1.7+.
-CIDER 0.10 was the final release which supported Java 6 and Clojure 1.5 and 1.6.
+CIDER officially supports Emacs 25.1+, Java 8+ and Clojure(Script) 1.8+.
+
+
+!!! note
+
+ CIDER 0.17 was the final release which supported Java 7 and Clojure(Script) 1.7.
## What's the relationship between CIDER and nrepl.el?
@@ -34,7 +38,7 @@ command to start a REPL process for you.
## Isn't IntelliJ's Cursive the best Clojure IDE?
-Cursive is pretty awesome. Depending on your programming preferences (using an IDE vs
+Cursive is pretty awesome! Depending on your programming preferences (using an IDE vs
building a custom editing experience tailored to your needs) it might be a better
option for you than CIDER.
@@ -43,6 +47,12 @@ option for you than CIDER.
The codenames are usually some of the favourite places of CIDER's head
maintainer (Bozhidar).
+!!! Tip
+
+ Bozhidar really loves Spain and the West Coast of the US, so he tends
+ to name really special releases with codenames related to them (e.g.
+ Seattle, California, Andalucia, etc).
+
## Is using CIDER a good idea if I'm new to both Emacs and Clojure?
There's nothing particularly complex in CIDER itself, but getting to
@@ -74,10 +84,21 @@ when the most important refactoring functionality from our sibling
project [clj-refactor](https://github.com/clojure-emacs/clj-refactor.el) lands
into CIDER.
+!!! Note
+
+ While there's no exact roadmap for the 1.0 release, we do have *a*
+ [roadmap](https://github.com/clojure-emacs/cider/blob/master/ROADMAP.md).
+
## Is it true that stable CIDER releases often happen around major Clojure conferences?
Yep. We want to give people a reason to talk about CIDER at such events. :-)
+!!! Tip
+
+ Inviting Bozhidar or any other core CIDER developers to present at Clojure
+ conferences is likely going to results in more CIDER releases,
+ so you should totally do this!
+
## How unstable is the MELPA build of CIDER?
It's pretty stable. Serious regression are introduced rather rarely and are
@@ -96,11 +117,16 @@ for the socket REPL.
That's extremely unlikely. Even if we eventually add support for the new socket REPL,
we'll continue supporting nREPL as well.
+!!! Note
+
+ Recently nREPL was migrated out of `clojure-contrib` and its development
+ has once again picked up. Check out nREPL's new home [here](https://github.com/nrepl/nREPL).
+
## Is CIDER's nREPL middleware Emacs specific?
Not at all. The functionality in `cider-nrepl` is pretty editor-agnostic and is
utilized by various editor plugins. Some prominent examples would be
-`vim-fireplace` and Eclipse's CCW.
+`vim-fireplace` and Visual Studio Code's `calva`.
## How can I see all the configuration options available in CIDER?
@@ -112,7 +138,7 @@ Sure! See [additional packages](additional_packages.md) for details.
## Where can I get help regarding CIDER?
-See the [Support](support.md) section of the manual.
+See the [Support](about/support.md) section of the manual.
## What should I do if I run into some issues with CIDER?
diff --git a/doc/hacking_on_cider.md b/doc/hacking_on_cider.md
index 6e23fef4..f260f519 100644
--- a/doc/hacking_on_cider.md
+++ b/doc/hacking_on_cider.md
@@ -12,17 +12,28 @@ simply clone the code from GitHub and use it. In general - avoid editing the
code of an installed package.
Alternatively you can simply load CIDER in your Emacs straight from its source
-repo:
+repo (you'll have to manually install all the packages CIDER depends on
+in advance).
+
+Additionally you will have to generate and require the
+[autoloads](https://www.gnu.org/software/emacs/manual/html_node/elisp/Autoload.html),
+otherwise you'll keep getting errors about missing commands. That's done
+automatically when installing via `package.el` but you'll have to do it
+manually in this case:
+
+```shell
+make autoloads # generates cider-autoloads.el
+```
+
+Then:
```el
;; load CIDER from its source code
(add-to-list 'load-path "~/projects/cider")
(require 'cider)
+(require 'cider-autoloads)
```
-Just keep in mind that you'll have to manually install all the packages CIDER
-depends on in advance.
-
### Changing the code
It's perfectly fine to load CIDER from `package.el` and then to start making
@@ -127,14 +138,14 @@ And then switch to Emacs 26.1 and test again:
```
(emacs-25.3-travis) ~/cider$ evm use Emacs-26-pretest-travis
-(emacs-26-pretest-travis) ~/cider$ cask install
-(emacs-26-pretest-travis) ~/cider$ make test
+(emacs-26.1-travis) ~/cider$ cask install
+(emacs-26.1-travis) ~/cider$ make test
```
You can test byte compilation too
```
-(emacs-26-pretest-travis) ~/cider$ make test-bytecomp
+(emacs-26.1-travis) ~/cider$ make test-bytecomp
```
When you are done working in docker, just `exit` the bash prompt, and the docker
diff --git a/doc/index.md b/doc/index.md
index 752aceda..65df2445 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -8,8 +8,14 @@ features are centered around `cider-mode`, an Emacs minor-mode that complements
compilation, debugging, definition and documentation lookup, running tests and
so on.
-CIDER is the successor to the now deprecated combination of using [SLIME][] +
-[swank-clojure][] for Clojure development.
+!!! Note
+
+ CIDER was originally inspired by the powerful Common Lisp interactive
+ development environment [SLIME][]. In the beginning we started by
+ adapting SLIME's core functionality to Clojure, but over the course of time
+ CIDER became pretty different from SLIME in many areas. Check out
+ [this presentation](https://www.youtube.com/watch?v=4X-1fJm25Ww&list=PLZdCLR02grLoc322bYirANEso3mmzvCiI&index=6)
+ if you'd like to know more about CIDER's early history.
**Please consider
[supporting financially its ongoing development](about/contributing.md#funding).**
@@ -28,10 +34,8 @@ using CIDER - you're constantly interacting with it and changing it.
You can find more details about the typical CIDER workflow in the
[Interactive Programming](interactive_programming.md) section. While we're a bit
short on video tutorials, you can check out this
-[tutorial about SLIME](https://www.youtube.com/watch?v=_B_4vhsmRRI) to get a
-feel about what do we mean by an "Interactive Development Environment". There
-are plenty of differences between CIDER and SLIME, but the core ideas are pretty
-much the same (and SLIME served as the principle inspiration for CIDER).
+[into to CIDER](https://www.youtube.com/watch?v=aYA4AAjLfT0) to get a
+feel about what do we mean by an "Interactive Development Environment".
CIDER's built on top of [nREPL][], the Clojure networked REPL server.
@@ -44,9 +48,14 @@ CIDER's basic architecture looks something like this:
Clojure code gets executed by an nREPL server. CIDER sends requests to the
server and processes its responses. The server's functionality is augmented by
additional nREPL middleware, designed specifically to address the needs of an
-interactive development environment like CIDER. Much of the middleware we
-developed for CIDER is editor-agnostic and is being used by other Clojure
-development environments as well (e.g. [vim-fireplace][] & [CCW][]).
+interactive development environment like CIDER.
+
+
+!!! Note
+
+ Much of the middleware we originally
+ developed for CIDER is editor-agnostic and is being used by other Clojure
+ development environments as well (e.g. [vim-fireplace][] & [calva][]).
CIDER packs plenty of features. Here are some of them (in no particular order):
@@ -75,21 +84,20 @@ CIDER packs plenty of features. Here are some of them (in no particular order):
* [Scratchpad](miscellaneous_features.md#using-a-scratchpad)
* [Minibuffer code evaluation](miscellaneous_features.md#evaluating-clojure-code-in-the-minibuffer)
* Integration with [company-mode][]
-* [Support for working with multiple simultaneous nREPL connections](managing_connections.md)
+* [Support for working with multiple REPLs](managing_connections.md)
![CIDER Screenshot](images/cider-overview.png)
-[nREPL]: https://github.com/clojure/tools.nrepl
+[nREPL]: https://github.com/nrepl/nrepl
[SLIME]: https://github.com/slime/slime
-[swank-clojure]: https://github.com/technomancy/swank-clojure
[Sly]: https://github.com/capitaomorte/sly
[Geiser]: https://github.com/jaor/geiser
[company-mode]: http://company-mode.github.io/
[leiningen]: http://leiningen.org/
[boot]: http://boot-clj.com/
-[piggieback]: https://github.com/clojure-emacs/piggieback
+[piggieback]: https://github.com/nrepl/piggieback
[vim-fireplace]: https://github.com/tpope/vim-fireplace
-[CCW]: https://github.com/laurentpetit/ccw
+[calva]: https://github.com/BetterThanTomorrow/calva
[cider-nrepl]: https://github.com/clojure-emacs/cider-nrepl
[clojure-mode]: https://github.com/clojure-emacs/clojure-mode
[inf-clojure]: https://github.com/clojure-emacs/inf-clojure
diff --git a/doc/installation.md b/doc/installation.md
index 79712459..69338553 100644
--- a/doc/installation.md
+++ b/doc/installation.md
@@ -1,9 +1,10 @@
## CIDER's nREPL middleware
-Much of CIDER's functionality depends on the presence of CIDER's
-own [nREPL middleware](https://github.com/clojure-emacs/cider-nrepl). Starting
-with version 0.11, When `cider-jack-in` (<kbd>C-c M-j</kbd>) is used, CIDER
-takes care of injecting it and its other dependencies.
+Much of CIDER's functionality depends on the presence of CIDER's own
+[nREPL
+middleware](https://github.com/clojure-emacs/cider-nrepl). Starting
+with version 0.11, When `cider-jack-in` (<kbd>C-c C-x (C-)j (C-)j</kbd>) is used,
+CIDER takes care of injecting it and its other dependencies.
**`profiles.clj` or `profile.boot` don't need to be modified anymore for the above use case!**
@@ -34,12 +35,14 @@ Use the convenient plugin for defaults, either in your project's
A minimal `profiles.clj` for CIDER would be:
```clojure
-{:repl {:plugins [[cider/cider-nrepl "0.17.0"]]}}
+{:repl {:plugins [[cider/cider-nrepl "0.18.0"]]}}
```
-**Be careful not to place this in the `:user` profile, as this way CIDER's
-middleware will always get loaded, causing `lein` to start slower. You really
-need it just for `lein repl` and this is what the `:repl` profile is for.**
+!!! warning
+
+ Be careful not to place this in the `:user` profile, as this way CIDER's
+ middleware will always get loaded, causing `lein` to start slower. You really
+ need it just for `lein repl` and this is what the `:repl` profile is for.
#### Using Boot
@@ -50,7 +53,7 @@ all of their projects using a `~/.boot/profile.boot` file like so:
(require 'boot.repl)
(swap! boot.repl/*default-dependencies*
- concat '[[cider/cider-nrepl "0.17.0"]])
+ concat '[[cider/cider-nrepl "0.18.0"]])
(swap! boot.repl/*default-middleware*
conj 'cider.nrepl/cider-middleware)
@@ -65,7 +68,7 @@ server with CIDER's own nREPL handler.
```clojure
(ns my-app
- (:require [clojure.tools.nrepl.server :as nrepl-server]
+ (:require [nrepl.server :as nrepl-server]
[cider.nrepl :refer (cider-nrepl-handler)]))
(defn -main
@@ -75,8 +78,10 @@ server with CIDER's own nREPL handler.
It goes without saying that your project should depend on `cider-nrepl`.
-***
+!!! note
-`x.y.z` should match the version of CIDER you're currently using (say `0.17.0`).
-For snapshot releases of CIDER you should use the snapshot of the plugin as well
-(say `0.17.0-SNAPSHOT`).
+ `x.y.z` should be compatible with the version of CIDER you're currently using.
+ The required version can be checked in `cider-required-middleware-version`.
+ Prior to CIDER 0.18, CIDER and cider-nrepl were always released together
+ and their versions had to match for things to work, but as the prominence
+ of cider-nrepl grew and many other tools started using, this was changed.
diff --git a/doc/interactive_programming.md b/doc/interactive_programming.md
index ba5ce55c..92906d01 100644
--- a/doc/interactive_programming.md
+++ b/doc/interactive_programming.md
@@ -22,28 +22,27 @@ Here's a list of `cider-mode`'s keybindings:
`cider-eval-last-sexp-and-replace` |<kbd>C-c C-v w</kbd> | Evaluate the form preceding point and replace it with its result.
`cider-eval-last-sexp-to-repl` |<kbd>C-c M-e</kbd> | Evaluate the form preceding point and output it result to the REPL buffer. If invoked with a prefix argument, takes you to the REPL buffer after being invoked.
`cider-insert-last-sexp-in-repl` |<kbd>C-c M-p</kbd> | Load the form preceding point in the REPL buffer.
-`cider-pprint-eval-last-sexp` |<kbd>C-c C-p</kbd> | Evaluate the form preceding point and pretty-print the result in a popup buffer. If invoked with a prefix argument, insert the result into the current buffer as a comment.
-`cider-pprint-eval-defun-at-point` |<kbd>C-c C-f</kbd> | Evaluate the top level form under point and pretty-print the result in a popup buffer. If invoked with a prefix argument, insert the result into the current buffer as a comment.
+`cider-pprint-eval-last-sexp` |<kbd>C-c C-v C-f e</kbd> | Evaluate the form preceding point and pretty-print the result in a popup buffer. If invoked with a prefix argument, insert the result into the current buffer as a comment.
+`cider-pprint-eval-defun-at-point` |<kbd>C-c C-v C-f d</kbd> | Evaluate the top level form under point and pretty-print the result in a popup buffer. If invoked with a prefix argument, insert the result into the current buffer as a comment.
`cider-eval-defun-at-point` |<kbd>C-M-x</kbd> <br/> <kbd>C-c C-c</kbd> | Evaluate the top level form under point and display the result in the echo area.
`cider-eval-sexp-at-point` |<kbd>C-c C-v v</kbd> | Evaluate the form around point.
`cider-eval-defun-at-point` |<kbd>C-u C-M-x</kbd> <br/> <kbd>C-u C-c C-c</kbd> | Debug the top level form under point and walk through its evaluation
-`cider-eval-defun-to-point` |<kbd>C-c C-v z</kbd> | Evaluate the preceding top-level form up to the point.
+`cider-eval-defun-up-to-point` |<kbd>C-c C-v z</kbd> | Evaluate the preceding top-level form up to the point.
`cider-eval-region` |<kbd>C-c C-v r</kbd> | Evaluate the region and display the result in the echo area.
`cider-interrupt` |<kbd>C-c C-b</kbd> | Interrupt any pending evaluations.
`cider-macroexpand-1` |<kbd>C-c C-m</kbd> | Invoke `macroexpand-1` on the form at point and display the result in a macroexpansion buffer. If invoked with a prefix argument, `macroexpand` is used instead of `macroexpand-1`.
`cider-macroexpand-all` |<kbd>C-c M-m</kbd> | Invoke `clojure.walk/macroexpand-all` on the form at point and display the result in a macroexpansion buffer.
`cider-eval-ns-form` |<kbd>C-c C-v n</kbd> | Eval the ns form.
-`cider-repl-set-ns` |<kbd>C-c M-n</kbd> | Switch the namespace of the REPL buffer to the namespace of the current buffer.
+`cider-repl-set-ns` |<kbd>C-c M-n (M-)n</kbd> | Switch the namespace of the REPL buffer to the namespace of the current buffer.
`cider-switch-to-repl-buffer` |<kbd>C-c C-z</kbd> | Switch to the relevant REPL buffer. Use a prefix argument to change the namespace of the REPL buffer to match the currently visited source file.
`cider-switch-to-repl-buffer` |<kbd>C-u C-u C-c C-z</kbd> | Switch to the REPL buffer based on a user prompt for a directory.
`cider-load-buffer-and-switch-to-repl-buffer` |<kbd>C-c M-z</kbd> | Load (eval) the current buffer and switch to the relevant REPL buffer. Use a prefix argument to change the namespace of the REPL buffer to match the currently visited source file.
-`cider-display-connection-info` |<kbd>C-c M-d</kbd> | Display default REPL connection details, including project directory name, buffer namespace, host and port.
-`cider-rotate-default-connection` |<kbd>C-c M-r</kbd> | Rotate and display the default nREPL connection.
+`cider-describe-connection` |<kbd>C-c M-d</kbd> | Display default REPL connection details, including project directory name, buffer namespace, host and port.
`cider-find-and-clear-repl-output` |<kbd>C-c C-o</kbd> | Clear the last output in the REPL buffer. With a prefix argument it will clear the entire REPL buffer, leaving only a prompt. Useful if you're running the REPL buffer in a side by side buffer.
`cider-load-buffer` |<kbd>C-c C-k</kbd> | Load (eval) the current buffer.
`cider-load-file` |<kbd>C-c C-l</kbd> | Load (eval) a Clojure file.
`cider-load-all-files` |<kbd>C-c C-M-l</kbd> | Load (eval) all Clojure files below a directory.
-`cider-refresh` |<kbd>C-c C-x</kbd> | Reload all modified files on the classpath. If invoked with a prefix argument, reload all files on the classpath. If invoked with a double prefix argument, clear the state of the namespace tracker before reloading.
+`cider-ns-refresh` |<kbd>C-c M-n (M-)r</kbd> | Reload all modified files on the classpath. If invoked with a prefix argument, reload all files on the classpath. If invoked with a double prefix argument, clear the state of the namespace tracker before reloading.
`cider-doc` |<kbd>C-c C-d d</kbd> <br/> <kbd>C-c C-d C-d</kbd> | Display doc string for the symbol at point. If invoked with a prefix argument, or no symbol is found at point, prompt for a symbol.
`cider-javadoc` |<kbd>C-c C-d j</kbd> <br/> <kbd>C-c C-d C-j</kbd> | Display JavaDoc (in your default browser) for the symbol at point. If invoked with a prefix argument, or no symbol is found at point, prompt for a symbol.
`cider-grimoire` |<kbd>C-c C-d r</kbd> <br/> <kbd>C-c C-d C-r</kbd> | Lookup symbol in Grimoire.
@@ -63,24 +62,31 @@ Here's a list of `cider-mode`'s keybindings:
`cider-test-rerun-failed-tests` |<kbd>C-c C-t r</kbd> <br/> <kbd>C-c C-t C-r</kbd> | Re-run test failures/errors.
`cider-test-show-report` |<kbd>C-c C-t b</kbd> <br/> <kbd>C-c C-t C-b</kbd> | Show the test report buffer.
`cider-find-var` |<kbd>M-.</kbd> | Jump to the definition of a symbol. If invoked with a prefix argument, or no symbol is found at point, prompt for a symbol.
+`cider-find-dwim-at-mouse` |<kbd>mouse-5</kbd> or <kbd>mouse-9</kbd> | Jump to the definition of a symbol using mouse.
+`xref-pop-marker-stack` |<kbd>mouse-4</kbd> or <kbd>mouse-8</kbd> | Jump back to where `cider-find-dwim-at-mouse` was invoked.
`cider-find-resource` |<kbd>C-c M-.</kbd> | Jump to the resource referenced by the string at point.
`cider-find-ns` |<kbd>C-c C-.</kbd> | Jump to some namespace on the classpath.
`cider-pop-back` |<kbd>M-,</kbd> | Return to your pre-jump location.
`complete-symbol` |<kbd>M-TAB</kbd> | Complete the symbol at point.
-`cider-quit` |<kbd>C-c C-q</kbd> | Quit the current nREPL connection. With a prefix argument it will quit all connections.
+`cider-quit` |<kbd>C-c C-q</kbd> | Quit the current nREPL connection.
-There's no need to memorize this list. In any Clojure buffer with `cider-mode`
-active you'll have a CIDER menu available, which lists all the most important
-commands and their keybindings. You can also invoke `C-h f RET cider-mode` to
-get a list of the keybindings for `cider-mode`.
+!!! Tip
+
+ There's no need to memorize this list. In any Clojure buffer with `cider-mode`
+ active you'll have a CIDER menu available, which lists all the most important
+ commands and their keybindings. You can also invoke `C-h f RET cider-mode` to
+ get a list of the keybindings for `cider-mode`.
![CIDER interactions menu](images/menu_example.png)
-An even better solution would be to install [which-key][], which will
-automatically show you a list of available keybindings as you start typing some
-keys. This will simplify your interactions with CIDER quite a lot (especially in
-the beginning). Here's what you'd see if you typed <kbd>C-c C-d</kbd> in a
-Clojure buffer:
+
+!!! Tip
+
+ An even better solution would be to install [which-key][], which will
+ automatically show you a list of available keybindings as you start typing some
+ keys. This will simplify your interactions with CIDER quite a lot (especially in
+ the beginning). Here's what you'd see if you typed <kbd>C-c C-d</kbd> in a
+ Clojure buffer:
![CIDER which-key](images/cider-which-key.png)
diff --git a/doc/managing_connections.md b/doc/managing_connections.md
index d425ad27..1b01c581 100644
--- a/doc/managing_connections.md
+++ b/doc/managing_connections.md
@@ -1,60 +1,113 @@
-## Overview
-
-You can connect to multiple nREPL servers using <kbd>M-x</kbd> `cider-jack-in`
-(or `cider-connect`) multiple times. To close the current nREPL connection, use
-<kbd>C-c C-q</kbd> (`cider-quit`). You can close all connections with
-<kbd>C-u C-c C-q</kbd>.
-
-CIDER maintains a list of nREPL connections and a single 'default'
-connection. When you execute CIDER commands in a Clojure editing buffer such as
-to compile a namespace, these commands are executed against a specific
-connection. This is controlled by the variable `cider-request-dispatch` - when
-it's set to `'dynamic` (the default), CIDER will try to infer which connection
-to use from the current project and currently visited file; when `'static`
-dispatch is used all requests will always be routed to the default connection
-(this was the default behavior in CIDER before 0.10).
-
-There's a handy command called `cider-toggle-request-dispatch`. You can use it
-to quickly switch between dynamic and static request dispatch. A common use-case
-for it would be to force temporary all evaluation commands to be using a
-particular (the default) connection.
-
-You can display the current nREPL connection using <kbd>C-c M-d</kbd>
-and rotate the default connection using <kbd>C-c M-r</kbd>. Another
-option for setting the default connection is to execute the command
-<kbd>M-x</kbd> `cider-make-connection-default` in the appropriate
-REPL buffer.
-
-## Connection browser
-
-You can obtain a list of all active connections using <kbd>M-x</kbd>
-`cider-connection-browser`. This buffer provides a few extra keybindings:
-
-Command |Keyboard shortcut | Description
--------------------------------------|--------------------------------|-------------------------------
-`cider-connections-make-default` |<kbd>d</kbd> | Make connection at point default.
-`cider-connections-close-connection` |<kbd>k</kbd> | Close connection at point.
-`cider-connection-browser` |<kbd>g</kbd> | Refresh connection browser.
-`cider-connections-goto-connection` |<kbd>RET</kbd> | Visit connection buffer.
-`cider-popup-buffer-quit-function` |<kbd>q</kbd> | Close window.
-
-## Switch to connection buffer
-
-The REPL buffers double as connection buffers.
-
-To switch to the relevant REPL buffer based on the Clojure namespace
-in the current Clojure buffer, use: <kbd>C-c C-z</kbd>. You can then
-use the same key combination to switch back to the Clojure buffer you
-came from.
-
-The single prefix <kbd>C-u C-c C-z</kbd>, will switch you to the
-relevant REPL buffer and set the namespace in that buffer based on
-namespace in the current Clojure buffer.
-
-## Renaming connections
-
-To change the designation used for CIDER buffers use <kbd>M-x</kbd>
-`cider-change-buffers-designation`. This changes the CIDER REPL
-buffer, nREPL connection buffer and nREPL server buffer. For example
-using `cider-change-buffers-designation` with the string "foo" would
-change `*cider-repl localhost*` to `*cider-repl foo*`.
+!!! Note
+
+ Because connections map one-to-one to REPL buffers, for the purpose of this
+ section we use "REPL" and "connection" interchangeably.
+
+## Sessions
+
+CIDER maintains a grouped view of opened connections through [Sesman]
+sessions. Each session is a collection of connections which share the same nREPL
+server.
+
+Start new sessions with
+
+ - <kbd>C-c C-x j j</kbd> `cider-jack-in-clj`
+ - <kbd>C-c C-x j s</kbd> `cider-jack-in-cljs`
+ - <kbd>C-c C-x j m</kbd> `cider-jack-in-clj&cljs`
+
+ - <kbd>C-c C-x c j</kbd> `cider-connect-clj`
+ - <kbd>C-c C-x c s</kbd> `cider-connect-cljs`
+ - <kbd>C-c C-x c m</kbd> `cider-connect-clj&cljs`
+
+Add new REPLs to the current session with
+
+ - <kbd>C-c C-x s j</kbd> `cider-connect-sibling-clj`
+ - <kbd>C-c C-x s s</kbd> `cider-connect-sibling-cljs`
+
+Session life-cycle management commands live in the [Sesman] map (<kbd>C-c
+C-s</kbd>)
+
+ - <kbd>C-c C-s s</kbd> `sesman-start`
+ - <kbd>C-c C-s r</kbd> `sesman-restart`
+ - <kbd>C-c C-s q</kbd> `sesman-quit`
+
+The command `sesman-start` wraps around all of the aforementioned `jack-in` and
+`connect` commands. You can also invoke same functionality with <kbd>M-x</kbd>
+`cider` or <kbd>C-c M-x</kbd>.
+
+To quit or restart individual connections use cider commands
+
+ - <kbd>C-c C-q</kbd> `cider-quit`
+ - <kbd>C-c M-r</kbd> `cider-restart`
+
+
+## Context Links
+
+Sessions can be linked to contexts (projects, directories and buffers)
+
+ - <kbd>C-c C-s b</kbd> `sesman-link-with-buffer`
+ - <kbd>C-c C-s d</kbd> `sesman-link-with-directory`
+ - <kbd>C-c C-s p</kbd> `sesman-link-with-project`
+ - <kbd>C-c C-s u</kbd> `sesman-unlink`
+
+## Displaying Session Info
+
+Retrieve info on all linked with the current context sessions with <kbd>C-c C-s
+i</kbd> (`sesman-info`). On <kbd>C-u</kbd>, display info for all CIDER
+sessions. For the connection specific information use CIDER's built-in
+`cider-describe-connection` (<kbd>C-c M-d</kbd>).
+
+An interactive view of all CIDER sessions is available through the
+`sesman-browser` (<kbd>C-c C-s w</kbd>).
+
+## Current Session
+
+All CIDER commands (evaluation, completion, switching to REPL etc.) operate on
+the relevant REPL within the current session. The current session is the most
+relevant session among all linked session. Session relevance is decided by the
+specificity of the linked contexts and recency of the REPL buffers.
+
+If the current context is linked to a single session then that session is the
+current one. If multiple sessions are linked to a context (say, a project) then
+the current session is the one containing the most recently viewed REPL.
+
+Links to more specific contexts have precedence. For example, if you have two
+sessions linked to the same project and another to a directory within that
+project, then the session linked to the directory is the current session. Thus,
+again, there is no ambiguity.
+
+By default [Sesman] allows multiple simultaneous links to projects and
+directories, but only one link per buffer. See `sesman-single-link-contexts` if
+you would like to change that.
+
+## Current REPL
+
+The current REPL is the most relevant REPL from the current session. REPL relevance
+is determined by the type of the current buffer. For example if the current
+buffer is a `clj` buffer then a `clj` REPL is selected. Ambiguous situations could
+arise when, for instance, there are multiple `clj` REPLs within a session, or
+the current buffer is a `cljc` buffer and both `clj` and `cljs` REPLs exist in
+the session. In such cases the current REPL is the most recently viewed REPL of
+the relevant type.
+
+Switch to the current REPL buffer with <kbd>C-c C-z</kbd>. You can then use the
+same key combination to switch back to the Clojure(Script) buffer that you came
+from.
+
+The single prefix <kbd>C-u C-c C-z</kbd>, will switch to the current REPL buffer
+and set the namespace in that buffer based on namespace in the current
+Clojure(Script) buffer.
+
+## Customizing Session and REPL Names
+
+By default session names consist of abbreviated project name, host and port
+(e.g. `project/dir:localhost:1234`). REPL buffer name consist of the session
+name and the REPL type specification post-fix
+(e.g. `*project/dir:localhost:1234(cljs:node)*`).
+
+You can customize session names with `cider-session-name-template` and REPL
+names with `nrepl-repl-buffer-name-template`. See also
+`cider-format-connection-params` for available formats.
+
+
+[Sesman]: https://github.com/vspinu/sesman
diff --git a/doc/miscellaneous_features.md b/doc/miscellaneous_features.md
index e9f90aa7..499aedcc 100644
--- a/doc/miscellaneous_features.md
+++ b/doc/miscellaneous_features.md
@@ -97,7 +97,7 @@ minor mode) by writing `#light` before the `(def` and reevaluating it.
## Code reloading
-`cider-refresh` wraps
+`cider-ns-refresh` wraps
[clojure.tools.namespace](https://github.com/clojure/tools.namespace), and as
such the same
[benefits](https://github.com/clojure/tools.namespace#reloading-code-motivation)
@@ -105,7 +105,7 @@ and
[caveats](https://github.com/clojure/tools.namespace#reloading-code-preparing-your-application)
regarding writing reloadable code also apply.
-Calling `cider-refresh` will cause all modified Clojure files on the classpath
+Calling `cider-ns-refresh` will cause all modified Clojure files on the classpath
to be reloaded. You can also provide a single prefix argument to reload all
Clojure files on the classpath unconditionally, or a double prefix argument to
first clear the state of the namespace tracker before reloading.
@@ -118,11 +118,11 @@ and
(followed by a normal refresh), respectively.
* You can define Clojure functions to be called before reloading, and after a
- successful reload, when using `cider-refresh`:
+ successful reload, when using `cider-ns-refresh`:
```el
-(setq cider-refresh-before-fn "user/stop-system!"
- cider-refresh-after-fn "user/start-system!")
+(setq cider-ns-refresh-before-fn "user/stop-system!"
+ cider-ns-refresh-after-fn "user/start-system!")
```
* These must be set to the namespace-qualified names of vars bound to functions
@@ -131,22 +131,27 @@ and
retries.
* By default, messages regarding the status of the in-progress reload will be
- displayed in the echo area after you call `cider-refresh`. The same
- information will also be recorded in the `*cider-refresh-log*` buffer, along
- with anything printed to `*out*` or `*err*` by `cider-refresh-before-fn` and
- `cider-refresh-start-fn`.
+ displayed in the echo area after you call `cider-ns-refresh`. The same
+ information will also be recorded in the `*cider-ns-refresh-log*` buffer, along
+ with anything printed to `*out*` or `*err*` by `cider-ns-refresh-before-fn` and
+ `cider-ns-refresh-start-fn`.
-* You can make the `*cider-refresh-log*` buffer display automatically after you
- call `cider-refresh` by setting the `cider-refresh-show-log-buffer` variable
+* You can make the `*cider-ns-refresh-log*` buffer display automatically after you
+ call `cider-ns-refresh` by setting the `cider-ns-refresh-show-log-buffer` variable
to a non-nil value (this will also prevent any related messages from also
being displayed in the echo area):
```el
-(setq cider-refresh-show-log-buffer t)
+(setq cider-ns-refresh-show-log-buffer t)
```
* By default, all modified Clojure buffers are prompted to be saved. This
- behaviour can be customized using `cider-save-files-on-cider-refresh`.
+ behaviour can be customized using `cider-ns-save-files-on-refresh`.
+
+In case `cider-ns-refresh` does not work for you the
+`cider-ns-reload`|`cider-ns-reload-all` commands can be used instead: they will
+evaluate the Clojure's out of the box `(require ... :reload|:reload-all)` at
+the REPL.
## Tracing function execution
@@ -203,8 +208,8 @@ at the spec browser.
![Spec Browser](images/spec_browser.png)
-You can also type the command <kbd>M-x</kbd> `cider-browse-spec-all`. This command will prompt you for
-a regex you can use to filter out the specs you are interested in, and will also take you to the spec browser.
+You can also type the command <kbd>M-x</kbd> `cider-browse-spec-all`. This command will prompt you for
+a regex you can use to filter out the specs you are interested in, and will also take you to the spec browser.
![Spec Browser](images/spec_browser_all.png)
@@ -218,7 +223,7 @@ Keyboard shortcut | Description
<kbd>p</kbd> | Go to previous spec.
<kbd>e</kbd> | Generate an example for the current browser spec.
-If your project contains a version of `org.clojure/test.check`, you can type <kbd>e</kbd> when browsing
+If your project contains a version of `org.clojure/test.check`, you can type <kbd>e</kbd> when browsing
a spec to generate and print an example of it.
![Spec Browser Example](images/spec_browser_gen_example.png)
diff --git a/doc/navigating_stacktraces.md b/doc/navigating_stacktraces.md
index 9e71f3fa..14c3ff64 100644
--- a/doc/navigating_stacktraces.md
+++ b/doc/navigating_stacktraces.md
@@ -47,8 +47,9 @@ more permanent, you can do so by customizing the
provide a direct link to the bug reporting page to help facilitate its
diagnosis and repair.
-Independently of the value of `cider-show-error-buffer` or `cider-stacktrace-suppressed-errors`,
-the error buffer is always generated in the background. Use `cider-visit-error-buffer` to visit
+Independently of the value of `cider-show-error-buffer` or
+`cider-stacktrace-suppressed-errors`, the error buffer is always
+generated in the background. Use `cider-selector` (`C-c M-s`) to visit
this buffer.
There are two more selective strategies for the error buffer:
@@ -64,12 +65,17 @@ There are two more selective strategies for the error buffer:
(setq cider-auto-select-error-buffer nil)
```
-* Error buffer stacktraces may be filtered by default. Valid filter types
-include `java`, `clj`, `repl`, `tooling`, and `dup`. Setting this to `nil` will
-show all stacktrace frames.
+* Error buffer stacktraces may be filtered by default. Valid filter
+types include `java`, `clj`, `repl`, `tooling`, and `dup`. There are
+also "positive" filtering types. The value `project` will cause only
+project frames to be shown or `all` will force all stackframes to be
+shown. Note that `project` and `all` are mutually exclusive. Whichever
+one is first will determine the behavior if they are both present.
```el
(setq cider-stacktrace-default-filters '(tooling dup))
+;; or
+(setq cider-stacktrace-default-filters '(project))
```
* Error messages may be wrapped for readability. If this value is nil, messages
diff --git a/doc/troubleshooting.md b/doc/troubleshooting.md
index e29aead2..bbb579f9 100644
--- a/doc/troubleshooting.md
+++ b/doc/troubleshooting.md
@@ -15,8 +15,10 @@ Emacs features a super powerful built-in
[Emacs Lisp debugger](http://www.gnu.org/software/emacs/manual/html_node/elisp/Edebug.html)
and using it is the best way to diagnose problems of any kind.
-Here's a [great crash course](https://www.youtube.com/watch?v=odkYXXYOxpo) on
-using the debugger.
+!!! Tip
+
+ Here's a [great crash course](https://www.youtube.com/watch?v=odkYXXYOxpo) on
+ using the debugger.
To debug some command you need to do the following:
@@ -40,8 +42,10 @@ it is pretty simple:
2. Invoke some commands.
3. Get the report with <kbd>M-x</kbd> `profiler-report`.
-If you intend to share the profiling results with someone it's a good idea to
-save the report buffer to a file with <kbd>C-x C-w</kbd>.
+!!! Tip
+
+ If you intend to share the profiling results with someone it's a good idea to
+ save the report buffer to a file with <kbd>C-x C-w</kbd>.
## Commonly encountered problems (and how to solve them)
@@ -139,21 +143,21 @@ effect of changes you have to restart Emacs.
This is a warning displayed on the REPL buffer when it starts, and usually looks like this:
-> **WARNING:** CIDER's version (0.12.0) does not match cider-nrepl's version (...). Things will break!
+> **WARNING:** CIDER 0.18.0 requires cider-nrepl x.y.z+, but you're currently using cider-nrepl a.b.c. Things will break!
-where `...` might be an actual version, like `0.10.0`, or it might be `not installed` or `nil`.
+where `a.b.c` might be an actual version, like `0.17.0`, or it might be `not installed` or `nil`.
The solution to this depends on what you see and on what you're doing.
#### You see a number like `X.X.X`, and you're starting the REPL with `cider-connect`
Your project specifies the wrong version for the cider-nrepl middleware. See the
-[instructions](http://cider.readthedocs.io/en/latest/installation/#ciders-nrepl-middleware)
+[instructions](http://docs.cider.mx/en/latest/installation/#ciders-nrepl-middleware)
on the Installation section.
#### You see `not installed` or `nil`, and you're starting the REPL with `cider-connect`
To use `cider-connect` you need to add the cider-nrepl middleware to your project. See the
-[instructions](http://cider.readthedocs.io/en/latest/installation/#ciders-nrepl-middleware)
+[instructions](http://docs.cider.mx/en/latest/installation/#ciders-nrepl-middleware)
on the Installation section.
#### You see `not installed` or `nil`, and you're starting the REPL with `cider-jack-in`
@@ -165,7 +169,7 @@ on the Installation section.
If the above doesn't work, you can try specifying the cider-nrepl middleware
manually, as per the
-[instructions](http://cider.readthedocs.io/en/latest/installation/#ciders-nrepl-middleware)
+[instructions](http://docs.cider.mx/en/latest/installation/#ciders-nrepl-middleware)
on the Installation section.
#### You see a number like `X.X.X`, and you're starting the REPL with `cider-jack-in`
diff --git a/doc/up_and_running.md b/doc/up_and_running.md
index 8098b3b3..8d7861e6 100644
--- a/doc/up_and_running.md
+++ b/doc/up_and_running.md
@@ -1,23 +1,22 @@
The only requirement to use CIDER is to have an nREPL server to which it may
connect. Many Clojurians favour the use of tools like Leiningen, Boot or Gradle
to start an nREPL server, but the use of one of them is not a prerequisite to
-use CIDER (however, it *is* required if you want to use the `cider-jack-in`
-command).
+use CIDER.
-## Setting up a Leiningen or Boot project (optional)
-
-[Leiningen][] is the de-facto standard build/project
-management tool for Clojure. [Boot][] is a newer build tool
-offering abstractions and libraries to construct more complex build
-scenarios. Both have a similar scope to the Maven build tool favoured by Java
-developers (and they actually reuse many things from the Maven ecosystem).
+## Setting up a Clojure project (optional)
CIDER features a command called `cider-jack-in` that will start an nREPL server
-for a particular Leiningen or Boot project and connect to it automatically.
-This functionality depends on Leiningen 2.5.2+ or Boot
-2.7.0+. Older versions are not supported. For Leiningen, follow the installation
-instructions on its web site to get it up and running and afterwards create a
-project like this:
+for a particular Clojure project and connect to it automatically. Most
+popular Clojure project management tools are supported by default - namely
+Leiningen, Boot, `clj` (`tools.deps`) and Gradle.
+
+!!! Note
+
+ This functionality depends on Leiningen 2.5.2+ or Boot
+ 2.7.0+. Older versions are not supported.
+
+Let's create a simple Clojure project using Leiningen now. Provided you've installed
+it already, all you need to do is:
```
$ lein new demo
@@ -27,52 +26,88 @@ The two main ways to obtain an nREPL connection are discussed in the following s
## Launch an nREPL server and client from Emacs
-Simply open in Emacs a file belonging to your `lein` or `boot` project (like
-`foo.clj`) and type <kbd>M-x</kbd> `cider-jack-in` <kbd>RET</kbd>. This will
-start an nREPL server with all the project dependencies loaded in and CIDER will
-automatically connect to it.
+Simply open in Emacs a file belonging to your project (like `foo.clj`) and type
+<kbd>M-x</kbd> `cider-jack-in` <kbd>RET</kbd>. This will start an nREPL server
+and CIDER will automatically connect to it.
+
+!!! Note
+
+ If it is a `lein`, `boot` or `tools.deps (deps.edn)` project nREPL will be
+ started with all dependencies loaded. Dependency auto-injection is currently
+ not support for Gradle projects.
Alternatively you can use <kbd>C-u M-x</kbd> `cider-jack-in` <kbd>RET</kbd> to
-specify the name of a `lein` or `boot` project, without having to visit any file
-in it. This option is also useful if your project contains both `project.clj`
-and `build.boot` and you want to launch a repl for one or the other.
+specify the name of a `lein`, `boot` or `tools.deps` project, without having to
+visit any file in it. This option is also useful if your project contains some
+combination of `project.clj`, `build.boot` and `deps.edn` and you want to launch
+a REPL for one or the other.
-In Clojure(Script) buffers the command `cider-jack-in` is bound to <kbd>C-c M-j</kbd>.
+!!! Tip
+
+ In Clojure(Script) buffers the command `cider-jack-in` is bound to <kbd>C-c C-x (C-)j (C-)j</kbd>.
For further customizing the command line used for `cider-jack-in`, you can
change the following (all string options):
- * `cider-lein-global-options`, `cider-boot-global-options`, `cider-gradle-global-options`: these are passed to the command directly, in first position (e.g. `-o` to `lein` enables offline mode).
- * `cider-lein-parameters`, `cider-boot-parameters`, `cider-gradle-parameters`: these are usually tasks names and their parameters (e.g.: `dev` for launching boot's dev task instead of the standard `repl -s wait`).
+ * `cider-lein-global-options`, `cider-boot-global-options`,
+ `cider-clojure-cli-global-options`, `cider-gradle-global-options`:
+ these are passed to the command directly, in first position
+ (e.g. `-o` to `lein` enables offline mode).
+ * `cider-lein-parameters`, `cider-boot-parameters`,
+ `cider-clojure-cli-parameters`, `cider-gradle-parameters`: these
+ are usually tasks names and their parameters (e.g.: `dev` for
+ launching boot's dev task instead of the standard `repl -s wait`).
+
+Note that if you try to run `cider-jack-in` outside a project
+directory normally you'd get a warning to confirm you really want to
+do this, as more often than not you'd probably do this
+accidentally. If you decide to proceed, CIDER will invoke the command
+configured in `cider-jack-in-default`. This used to be `lein` prior to
+CIDER 0.17 and it was switched to Clojure's CLI (`clj`) afterwards.
+
+!!! Tip
+
+ You can set `cider-allow-jack-in-without-project` to `t` if you'd like to
+ disable the warning displayed when jacking-in outside a project.
## Connect to a running nREPL server
-You can go to your project's directory in a terminal and type there:
+Go to your project's directory in a terminal and type there:
```
$ lein repl
```
-Or:
+Or for `boot`:
```
$ boot repl -s wait (or whatever task launches a repl)
```
-Alternatively you can start nREPL either manually or by the facilities provided by your
-project's build tool (Maven, etc).
+It is also possible for plain `clj`, although the command is somewhat longer:
+
+```
+$ clj -Sdeps '{:deps {cider/cider-nrepl {:mvn/version "0.18.0-SNAPSHOT"} }}' -e '(require (quote cider-nrepl.main)) (cider-nrepl.main/init ["cider.nrepl/cider-middleware"])'
+```
+
+Alternatively you can start nREPL either manually or by the facilities
+provided by your project's build tool (Maven, etc).
-After you get your nREPL server running go back to Emacs. Typing there
-<kbd>M-x</kbd> `cider-connect` <kbd>RET</kbd> will allow you to connect to the
+After you get your nREPL server running, go back to Emacs. Type there
+<kbd>M-x</kbd> `cider-connect` <kbd>RET</kbd> to connect to the
running nREPL server.
-In Clojure(Script) buffers the command `cider-connect` is bound to <kbd>C-c M-c</kbd>.
+!!! Tip
+
+ In Clojure(Script) buffers the command `cider-connect` is bound to <kbd>C-c M-c</kbd>.
You can configure known endpoints used by the `cider-connect` command offered
via a completing read. This is useful if you have a list of common host/ports
you want to establish remote nREPL connections to. Using an optional label is
helpful for identifying each host.
-```el
-(setq cider-known-endpoints '(("host-a" "10.10.10.1" "7888") ("host-b" "7888")))
+```
+(setq cider-known-endpoints
+ '(("host-a" "10.10.10.1" "7888")
+ ("host-b" "7888")))
```
diff --git a/doc/using_the_repl.md b/doc/using_the_repl.md
index f240b06d..e9888cfd 100644
--- a/doc/using_the_repl.md
+++ b/doc/using_the_repl.md
@@ -24,8 +24,6 @@ Keyboard shortcut | Description
<kbd>M-p</kbd> <br/> <kbd>M-n</kbd> | Search the previous/next item in history using the current input as search pattern. If <kbd>M-p/M-n</kbd> is typed two times in a row, the second invocation uses the same search pattern (even if the current input has changed).
<kbd>M-s</kbd> <br/> <kbd>M-r</kbd> | Search forward/reverse through command history with regex.
<kbd>C-c C-n</kbd> <br/> <kbd>C-c C-p</kbd> | Move between the current and previous prompts in the REPL buffer. Pressing <kbd>RET</kbd> on a line with old input copies that line to the newest prompt.
-<kbd>C-c C-x</kbd> | Reload all modified files on the classpath.
-<kbd>C-u C-c C-x</kbd> | Reload all files on the classpath.
<kbd>TAB</kbd> | Complete symbol at point.
<kbd>C-c C-d d</kbd> <br/> <kbd>C-c C-d C-d</kbd> | Display doc string for the symbol at point. If invoked with a prefix argument, or no symbol is found at point, prompt for a symbol
<kbd>C-c C-d j</kbd> <br/> <kbd>C-c C-d C-j</kbd> | Display JavaDoc (in your default browser) for the symbol at point. If invoked with a prefix argument, or no symbol is found at point, prompt for a symbol.
diff --git a/mkdocs.yml b/mkdocs.yml
index 2e000976..5090ff48 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -1,7 +1,9 @@
site_name: "CIDER: The Clojure Interactive Development Environment that Rocks"
repo_url: https://github.com/clojure-emacs/cider
site_dir: html
-copyright: "Copyright (C) 2016-2018 Bozhidar Batsov, Artur Malabarba and CIDER contributors"
+edit_uri: edit/master/doc/
+site_favicon: favicon.ico
+copyright: "Copyright &copy; 2016-2018 Bozhidar Batsov, Artur Malabarba and CIDER contributors"
docs_dir: doc
pages:
- Home: index.md
@@ -10,11 +12,11 @@ pages:
- ClojureScript: clojurescript.md
- Interactive Programming: interactive_programming.md
- Using the REPL: using_the_repl.md
+- Managing Connections: managing_connections.md
- Code Completion: code_completion.md
- Navigating Stacktraces: navigating_stacktraces.md
- Running Tests: running_tests.md
- Debugging: debugging.md
-- Managing Connections: managing_connections.md
- Miscellaneous Features: miscellaneous_features.md
- Configuration: configuration.md
- Indentation Specification: indent_spec.md
@@ -36,3 +38,6 @@ extra_css:
- css/extra.css
theme_dir: /usr/share/mkdocs/themes/mkdocs_bootstrap
use_directory_urls: false
+markdown_extensions:
+ - admonition
+theme: readthedocs
diff --git a/nrepl-client.el b/nrepl-client.el
index 38e0cb53..7db01ec9 100644
--- a/nrepl-client.el
+++ b/nrepl-client.el
@@ -85,16 +85,18 @@
:prefix "nrepl-"
:group 'applications)
-(defcustom nrepl-buffer-name-separator " "
- "Used in constructing the REPL buffer name.
-The `nrepl-buffer-name-separator' separates cider-repl from the project name."
- :type '(string)
- :group 'nrepl)
-
-(defcustom nrepl-buffer-name-show-port nil
- "Show the connection port in the nrepl REPL buffer name, if set to t."
- :type 'boolean
- :group 'nrepl)
+;; (defcustom nrepl-buffer-name-separator " "
+;; "Used in constructing the REPL buffer name.
+;; The `nrepl-buffer-name-separator' separates cider-repl from the project name."
+;; :type '(string)
+;; :group 'nrepl)
+(make-obsolete-variable 'nrepl-buffer-name-separator 'cider-session-name-template "0.18")
+
+;; (defcustom nrepl-buffer-name-show-port nil
+;; "Show the connection port in the nrepl REPL buffer name, if set to t."
+;; :type 'boolean
+;; :group 'nrepl)
+(make-obsolete-variable 'nrepl-buffer-name-show-port 'cider-session-name-template "0.18")
(defcustom nrepl-connected-hook nil
"List of functions to call when connecting to the nREPL server."
@@ -133,30 +135,17 @@ When true some special buffers like the server buffer will be hidden."
:type 'boolean
:group 'nrepl)
-(defcustom nrepl-prompt-to-kill-server-buffer-on-quit t
- "If non-nil, prompt the user for confirmation before killing the nrepl server buffer and associated process."
- :type 'boolean
- :group 'nrepl)
-
-(defvar nrepl-create-client-buffer-function 'nrepl-create-client-buffer-default
- "Name of a function that returns a client process buffer.
-It is called with one argument, a plist containing :host, :port and :proc
-as returned by `nrepl-connect'.")
-
-(defvar nrepl-use-this-as-repl-buffer 'new
- "Name of the buffer to use as REPL buffer.
-In case of a special value 'new, a new buffer is created.")
-
;;; Buffer Local Declarations
;; These variables are used to track the state of nREPL connections
-(defvar-local nrepl-client-buffers nil
- "List of buffers connected to this server.")
(defvar-local nrepl-connection-buffer nil)
(defvar-local nrepl-server-buffer nil)
+(defvar-local nrepl-messages-buffer nil)
(defvar-local nrepl-endpoint nil)
(defvar-local nrepl-project-dir nil)
+(defvar-local nrepl-is-server nil)
+(defvar-local nrepl-server-command nil)
(defvar-local nrepl-tunnel-buffer nil)
(defvar-local nrepl-session nil
@@ -191,40 +180,18 @@ To be used for tooling calls (i.e. completion, eldoc, etc)")
;;; nREPL Buffer Names
-(defconst nrepl-message-buffer-name-template "*nrepl-messages %s*")
+(defconst nrepl-message-buffer-name-template "*nrepl-messages %s(%r:%S)*")
(defconst nrepl-error-buffer-name "*nrepl-error*")
-(defconst nrepl-repl-buffer-name-template "*cider-repl%s*")
-(defconst nrepl-connection-buffer-name-template "*nrepl-connection%s*")
-(defconst nrepl-server-buffer-name-template "*nrepl-server%s*")
-(defconst nrepl-tunnel-buffer-name-template "*nrepl-tunnel%s*")
-
-(defun nrepl-format-buffer-name-template (buffer-name-template designation)
- "Apply the DESIGNATION to the corresponding BUFFER-NAME-TEMPLATE."
- (format buffer-name-template
- (if (> (length designation) 0)
- (concat nrepl-buffer-name-separator designation)
- "")))
-
-(defun nrepl-make-buffer-name (buffer-name-template &optional project-dir host port dup-ok)
- "Generate a buffer name using BUFFER-NAME-TEMPLATE.
-
-If not supplied PROJECT-DIR, HOST and PORT default to the buffer local
-value of the `nrepl-project-dir' and `nrepl-endpoint'.
-
-The name will include the project name if available or the endpoint host if
-it is not. The name will also include the connection port if
-`nrepl-buffer-name-show-port' is true.
-
-If optional DUP-OK is non-nil, the returned buffer is not \"uniquified\" by
-`generate-new-buffer-name'."
- (let* ((project-dir (or project-dir nrepl-project-dir))
- (project-name (when project-dir (file-name-nondirectory (directory-file-name project-dir))))
- (nrepl-proj-port (or port (cadr nrepl-endpoint)))
- (name (nrepl-format-buffer-name-template
- buffer-name-template
- (concat (if project-name project-name (or host (car nrepl-endpoint)))
- (if (and nrepl-proj-port nrepl-buffer-name-show-port)
- (format ":%s" nrepl-proj-port) "")))))
+(defconst nrepl-repl-buffer-name-template "*cider-repl %s(%r:%S)*")
+(defconst nrepl-server-buffer-name-template "*nrepl-server %s*")
+(defconst nrepl-tunnel-buffer-name-template "*nrepl-tunnel %s*")
+
+(defun nrepl-make-buffer-name (template params &optional dup-ok)
+ "Generate a buffer name using TEMPLATE and PARAMS.
+TEMPLATE and PARAMS are as in `cider-format-connection-params'. If
+optional DUP-OK is non-nil, the returned buffer is not \"uniquified\" by a
+call to `generate-new-buffer-name'."
+ (let ((name (cider-format-connection-params template params)))
(if dup-ok
name
(generate-new-buffer-name name))))
@@ -233,39 +200,34 @@ If optional DUP-OK is non-nil, the returned buffer is not \"uniquified\" by
"Apply a prefix to BUFFER-NAME that will hide the buffer."
(concat (if nrepl-hide-special-buffers " " "") buffer-name))
-(defun nrepl-connection-buffer-name (&optional project-dir host port)
- "Return the name of the connection buffer.
-PROJECT-DIR, HOST and PORT are as in `/nrepl-make-buffer-name'."
- (nrepl--make-hidden-name
- (nrepl-make-buffer-name nrepl-connection-buffer-name-template
- project-dir host port)))
-
-(defun nrepl-connection-identifier (conn)
- "Return the string which identifies a connection CONN."
- (thread-last (buffer-name conn)
- (replace-regexp-in-string "\\`*cider-repl " "")
- (replace-regexp-in-string "*\\'" "" )))
+(defun nrepl-repl-buffer-name (params &optional dup-ok)
+ "Return the name of the repl buffer.
+PARAMS and DUP-OK are as in `nrepl-make-buffer-name'."
+ (nrepl-make-buffer-name nrepl-repl-buffer-name-template params dup-ok))
-(defun nrepl-server-buffer-name (&optional project-dir host port)
+(defun nrepl-server-buffer-name (params)
"Return the name of the server buffer.
-PROJECT-DIR, HOST and PORT are as in `nrepl-make-buffer-name'."
+PARAMS is as in `nrepl-make-buffer-name'."
(nrepl--make-hidden-name
- (nrepl-make-buffer-name nrepl-server-buffer-name-template
- project-dir host port)))
+ (nrepl-make-buffer-name nrepl-server-buffer-name-template params)))
-(defun nrepl-tunnel-buffer-name (&optional project-dir host port)
+(defun nrepl-tunnel-buffer-name (params)
"Return the name of the tunnel buffer.
-PROJECT-DIR, HOST and PORT are as in `nrepl-make-buffer-name'."
+PARAMS is as in `nrepl-make-buffer-name'."
(nrepl--make-hidden-name
- (nrepl-make-buffer-name nrepl-tunnel-buffer-name-template
- project-dir host port)))
+ (nrepl-make-buffer-name nrepl-tunnel-buffer-name-template params)))
+
+(defun nrepl-messages-buffer-name (params)
+ "Return the name for the message buffer given connection PARAMS."
+ (nrepl-make-buffer-name nrepl-message-buffer-name-template params))
;;; Utilities
(defun nrepl-op-supported-p (op connection)
"Return t iff the given operation OP is supported by the nREPL CONNECTION."
- (with-current-buffer connection
- (and nrepl-ops (nrepl-dict-get nrepl-ops op))))
+ (when (buffer-live-p connection)
+ (with-current-buffer connection
+ (and nrepl-ops (nrepl-dict-get nrepl-ops op)))))
(defun nrepl-aux-info (key connection)
"Return KEY's aux info, as returned via the :describe op for CONNECTION."
@@ -507,10 +469,11 @@ and kill the process buffer."
(nrepl--clear-client-sessions client-buffer)
(with-current-buffer client-buffer
(run-hooks 'nrepl-disconnected-hook)
- (when (buffer-live-p nrepl-server-buffer)
- (with-current-buffer nrepl-server-buffer
- (setq nrepl-client-buffers (delete client-buffer nrepl-client-buffers)))
- (nrepl--maybe-kill-server-buffer nrepl-server-buffer))))))
+ (let ((server-buffer nrepl-server-buffer))
+ (when (and (buffer-live-p server-buffer)
+ (not (plist-get (process-plist process) :no-server-kill)))
+ (setq nrepl-server-buffer nil)
+ (nrepl--maybe-kill-server-buffer server-buffer)))))))
;;; Network
@@ -569,7 +532,8 @@ If NO-ERROR is non-nil, show messages instead of throwing an error."
(ssh (or (executable-find "ssh")
(error "[nREPL] Cannot locate 'ssh' executable")))
(cmd (nrepl--ssh-tunnel-command ssh remote-dir port))
- (tunnel-buf (nrepl-tunnel-buffer-name))
+ (tunnel-buf (nrepl-tunnel-buffer-name
+ `((:host ,host) (:port ,port))))
(tunnel (start-process-shell-command "nrepl-tunnel" tunnel-buf cmd)))
(process-put tunnel :waiting-for-port t)
(set-process-filter tunnel (nrepl--ssh-tunnel-filter port))
@@ -618,6 +582,7 @@ If NO-ERROR is non-nil, show messages instead of throwing an error."
;;; Client: Process Handling
+
(defun nrepl--kill-process (proc)
"Kill PROC using the appropriate, os specific way.
Implement a workaround to clean up an orphaned JVM process left around
@@ -626,35 +591,38 @@ after exiting the REPL on some windows machines."
(interrupt-process proc)
(kill-process proc)))
+(defun nrepl-kill-server-buffer (server-buf)
+ "Kill SERVER-BUF and its process."
+ (when (buffer-live-p server-buf)
+ (let ((proc (get-buffer-process server-buf)))
+ (when (process-live-p proc)
+ (set-process-query-on-exit-flag proc nil)
+ (nrepl--kill-process proc))
+ (kill-buffer server-buf))))
+
(defun nrepl--maybe-kill-server-buffer (server-buf)
- "Kill SERVER-BUF and its process, subject to user confirmation.
-Do nothing if there is a REPL connected to that server."
- (with-current-buffer server-buf
- ;; Don't kill the server if there is a REPL connected to it.
- (when (and (not nrepl-client-buffers)
- (or (not nrepl-prompt-to-kill-server-buffer-on-quit)
- (y-or-n-p "Also kill server process and buffer? ")))
- (let ((proc (get-buffer-process server-buf)))
- (when (process-live-p proc)
- (set-process-query-on-exit-flag proc nil)
- (nrepl--kill-process proc))
- (kill-buffer server-buf)))))
-
-;; `nrepl-start-client-process' is called from `nrepl-server-filter'. It
-;; starts the client process described by `nrepl-client-filter' and
-;; `nrepl-client-sentinel'.
-(defun nrepl-start-client-process (&optional host port server-proc)
+ "Kill SERVER-BUF and its process.
+Do not kill the server if there is a REPL connected to that server."
+ (when (buffer-live-p server-buf)
+ (with-current-buffer server-buf
+ ;; Don't kill if there is at least one REPL connected to it.
+ (when (not (seq-find (lambda (b)
+ (eq (buffer-local-value 'nrepl-server-buffer b)
+ server-buf))
+ (buffer-list)))
+ (nrepl-kill-server-buffer server-buf)))))
+
+(defun nrepl-start-client-process (&optional host port server-proc buffer-builder)
"Create new client process identified by HOST and PORT.
In remote buffers, HOST and PORT are taken from the current tramp
connection. SERVER-PROC must be a running nREPL server process within
-Emacs. This function creates connection buffer by a call to
-`nrepl-create-client-buffer-function'. Return newly created client
-process."
+Emacs. BUFFER-BUILDER is a function of one argument (endpoint returned by
+`nrepl-connect') which returns a client buffer. Return the newly created
+client process."
(let* ((endpoint (nrepl-connect host port))
(client-proc (plist-get endpoint :proc))
- (host (plist-get endpoint :host))
- (port (plist-get endpoint :port))
- (client-buf (funcall nrepl-create-client-buffer-function endpoint)))
+ (builder (or buffer-builder (error "`buffer-builder' must be provided")))
+ (client-buf (funcall builder endpoint)))
(set-process-buffer client-proc client-buf)
@@ -669,7 +637,7 @@ process."
(when-let* ((server-buf (and server-proc (process-buffer server-proc))))
(setq nrepl-project-dir (buffer-local-value 'nrepl-project-dir server-buf)
nrepl-server-buffer server-buf))
- (setq nrepl-endpoint `(,host ,port)
+ (setq nrepl-endpoint endpoint
nrepl-tunnel-buffer (when-let* ((tunnel (plist-get endpoint :tunnel)))
(process-buffer tunnel))
nrepl-pending-requests (make-hash-table :test 'equal)
@@ -684,7 +652,6 @@ process."
(defun nrepl--init-client-sessions (client)
"Initialize CLIENT connection nREPL sessions.
-
We create two client nREPL sessions per connection - a main session and a
tooling session. The main session is general purpose and is used for pretty
much every request that needs a session. The tooling session is used only
@@ -759,7 +726,7 @@ to the REPL."
(defvar cider-buffer-ns)
(defvar cider-special-mode-truncate-lines)
-(declare-function cider-need-input "cider-interaction")
+(declare-function cider-need-input "cider-client")
(declare-function cider-set-buffer-ns "cider-mode")
(defun nrepl-make-response-handler (buffer value-handler stdout-handler
@@ -824,7 +791,10 @@ the corresponding type of response."
(when (member "eval-error" status)
(funcall (or eval-error-handler nrepl-err-handler)))
(when (member "namespace-not-found" status)
- (message "Namespace not found."))
+ ;; nREPL 0.4.3 started echoing back the name of the missing ns
+ (if ns
+ (message "Namespace `%s' not found." ns)
+ (message "Namespace not found.")))
(when (member "need-input" status)
(cider-need-input buffer))
(when (member "done" status)
@@ -839,7 +809,7 @@ the corresponding type of response."
;; Requests can be asynchronous (sent with `nrepl-send-request') or
;; synchronous (send with `nrepl-send-sync-request'). The request is a pair list
;; of operation name and operation parameters. The core operations are described
-;; at https://github.com/clojure/tools.nrepl/blob/master/doc/ops.md. CIDER adds
+;; at https://github.com/nrepl/nrepl/blob/master/doc/ops.md. CIDER adds
;; many more operations through nREPL middleware. See
;; https://github.com/clojure-emacs/cider-nrepl#supplied-nrepl-middleware for
;; the up-to-date list.
@@ -873,7 +843,7 @@ Optional argument TOOLING Set to t if desiring the tooling session rather than t
"Dynamically bound to t while a sync request is ongoing.")
(declare-function cider-repl-emit-interactive-stderr "cider-repl")
-(declare-function cider--render-stacktrace-causes "cider-interaction")
+(declare-function cider--render-stacktrace-causes "cider-eval")
(defun nrepl-send-sync-request (request connection &optional abort-on-input tooling)
"Send REQUEST to the nREPL server synchronously using CONNECTION.
@@ -1017,39 +987,41 @@ session."
;; nrepl communication client (`nrepl-client-filter') when the message "nREPL
;; server started on port ..." is detected.
-(defvar-local nrepl-post-client-callback nil
- "Function called after the client process is started.
-Used by `nrepl-start-server-process'.")
+;; internal variables used for state transfer between nrepl-start-server-process
+;; and nrepl-server-filter.
+(defvar-local nrepl-on-port-callback nil)
-(defun nrepl-start-server-process (directory cmd &optional callback)
+(defun nrepl-server-p (buffer-or-process)
+ "Return t if BUFFER-OR-PROCESS is an nREPL server."
+ (let ((buffer (if (processp buffer-or-process)
+ (process-buffer buffer-or-process)
+ buffer-or-process)))
+ (buffer-local-value 'nrepl-is-server buffer)))
+
+(defun nrepl-start-server-process (directory cmd on-port-callback)
"Start nREPL server process in DIRECTORY using shell command CMD.
-Return a newly created process.
-Set `nrepl-server-filter' as the process filter, which starts REPL process
-with its own buffer once the server has started.
-If CALLBACK is non-nil, it should be function of 1 argument. Once the
-client process is started, the function is called with the client buffer."
+Return a newly created process. Set `nrepl-server-filter' as the process
+filter, which starts REPL process with its own buffer once the server has
+started. ON-PORT-CALLBACK is a function of one argument (server buffer)
+which is called by the process filter once the port of the connection has
+been determined."
(let* ((default-directory (or directory default-directory))
- (serv-buf (get-buffer-create (generate-new-buffer-name
- (nrepl-server-buffer-name directory))))
- (serv-proc (start-file-process-shell-command
- "nrepl-server" serv-buf cmd)))
- (set-process-filter serv-proc 'nrepl-server-filter)
- (set-process-sentinel serv-proc 'nrepl-server-sentinel)
- (set-process-coding-system serv-proc 'utf-8-unix 'utf-8-unix)
+ (serv-buf (get-buffer-create
+ (nrepl-server-buffer-name
+ `(:project-dir ,default-directory)))))
(with-current-buffer serv-buf
- (setq nrepl-project-dir directory)
- (setq nrepl-post-client-callback callback)
- ;; Ensure that `nrepl-start-client-process' sees right things. This
- ;; causes warnings about making a local within a let-bind. This is safe
- ;; as long as `serv-buf' is not the buffer where the let-binding was
- ;; started. http://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Buffer_002dLocal.html
- (setq-local nrepl-create-client-buffer-function
- nrepl-create-client-buffer-function)
- (setq-local nrepl-use-this-as-repl-buffer
- nrepl-use-this-as-repl-buffer))
- (message "Starting nREPL server via %s..."
- (propertize cmd 'face 'font-lock-keyword-face))
- serv-proc))
+ (setq nrepl-is-server t
+ nrepl-project-dir default-directory
+ nrepl-server-command cmd
+ nrepl-on-port-callback on-port-callback))
+ (let ((serv-proc (start-file-process-shell-command
+ "nrepl-server" serv-buf cmd)))
+ (set-process-filter serv-proc 'nrepl-server-filter)
+ (set-process-sentinel serv-proc 'nrepl-server-sentinel)
+ (set-process-coding-system serv-proc 'utf-8-unix 'utf-8-unix)
+ (message "[nREPL] Starting server via %s..."
+ (propertize cmd 'face 'font-lock-keyword-face))
+ serv-proc)))
(defun nrepl-server-filter (process output)
"Process nREPL server output from PROCESS contained in OUTPUT."
@@ -1067,27 +1039,24 @@ client process is started, the function is called with the client buffer."
(when moving
(goto-char (process-mark process))
(when-let* ((win (get-buffer-window)))
- (set-window-point win (point))))))
- ;; detect the port the server is listening on from its output
- (when (string-match "nREPL server started on port \\([0-9]+\\)" output)
- (let ((port (string-to-number (match-string 1 output))))
- (message "nREPL server started on %s" port)
- (with-current-buffer server-buffer
- (let* ((client-proc (nrepl-start-client-process nil port process))
- (client-buffer (process-buffer client-proc)))
- (setq nrepl-client-buffers
- (cons client-buffer
- (delete client-buffer nrepl-client-buffers)))
-
- (when (functionp nrepl-post-client-callback)
- (funcall nrepl-post-client-callback client-buffer)))))))))
-
-(declare-function cider--close-connection-buffer "cider-client")
-
+ (set-window-point win (point)))))
+ ;; detect the port the server is listening on from its output
+ (when (and (null nrepl-endpoint)
+ (string-match "nREPL server started on port \\([0-9]+\\)" output))
+ (let ((port (string-to-number (match-string 1 output))))
+ (setq nrepl-endpoint (list :host "localhost" :port port))
+ (message "[nREPL] server started on %s" port)
+ (when nrepl-on-port-callback
+ (funcall nrepl-on-port-callback (process-buffer process)))))))))
+
+(declare-function cider--close-connection "cider-connection")
(defun nrepl-server-sentinel (process event)
"Handle nREPL server PROCESS EVENT."
(let* ((server-buffer (process-buffer process))
- (clients (buffer-local-value 'nrepl-client-buffers server-buffer))
+ (clients (seq-filter (lambda (b)
+ (eq (buffer-local-value 'nrepl-server-buffer b)
+ server-buffer))
+ (buffer-list)))
(problem (if (and server-buffer (buffer-live-p server-buffer))
(with-current-buffer server-buffer
(buffer-substring (point-min) (point-max)))
@@ -1098,7 +1067,7 @@ client process is started, the function is called with the client buffer."
((string-match-p "^killed\\|^interrupt" event)
nil)
((string-match-p "^hangup" event)
- (mapc #'cider--close-connection-buffer clients))
+ (mapc #'cider--close-connection clients))
;; On Windows, a failed start sends the "finished" event. On Linux it sends
;; "exited abnormally with code 1".
(t (error "Could not start nREPL server: %s" problem)))))
@@ -1108,13 +1077,10 @@ client process is started, the function is called with the client buffer."
(defcustom nrepl-log-messages nil
"If non-nil, log protocol messages to an nREPL messages buffer.
-
-This is extremely useful for debug purposes, as it allows you to
-inspect the communication between Emacs and an nREPL server.
-
-Enabling the logging might have a negative impact on performance,
-so it's not recommended to keep it enabled unless you need to
-debug something."
+This is extremely useful for debug purposes, as it allows you to inspect
+the communication between Emacs and an nREPL server. Enabling the logging
+might have a negative impact on performance, so it's not recommended to
+keep it enabled unless you need to debug something."
:type 'boolean
:group 'nrepl
:safe #'booleanp)
@@ -1149,6 +1115,7 @@ operations.")
\\{nrepl-messages-mode-map}"
(when cider-special-mode-truncate-lines
(setq-local truncate-lines t))
+ (setq-local sesman-system 'CIDER)
(setq-local electric-indent-chars nil)
(setq-local comment-start ";")
(setq-local comment-end "")
@@ -1197,7 +1164,7 @@ This in effect enables or disables the logging of nREPL messages."
(defcustom nrepl-message-colors
'("red" "brown" "coral" "orange" "green" "deep sky blue" "blue" "dark violet")
- "Colors used in `nrepl-messages-buffer'."
+ "Colors used in the messages buffer."
:type '(repeat color)
:group 'nrepl)
@@ -1328,20 +1295,20 @@ it into the buffer."
(pp object (current-buffer))
(insert "\n")))))
-(defun nrepl-messages-buffer-name (conn)
- "Return the name for the message buffer matching CONN."
- (format nrepl-message-buffer-name-template (nrepl-connection-identifier conn)))
-
(defun nrepl-messages-buffer (conn)
"Return or create the buffer for CONN.
The default buffer name is *nrepl-messages connection*."
- (let ((msg-buffer-name (nrepl-messages-buffer-name conn)))
- (or (get-buffer msg-buffer-name)
- (let ((buffer (get-buffer-create msg-buffer-name)))
- (with-current-buffer buffer
- (buffer-disable-undo)
- (nrepl-messages-mode)
- buffer)))))
+ (with-current-buffer conn
+ (or (and (buffer-live-p nrepl-messages-buffer)
+ nrepl-messages-buffer)
+ (setq nrepl-messages-buffer
+ (let ((buffer (get-buffer-create
+ (nrepl-messages-buffer-name
+ (cider--gather-connect-params)))))
+ (with-current-buffer buffer
+ (buffer-disable-undo)
+ (nrepl-messages-mode)
+ buffer))))))
(defun nrepl-error-buffer ()
"Return or create the buffer.
@@ -1363,17 +1330,7 @@ The default buffer name is *nrepl-error*."
(set-window-point win (point-max)))
(setq buffer-read-only t)))
-(defun nrepl-create-client-buffer-default (endpoint)
- "Create an nREPL client process buffer.
-ENDPOINT is a plist returned by `nrepl-connect'."
- (let ((buffer (generate-new-buffer
- (nrepl-connection-buffer-name default-directory
- (plist-get endpoint :host)
- (plist-get endpoint :port)))))
- (with-current-buffer buffer
- (buffer-disable-undo)
- (setq-local kill-buffer-query-functions nil))
- buffer))
+(make-obsolete 'nrepl-default-client-buffer-builder nil "0.18")
(provide 'nrepl-client)
diff --git a/test/cider-apropos-tests.el b/test/cider-apropos-tests.el
index 5e2a6530..2c44e02c 100644
--- a/test/cider-apropos-tests.el
+++ b/test/cider-apropos-tests.el
@@ -28,7 +28,6 @@
;;; Code:
(require 'buttercup)
-(require 'cider)
(require 'cider-apropos)
(describe "cider-apropos"
diff --git a/test/cider-classpath-tests.el b/test/cider-classpath-tests.el
index 98fdceb7..19b14298 100644
--- a/test/cider-classpath-tests.el
+++ b/test/cider-classpath-tests.el
@@ -28,7 +28,6 @@
;;; Code:
(require 'buttercup)
-(require 'cider)
(require 'cider-classpath)
(describe "cider-classpath"
diff --git a/test/cider-client-tests.el b/test/cider-client-tests.el
index 2231e4ce..02b797f6 100644
--- a/test/cider-client-tests.el
+++ b/test/cider-client-tests.el
@@ -29,187 +29,11 @@
(require 'buttercup)
-(require 'cider)
(require 'cider-client)
-(require 'cider-connection-test-utils)
+(require 'cider-connection)
;;; cider-client tests
-(describe "cider-current-connection"
-
- (describe "when there are no active connections"
- :var (cider-connections)
- (it "returns nil"
- (setq cider-connections nil)
- (expect (cider-current-connection) :not :to-be-truthy)
- (expect (cider-current-connection "clj") :not :to-be-truthy)
- (expect (cider-current-connection "cljs") :not :to-be-truthy)))
-
- (describe "when active connections are available"
-
- (it "always returns the latest connection"
- (with-connection-buffer "clj" bb1
- (with-connection-buffer "cljs" bb2
- (with-connection-buffer "clj" b1
- (with-connection-buffer "cljs" b2
- (expect (cider-current-connection) :to-equal b2)
-
- ;; follows type arguments
- (expect (cider-current-connection "clj") :to-equal b1)
- (expect (cider-current-connection "cljs") :to-equal b2)
-
- ;; follows file type
- (with-temp-buffer
- (setq major-mode 'clojure-mode)
- (expect (cider-current-connection) :to-equal b1))
-
- (with-temp-buffer
- (setq major-mode 'clojurescript-mode)
- (expect (cider-current-connection) :to-equal b2)))))))
-
- (it "always returns the most recently used connection"
- (with-connection-buffer "clj" bb1
- (with-connection-buffer "cljs" bb2
- (with-connection-buffer "clj" b1
- (with-connection-buffer "cljs" b2
-
- (switch-to-buffer bb2)
- (switch-to-buffer bb1)
- (expect (cider-current-connection) :to-equal bb1)
-
- ;; follows type arguments
- (expect (cider-current-connection "clj") :to-equal bb1)
- (expect (cider-current-connection "cljs") :to-equal bb2)
-
- ;; follows file type
- (with-temp-buffer
- (setq major-mode 'clojure-mode)
- (expect (cider-current-connection) :to-equal bb1))
-
- (with-temp-buffer
- (setq major-mode 'clojurescript-mode)
- (expect (cider-current-connection) :to-equal bb2)))))))
-
- (describe "when current buffer is a 'multi' buffer"
- (describe "when there is only one connection available"
- (it "returns the only connection"
- (with-connection-buffer "clj" b
- (with-temp-buffer
- (clojure-mode)
- (expect (cider-current-connection "clj") :to-equal b))
- (with-temp-buffer
- (clojurec-mode)
- (expect (cider-current-connection "clj") :to-equal b))))))
-
- (describe "when type argument is given"
- (describe "when connection of that type exists"
- (it "returns that connection buffer"
- ;; for clj
- (with-connection-buffer "clj" b1
- (with-connection-buffer "cljs" b2
- (expect (cider-current-connection "clj") :to-equal b1)))
- ;; for cljs
- (with-connection-buffer "cljs" b1
- (with-connection-buffer "clj" b2
- (expect (cider-current-connection "cljs") :to-equal b1)))))
-
- (describe "when connection of that type doesn't exists"
- (it "returns nil"
- ;; for clj
- (with-connection-buffer "cljs" b1
- (expect (cider-current-connection "clj") :to-equal nil))
-
- ;; for cljs
- (with-connection-buffer "clj" b2
- (expect (cider-current-connection "cljs") :to-equal nil)))))
-
- (describe "when type argument is not given"
- (describe "when a connection matching current file extension exists"
- (it "returns that connection buffer"
- ;; for clj
- (with-connection-buffer "clj" b1
- (with-connection-buffer "cljs" b2
- (with-temp-buffer
- (setq major-mode 'clojure-mode)
- (expect (cider-current-connection) :to-equal b1))))
-
- ;; for cljs
- (with-connection-buffer "cljs" b1
- (with-connection-buffer "clj" b2
- (with-temp-buffer
- (setq major-mode 'clojurescript-mode)
- (expect (cider-current-connection) :to-equal b1))))))
-
- (describe "when a connection matching current file extension doesn't exist"
- (it "returns the latest connection buffer"
- ;; for clj
- (with-connection-buffer "clj" b1
- (with-temp-buffer
- (setq major-mode 'clojurescript-mode)
- (expect (cider-current-connection) :to-equal b1)))
-
- ;; for cljs
- (with-connection-buffer "cljs" b2
- (with-temp-buffer
- (setq major-mode 'clojure-mode)
- (expect (cider-current-connection) :to-equal b2))))))))
-
-(describe "cider-other-connection"
- (describe "when there are no active connections"
- :var (cider-connections)
- (it "returns nil"
- (setq cider-connections nil)
- (expect (cider-other-connection) :to-equal nil)))
-
- (describe "when there is only 1 active connection"
- (it "returns nil"
- ;; for clj
- (with-connection-buffer "clj" b1
- (expect (cider-other-connection) :to-equal nil)
- (expect (cider-other-connection b1) :to-equal nil))
- ;; for cljs
- (with-connection-buffer "cljs" b1
- (expect (cider-other-connection) :to-equal nil)
- (expect (cider-other-connection b1) :to-equal nil))))
-
- (describe "when active connections are available"
- (describe "when a connection of other type doesn't exist"
- (it "returns nil"
- ;; for clj
- (with-connection-buffer "clj" b1
- (with-connection-buffer "clj" b2
- (expect (cider-other-connection) :to-equal nil)
- (expect (cider-other-connection b1) :to-equal nil)
- (expect (cider-other-connection b2) :to-equal nil)))
- ;; for cljs
- (with-connection-buffer "cljs" b1
- (with-connection-buffer "cljs" b2
- (expect (cider-other-connection) :to-equal nil)
- (expect (cider-other-connection b1) :to-equal nil)
- (expect (cider-other-connection b2) :to-equal nil)))))
-
- (describe "when a connection of other type exists"
- (it "returns that connection"
- (with-connection-buffer "clj" b1
- (with-connection-buffer "cljs" b2
- (expect (cider-other-connection) :to-equal b1)
- (expect (cider-other-connection b1) :to-equal b2)
- (expect (cider-other-connection b2) :to-equal b1)))))
-
- (describe "when there are multiple active connections"
- (it "always returns the latest connection"
-
- (with-connection-buffer "clj" bb1
- (with-connection-buffer "cljs" bb2
- (with-connection-buffer "clj" b1
- (with-connection-buffer "cljs" b2
- (expect (cider-other-connection) :to-equal b1)
- (expect (cider-other-connection b1) :to-equal b2)
- (expect (cider-other-connection b2) :to-equal b1)
- ;; older connections still work
- (expect (cider-other-connection bb1) :to-equal b2)
- (expect (cider-other-connection bb2) :to-equal b1)))))))))
-
(describe "cider-var-info"
(it "returns vars info as an alist"
(spy-on 'cider-sync-request:info :and-return-value
@@ -226,181 +50,45 @@
"tag" "class java.lang.String"
"status" ("done")))
(spy-on 'cider-ensure-op-supported :and-return-value t)
- (spy-on 'cider-current-session :and-return-value nil)
+ (spy-on 'cider-nrepl-eval-session :and-return-value nil)
(spy-on 'cider-current-ns :and-return-value "user")
(expect (nrepl-dict-get (cider-var-info "str") "doc")
:to-equal "stub")
(expect (cider-var-info "") :to-equal nil)))
-(describe "cider-toggle-buffer-connection"
- (spy-on 'message :and-return-value nil)
-
- (describe "when there are multiple connections"
- (it "toggles between multiple buffers"
- (with-connection-buffer "clj" clj-buffer
- (with-connection-buffer "cljs" cljs-buffer
- (with-temp-buffer
- (setq major-mode 'clojurec-mode)
- (expect (cider-connections)
- :to-equal (list cljs-buffer clj-buffer))
-
- (cider-toggle-buffer-connection)
- (expect (cider-connections)
- :to-equal (list clj-buffer))
- (cider-toggle-buffer-connection)
- (expect (cider-connections)
- :to-equal (list cljs-buffer))
-
- (cider-toggle-buffer-connection t)
- (expect (cider-connections)
- :to-equal (list cljs-buffer clj-buffer)))))))
-
- (describe "when there is a single connection"
- (it "reports a user error"
- (with-connection-buffer "clj" clj-buffer
- (with-temp-buffer
- (setq major-mode 'clojurec-mode)
- (expect (cider-connections)
- :to-equal (list clj-buffer))
-
- (expect (cider-toggle-buffer-connection) :to-throw 'user-error)
-
- (expect (cider-connections)
- :to-equal (list clj-buffer))
-
- (expect (local-variable-p 'cider-connections)
- :to-be nil))))))
-
-(describe "cider-make-connection-default"
- :var (connections)
-
- (it "makes the nrepl connection buffer, the default connection"
- (cider-test-with-buffers
- (a b)
- ;; Add one connection
- (cider-make-connection-default a)
- (expect (cider-default-connection) :to-equal a)
- ;; Add second connection
- (cider-make-connection-default b)
- (expect (cider-default-connection) :to-equal b)
- ;; Re-add first connection
- (cider-make-connection-default a)
- (expect (cider-default-connection) :to-equal a)))
-
- (it "moves the connection buffer to the front of `cider-connections'"
- (setq connections (cider-connections))
- (cider-test-with-buffers
- (a b)
- ;; Add one connection
- (cider-make-connection-default a)
- (expect (cider-connections) :to-equal (append (list a) connections))
- ;; Add second connection
- (cider-make-connection-default b)
- (expect (cider-connections) :to-equal (append (list b a) connections))
- ;; Re-add first connection
- (cider-make-connection-default a)
- (expect (cider-connections) :to-equal (append (list a b) connections)))))
-
-(describe "cider-connections"
- :var (connections)
- (it "removes a connection buffer from connections list, when it is killed"
- (setq connections (cider-connections))
- (cider-test-with-buffers
- (a b)
- (cider-make-connection-default a)
- (cider-make-connection-default b)
- (kill-buffer a)
- (expect (cider-default-connection) :to-equal b)
- (expect (cider-connections) :to-equal (append (list b) connections)))))
-
-(describe "cider-rotate-default-connection"
- (it "rotates the default nREPL connections in `cider-connections'"
- ;; to mute the output on stdout
- (spy-on 'message :and-return-value nil)
- (cider-test-with-buffers
- (a b c)
- (cider-make-connection-default c)
- (cider-make-connection-default b)
- (cider-make-connection-default a)
- (expect (cider-default-connection) :to-equal a)
- (cider-rotate-default-connection)
- (expect (cider-default-connection) :to-equal b)
- (cider-rotate-default-connection)
- (expect (cider-default-connection) :to-equal c)
- (cider-rotate-default-connection)
- (expect (cider-default-connection) :to-equal a))))
-
-(describe "cider--connection-info"
- (spy-on 'cider--java-version :and-return-value "1.7")
- (spy-on 'cider--clojure-version :and-return-value "1.7.0")
- (spy-on 'cider--nrepl-version :and-return-value "0.2.1")
-
- (describe "when current project is known"
- (it "returns information about the given connection buffer"
- (with-temp-buffer
- (setq-local nrepl-endpoint '("localhost" 4005))
- (setq-local nrepl-project-dir "proj")
- (setq-local cider-repl-type "clj")
- (expect (cider--connection-info (current-buffer))
- :to-equal "CLJ proj@localhost:4005 (Java 1.7, Clojure 1.7.0, nREPL 0.2.1)"))))
-
- (describe "when current project is not known"
- (it "returns information about the connection buffer without project name"
- (with-temp-buffer
- (setq-local nrepl-endpoint '("localhost" 4005))
- (setq-local cider-repl-type "clj")
- (expect (cider--connection-info (current-buffer))
- :to-equal "CLJ <no project>@localhost:4005 (Java 1.7, Clojure 1.7.0, nREPL 0.2.1)")))))
-
-(describe "cider--close-connection-buffer"
- :var (connections)
- (it "removes the connection from `cider-connections'"
- (setq connections (cider-connections))
- (cider-test-with-buffers
- (a b)
- (cider-make-connection-default a)
- (cider-make-connection-default b)
- ;; closing a buffer should see it removed from the connection list
- (cider--close-connection-buffer a)
- (expect (buffer-live-p a) :not :to-be-truthy)
- (expect (cider-connections) :to-equal (cons b connections))
- (expect (cider-default-connection) :to-equal b))))
-
-(describe "cider-connection-type-for-buffer"
+(describe "cider-repl-type-for-buffer"
:var (cider-repl-type)
(it "returns the matching connection type based on the mode of current buffer"
;; clojure mode
(with-temp-buffer
(clojure-mode)
- (expect (cider-connection-type-for-buffer) :to-equal "clj"))
+ (expect (cider-repl-type-for-buffer) :to-equal "clj"))
;; clojurescript mode
(with-temp-buffer
(clojurescript-mode)
- (expect (cider-connection-type-for-buffer) :to-equal "cljs")))
+ (expect (cider-repl-type-for-buffer) :to-equal "cljs")))
(it "returns the connection type based on `cider-repl-type'"
;; clj
(setq cider-repl-type "clj")
- (expect (cider-connection-type-for-buffer) :to-equal "clj")
+ (expect (cider-repl-type-for-buffer) :to-equal "clj")
;; cljs
(setq cider-repl-type "cljs")
- (expect (cider-connection-type-for-buffer) :to-equal "cljs"))
+ (expect (cider-repl-type-for-buffer) :to-equal "cljs"))
(it "returns nil as its default value"
(setq cider-repl-type nil)
- (expect (cider-connection-type-for-buffer) :to-equal nil)))
-
+ (expect (cider-repl-type-for-buffer) :to-equal nil)))
(describe "cider-nrepl-send-unhandled-request"
(it "returns the id of the request sent to nREPL server and ignores the response"
(spy-on 'process-send-string :and-return-value nil)
- (with-temp-buffer
+ (with-repl-buffer "cider-nrepl-send-request" "clj" b
(setq-local nrepl-pending-requests (make-hash-table :test 'equal))
(setq-local nrepl-completed-requests (make-hash-table :test 'equal))
- (let* ((cider-connections (list (current-buffer)))
- (id (cider-nrepl-send-unhandled-request '("op" "t" "extra" "me"))))
+ (let ((id (cider-nrepl-send-unhandled-request '("op" "t" "extra" "me"))))
;; the request should never be marked as pending
(expect (gethash id nrepl-pending-requests) :not :to-be-truthy)
@@ -411,72 +99,6 @@
(ignore-errors
(kill-buffer "*nrepl-messages*"))))
-(describe "cider-change-buffers-designation"
- (it "changes designation in all cider buffer names"
- (with-temp-buffer
- (let ((server-buffer (current-buffer)))
- (with-temp-buffer
- (let* ((connection-buffer (current-buffer))
- (cider-connections (list connection-buffer)))
- (setq-local nrepl-server-buffer server-buffer)
- (cider-change-buffers-designation "bob")
- (expect (buffer-name connection-buffer) :to-equal "*cider-repl bob*")
- (expect (buffer-name server-buffer) :to-equal "*nrepl-server bob*")
- (with-current-buffer connection-buffer
- (expect (buffer-name) :to-equal "*cider-repl bob*"))))))))
-
-
-(describe "cider-extract-designation-from-current-repl-buffer"
-
- (describe "when the buffers have a designation"
- (it "returns that designation string"
- (with-temp-buffer
- (let ((cider-connections (list (current-buffer)))
- (nrepl-repl-buffer-name-template "*cider-repl%s*"))
- (rename-buffer "*cider-repl bob*")
- (switch-to-buffer (current-buffer))
- (with-temp-buffer
- (switch-to-buffer (current-buffer))
- (expect (cider-extract-designation-from-current-repl-buffer)
- :to-equal "bob")
- (rename-buffer "*cider-repl apa*")
- (push (current-buffer) cider-connections)
- (expect (cider-extract-designation-from-current-repl-buffer)
- :to-equal "apa")
- (setq-local cider-connections (list (current-buffer)))
- (expect (cider-extract-designation-from-current-repl-buffer)
- :to-equal "apa"))))))
-
- (describe "when the buffers don't have a designation"
- (it "returns <no designation>"
- (with-temp-buffer
- (let* ((connection-buffer (current-buffer))
- (cider-connections (list connection-buffer)))
- (with-temp-buffer
- (let ((repl-buffer (current-buffer)))
- (rename-buffer "*cider-repl*")
- (with-temp-buffer
- (with-current-buffer connection-buffer
- (setq-local nrepl-repl-buffer repl-buffer))
- (expect (cider-extract-designation-from-current-repl-buffer)
- :to-equal "<no designation>")))))))))
-
-
-(describe "cider-project-name"
- (it "returns the project name extracted from the project dir"
- (expect (cider-project-name nil) :to-equal "-")
- (expect (cider-project-name "") :to-equal "-")
- (expect (cider-project-name "path/to/project") :to-equal "project")
- (expect (cider-project-name "path/to/project/") :to-equal "project")))
-
-(describe "cider-ensure-connected"
- (it "returns nil when a cider connection is available"
- (spy-on 'cider-connected-p :and-return-value t)
- (expect (cider-ensure-connected) :to-equal nil))
- (it "raises a user-error in the absence of a connection"
- (spy-on 'cider-connected-p :and-return-value nil)
- (expect (cider-ensure-connected) :to-throw 'user-error)))
-
(describe "cider-ensure-op-supported"
(it "returns nil when the op is supported"
(spy-on 'cider-nrepl-op-supported-p :and-return-value t)
diff --git a/test/cider-common-tests.el b/test/cider-common-tests.el
index b222f3ba..4b8144fc 100644
--- a/test/cider-common-tests.el
+++ b/test/cider-common-tests.el
@@ -28,7 +28,6 @@
;;; Code:
(require 'buttercup)
-(require 'cider)
(require 'cider-common)
;;; cider-common tests
diff --git a/test/cider-connection-tests.el b/test/cider-connection-tests.el
new file mode 100644
index 00000000..4d2c3b0e
--- /dev/null
+++ b/test/cider-connection-tests.el
@@ -0,0 +1,365 @@
+ ;;; cider-connection-tests.el
+
+;; Copyright © 2012-2018 Tim King, Bozhidar Batsov, Vitalie Spinu
+
+;; Author: Tim King <kingtim@gmail.com>
+;; Bozhidar Batsov <bozhidar@batsov.com>
+;; Vitalie Spinu <spinuvit@gmail.com>
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation, either version 3 of the
+;; License, or (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see `http://www.gnu.org/licenses/'.
+
+;;; Commentary:
+
+;; This file is part of CIDER
+
+;;; Code:
+
+(require 'buttercup)
+(require 'sesman)
+(require 'cider-connection)
+(require 'cider-connection-test-utils)
+
+(describe "cider-ensure-connected"
+ :var (sesman-sessions-hashmap sesman-links-alist ses-name ses-name2)
+
+ (before-each
+ (setq sesman-sessions-hashmap (make-hash-table :test #'equal)
+ sesman-links-alist nil
+ ses-name "a-session"
+ ses-name2 "b-session"))
+
+ (it "returns nil when a cider connection is available"
+ (let ((default-directory "/tmp/a-dir"))
+ (with-repl-buffer "cider-ensure-session" "clj" b
+ (expect (cider-ensure-connected) :to-equal
+ (list "cider-ensure-session" b)))))
+
+ (it "raises a user-error in the absence of a connection"
+ (expect (cider-ensure-connected) :to-throw 'user-error)))
+
+(describe "cider-current-repl"
+
+ :var (sesman-sessions-hashmap sesman-links-alist ses-name ses-name2)
+
+ (before-each
+ (setq sesman-sessions-hashmap (make-hash-table :test #'equal)
+ sesman-links-alist nil
+ ses-name "a-session"
+ ses-name2 "b-session"))
+
+ (describe "when there are no active connections"
+ (it "returns nil"
+ (expect (cider-current-repl) :not :to-be-truthy)
+ (expect (cider-current-repl "clj") :not :to-be-truthy)
+ (expect (cider-current-repl "cljs") :not :to-be-truthy)))
+
+ (describe "when active connections are available"
+
+ (it "always returns the latest connection"
+ (let ((default-directory "/tmp/a-dir"))
+ (with-repl-buffer ses-name "clj" bb1
+ (with-repl-buffer ses-name "cljs" bb2
+ (with-repl-buffer ses-name "clj" b1
+ (with-repl-buffer ses-name "cljs" b2
+ (expect (cider-current-repl) :to-equal b2)
+
+ ;; follows type arguments
+ (expect (cider-current-repl "clj") :to-equal b1)
+ (expect (cider-current-repl "cljs") :to-equal b2)
+
+ ;; follows file type
+ (with-temp-buffer
+ (setq major-mode 'clojure-mode)
+ (expect (cider-current-repl) :to-equal b1))
+
+ (with-temp-buffer
+ (setq major-mode 'clojurescript-mode)
+ (expect (cider-current-repl) :to-equal b2))))))))
+
+ (it "always returns the most recently used connection"
+ (let ((default-directory "/tmp/a-dir"))
+ (with-repl-buffer ses-name "clj" bb1
+ (with-repl-buffer ses-name "cljs" bb2
+ (with-repl-buffer ses-name "clj" b1
+ (with-repl-buffer ses-name "cljs" b2
+
+ (switch-to-buffer bb2)
+ (switch-to-buffer bb1)
+ (expect (cider-current-repl) :to-equal bb1)
+
+ ;; follows type arguments
+ (expect (cider-current-repl "clj") :to-equal bb1)
+ (message "%S" (seq-take (buffer-list) 10))
+ (expect (cider-current-repl "cljs") :to-equal bb2)
+
+ ;; follows file type
+ (with-temp-buffer
+ (setq major-mode 'clojure-mode)
+ (expect (cider-current-repl) :to-equal bb1))
+
+ (with-temp-buffer
+ (setq major-mode 'clojurescript-mode)
+ (expect (cider-current-repl) :to-equal bb2))))))))
+
+ (describe "when current buffer is a 'multi' buffer"
+ (describe "when there is only one connection available"
+ (it "returns the only connection"
+ (let ((default-directory "/tmp/a-dir"))
+ (with-repl-buffer ses-name "clj" b
+ (with-temp-buffer
+ (clojure-mode)
+ (expect (cider-current-repl "clj") :to-equal b))
+ (with-temp-buffer
+ (clojurec-mode)
+ (expect (cider-current-repl "clj") :to-equal b)))))))
+
+ (describe "when type argument is given"
+
+ (describe "when connection of that type exists"
+ (it "returns that connection buffer"
+ (let ((default-directory "/tmp/a-dir"))
+ ;; for clj
+ (with-repl-buffer ses-name "clj" b1
+ (with-repl-buffer ses-name "cljs" b2
+ (expect (cider-current-repl "clj") :to-equal b1)))
+ ;; for cljs
+ (with-repl-buffer ses-name "cljs" b1
+ (with-repl-buffer ses-name "clj" b2
+ (expect (cider-current-repl "cljs") :to-equal b1))))))
+
+ (describe "when connection of that type doesn't exists"
+ (it "returns nil"
+ ;; for clj
+ (with-repl-buffer ses-name "cljs" b1
+ (expect (cider-current-repl "clj") :to-equal nil))
+
+ ;; for cljs
+ (with-repl-buffer ses-name "clj" b2
+ (expect (cider-current-repl "cljs") :to-equal nil))))
+
+ (describe "when type argument is not given"
+
+ (describe "when a connection matching current file extension exists"
+ (it "returns that connection buffer"
+ (let ((default-directory "/tmp/a-dir"))
+ ;; for clj
+ (with-repl-buffer ses-name "clj" b1
+ (with-repl-buffer ses-name "cljs" b2
+ (with-temp-buffer
+ (setq major-mode 'clojure-mode)
+ (expect (cider-current-repl) :to-equal b1))))
+
+ ;; for cljs
+ (with-repl-buffer ses-name "cljs" b1
+ (with-repl-buffer ses-name "clj" b2
+ (with-temp-buffer
+ (setq major-mode 'clojurescript-mode)
+ (expect (cider-current-repl) :to-equal b1)))))))
+
+ (describe "when a connection matching current file extension doesn't exist"
+ (it "returns nil"
+ ;; for clj
+ (with-repl-buffer ses-name "clj" b1
+ (with-temp-buffer
+ (setq major-mode 'clojurescript-mode)
+ (expect (cider-current-repl) :to-equal nil)))
+
+ ;; for cljs
+ (with-repl-buffer ses-name "cljs" b2
+ (with-temp-buffer
+ (setq major-mode 'clojure-mode)
+ (expect (cider-current-repl) :to-equal nil))))))))
+
+ (describe "when multiple sessions exist"
+ (it "always returns the most recently used connection"
+ (let ((a-dir "/tmp/a-dir")
+ (b-dir "/tmp/b-dir"))
+ (let ((default-directory a-dir))
+ (with-repl-buffer ses-name "clj" bb1
+ (with-repl-buffer ses-name "cljs" bb2
+ (let ((default-directory a-dir))
+ (with-repl-buffer ses-name2 "clj" b1
+ (with-repl-buffer ses-name2 "cljs" b2
+
+ (switch-to-buffer bb2)
+ (switch-to-buffer bb1)
+ (expect (cider-current-repl) :to-equal bb1)
+
+ ;; follows type arguments
+ (expect (cider-current-repl "clj") :to-equal bb1)
+ (expect (cider-current-repl "cljs") :to-equal bb2)
+
+ ;; follows file type
+ (with-temp-buffer
+ (setq major-mode 'clojure-mode)
+ (expect (cider-current-repl) :to-equal bb1))
+ (with-temp-buffer
+ (setq major-mode 'clojurescript-mode)
+ (expect (cider-current-repl) :to-equal bb2))
+
+ (switch-to-buffer b2)
+ (message "%S" (sesman-sessions 'CIDER))
+ (with-temp-buffer
+ (expect (cider-current-repl) :to-equal b2))
+ (with-temp-buffer
+ (setq major-mode 'clojure-mode)
+ (expect (cider-current-repl) :to-equal b1))
+ (with-temp-buffer
+ (setq major-mode 'clojurescript-mode)
+ (expect (cider-current-repl) :to-equal b2))))))))))))
+
+(describe "cider-repls"
+
+ :var (sesman-sessions-hashmap sesman-links-alist ses-name ses-name2)
+
+ (before-each
+ (setq sesman-sessions-hashmap (make-hash-table :test #'equal)
+ sesman-links-alist nil
+ ses-name "a-session"
+ ses-name2 "b-session"))
+
+ (describe "when there are no active connections"
+ (it "returns nil"
+ (expect (cider-repls) :to-equal nil)
+ (expect (cider-repls "clj") :to-equal nil)
+ (expect (cider-repls "cljs") :to-equal nil)))
+
+ (describe "when multiple sessions exist"
+ (it "always returns the most recently used connection"
+ (let ((a-dir "/tmp/a-dir")
+ (b-dir "/tmp/b-dir"))
+ (let ((default-directory a-dir))
+ (with-repl-buffer ses-name "clj" bb1
+ (with-repl-buffer ses-name "cljs" bb2
+ (let ((default-directory b-dir))
+ (with-repl-buffer ses-name2 "clj" b1
+ (with-repl-buffer ses-name2 "cljs" b2
+
+ (expect (cider-repls) :to-equal (list b2 b1))
+
+ (switch-to-buffer bb1)
+ (expect (cider-repls) :to-equal (list bb2 bb1))
+
+ ;; follows type arguments
+ (expect (cider-repls "clj") :to-equal (list bb1))
+ (expect (cider-repls "cljs") :to-equal (list bb2))
+
+ (switch-to-buffer bb2)
+ ;; follows file type
+ (let ((default-directory b-dir))
+ (with-temp-buffer
+ (setq major-mode 'clojure-mode)
+ (expect (cider-repls) :to-equal (list b2 b1))
+ (expect (cider-repls "clj") :to-equal (list b1))))
+
+ (let ((default-directory a-dir))
+ (with-temp-buffer
+ (setq major-mode 'clojurescript-mode)
+ (expect (cider-repls) :to-equal (list bb2 bb1))
+ (expect (cider-repls "cljs") :to-equal (list bb2)))))))))))))
+
+ (describe "killed buffers"
+ (it "do not show up in it"
+ (let ((default-directory "/tmp/some-dir"))
+ (cider-test-with-buffers
+ (a b)
+ (let ((session (list "some-session" a b)))
+ (with-current-buffer a
+ (setq cider-repl-type "clj"))
+ (with-current-buffer b
+ (setq cider-repl-type "clj"))
+ (sesman-register 'CIDER session)
+ (expect (cider-repls) :to-equal (list a b))
+ (kill-buffer b)
+ (expect (cider-repls) :to-equal (list a))
+ (sesman-unregister 'CIDER session)))))))
+
+(describe "cider--connection-info"
+ (spy-on 'cider--java-version :and-return-value "1.7")
+ (spy-on 'cider--clojure-version :and-return-value "1.7.0")
+ (spy-on 'cider--nrepl-version :and-return-value "0.2.1")
+
+ (describe "when current project is known"
+ (it "returns information about the given connection buffer"
+ (with-temp-buffer
+ (setq-local nrepl-endpoint '(:host "localhost" :port 4005))
+ (setq-local nrepl-project-dir "proj")
+ (setq-local cider-repl-type "clj")
+ (expect (cider--connection-info (current-buffer))
+ :to-equal "CLJ proj@localhost:4005 (Java 1.7, Clojure 1.7.0, nREPL 0.2.1)"))))
+
+ (describe "when current project is not known"
+ (it "returns information about the connection buffer without project name"
+ (with-temp-buffer
+ (setq-local nrepl-endpoint '(:host "localhost" :port 4005))
+ (setq-local cider-repl-type "clj")
+ (expect (cider--connection-info (current-buffer))
+ :to-equal "CLJ <no project>@localhost:4005 (Java 1.7, Clojure 1.7.0, nREPL 0.2.1)")))))
+
+(describe "cider--close-connection"
+ (it "removes the REPL from sesman session"
+ (let ((default-directory "/tmp/some-dir"))
+ (cider-test-with-buffers
+ (a b)
+ (let ((session (list "some-session" a b)))
+ (with-current-buffer a
+ (setq cider-repl-type "clj"))
+ (with-current-buffer b
+ (setq cider-repl-type "clj"))
+ (sesman-register 'CIDER session)
+ (expect (cider-repls) :to-equal (list a b))
+ (cider--close-connection b)
+ (message "%S" sesman-links-alist)
+ (expect (buffer-live-p b) :not :to-be-truthy)
+ (expect (cider-repls) :to-equal (list a))
+ (sesman-unregister 'CIDER session))))))
+
+(describe "cider-format-connection-params"
+ (describe "correctly abbreviates short directory names"
+ (expect (cider-format-connection-params "%J" '(:project-dir "~"))
+ :to-equal "~")
+ (expect (cider-format-connection-params "%j" '(:project-dir "~"))
+ :to-equal "~")
+ (expect (cider-format-connection-params "%J" '(:project-dir "~/"))
+ :to-equal "~")
+ (expect (cider-format-connection-params "%J" '(:project-dir "/"))
+ :to-equal "/")
+ (expect (cider-format-connection-params "%J" '(:project-dir "/etc/"))
+ :to-equal "/etc")))
+
+(describe "cider-jack-in-clj&cljs"
+ :var (sesman-sessions-hashmap sesman-links-alist cider-default-cljs-repl)
+ (before-each
+ (setq sesman-sessions-hashmap (make-hash-table :test #'equal)
+ sesman-links-alist nil
+ cider-default-cljs-repl 'node)
+ (spy-on 'cider--gather-session-params
+ :and-return-value '(:project-dir "some/project" :host "localhost" :port 1234))
+ (spy-on 'nrepl-start-server-process
+ :and-return-value nil)
+ (spy-on 'sesman-linked-sessions
+ :and-return-value '(("a-session")))
+ (spy-on 'y-or-n-p
+ :and-return-value t)
+ (cider-jack-in-clj&cljs '(:project-dir "some/project" :host "localhost" :port 1234))
+ (cider-jack-in-clj&cljs '(:project-dir "some/project" :host "localhost"))
+ (cider-jack-in-clj&cljs '(:project-dir "some/project"))
+ (cider-jack-in-clj&cljs '(:project-dir "some/project" :host "other-host"))
+ (cider-jack-in-clj&cljs '(:project-dir "some/other/project")))
+ (it "detects existing project"
+ (expect 'y-or-n-p :to-have-been-called-times 3)))
+
+
diff --git a/test/cider-debug-tests.el b/test/cider-debug-tests.el
index f93732b7..e6afa658 100644
--- a/test/cider-debug-tests.el
+++ b/test/cider-debug-tests.el
@@ -28,7 +28,8 @@
;;; Code:
(require 'buttercup)
-(require 'cider)
+(require 'clojure-mode)
+(require 'cider-debug)
(describe "cider--debug-prompt"
(it "changes the font face to `cider-debug-prompt-face' for the first char"
diff --git a/test/cider-error-parsing-tests.el b/test/cider-error-parsing-tests.el
index ce137f49..299d47b3 100644
--- a/test/cider-error-parsing-tests.el
+++ b/test/cider-error-parsing-tests.el
@@ -27,7 +27,7 @@
;;; Code:
-(require 'cider)
+(require 'cider-eval)
(require 'buttercup)
(describe "cider-extract-error-info"
diff --git a/test/cider-find-tests.el b/test/cider-find-tests.el
new file mode 100644
index 00000000..3fcb3cb0
--- /dev/null
+++ b/test/cider-find-tests.el
@@ -0,0 +1,37 @@
+;;; cider-find-tests.el
+
+;; Copyright © 2012-2018 Bozhidar Batsov
+
+;; Author: Bozhidar Batsov <bozhidar@batsov.com>
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation, either version 3 of the
+;; License, or (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see `http://www.gnu.org/licenses/'.
+
+;;; Commentary:
+
+;; This file is part of CIDER
+
+;;; Code:
+
+(require 'buttercup)
+(require 'cider-find)
+
+(describe "cider-find-ns"
+ (it "raises a user error if cider is not connected"
+ (spy-on 'cider-connected-p :and-return-value nil)
+ (expect (cider-find-ns) :to-throw 'user-error))
+ (it "raises a user error if the op is not supported"
+ (spy-on 'cider-nrepl-op-supported-p :and-return-value nil)
+ (expect (cider-find-ns) :to-throw 'user-error)))
diff --git a/test/cider-font-lock-tests.el b/test/cider-font-lock-tests.el
index fd4a6db5..1d135d9f 100644
--- a/test/cider-font-lock-tests.el
+++ b/test/cider-font-lock-tests.el
@@ -64,9 +64,11 @@
(describe "reader conditional font-lock"
(describe "when cider is connected"
+
(it "uses cider-reader-conditional-face"
(spy-on 'cider-connected-p :and-return-value t)
- (spy-on 'cider-project-connections-types :and-return-value '("clj"))
+ (spy-on 'cider-repls :and-return-value '(list t))
+ (spy-on 'cider-repl-type :and-return-value "clj")
(cider--test-with-temp-buffer "#?(:clj 'clj :cljs 'cljs :cljr 'cljr)"
(let ((cider-font-lock-reader-conditionals t)
(found (cider--face-exists-in-range-p (point-min) (point-max)
@@ -75,19 +77,21 @@
(it "highlights unmatched reader conditionals"
(spy-on 'cider-connected-p :and-return-value t)
- (spy-on 'cider-project-connections-types :and-return-value '("clj"))
+ (spy-on 'cider-repls :and-return-value '(list t))
+ (spy-on 'cider-repl-type :and-return-value "clj")
(cider--test-with-temp-buffer "#?(:clj 'clj :cljs 'cljs :cljr 'cljr)"
(let ((cider-font-lock-reader-conditionals t))
(expect (cider--face-exists-in-range-p 4 12 'cider-reader-conditional-face)
:not :to-be-truthy)
(expect (cider--face-covers-range-p 14 24 'cider-reader-conditional-face)
:to-be-truthy)
- (expect (cider--face-covers-range-p 26 36 'cider-reader-conditional-face)
+ (expect (cider--face-covers-range-p 26 34 'cider-reader-conditional-face)
:to-be-truthy))))
(it "works with splicing"
(spy-on 'cider-connected-p :and-return-value t)
- (spy-on 'cider-project-connections-types :and-return-value '("clj"))
+ (spy-on 'cider-repls :and-return-value '(list t))
+ (spy-on 'cider-repl-type :and-return-value "clj")
(cider--test-with-temp-buffer "[1 2 #?(:clj [3 4] :cljs [5 6] :cljr [7 8])]"
(let ((cider-font-lock-reader-conditionals t))
(expect (cider--face-exists-in-range-p 1 18 'cider-reader-conditional-face)
@@ -99,15 +103,8 @@
(it "does not apply inside strings or comments"
(spy-on 'cider-connected-p :and-return-value t)
- (spy-on 'cider-project-connections-types :and-return-value '("clj"))
- (cider--test-with-temp-buffer "\"#?(:clj 'clj :cljs 'cljs :cljr 'cljr)\" ;; #?(:clj 'clj :cljs 'cljs :cljr 'cljr)"
- (let ((cider-font-lock-reader-conditionals t))
- (expect (cider--face-exists-in-range-p (point-min) (point-max) 'cider-reader-conditional-face)
- :not :to-be-truthy))))
-
- (it "does not apply inside strings or comments"
- (spy-on 'cider-connected-p :and-return-value t)
- (spy-on 'cider-project-connections-types :and-return-value '("clj"))
+ (spy-on 'cider-repls :and-return-value '(list t))
+ (spy-on 'cider-repl-type :and-return-value "clj")
(cider--test-with-temp-buffer "\"#?(:clj 'clj :cljs 'cljs :cljr 'cljr)\" ;; #?(:clj 'clj :cljs 'cljs :cljr 'cljr)"
(let ((cider-font-lock-reader-conditionals t))
(expect (cider--face-exists-in-range-p (point-min) (point-max) 'cider-reader-conditional-face)
@@ -115,7 +112,8 @@
(it "highlights all unmatched reader conditionals"
(spy-on 'cider-connected-p :and-return-value t)
- (spy-on 'cider-project-connections-types :and-return-value '("cljs"))
+ (spy-on 'cider-repls :and-return-value '(list t))
+ (spy-on 'cider-repl-type :and-return-value "cljs")
(cider--test-with-temp-buffer
"#?(:clj 'clj :cljs 'cljs :cljr 'cljr)\n#?(:clj 'clj :cljs 'cljs :cljr 'cljr)\n"
(let ((cider-font-lock-reader-conditionals t))
@@ -130,7 +128,8 @@
(it "does not highlight beyond the limits of the reader conditional group"
(spy-on 'cider-connected-p :and-return-value t)
- (spy-on 'cider-project-connections-types :and-return-value '("clj"))
+ (spy-on 'cider-repls :and-return-value '(list t))
+ (spy-on 'cider-repl-type :and-return-value "clj")
(cider--test-with-temp-buffer
"#?(:clj 'clj :cljs 'cljs :cljr 'cljr)\n#?(:clj 'clj :cljs 'cljs :cljr 'cljr)\n"
(let ((cider-font-lock-reader-conditionals t))
@@ -144,12 +143,13 @@
(describe "when multiple connections are connected"
(it "is disabled"
(spy-on 'cider-connected-p :and-return-value nil)
- (spy-on 'cider-project-connections-types :and-return-value '("clj" "cljs"))
+ (spy-on 'cider-repls :and-return-value '(list t))
+ (spy-on 'cider-repl-type :and-return-value '("clj" "cljs"))
(cider--test-with-temp-buffer "#?(:clj 'clj :cljs 'cljs :cljr 'cljr)"
(let ((cider-font-lock-reader-conditionals t))
(expect (cider--face-exists-in-range-p (point-min) (point-max) 'cider-reader-conditional-face)
:not :to-be-truthy)))))
-
+
(describe "when cider is not connected"
(it "is disabled"
(spy-on 'cider-connected-p :and-return-value nil)
diff --git a/test/cider-grimoire-tests.el b/test/cider-grimoire-tests.el
index 66c7f6ec..0fd1c80b 100644
--- a/test/cider-grimoire-tests.el
+++ b/test/cider-grimoire-tests.el
@@ -28,7 +28,6 @@
;;; Code:
(require 'buttercup)
-(require 'cider)
(require 'cider-grimoire)
;;; grimoire tests
diff --git a/test/cider-interaction-tests.el b/test/cider-interaction-tests.el
index 46ac0c75..6dc8e379 100644
--- a/test/cider-interaction-tests.el
+++ b/test/cider-interaction-tests.el
@@ -1,4 +1,4 @@
-;;; cider-interaction-tests.el
+;;; cider-eval-tests.el
;; Copyright © 2012-2018 Tim King, Bozhidar Batsov
@@ -28,7 +28,7 @@
;;; Code:
(require 'buttercup)
-(require 'cider-interaction)
+(require 'cider-eval)
(require 'cider-connection-test-utils)
(describe "cider--var-namespace"
@@ -55,11 +55,6 @@
(expect (funcall cider-to-nrepl-filename-function unix-file-name)
:to-equal unix-file-name)))))
-(describe "cider-refresh"
- (it "raises a user error if cider is not connected"
- (spy-on 'cider-connected-p :and-return-value nil)
- (expect (cider-refresh) :to-throw 'user-error)))
-
(describe "cider-quit"
(it "raises a user error if cider is not connected"
(spy-on 'cider-connected-p :and-return-value nil)
@@ -70,14 +65,6 @@
(spy-on 'cider-connected-p :and-return-value nil)
(expect (cider-restart) :to-throw 'user-error)))
-(describe "cider-find-ns"
- (it "raises a user error if cider is not connected"
- (spy-on 'cider-connected-p :and-return-value nil)
- (expect (cider-find-ns) :to-throw 'user-error))
- (it "raises a user error if the op is not supported"
- (spy-on 'cider-nrepl-op-supported-p :and-return-value nil)
- (expect (cider-find-ns) :to-throw 'user-error)))
-
(describe "cider-load-all-project-ns"
(it "raises a user error if cider is not connected"
(spy-on 'cider-connected-p :and-return-value nil)
@@ -89,19 +76,21 @@
(describe "cider-load-file"
(it "works as expected in empty Clojure buffers"
(spy-on 'cider-request:load-file :and-return-value nil)
- (with-connection-buffer "clj" b
- (with-temp-buffer
- (clojure-mode)
- (setq buffer-file-name (make-temp-name "tmp.clj"))
- (expect (cider-load-buffer) :not :to-throw)))))
+ (let ((default-directory "/tmp/a-dir"))
+ (with-repl-buffer "load-file-session" "clj" b
+ (with-temp-buffer
+ (clojure-mode)
+ (setq buffer-file-name (make-temp-name "tmp.clj"))
+ (expect (cider-load-buffer) :not :to-throw))))))
(describe "cider-interactive-eval"
(it "works as expected in empty Clojure buffers"
(spy-on 'cider-nrepl-request:eval :and-return-value nil)
- (with-connection-buffer "clj" b
- (with-temp-buffer
- (clojure-mode)
- (expect (cider-interactive-eval "(+ 1)") :not :to-throw)))))
+ (let ((default-directory "/tmp/a-dir"))
+ (with-repl-buffer "interaction-session" "clj" b
+ (with-temp-buffer
+ (clojure-mode)
+ (expect (cider-interactive-eval "(+ 1)") :not :to-throw))))))
(describe "cider--calculate-opening-delimiters"
(it "returns the right opening delimiters"
diff --git a/test/cider-locals-tests.el b/test/cider-locals-tests.el
index 26d7bdf8..43dde7be 100644
--- a/test/cider-locals-tests.el
+++ b/test/cider-locals-tests.el
@@ -19,7 +19,6 @@
(require 'buttercup)
(require 'cider-mode)
-(require 'cider)
(defmacro cider--test-with-content (content expected &rest body)
(declare (indent 2)
diff --git a/test/cider-ns-tests.el b/test/cider-ns-tests.el
new file mode 100644
index 00000000..ed28984a
--- /dev/null
+++ b/test/cider-ns-tests.el
@@ -0,0 +1,34 @@
+;;; cider-ns-tests.el
+
+;; Copyright © 2018 Bozhidar Batsov
+
+;; Author: Bozhidar Batsov <bozhidar@batsov.com>
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software: you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation, either version 3 of the
+;; License, or (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see `http://www.gnu.org/licenses/'.
+
+;;; Commentary:
+
+;; This file is part of CIDER
+
+;;; Code:
+
+(require 'buttercup)
+(require 'cider-ns)
+
+(describe "cider-ns-refresh"
+ (it "raises a user error if cider is not connected"
+ (spy-on 'cider-connected-p :and-return-value nil)
+ (expect (cider-ns-refresh) :to-throw 'user-error)))
diff --git a/test/cider-repl-tests.el b/test/cider-repl-tests.el
index 0a40ac0f..5229b03a 100644
--- a/test/cider-repl-tests.el
+++ b/test/cider-repl-tests.el
@@ -36,8 +36,7 @@
(spy-on 'cider--java-version :and-return-value "1.8.0_31")
(spy-on 'cider--clojure-version :and-return-value "1.8.0")
(spy-on 'cider--nrepl-version :and-return-value "0.2.12")
- (spy-on 'cider--connection-host :and-return-value "localhost")
- (spy-on 'cider--connection-port :and-return-value "54018")
+ (setq nrepl-endpoint (list :host "localhost" :port "54018"))
(setq cider-version "0.12.0")
(setq cider-codename "Seattle"))
@@ -154,3 +153,61 @@ PROPERTY shoudl be a symbol of either 'text, 'ansi-context or
(expect (cider-repl--build-config-expression)
:to-equal
"(do (set! *print-length* 10) (set! *print-level* 10))"))))
+
+(describe "cider-locref-at-point"
+ (it "works with stdout-stacktrace refs"
+ (with-temp-buffer
+ (insert "\n\tat clojure.lang.AFn.applyToHelper(AFn.java:160)")
+ (expect (cider-locref-at-point)
+ :to-equal
+ '(:type stdout-stacktrace :highlight (3 . 50) :var "clojure.lang.AFn.applyToHelper" :file "AFn.java" :line 160))
+ (insert "\n\tat cljs.analyzer$macroexpand_1_STAR_$fn__4642.invoke(analyzer.cljc:3286)")
+ (expect (cider-locref-at-point)
+ :to-equal
+ '(:type stdout-stacktrace :highlight (52 . 124) :var "cljs.analyzer" :file "analyzer.cljc" :line 3286))
+ (insert "\n\tat cljs.closure$compile_file.invoke(closure.clj:531)")
+ (expect (cider-locref-at-point)
+ :to-equal
+ '(:type stdout-stacktrace :highlight (126 . 178) :var "cljs.closure" :file "closure.clj" :line 531))))
+ (it "works with print-stacktrace"
+ (with-temp-buffer
+ (insert "\n[clojure.core$eval invoke core.clj 3202]")
+ (expect (cider-locref-at-point)
+ :to-equal
+ '(:type print-stacktrace :highlight (2 . 42) :var "clojure.core" :file "core.clj" :line 3202))))
+ (it "works with avis exceptions"
+ (with-temp-buffer
+ (insert "\n java.util.concurrent.ThreadPoolExecutor$Worker.run ThreadPoolExecutor.java: 624
+ clojure.tools.nrepl.middleware.interruptible-eval/run-next/fn interruptible_eval.clj: 190")
+ (expect (cider-locref-at-point)
+ :to-equal
+ '(:type aviso-stacktrace :highlight (121 . 213) :var "clojure.tools.nrepl.middleware.interruptible-eval" :file "interruptible_eval.clj" :line 190))
+ (line-move -1)
+ (expect (cider-locref-at-point)
+ :to-equal
+ '(:type aviso-stacktrace :highlight (26 . 107) :var "java.util.concurrent.ThreadPoolExecutor" :file "ThreadPoolExecutor.java" :line 624))))
+ (it "works with timbre logs"
+ (with-temp-buffer
+ (insert "\n18-05-12 10:17:52 galago ERROR [errors:8] - An error
+18-05-12 10:17:52 galago WARN [errors:8] - A warning
+18-05-12 10:17:52 galago INFO [errors:8] - An info")
+ (expect (cider-locref-at-point)
+ :to-equal
+ '(:type timbre-log :highlight (138 . 148) :var "errors" :file nil :line 8))
+ (line-move -1)
+ (expect (cider-locref-at-point)
+ :to-equal
+ '(:type timbre-log :highlight (85 . 95) :var "errors" :file nil :line 8))))
+ (it "works with cljs warnings"
+ (with-temp-buffer
+ (insert "\nWARNING: Wrong number of args (1) passed to aaa/bbb at line 42 /path/to/aaa/bbb.cljc")
+ (expect (cider-locref-at-point)
+ :to-equal
+ '(:type cljs-message :highlight (54 . 86) :var nil :file "/path/to/aaa/bbb.cljc" :line 42))))
+ (it "works with reflection warnings"
+ (with-temp-buffer
+ (insert "\nReflection warning, cider/nrepl/middleware/slurp.clj:103:16 - reference to field getInputStream can't be resolved.")
+ (move-to-column 20)
+ (expect (cider-locref-at-point)
+ :to-equal
+ '(:type reflection :highlight (22 . 61) :var nil :file "cider/nrepl/middleware/slurp.clj" :line 103)))))
diff --git a/test/cider-selector-tests.el b/test/cider-selector-tests.el
index 63eee4c9..88a87949 100644
--- a/test/cider-selector-tests.el
+++ b/test/cider-selector-tests.el
@@ -28,8 +28,8 @@
;;; Code:
(require 'buttercup)
-(require 'cider)
(require 'cider-selector)
+(require 'cider-connection-test-utils)
;; selector
(defun cider-invoke-selector-method-by-key (ch)
@@ -46,18 +46,6 @@
(cider-invoke-selector-method-by-key method)
(expect (current-buffer) :to-equal expected-buffer)))))
-(describe "cider-selector-n"
- :var (cider-endpoint cider-connections)
- (it "switches to the connection browser buffer"
- (with-temp-buffer
- (setq cider-endpoint '("123.123.123.123" 4006)
- cider-connections (list (current-buffer)))
- (with-temp-buffer
- ;; switch to another buffer
- (cider-invoke-selector-method-by-key ?n)
- (expect (current-buffer) :to-equal
- (get-buffer cider--connection-browser-buffer-name))))))
-
(describe "cider-seletor-method-c"
(it "switches to most recently visited clojure-mode buffer"
(cider--test-selector-method ?c 'clojure-mode "*testfile*.clj")))
@@ -68,16 +56,19 @@
(cider--test-selector-method ?e 'emacs-lisp-mode "*testfile*.el")))
(describe "cider-seletor-method-r"
- :var (cider-current-repl-buffer)
+ :var (cider-current-repl)
(it "switches to current REPL buffer"
- (spy-on 'cider-current-repl-buffer :and-return-value "*cider-repl xyz*")
+ (spy-on 'cider-current-repl :and-return-value "*cider-repl xyz*")
(cider--test-selector-method ?r 'cider-repl-mode "*cider-repl xyz*")))
-(describe "cider-selector-method-m"
- :var (cider-current-messages-buffer)
- (it "switches to current connection's *nrepl-messages* buffer"
- (spy-on 'cider-current-messages-buffer :and-return-value "*nrepl-messages conn-id*")
- (cider--test-selector-method ?m nil "*nrepl-messages conn-id*")))
+;; FIXME: should work but doesn't with a nonsense error
+;; (describe "cider-selector-method-m"
+;; (it "switches to current connection's *nrepl-messages* buffer"
+;; (let ((buf (get-buffer-create "*nrepl-messages some-id*")))
+;; (with-repl-buffer "a-session" "clj" _
+;; (setq-local nrepl-messages-buffer buf)
+;; (message "%S" (nrepl-messages-buffer (cider-current-repl)))
+;; (cider--test-selector-method ?m nil "*nrepl-messages some-id*")))))
(describe "cider-seletor-method-x"
(it "switches to *cider-error* buffer"
@@ -88,7 +79,7 @@
(cider--test-selector-method ?d 'cider-stacktrace-mode "*cider-doc*")))
(describe "cider-seletor-method-s"
- :var (cider-find-or-create-scratch-buffer)
+ :var (cider-scratch-find-or-create-buffer)
(it "switches to *cider-scratch* buffer"
- (spy-on 'cider-find-or-create-scratch-buffer :and-return-value "*cider-scratch*")
+ (spy-on 'cider-scratch-find-or-create-buffer :and-return-value "*cider-scratch*")
(cider--test-selector-method ?s 'cider-docview-mode "*cider-scratch*")))
diff --git a/test/cider-stacktrace-tests.el b/test/cider-stacktrace-tests.el
index 91e4da15..94c09566 100644
--- a/test/cider-stacktrace-tests.el
+++ b/test/cider-stacktrace-tests.el
@@ -28,7 +28,6 @@
;;; Code:
(require 'buttercup)
-(require 'cider)
(require 'cider-stacktrace)
;;; cider-stacktrace tests
diff --git a/test/cider-tests.el b/test/cider-tests.el
index 179fcd71..61796cc3 100644
--- a/test/cider-tests.el
+++ b/test/cider-tests.el
@@ -34,46 +34,73 @@
(it "opens without error"
(customize-group 'cider)))
-;;; connection browser
-
-(describe "cider-connections-buffer"
- (it "lists all the active connections"
- (with-temp-buffer
- (rename-buffer "*cider-repl test1*")
- (let ((b1 (current-buffer)))
- (setq-local nrepl-endpoint '("localhost" 4005))
- (setq-local nrepl-project-dir "proj")
- (setq-local cider-repl-type "clj")
- (with-temp-buffer
- (rename-buffer "*cider-repl test2*")
- (let ((b2 (current-buffer)))
- (setq-local nrepl-endpoint '("123.123.123.123" 4006))
- (setq-local cider-repl-type "clj")
- (let ((cider-connections (list b1 b2)))
- (cider-connection-browser)
- (with-current-buffer "*cider-connections*"
- (expect (buffer-string) :to-equal " REPL Host Port Project Type
-
-* *cider-repl test1* localhost 4005 proj Clojure
- *cider-repl test2* 123.123.123.123 4006 - Clojure\n\n")
-
- (goto-line 4) ; somewhere in the second connection listed
- (cider-connections-make-default)
- (expect (car cider-connections) :to-equal b2)
- (message "%s" (cider-connections))
- (expect (buffer-string) :to-equal " REPL Host Port Project Type
-
- *cider-repl test1* localhost 4005 proj Clojure
-* *cider-repl test2* 123.123.123.123 4006 - Clojure\n\n")
- (goto-line 4) ; somewhere in the second connection listed
- (cider-connections-close-connection)
- (expect cider-connections :to-equal (list b1))
- (expect (buffer-string) :to-equal " REPL Host Port Project Type
-
-* *cider-repl test1* localhost 4005 proj Clojure\n\n")
- (cider-connections-goto-connection)
- (expect (current-buffer) :to-equal b1)
- (kill-buffer "*cider-connections*")))))))))
+(describe "cider-figwheel-main-init-form"
+ ;; whitespace checks sprinkled amongst other tests
+ (describe "from options"
+ (it "leaves keywords alone"
+ (let ((cider-figwheel-main-default-options ":dev "))
+ (expect (cider-figwheel-main-init-form) :to-equal "(do (require 'figwheel.main) (figwheel.main/start :dev))")))
+ (it "leaves maps alone"
+ (let ((cider-figwheel-main-default-options " {:a 1 :b 2}"))
+ (expect (cider-figwheel-main-init-form) :to-equal "(do (require 'figwheel.main) (figwheel.main/start {:a 1 :b 2}))")))
+ (it "leaves s-exprs alone"
+ (let ((cider-figwheel-main-default-options " (hashmap :a 1 :b 2)"))
+ (expect (cider-figwheel-main-init-form) :to-equal "(do (require 'figwheel.main) (figwheel.main/start (hashmap :a 1 :b 2)))")))
+ (it "prepends colon to plain names"
+ (let ((cider-figwheel-main-default-options " dev"))
+ (expect (cider-figwheel-main-init-form) :to-equal "(do (require 'figwheel.main) (figwheel.main/start :dev))"))))
+
+ (describe "from minibuffer"
+ (before-each
+ ;; not necessary as of this writing, but it can't hurt
+ (setq-local cider-figwheel-main-default-options nil))
+ (it "leaves keywords alone"
+ (spy-on 'read-from-minibuffer :and-return-value " :prod ")
+ (expect (cider-figwheel-main-init-form) :to-equal "(do (require 'figwheel.main) (figwheel.main/start :prod))"))
+ (it "leaves maps alone"
+ (spy-on 'read-from-minibuffer :and-return-value " {:c 3 :d 4}")
+ (expect (cider-figwheel-main-init-form) :to-equal "(do (require 'figwheel.main) (figwheel.main/start {:c 3 :d 4}))"))
+ (it "leaves s-exprs alone"
+ (spy-on 'read-from-minibuffer :and-return-value "(keyword \"dev\") ")
+ (expect (cider-figwheel-main-init-form) :to-equal "(do (require 'figwheel.main) (figwheel.main/start (keyword \"dev\")))"))
+ (it "prepends colon to plain names"
+ (spy-on 'read-from-minibuffer :and-return-value "prod ")
+ (expect (cider-figwheel-main-init-form) :to-equal "(do (require 'figwheel.main) (figwheel.main/start :prod))"))))
+
+(describe "cider-project-type"
+ (describe "when there is a single project"
+ (it "returns that type"
+ (spy-on 'cider--identify-buildtools-present
+ :and-return-value '(lein))
+ (expect (cider-project-type) :to-equal 'lein)))
+
+ (describe "when there are multiple possible project types"
+ (before-all
+ (spy-on 'cider--identify-buildtools-present
+ :and-return-value '(build-tool1 build-tool2))
+ ;; user choice build-tool2
+ (spy-on 'completing-read :and-return-value "build-tool2"))
+
+ (it "returns the choice entered by user"
+ (expect (cider-project-type) :to-equal 'build-tool2))
+
+ (it "respects the value of `cider-preferred-build-tool'"
+ (let ((cider-preferred-build-tool 'build-tool1))
+ (expect (cider-project-type) :to-equal 'build-tool1))
+
+ (let ((cider-preferred-build-tool "invalid choice"))
+ (expect (cider-project-type) :to-equal 'build-tool2))
+
+ (let ((cider-preferred-build-tool 'build-tool3))
+ (expect (cider-project-type) :to-equal 'build-tool2))))
+
+ (describe "when there are no choices available"
+ (it "returns the value of `cider-jack-in-default'"
+ (spy-on 'cider--identify-buildtools-present
+ :and-return-value '())
+ (expect (cider-project-type) :to-equal cider-jack-in-default))))
+
+;;; cider-jack-in tests
(describe "cider-inject-jack-in-dependencies"
:var (cider-jack-in-dependencies cider-jack-in-nrepl-middlewares cider-jack-in-lein-plugins cider-jack-in-dependencies-exclusions)
@@ -86,25 +113,25 @@
(setq-local cider-jack-in-dependencies-exclusions '()))
(it "can inject dependencies in a lein project"
- (expect (cider-inject-jack-in-dependencies "" "repl :headless" "lein")
+ (expect (cider-inject-jack-in-dependencies "" "repl :headless" 'lein)
:to-equal "update-in :dependencies conj \\[org.clojure/tools.nrepl\\ \\\"0.2.12\\\"\\] -- update-in :plugins conj \\[cider/cider-nrepl\\ \\\"0.10.0-SNAPSHOT\\\"\\] -- repl :headless"))
(it "can inject dependencies in a lein project with an exclusion"
- (setq-local cider-jack-in-dependencies-exclusions '(("org.clojure/tools.nrepl" ("org.clojure/clojure"))))
- (expect (cider-inject-jack-in-dependencies "" "repl :headless" "lein")
- :to-equal "update-in :dependencies conj \\[org.clojure/tools.nrepl\\ \\\"0.2.12\\\"\\ \\:exclusions\\ \\[org.clojure/clojure\\]\\] -- update-in :plugins conj \\[cider/cider-nrepl\\ \\\"0.10.0-SNAPSHOT\\\"\\] -- repl :headless"))
+ (setq-local cider-jack-in-dependencies-exclusions '(("org.clojure/tools.nrepl" ("org.clojure/clojure"))))
+ (expect (cider-inject-jack-in-dependencies "" "repl :headless" 'lein)
+ :to-equal "update-in :dependencies conj \\[org.clojure/tools.nrepl\\ \\\"0.2.12\\\"\\ \\:exclusions\\ \\[org.clojure/clojure\\]\\] -- update-in :plugins conj \\[cider/cider-nrepl\\ \\\"0.10.0-SNAPSHOT\\\"\\] -- repl :headless"))
(it "can inject dependencies in a lein project with multiple exclusions"
- (setq-local cider-jack-in-dependencies-exclusions '(("org.clojure/tools.nrepl" ("org.clojure/clojure" "foo.bar/baz"))))
- (expect (cider-inject-jack-in-dependencies "" "repl :headless" "lein")
- :to-equal "update-in :dependencies conj \\[org.clojure/tools.nrepl\\ \\\"0.2.12\\\"\\ \\:exclusions\\ \\[org.clojure/clojure\\ foo.bar/baz\\]\\] -- update-in :plugins conj \\[cider/cider-nrepl\\ \\\"0.10.0-SNAPSHOT\\\"\\] -- repl :headless"))
+ (setq-local cider-jack-in-dependencies-exclusions '(("org.clojure/tools.nrepl" ("org.clojure/clojure" "foo.bar/baz"))))
+ (expect (cider-inject-jack-in-dependencies "" "repl :headless" 'lein)
+ :to-equal "update-in :dependencies conj \\[org.clojure/tools.nrepl\\ \\\"0.2.12\\\"\\ \\:exclusions\\ \\[org.clojure/clojure\\ foo.bar/baz\\]\\] -- update-in :plugins conj \\[cider/cider-nrepl\\ \\\"0.10.0-SNAPSHOT\\\"\\] -- repl :headless"))
(it "can inject dependencies in a boot project"
- (expect (cider-inject-jack-in-dependencies "" "repl -s wait" "boot")
+ (expect (cider-inject-jack-in-dependencies "" "repl -s wait" 'boot)
:to-equal "-i \"(require 'cider.tasks)\" -d org.clojure/tools.nrepl\\:0.2.12 -d cider/cider-nrepl\\:0.10.0-SNAPSHOT cider.tasks/add-middleware -m cider.nrepl/cider-middleware repl -s wait"))
(it "can inject dependencies in a gradle project"
- (expect (cider-inject-jack-in-dependencies "" "--no-daemon clojureRepl" "gradle")
+ (expect (cider-inject-jack-in-dependencies "" "--no-daemon clojureRepl" 'gradle)
:to-equal "--no-daemon clojureRepl")))
(describe "when there are multiple dependencies"
@@ -113,11 +140,11 @@
(setq-local cider-jack-in-nrepl-middlewares '("refactor-nrepl.middleware/wrap-refactor" "cider.nrepl/cider-middleware"))
(setq-local cider-jack-in-dependencies-exclusions '()))
(it "can inject dependencies in a lein project"
- (expect (cider-inject-jack-in-dependencies "" "repl :headless" "lein")
+ (expect (cider-inject-jack-in-dependencies "" "repl :headless" 'lein)
:to-equal "update-in :dependencies conj \\[org.clojure/tools.nrepl\\ \\\"0.2.12\\\"\\] -- update-in :plugins conj \\[refactor-nrepl\\ \\\"2.0.0\\\"\\] -- update-in :plugins conj \\[cider/cider-nrepl\\ \\\"0.11.0\\\"\\] -- repl :headless"))
(it "can inject dependencies in a boot project"
- (expect (cider-inject-jack-in-dependencies "" "repl -s wait" "boot")
+ (expect (cider-inject-jack-in-dependencies "" "repl -s wait" 'boot)
:to-equal "-i \"(require 'cider.tasks)\" -d org.clojure/tools.nrepl\\:0.2.12 -d refactor-nrepl\\:2.0.0 -d cider/cider-nrepl\\:0.11.0 cider.tasks/add-middleware -m refactor-nrepl.middleware/wrap-refactor -m cider.nrepl/cider-middleware repl -s wait")))
(describe "when there are global options"
@@ -127,14 +154,14 @@
(setq-local cider-jack-in-lein-plugins '(("cider/cider-nrepl" "0.11.0")))
(setq-local cider-jack-in-dependencies-exclusions '()))
(it "can concat in a lein project"
- (expect (cider-inject-jack-in-dependencies "-o -U" "repl :headless" "lein")
- :to-equal "-o -U update-in :dependencies conj \\[org.clojure/tools.nrepl\\ \\\"0.2.12\\\"\\] -- update-in :plugins conj \\[cider/cider-nrepl\\ \\\"0.11.0\\\"\\] -- repl :headless"))
+ (expect (cider-inject-jack-in-dependencies "-o -U" "repl :headless" 'lein)
+ :to-equal "-o -U update-in :dependencies conj \\[org.clojure/tools.nrepl\\ \\\"0.2.12\\\"\\] -- update-in :plugins conj \\[cider/cider-nrepl\\ \\\"0.11.0\\\"\\] -- repl :headless"))
(it "can concat in a boot project"
- (expect (cider-inject-jack-in-dependencies "-C -o" "repl -s wait" "boot")
- :to-equal "-C -o -i \"(require 'cider.tasks)\" -d org.clojure/tools.nrepl\\:0.2.12 -d cider/cider-nrepl\\:0.11.0 cider.tasks/add-middleware -m cider.nrepl/cider-middleware repl -s wait"))
+ (expect (cider-inject-jack-in-dependencies "-C -o" "repl -s wait" 'boot)
+ :to-equal "-C -o -i \"(require 'cider.tasks)\" -d org.clojure/tools.nrepl\\:0.2.12 -d cider/cider-nrepl\\:0.11.0 cider.tasks/add-middleware -m cider.nrepl/cider-middleware repl -s wait"))
(it "can concat in a gradle project"
- (expect (cider-inject-jack-in-dependencies "-m" "--no-daemon clojureRepl" "gradle")
- :to-equal "-m --no-daemon clojureRepl")))
+ (expect (cider-inject-jack-in-dependencies "-m" "--no-daemon clojureRepl" 'gradle)
+ :to-equal "-m --no-daemon clojureRepl")))
(describe "when there are predicates"
:var (plugins-predicate middlewares-predicate)
@@ -185,10 +212,10 @@
:and-return-value '(("refactor-nrepl" "2.0.0") ("cider/cider-nrepl" "0.11.0")))
(setq-local cider-jack-in-dependencies-exclusions '()))
(it "uses them in a lein project"
- (expect (cider-inject-jack-in-dependencies "" "repl :headless" "lein")
+ (expect (cider-inject-jack-in-dependencies "" "repl :headless" 'lein)
:to-equal "update-in :dependencies conj \\[org.clojure/tools.nrepl\\ \\\"0.2.12\\\"\\] -- update-in :plugins conj \\[refactor-nrepl\\ \\\"2.0.0\\\"\\] -- update-in :plugins conj \\[cider/cider-nrepl\\ \\\"0.11.0\\\"\\] -- repl :headless"))
(it "uses them in a boot project"
- (expect (cider-inject-jack-in-dependencies "" "repl -s wait" "boot")
+ (expect (cider-inject-jack-in-dependencies "" "repl -s wait" 'boot)
:to-equal "-i \"(require 'cider.tasks)\" -d org.clojure/tools.nrepl\\:0.2.12 -d refactor-nrepl\\:2.0.0 -d cider/cider-nrepl\\:0.11.0 cider.tasks/add-middleware -m refactor-nrepl.middleware/wrap-refactor -m cider.nrepl/cider-middleware repl -s wait"))))
(describe "cider-jack-in-auto-inject-clojure"
@@ -212,38 +239,18 @@
(expect (cider-add-clojure-dependencies-maybe nil)
:to-equal '(("Hello, I love you" "won't you tell me your name"))))))
-(describe "cider-project-type"
- (describe "when there is a single project"
- (it "returns that type"
- (spy-on 'cider--identify-buildtools-present
- :and-return-value '("lein"))
- (expect (cider-project-type) :to-equal "lein")))
-
- (describe "when there are multiple possible project types"
- (before-all
- (spy-on 'cider--identify-buildtools-present
- :and-return-value '("build1" "build2"))
- ;; user choice build2
- (spy-on 'completing-read :and-return-value "build2"))
-
- (it "returns the choice entered by user"
- (expect (cider-project-type) :to-equal "build2"))
-
- (it "respects the value of `cider-preferred-build-tool'"
- (let ((cider-preferred-build-tool "build1"))
- (expect (cider-project-type) :to-equal "build1"))
-
- (let ((cider-preferred-build-tool "invalid choice"))
- (expect (cider-project-type) :to-equal "build2"))
-
- (let ((cider-preferred-build-tool "build3"))
- (expect (cider-project-type) :to-equal "build2"))))
-
- (describe "when there are no choices available"
- (it "returns the value of `cider-default-repl-command'"
- (spy-on 'cider--identify-buildtools-present
- :and-return-value '())
- (expect (cider-project-type) :to-equal cider-default-repl-command))))
+(describe "cider-normalize-cljs-init-options"
+ (describe "from options"
+ (it "leaves keywords alone"
+ (expect (cider-normalize-cljs-init-options ":dev") :to-equal ":dev"))
+ (it "leaves maps alone"
+ (expect (cider-normalize-cljs-init-options "{:a 1 :b 2}") :to-equal "{:a 1 :b 2}"))
+ (it "leaves s-exprs alone"
+ (expect (cider-normalize-cljs-init-options "(hashmap :a 1 :b 2)") :to-equal "(hashmap :a 1 :b 2)"))
+ (it "leaves vectors alone"
+ (expect (cider-normalize-cljs-init-options "[1 2 3]") :to-equal "[1 2 3]"))
+ (it "prepends colon to plain names"
+ (expect (cider-normalize-cljs-init-options "dev") :to-equal ":dev"))))
(provide 'cider-tests)
diff --git a/test/cider-util-tests.el b/test/cider-util-tests.el
index cd5042fc..b38cc941 100644
--- a/test/cider-util-tests.el
+++ b/test/cider-util-tests.el
@@ -28,7 +28,6 @@
;;; Code:
(require 'buttercup)
-(require 'cider)
(require 'cider-util)
;;; cider-util tests
@@ -184,11 +183,11 @@
;; :var (cider-version)
;; (it "returns the manual correct url for stable cider versions"
;; (setq cider-version "0.11.0")
-;; (expect (cider-manual-url) :to-equal "http://cider.readthedocs.io/en/stable/"))
+;; (expect (cider-manual-url) :to-equal "http://docs.cider.mx/en/stable/"))
;; (it "returns the manual correct url for snapshot cider versions"
;; (setq cider-version "0.11.0-snapshot")
-;; (expect (cider-manual-url) :to-equal "http://cider.readthedocs.io/en/latest/")))
+;; (expect (cider-manual-url) :to-equal "http://docs.cider.mx/en/latest/")))
(describe "cider-refcard-url"
:var (cider-version)
diff --git a/test/nrepl-client-tests.el b/test/nrepl-client-tests.el
index 5716e0a4..840525c2 100644
--- a/test/nrepl-client-tests.el
+++ b/test/nrepl-client-tests.el
@@ -28,38 +28,22 @@
;;; Code:
(require 'buttercup)
-(require 'cider)
-
-(describe "nrepl-connection-buffer-name"
- :var (nrepl-hide-special-buffers nrepl-endpoint)
- (before-all (setq-local nrepl-endpoint '("localhost" 1)))
-
- (describe "when nrepl-hide-special-buffers is nil"
- (it "returns the name of the connection buffer, which would make it visible in buffer changing commands"
- (expect (nrepl-connection-buffer-name)
- :to-equal "*nrepl-connection localhost*")))
-
- (describe "when nrepl-hide-special-buffers is t"
- (it "returns the name of the connection buffer, which hides it in buffer changing commands"
- (setq nrepl-hide-special-buffers t)
- (expect (nrepl-connection-buffer-name)
- :to-equal " *nrepl-connection localhost*"))))
+(require 'nrepl-client)
(describe "nrepl-server-buffer-name"
- :var (nrepl-hide-special-buffers nrepl-endpoint)
- (before-all (setq-local nrepl-endpoint '("localhost" 1)))
-
- (describe "when nrepl-hide-special-buffers is nil"
- (it "returns the name of the server buffer, which would make it visible in buffer changing commands"
- (setq nrepl-hide-special-buffers nil)
- (expect (nrepl-server-buffer-name)
- :to-equal "*nrepl-server localhost*")))
+ :var (nrepl-hide-special-buffers params default-directory
+ cider-session-name-template)
+ (before-all
+ (setq default-directory "path/to/dirA/")
+ (setq params '(:host "localhost" :port 1))
+ (setq cider-session-name-template "%J:%h:%p"))
(describe "when nrepl-hide-special-buffers is t"
(it "returns the name of the server buffer, which hides it in buffer changing commands"
- (setq nrepl-hide-special-buffers t)
- (expect (nrepl-server-buffer-name)
- :to-equal " *nrepl-server localhost*"))))
+ (setq nrepl-hide-special-buffers t
+ nrepl-server-buffer-name-template "*nrepl-server %h:%p*")
+ (expect (nrepl-server-buffer-name params)
+ :to-equal " *nrepl-server localhost:1*"))))
(describe "nrepl-dbing-response"
@@ -75,108 +59,36 @@
:to-equal
'("2" "39f630b9-9545-4ea0-860e-9846681d0741" ("done")))))
-(describe "nrepl-format-buffer-name-template"
- :var (nrepl-buffer-name-separator)
- ;; TODO
- (it "returns the string after applying the designation to the template"
- (expect (nrepl-format-buffer-name-template "*template%s*" "designation-foo")
- :to-equal "*template designation-foo*"))
-
- (it "respects the value of `nrepl-buffer-name-separator'"
- (setq-local nrepl-buffer-name-separator "_")
- (expect (nrepl-format-buffer-name-template "*template%s*" "designation-foo")
- :to-equal "*template_designation-foo*"))
-
- (it "handles nil designation in template"
- (expect (nrepl-format-buffer-name-template "*template%s*" nil)
- :to-equal "*template*"))
-
- (it "handles empty designation in template"
- (expect (nrepl-format-buffer-name-template "*template%s*" "")
- :to-equal "*template*")))
-
(describe "nrepl-make-buffer-name"
+ :var (default-directory cider-session-name-template)
+ (before-all
+ (setq default-directory "path/to/dirA/")
+ (setq cider-session-name-template "%J:%h:%p"))
(it "generates a buffer name from the given template"
- (with-temp-buffer
- (setq-local nrepl-endpoint '("localhost" 1))
- (expect (nrepl-make-buffer-name "*buff-name%s*")
- :to-equal "*buff-name localhost*")))
+ (let ((params '(:host "localhost" :port 1)))
+ (expect (nrepl-make-buffer-name "*buff-name %s*" params)
+ :to-equal "*buff-name to/dirA:localhost:1*")))
- (it "respects the value of `nrepl-project-dir'"
+ (it "respects the value of param `:project-dir'"
(with-temp-buffer
- (setq-local nrepl-project-dir "proj")
- (expect (nrepl-make-buffer-name "*buff-name%s*")
- :to-equal "*buff-name proj*")))
+ (let ((params '(:project-dir "path/to/dirB" :host "localhost" :port 1)))
+ (expect (nrepl-make-buffer-name "*buff-name %s*" params)
+ :to-equal "*buff-name to/dirB:localhost:1*"))))
- (it "respects the value of `nrepl-buffer-name-separator'"
- (with-temp-buffer
- (setq-local nrepl-project-dir "proj")
- (setq-local nrepl-buffer-name-separator "X")
- (expect (nrepl-make-buffer-name "*buff-name%s*")
- :to-equal "*buff-nameXproj*")))
-
- (it "can include nREPL port in the buffer name"
- (with-temp-buffer
- (setq-local nrepl-buffer-name-show-port t)
- (setq-local nrepl-endpoint '("localhost" 4009))
- (expect (nrepl-make-buffer-name "*buff-name%s*")
- :to-equal "*buff-name localhost:4009*")))
-
- (it "can ignore the nREPL port in the buffer name"
- (with-temp-buffer
- (setq-local nrepl-buffer-name-show-port nil)
- (setq-local nrepl-endpoint '("localhost" 4009))
- (expect (nrepl-make-buffer-name "*buff-name%s*")
- :to-equal "*buff-name localhost*")))
-
- (it "handles the project name and nREPL port given together"
- (with-temp-buffer
- (setq-local nrepl-buffer-name-show-port t)
- (setq-local nrepl-project-dir "proj")
- (setq-local nrepl-endpoint '("localhost" 4009))
- (expect (nrepl-make-buffer-name "*buff-name%s*")
- :to-equal "*buff-name proj:4009*")))
-
- (it "handles two buffers for the same project"
- (with-temp-buffer
- (setq-local nrepl-project-dir "proj")
- (let* ((cider-new-buffer (nrepl-make-buffer-name "*buff-name%s*")))
- (get-buffer-create cider-new-buffer)
- (expect cider-new-buffer :to-equal "*buff-name proj*")
- (with-temp-buffer
- (setq-local nrepl-project-dir "proj")
- (expect (nrepl-make-buffer-name "*buff-name%s*")
- :to-match "buff-name proj\\*<1\\|2>")
- ;; We accept either 1 or 2 as the suffix for duplicate buffers
- ;; This is due to a potential bug in emacs25 in the fn generate-new-buffer-name
- ;; Refer http://lists.gnu.org/archive/html/bug-gnu-emacs/2016-04/msg01253.html for details
- (kill-buffer cider-new-buffer)))))
-
- (it "handles duplicate project port"
- (with-temp-buffer
- (setq-local nrepl-buffer-name-show-port t)
- (setq-local nrepl-project-dir "proj")
- (setq-local nrepl-endpoint '("localhost" 4009))
- (let* ((cider-new-buffer (nrepl-make-buffer-name "*buff-name%s*")))
- (get-buffer-create cider-new-buffer)
- (expect cider-new-buffer :to-equal "*buff-name proj:4009*")
- (with-temp-buffer
- (setq-local nrepl-buffer-name-show-port t)
- (setq-local nrepl-project-dir "proj")
- (setq-local nrepl-endpoint '("localhost" 4009))
- (expect (nrepl-make-buffer-name "*buff-name%s*")
- :to-match "buff-name proj:4009\\*<1\\|2>")
- (kill-buffer cider-new-buffer))))))
-
-(describe "cider-clojure-buffer-name"
- (it "returns a buffer name using `nrepl-repl-buffer-name-template'"
+ (it "understands all formats"
(with-temp-buffer
- (setq-local nrepl-endpoint '("localhost" 1))
- (expect (nrepl-make-buffer-name nrepl-repl-buffer-name-template)
- :to-equal "*cider-repl localhost*")))
+ (let ((params '(:project-dir "path/to/dirB" :host "localhost" :port 100
+ :repl-type "cljs" :cljs-repl-type "node")))
+ (expect (nrepl-make-buffer-name "*buff-name %j:%J:%h:%H:%p:%r:%S*" params)
+ :to-equal "*buff-name dirB:to/dirB:localhost:100:cljs:node*"))))
- (it "respects the value of `nrepl-project-dir'"
+ (it "strips trailing separators"
(with-temp-buffer
- (setq-local nrepl-project-dir "/a/test/directory/project")
- (expect (nrepl-make-buffer-name nrepl-repl-buffer-name-template)
- :to-equal "*cider-repl project*"))))
+ (let ((params '(:project-dir "path/to/dirB" :host "localhost" :port 100
+ :repl-type "cljs" :cljs-repl-type nil)))
+ (expect (nrepl-make-buffer-name "*buff-name [%r:%S]*" params)
+ :to-equal "*buff-name [cljs]*")
+ (expect (nrepl-make-buffer-name "*buff-name (%r:%S)*" params)
+ :to-equal "*buff-name (cljs)*")
+ (expect (nrepl-make-buffer-name "*buff-name %r:%S*" params)
+ :to-equal "*buff-name cljs*")))))
diff --git a/test/utils/cider-connection-test-utils.el b/test/utils/cider-connection-test-utils.el
index 1ea36c95..a9c53ad3 100644
--- a/test/utils/cider-connection-test-utils.el
+++ b/test/utils/cider-connection-test-utils.el
@@ -30,26 +30,29 @@
(require 'cider)
(require 'cider-client)
-(defmacro with-connection-buffer (type symbol &rest body)
+(defmacro with-repl-buffer (ses-name type symbol &rest body)
"Run BODY in a temp buffer, with the given repl TYPE.
-SYMBOL is locally let-bound to the current buffer."
- (declare (indent 2)
+SES-NAME is Sesman's session. SYMBOL is locally let-bound to the
+current buffer."
+ (declare (indent 3)
(debug (sexp sexp &rest form)))
`(with-temp-buffer
(setq major-mode 'cider-repl-mode)
(setq cider-repl-type ,type)
+ (setq sesman-system 'CIDER)
+ (sesman-add-object 'CIDER ,ses-name (current-buffer) t)
;; `with-current-buffer' doesn't bump the buffer up the list.
(switch-to-buffer (current-buffer))
- (rename-buffer (format "*cider-repl %s-%s*" ,type (random 10000)) t)
- (let ((cider-connections (cons (current-buffer) cider-connections))
- (,symbol (current-buffer)))
- ,@body)))
+ (rename-buffer (format "*%s:%s:%s*(%s)"
+ ,ses-name ,(symbol-name symbol) ,type (random 10000)) t)
+ (let ((,symbol (current-buffer)))
+ ,@body
+ (sesman-remove-object 'CIDER ,ses-name (current-buffer) t 'no-error))))
(defmacro cider-test-with-buffers (buffer-names &rest body)
(let ((create (lambda (b) (list b `(generate-new-buffer " *temp*")))))
`(let (,@(mapcar create buffer-names))
- (unwind-protect
- ,@body
- (mapc 'kill-buffer (list ,@buffer-names))))))
+ ,@body
+ (mapc 'kill-buffer (list ,@buffer-names)))))
(provide 'cider-connection-test-utils)