diff options
-rw-r--r-- | CHANGELOG.md | 7 | ||||
-rw-r--r-- | cider-util.el | 81 | ||||
-rw-r--r-- | test/cider-util-tests.el | 67 |
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" |