summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md7
-rw-r--r--cider-util.el81
-rw-r--r--test/cider-util-tests.el67
3 files changed, 145 insertions, 10 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3a8732ab..cf9460f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@
### New features
+* 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.
@@ -23,9 +27,6 @@
### New features
-* 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.
* [#2248](https://github.com/clojure-emacs/cider/pull/2248): `cider-repl` can now display recognized images in the REPL buffer.
* [#2172](https://github.com/clojure-emacs/cider/pull/2172): Render diffs for expected / actual test results.
* [#2167](https://github.com/clojure-emacs/cider/pull/2167): Add new defcustom `cider-jdk-src-paths`. Configure it to connect stack trace links to Java source code.
diff --git a/cider-util.el b/cider-util.el
index 6ad2acd3..95edc087 100644
--- a/cider-util.el
+++ b/cider-util.el
@@ -96,17 +96,84 @@ 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-top-level-comment-p ()
+ "Return non-nil if point is in a comment form."
+ (save-excursion
+ (end-of-defun)
+ (clojure-backward-logical-sexp 1)
+ (forward-char 1)
+ (clojure-forward-logical-sexp 1)
+ (clojure-backward-logical-sexp 1)
+ (looking-at-p "comment")))
+
+(defcustom cider-eval-toplevel-inside-comment-form nil
+ "Eval top level forms inside comment forms instead of the comment form itself.
+Experimental. Function `cider-defun-at-point' is used extensively so if we
+change this heuristic it needs to be bullet-proof and desired. While
+testing, give an easy way to turn this new behavior off."
+ :group 'cider
+ :type 'boolean
+ :package-version '(cider . "0.18.0"))
+
+(defun cider-sexp-starts-until-position (position)
+ "Returns the starting points for forms before POSITION.
+Positions are in descending order to aide in finding the first starting
+position before the current position."
+ (save-excursion
+ (let (sexp-positions)
+ (condition-case nil
+ (while (< (point) position)
+ (clojure-forward-logical-sexp 1)
+ (clojure-backward-logical-sexp 1)
+ (push (point) sexp-positions)
+ (clojure-forward-logical-sexp 1))
+ (scan-error nil))
+ sexp-positions)))
+
+(defun cider-defun-inside-comment-form (&optional bounds)
+ "Return the toplevel form inside a comment containing point.
+Assumes point is inside a (comment ....) form and will return the text of
+that form or if BOUNDS, will return a list of the starting and ending
+position."
+ (save-excursion
+ (save-match-data
+ (let ((original-position (point))
+ cider-comment-start cider-comment-end)
+ (end-of-defun)
+ (setq cider-comment-end (point))
+ (clojure-backward-logical-sexp 1) ;; beginning of comment form
+ (setq cider-comment-start (point))
+ (forward-char 1) ;; skip paren so we start at comment
+ (clojure-forward-logical-sexp) ;; skip past the comment form itself
+ (if-let* ((sexp-start (seq-find (lambda (beg-pos) (< beg-pos original-position))
+ (cider-sexp-starts-until-position cider-comment-end))))
+ (progn
+ (goto-char sexp-start)
+ (clojure-forward-logical-sexp 1)
+ (cider--text-or-limits bounds sexp-start (point)))
+ (cider--text-or-limits bounds cider-comment-start cider-comment-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
instead."
- (save-excursion
- (save-match-data
- (end-of-defun)
- (let ((end (point)))
- (clojure-backward-logical-sexp 1)
- (funcall (if bounds #'list #'buffer-substring-no-properties)
- (point) end)))))
+ (if (and cider-eval-toplevel-inside-comment-form
+ (cider-top-level-comment-p))
+ (cider-defun-inside-comment-form bounds)
+ (save-excursion
+ (save-match-data
+ (end-of-defun)
+ (let ((end (point)))
+ (clojure-backward-logical-sexp 1)
+ (cider--text-or-limits bounds (point) end))))))
(defun cider-ns-form ()
"Retrieve the ns form."
diff --git a/test/cider-util-tests.el b/test/cider-util-tests.el
index 592d72ef..db23adcd 100644
--- a/test/cider-util-tests.el
+++ b/test/cider-util-tests.el
@@ -126,6 +126,73 @@
(insert "'")
(expect (cider-sexp-at-point 'bounds) :to-equal '(5 15))))))
+(defmacro cider-buffer-with-text
+ (text &rest body)
+ "Run body in a temporary clojure buffer with TEXT.
+TEXT is a string with a | indicating where point is. The | will be erased
+and point left there."
+ (declare (indent 2))
+ `(progn
+ (with-temp-buffer
+ (erase-buffer)
+ (clojure-mode)
+ (insert ,text)
+ (goto-char (point-min))
+ (re-search-forward "|")
+ (delete-char -1)
+ ,@body)))
+
+(describe "cider-defun-inside-comment-form"
+ (describe "when param 'bounds is not given"
+ (it "returns the form containing point"
+ (cider-buffer-with-text
+ "(comment
+ (wrong)
+ (wrong)
+ (rig|ht)
+ (wrong))"
+ (expect (cider-defun-inside-comment-form) :to-equal "(right)")))
+ (it "attaches to the previous top level comment"
+ (cider-buffer-with-text
+ "(comment
+ (wrong)
+ (wrong)
+ (right) |
+ (wrong))"
+ (expect (cider-defun-inside-comment-form) :to-equal "(right)")))
+ (it "attaches to the next top level comment even if its on the same line"
+ (cider-buffer-with-text
+ "(comment
+ (wrong)
+ (wrong)
+ (right)
+ | (wrong))"
+ (expect (cider-defun-inside-comment-form) :to-equal "(right)")))
+ (it "returns the top level even if heavily nested"
+ (cider-buffer-with-text
+ "(comment
+ (wrong)
+ (wrong)
+ (((((r|ight)))))
+ (wrong))"
+ (expect (cider-defun-inside-comment-form) :to-equal "(((((right)))))")))
+ (it "works even on the last form in a comment"
+ (cider-buffer-with-text
+ "(comment
+ (wrong)
+ (wrong)
+ (right|))"
+ (expect (cider-defun-inside-comment-form) :to-equal "(right)")))
+ (it "works on the last form even after the comment form"
+ (cider-buffer-with-text
+ "(comment (wrong) (wrong) (right)) |"
+ (expect (cider-defun-inside-comment-form) :to-equal "(right)"))))
+ (describe "returns entire comment form when"
+ (it "the comment form is empty"
+ (cider-buffer-with-text
+ "(comment|)"
+ (expect (cider-defun-inside-comment-form) :to-equal "(comment)")))))
+
(describe "cider-defun-at-point"
(describe "when the param 'bounds is not given"
(it "returns the defun at point"