Skip to content

Commit

Permalink
[Fix #2161] Add new interactive command cider-eval-defun-to-point
Browse files Browse the repository at this point in the history
It evaluates the current top-level form up to the point. You can think of this
as a poor man's contextual evaluation.

The command is bound by default to `C-c C-v (C-)z` in `cider-mode`.
  • Loading branch information
bbatsov committed Jan 14, 2018
1 parent 3f7608f commit 2db31b0
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 3 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

## master (unreleased)

### New features

* [#2161](https://github.com/clojure-emacs/cider/issues/2161): Add new interactive command `cider-eval-defun-to-point` which is bound to `C-c C-v (C-)z`. It evaluates the current top-level form up to the point.

### Bugs Fixed

* [#1913](https://github.com/clojure-emacs/cider/issues/1913): Fix `cider-toggle-buffer-connection` to allow cycling of connection and restoring all connections in cljc buffers.
* [#2148](https://github.com/clojure-emacs/cider/issues/2148): Fix `jump to definition` working properly when remote `cider-jack-in` and `cider-connect`.
* [#2148](https://github.com/clojure-emacs/cider/issues/2148): Fix `jump to definition` working properly when remote `cider-jack-in` and `cider-connect`.

### Changes

Expand Down
48 changes: 47 additions & 1 deletion cider-interaction.el
Original file line number Diff line number Diff line change
Expand Up @@ -1406,6 +1406,50 @@ command `cider-debug-defun-at-point'."
(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
Expand Down Expand Up @@ -1458,12 +1502,14 @@ passing arguments."
(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)
;; 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-.") #'cider-read-and-eval-defun-at-point)
(define-kee map (kbd "C-z") #'cider-eval-defun-to-point)))


;; Connection and REPL
Expand Down
1 change: 1 addition & 0 deletions cider-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ Configure `cider-cljs-*-repl' to change the ClojureScript REPL to use for your b
(defconst cider-mode-eval-menu
'("CIDER Eval" :visible cider-connections
["Eval top-level sexp" cider-eval-defun-at-point]
["Eval top-level sexp to point" cider-eval-defun-to-point]
["Eval current sexp" cider-eval-sexp-at-point]
["Eval last sexp" cider-eval-last-sexp]
["Eval selected region" cider-eval-region]
Expand Down
12 changes: 12 additions & 0 deletions cider.el
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,18 @@ gets associated with it."
;; always prompt
(t (cider-assoc-project-with-connection nil conn))))))))

(defun cider-repl-set-type (&optional type)
"Set REPL TYPE to \"clj\" or \"cljs\"."
(interactive)
(let ((type (or type (completing-read "Set REPL type to: " '("clj" "cljs")))))
(setq cider-repl-type type)))

(defun cider-connect-clojurescript ()
"Connect to a clojurescript repl."
(interactive)
(cider-connect)
(cider-repl-set-type "cljs"))

(defun cider-current-host ()
"Retrieve the current host."
(if (and (stringp buffer-file-name)
Expand Down
3 changes: 2 additions & 1 deletion doc/interactive_programming.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ Here's a list of `cider-mode`'s keybindings:
`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-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</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.
Expand Down
18 changes: 18 additions & 0 deletions test/cider-interaction-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,21 @@
(with-temp-buffer
(clojure-mode)
(expect (cider-interactive-eval "(+ 1)") :not :to-throw)))))

(describe "cider--calculate-opening-delimiters"
(it "returns the right opening delimiters"
(with-temp-buffer
(clojure-mode)
(insert "(let [a 1] (let [b 2] (+ a b)))")
(backward-char 2)
(expect (cider--calculate-opening-delimiters) :to-equal '(40 40)))))

(describe "cider--matching-delimiter"
(it "returns the right closing delimiter"
(expect (cider--matching-delimiter ?\() :to-equal ?\))
(expect (cider--matching-delimiter ?\{) :to-equal ?\})
(expect (cider--matching-delimiter ?\[) :to-equal ?\]))
(it "returns the right opening delimiter"
(expect (cider--matching-delimiter ?\)) :to-equal ?\()
(expect (cider--matching-delimiter ?\}) :to-equal ?\{)
(expect (cider--matching-delimiter ?\]) :to-equal ?\[)))

0 comments on commit 2db31b0

Please sign in to comment.