Welcome! Please see the About page for a little more info on how this works.

+1 vote
in REPL by
edited by

This is an Emacs question, but there are so many Clojure Emacs users, I am asking here as I’ve been amazed at how knowledgeable and helpful people have been.

I’m using Cider in Emacs. Historically, I’ve tended to just reload the file and then type directly into the REPL. I’ve recently noticed another style of writing/testing from within a (comment. . .) block. I really like this style, but it’s not quite working seamlessly for me. (I understand this works well within JSCode, but I can’t retrain my fingers . . .)

There are two command that appear to be most useful for evaluating code from a source file to the REPL:

1) cider-eval-sexp-at-point
2) cider-eval-last-sexp-to-repl

My problem with cider-eval-sexp-at-point, is that the results are sent to the Messages buffer, not to the REPL

The problem with cider-eval-last-sexp-to-repl is that it does though it send the results of evaluation to the REPL buffer, it does so at wherever the cursor is, not at the end of the REPL. Also, it does not execute a “return”, so the cursor is not ready for another evaluation. Ideally, I would like to evaluate another chunk of code and have it end with a fresh cursor.

Perhaps there is a way to do this already, which would be ideal. Otherwise, I was thinking of writing a new Emacs command to effect this functionality.

As a longtime user of Emacs (currently Doom Emacs), I’ve written a handful of basic functions, but this is more complicated than I’ve tried before. So any advice would be appreciated. (I may also post this on an Emacs site as well.)

Here then would be the basic function:
1) Start in a Clojure file, with the point at the end of an sexp.
2) Execute the function.
3) The sexp is evaluated.
4) In REPL buffer, the current point would move to the end of the REPL, refreshing the cursor if needed.
5) The evaluated results would then be printed (at end of the REPL).
6) A “return” would then be sent to the REPL, so there would be a fresh cursor.
7) The original point within the Clojure file would not have moved.
8) This function could then be repeated from within the Clojure file without having to move to and from the REPL.

This is almost the functionality of cider-eval-last-sexp-to-repl. As mentioned above, it doesn’t first go to the end of the REPL and it also does not send a “return”.

Any advice would be appreciated.

1 Answer

+1 vote
by

Hi I extended CIDER and clojure-mode to work well exactly in this manner. You're right that the eval functions you are looking at are trying to show their results in the source buffer which is useful in some situations but the repl offers a lot more.

There is a keybinding C-c C-j which points at the cider-insert-commands-map. Here you'll find

  • C-c C-j e : cider-insert-last-sexp-in-repl
  • C-c C-j d: cider-insert-defun-in-repl
  • C-c C-j r: cider-insert-region-in-repl
  • C-c C-j n: cider-insert-ns-form-in-repl

The important ones here are e and d for last form and top-level form. These will send the forms to the repl. But there are some config that need to be set.

By default, this workflow puts the forms in the repl and anticipates you to keep typing. But we want to not put focus there and send the form to the repl to evaluate. So we want to set two options:

(setq cider-switch-to-repl-on-insert nil)
(setq cider-invert-insert-eval-p t)

the first option says we don't want to put the focus in the repl, keep the point (emacs lingo for the cursor) where it currently is. The second option is how we tell CIDER that we don't want to keep typing and modify the form, just eval it.

there's only one thing left to do. When sending the top level form with C-c C-j d, by default, CIDER might send the whole top level form

(comment
  (map inc (range 3))
  )

But we want to modify what is considered a top level form and when in the comment form consider (map inc (range 3)) a top level form, not the whole comment form. This is remedied by

(setq clojure-toplevel-inside-comment-form t)

Now you can send the top level form from anywhere inside of the (map inc (range 3)) and it will send the top level form you mean rather than the whole comment form.

by
edited by
dpsutton - What a fast and brilliant answer! This adjusts Cider to my preferred work flow. The extra setting to keep the comment block from being the top-level form was something I hadn’t even expected to be able to fix. I’ve modified my config.el file and everything works exactly as I had hoped. Binding  'cider-insert-defun-in-repl (shown below) really optimizes my workflow.

Thanks again!

(define-key cider-mode-map (kbd "C-c RET") 'cider-insert-defun-in-repl)
...