branch: elpa/cider
commit ef954ef0c5088a4ddbaebf3c8b1677ce34706772
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>

    Let cider-macrostep expand the operator n/p jumped to
    
    cider-macrostep-next-expandable/previous-expandable leave point at the
    start of the target operator, but cider-macrostep-expand followed the
    'sexp before point' convention, so expanding right there failed with
    'No sexp before point to expand' - the user had to manually jump to the
    end of the form first.
    
    When point sits on a highlighted expandable operator, expand that
    operator's enclosing form. The plain 'sexp before point' behavior is
    unchanged everywhere else.
---
 CHANGELOG.md                  |  1 +
 lisp/cider-macrostep.el       | 37 ++++++++++++++++++++++++++++++++-----
 test/cider-macrostep-tests.el | 19 +++++++++++++++++++
 3 files changed, 52 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 895ac6b399..6428ef4402 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
 
 ### Bugs fixed
 
+- [#4115](https://github.com/clojure-emacs/cider/pull/4115): Fix inline macro 
stepping (`cider-macrostep`): pressing `e`/`RET` right after `n`/`p` now 
expands the operator you jumped to, instead of erroring with "No sexp before 
point to expand".
 - [#4111](https://github.com/clojure-emacs/cider/issues/4111): Fix the 
macroexpansion commands refusing to expand `let`, `fn`, `loop` and `letfn` 
(macros that double as special forms), restore the no-op expansion of non-macro 
forms (useful for normalizing reader syntax like `::auto/kw` and `#(...)`), and 
stop gating `cider-macroexpand-all` on the top-level operator (a 
fully-recursive expansion can reach macros in nested sub-forms).
 
 ## 2.0.0 (2026-07-15)
diff --git a/lisp/cider-macrostep.el b/lisp/cider-macrostep.el
index 5b4340bdc2..87b5d629b2 100644
--- a/lisp/cider-macrostep.el
+++ b/lisp/cider-macrostep.el
@@ -404,6 +404,28 @@ faces from `cider-macrostep-gensym-faces'.  A no-op when 
disabled."
        (or (seq-find (lambda (p) (< p (point))) (reverse positions))
            (car (last positions)))))))
 
+(defun cider-macrostep--expandable-overlay-at (pos)
+  "Return the expandable-operator overlay covering POS, if any."
+  (seq-find (lambda (ov)
+              (and (overlay-buffer ov)
+                   (<= (overlay-start ov) pos)
+                   (< pos (overlay-end ov))))
+            cider-macrostep--expandable-overlays))
+
+(defun cider-macrostep--expand-bounds ()
+  "Return the (BEG . END) bounds of the form to expand, or nil.
+When point sits on a highlighted expandable operator - where `n'/`p' leave
+it - expand that operator's enclosing form.  Otherwise fall back to the sexp
+before point, the `\\[cider-eval-last-sexp]' convention that
+`cider-macrostep-expand' documents."
+  (or (when-let* ((ov (cider-macrostep--expandable-overlay-at (point))))
+        (save-excursion
+          (goto-char (overlay-start ov))
+          (ignore-errors
+            (backward-up-list)
+            (cons (point) (progn (forward-sexp) (point))))))
+      (cider-macrostep--form-bounds)))
+
 (defvar cider-macrostep-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map (kbd "e") #'cider-macrostep-expand)
@@ -473,13 +495,16 @@ Enter `cider-macrostep-mode' when it isn't already 
active."
 ;;;###autoload
 (defun cider-macrostep-expand ()
   "Expand the macro form before point one step, inline.
-Place point right after the form, as with `\\[cider-eval-last-sexp]'.
+Place point right after the form, as with `\\[cider-eval-last-sexp]'.  When
+point is on a highlighted expandable operator - where `n'/`p' land it - that
+operator's form is expanded, so you can step with `n' then expand without
+repositioning.
 
 Start a `cider-macrostep-mode' session when one isn't active; further
 expansions and collapses then use that mode's key bindings."
   (interactive)
   (cider-ensure-session)
-  (pcase-let ((`(,beg . ,end) (or (cider-macrostep--form-bounds)
+  (pcase-let ((`(,beg . ,end) (or (cider-macrostep--expand-bounds)
                                   (user-error "No sexp before point to 
expand"))))
     (let ((operator (cider-macrostep--operator beg)))
       (cider-ensure-macro operator)
@@ -499,11 +524,13 @@ every level.  Place point right after the form, as with
 `\\[cider-eval-last-sexp]'.
 
 Like `cider-macrostep-expand', this starts a `cider-macrostep-mode' session
-when one isn't active.  It does not require the form's head to be a macro,
-since a fully-recursive expansion can reach macros in nested sub-forms."
+when one isn't active, and expands the form of the expandable operator at
+point when `n'/`p' has landed there.  It does not require the form's head to
+be a macro, since a fully-recursive expansion can reach macros in nested
+sub-forms."
   (interactive)
   (cider-ensure-session)
-  (pcase-let ((`(,beg . ,end) (or (cider-macrostep--form-bounds)
+  (pcase-let ((`(,beg . ,end) (or (cider-macrostep--expand-bounds)
                                   (user-error "No sexp before point to 
expand"))))
     (let* ((code (buffer-substring-no-properties beg end))
            (expansion (cider-macrostep--expand code "macroexpand-all")))
diff --git a/test/cider-macrostep-tests.el b/test/cider-macrostep-tests.el
index 516841cc6b..c81a925971 100644
--- a/test/cider-macrostep-tests.el
+++ b/test/cider-macrostep-tests.el
@@ -176,6 +176,25 @@
       (setq cider-macrostep--expandable-overlays nil)
       (expect (cider-macrostep-next-expandable) :to-throw 'user-error))))
 
+(describe "cider-macrostep--expand-bounds"
+  (it "expands the enclosing form when point is on an expandable operator"
+    ;; This is where `n'/`p' land point (the start of the operator).  Before,
+    ;; `e'/`RET' there errored with \"No sexp before point to expand\".
+    (with-temp-buffer
+      (clojure-mode)
+      (insert "(aaa (bbb ccc))")
+      (setq cider-macrostep--expandable-overlays
+            (list (make-overlay 7 10)))     ; `bbb' operator
+      (goto-char 7)                         ; point at the start of `bbb'
+      (expect (cider-macrostep--expand-bounds) :to-equal '(6 . 15))))
+  (it "falls back to the sexp before point elsewhere"
+    (with-temp-buffer
+      (clojure-mode)
+      (insert "(aaa (bbb))")
+      (setq cider-macrostep--expandable-overlays nil)
+      (goto-char (point-max))               ; after the whole form
+      (expect (cider-macrostep--expand-bounds) :to-equal '(1 . 12)))))
+
 (describe "cider-macrostep gensym coloring"
   (it "matches gensyms but not ordinary symbols"
     (with-temp-buffer

Reply via email to