Skip to content

Commit 8bda52a

Browse files
committed
[Fix #3157] Jump to the frame's own source in stacktraces
Clicking a stack frame for a top-level anonymous function (the deftest pattern) jumped to `clojure.core/fn': `cider-stacktrace-navigate' resolved the frame's `.../fn' var via `cider-var-info', which resolves `fn' to `clojure.core/fn' and clobbers the frame's own (correct) location. The middleware now populates each frame's file-url with a resolved absolute path and an accurate line (verified against current orchard), so prefer that directly and skip the var resolution. Frames without a file-url (e.g. REPL evaluations with NO_SOURCE_FILE) keep the old var/class fallback. The frame button now carries file-url for this.
1 parent 0cca5a3 commit 8bda52a

3 files changed

Lines changed: 67 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
### Bugs fixed
4343

44+
- [#4066](https://github.com/clojure-emacs/cider/pull/4066): Jump to the actual source when clicking a stack frame for a top-level anonymous function (the `deftest` shape), instead of landing in `clojure.core/fn` ([#3157](https://github.com/clojure-emacs/cider/issues/3157)).
4445
- [#4058](https://github.com/clojure-emacs/cider/pull/4058): Signal a helpful error when `cider-javadoc` gets an unresolvable (non-absolute) Javadoc URL from the middleware, instead of silently doing nothing ([#2969](https://github.com/clojure-emacs/cider/issues/2969)).
4546
- [#4054](https://github.com/clojure-emacs/cider/pull/4054): Interrupt every REPL an evaluation is dispatched to. In a `.cljc` buffer `cider-interrupt` previously interrupted only one of the two REPLs ([#4036](https://github.com/clojure-emacs/cider/issues/4036)).
4647
- [#4053](https://github.com/clojure-emacs/cider/pull/4053): Restore point to the place a debug session was started from when you quit it with `q`, instead of leaving it stranded at the last breakpoint ([#1595](https://github.com/clojure-emacs/cider/issues/1595)).

lisp/cider-stacktrace.el

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -543,25 +543,38 @@ Achieved by destructively manipulating `cider-stacktrace-suppressed-errors'."
543543

544544
(defun cider-stacktrace-navigate (button)
545545
"Navigate to the stack frame source represented by the BUTTON."
546-
(let* ((var (button-get button 'var))
547-
(class (button-get button 'class))
548-
(method (button-get button 'method))
549-
(info (or (and var (cider-var-info var))
550-
(and class method (cider-member-info class method))
551-
(nrepl-dict)))
552-
;; Stacktrace returns more accurate line numbers, but if the function's
553-
;; line was unreliable, then so is the stacktrace by the same amount.
554-
;; Set `line-shift' to the number of lines from the beginning of defn.
555-
(line-shift (- (or (button-get button 'line) 0)
556-
(or (nrepl-dict-get info "line") 1)))
557-
(file (or
558-
(nrepl-dict-get info "file")
559-
(button-get button 'file)))
560-
;; give priority to `info` files as `info` returns full paths.
561-
(info (nrepl-dict-put info "file" file)))
562-
(cider--jump-to-loc-from-info info cider-stacktrace-navigate-to-other-window)
563-
(forward-line line-shift)
564-
(back-to-indentation)))
546+
(let ((file-url (button-get button 'file-url))
547+
(line (button-get button 'line)))
548+
(if (and file-url line)
549+
;; The middleware resolves file-url to an absolute path with an
550+
;; accurate line, even for top-level anonymous functions. Trust it
551+
;; directly rather than re-resolving the var: for an anonymous
552+
;; `.../fn' frame `cider-var-info' would misresolve `fn' to
553+
;; `clojure.core/fn' and jump there instead (#3157).
554+
(cider--jump-to-loc-from-info
555+
(nrepl-dict "file" file-url "line" line)
556+
cider-stacktrace-navigate-to-other-window)
557+
;; No file-url (e.g. a REPL frame with NO_SOURCE_FILE): fall back to
558+
;; resolving the var or class member.
559+
(let* ((var (button-get button 'var))
560+
(class (button-get button 'class))
561+
(method (button-get button 'method))
562+
(info (or (and var (cider-var-info var))
563+
(and class method (cider-member-info class method))
564+
(nrepl-dict)))
565+
;; Stacktrace returns more accurate line numbers, but if the
566+
;; function's line was unreliable, then so is the stacktrace by the
567+
;; same amount. Set `line-shift' to the number of lines from the
568+
;; beginning of defn.
569+
(line-shift (- (or line 0)
570+
(or (nrepl-dict-get info "line") 1)))
571+
(file (or (nrepl-dict-get info "file")
572+
(button-get button 'file)))
573+
;; give priority to `info` files as `info` returns full paths.
574+
(info (nrepl-dict-put info "file" file)))
575+
(cider--jump-to-loc-from-info info cider-stacktrace-navigate-to-other-window)
576+
(forward-line line-shift)
577+
(back-to-indentation)))))
565578

566579
(declare-function cider-find-var "cider-find")
567580

@@ -677,15 +690,15 @@ This associates text properties to enable filtering and source navigation."
677690
'url url
678691
'follow-link t
679692
'action (lambda (x) (browse-url (button-get x 'url)))))
680-
(nrepl-dbind-response frame (file line flags class method name var ns fn)
693+
(nrepl-dbind-response frame (file file-url line flags class method name var ns fn)
681694
(when (or class file fn method ns name)
682695
(let ((flags (mapcar #'intern flags))) ; strings -> symbols
683696
(insert-text-button (format "%26s:%5d %s/%s"
684697
(if (member 'repl flags) "REPL" file) (or line -1)
685698
(if (member 'clj flags) ns class)
686699
(if (member 'clj flags) fn method))
687700
'var var 'class class 'method method
688-
'name name 'file file 'line line
701+
'name name 'file file 'file-url file-url 'line line
689702
'flags flags 'follow-link t
690703
'action #'cider-stacktrace-navigate
691704
'help-echo (cider-stacktrace-tooltip

test/cider-stacktrace-tests.el

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,35 @@
267267
:to-be-truthy)
268268
(expect (or both shown1 shown2)
269269
:to-be nil))))
270+
271+
(describe "cider-stacktrace-navigate"
272+
(it "jumps via the frame's file-url without re-resolving the var (#3157)"
273+
(spy-on 'cider--jump-to-loc-from-info)
274+
(spy-on 'cider-var-info)
275+
(with-temp-buffer
276+
;; An anonymous-fn frame: the var `repro/fn' would misresolve to
277+
;; `clojure.core/fn', but the frame carries a resolved file-url + line.
278+
(let ((button (insert-text-button "frame"
279+
'var "repro/fn"
280+
'file-url "file:/tmp/repro.clj"
281+
'file "repro.clj"
282+
'line 12)))
283+
(cider-stacktrace-navigate button)
284+
(expect 'cider-var-info :not :to-have-been-called)
285+
(let ((info (car (car (spy-calls-all-args 'cider--jump-to-loc-from-info)))))
286+
(expect (nrepl-dict-get info "file") :to-equal "file:/tmp/repro.clj")
287+
(expect (nrepl-dict-get info "line") :to-equal 12)))))
288+
289+
(it "falls back to var resolution when the frame has no file-url"
290+
(spy-on 'cider--jump-to-loc-from-info)
291+
(spy-on 'cider-var-info :and-return-value
292+
(nrepl-dict "file" "file:/tmp/core.clj" "line" 100))
293+
(with-temp-buffer
294+
(let ((button (insert-text-button "frame"
295+
'var "repro/named"
296+
'file-url nil
297+
'file "repro.clj"
298+
'line 12)))
299+
(cider-stacktrace-navigate button)
300+
(expect 'cider-var-info :to-have-been-called-with "repro/named")
301+
(expect 'cider--jump-to-loc-from-info :to-have-been-called)))))

0 commit comments

Comments
 (0)