Skip to content

Commit

Permalink
*cider-error*: open a given exception in the Inspector by clicking it
Browse files Browse the repository at this point in the history
Fixes #3565
  • Loading branch information
vemv committed Nov 4, 2023
1 parent 1299a4e commit bbef4f4
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## master (unreleased)

### New features

- [#3565](https://github.com/clojure-emacs/cider/issues/3565): [`*cider-error*`](https://docs.cider.mx/cider/usage/dealing_with_errors.html#inspector-integration): open a given Exception in the [Inspector](https://docs.cider.mx/cider/debugging/inspector.html) by clicking it, or hitting <kbd>p</kbd>.

### Changes

- CIDER [Inspector](https://docs.cider.mx/cider/debugging/inspector.html): display Java class/method/field info when available.
Expand Down
24 changes: 24 additions & 0 deletions cider-inspector.el
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,17 @@ See `cider-sync-request:inspect-push' and `cider-inspector--render-value'"
(cider-inspector--render-value result 'v2)
(cider-inspector-next-inspectable-object 1))))

(defun cider-inspector-inspect-last-exception (index)
"Inspects the exception in the cause stack identified by INDEX."
(interactive)
(cl-assert (numberp index))
(setq cider-inspector--current-repl (cider-current-repl))
(let ((result (cider-sync-request:inspect-last-exception index 'v2)))
(when (nrepl-dict-get result "value")
(push (point) cider-inspector-location-stack)
(cider-inspector--render-value result 'v2)
(cider-inspector-next-inspectable-object 1))))

(defun cider-inspector-previous-sibling ()
"Inspect the previous sibling value within a sequential parent.
See `cider-sync-request:inspect-previous-sibling' and `cider-inspector--render-value'"
Expand Down Expand Up @@ -387,6 +398,19 @@ instead of just its \"value\" entry."
result
(nrepl-dict-get result "value"))))

;;;###autoload
(defun cider-sync-request:inspect-last-exception (index &optional v2)
"Inspects the exception in the cause stack identified by INDEX,
V2 indicates if the entire response should be returned
instead of just its \"value\" entry."
(cl-assert (numberp index))
(let ((result (thread-first `("op" "inspect-last-exception"
"index" ,index)
(cider-nrepl-send-sync-request cider-inspector--current-repl))))
(if v2
result
(nrepl-dict-get result "value"))))

(defun cider-sync-request:inspect-next-sibling (&optional v2)
"Inspect the next sibling value within a sequential parent,
V2 indicates if the entire response should be returned
Expand Down
48 changes: 41 additions & 7 deletions cider-stacktrace.el
Original file line number Diff line number Diff line change
Expand Up @@ -799,19 +799,52 @@ the NAME. The whole group is prefixed by string INDENT."
(cider-stacktrace--insert-named-group indent2 "extras: \n")
(cider-stacktrace-emit-indented extra (concat indent2 " ") nil t)))))))

(defun cider-stacktrace-render-cause (buffer cause num note)
"Emit into BUFFER the CAUSE NUM, exception class, message, data, and NOTE."
(declare-function cider-inspector-inspect-last-exception "cider-inspector")

(defun cider-stacktrace--inspect-class (event)
"Mouse handler for EVENT."
(interactive "e")
(let* ((pos (posn-point (event-end event)))
(window (posn-window (event-end event)))
(buffer (window-buffer window))
(inspect-index (with-current-buffer buffer
(get-text-property pos 'inspect-index))))
(cider-inspector-inspect-last-exception inspect-index)))

(defun cider-stacktrace--inspect-class-kbd ()
"Keyboard handler."
(interactive)
(when-let ((inspect-index (get-text-property (point) 'inspect-index)))
(cider-inspector-inspect-last-exception inspect-index)))

(defvar cider-stacktrace-exception-map
(let ((map (make-sparse-keymap)))
(define-key map [mouse-1] #'cider-stacktrace--inspect-class)
(define-key map (kbd "p") #'cider-stacktrace--inspect-class-kbd)
(define-key map (kbd "i") #'cider-stacktrace--inspect-class-kbd)
map))

(defun cider-stacktrace-render-cause (buffer cause num note &optional inspect-index)
"Emit into BUFFER the CAUSE NUM, exception class, message, data, and NOTE,
make INSPECT-INDEX actionable if present."
(with-current-buffer buffer
(nrepl-dbind-response cause (class message data spec stacktrace)
(let ((indent " ")
(class-face 'cider-stacktrace-error-class-face)
(message-face 'cider-stacktrace-error-message-face))
(cider-propertize-region `(cause ,num)
;; Detail level 0: exception class
(cider-propertize-region '(detail 0)
(cider-propertize-region `(detail
0

inspect-index
,inspect-index

keymap
,cider-stacktrace-exception-map)
(insert (format "%d. " num)
(propertize note 'font-lock-face 'font-lock-comment-face) " "
(propertize class 'font-lock-face class-face)
(propertize class 'font-lock-face class-face 'mouse-face 'highlight)
"\n"))
;; Detail level 1: message + ex-data
(cider-propertize-region '(detail 1)
Expand Down Expand Up @@ -885,10 +918,11 @@ through the `cider-stacktrace-suppressed-errors' variable."
(cider-stacktrace-render-suppression-toggle buffer error-types)
(insert "\n\n"))
;; Stacktrace exceptions & frames
(let ((num (length causes)))
(let* ((causes-length (length causes))
(num causes-length))
(dolist (cause causes)
(let ((note (if (= num (length causes)) "Unhandled" "Caused by")))
(cider-stacktrace-render-cause buffer cause num note)
(let ((note (if (= num causes-length) "Unhandled" "Caused by")))
(cider-stacktrace-render-cause buffer cause num note (- causes-length num))
(setq num (1- num))))))
(cider-stacktrace-initialize causes)
(font-lock-refresh-defaults)))
Expand Down
18 changes: 18 additions & 0 deletions doc/modules/ROOT/pages/usage/dealing_with_errors.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,21 @@ Cider recognizes stacktraces printed in the following formats:
buffer. When `cider-stacktrace-analyze-at-point` fails to detect the
stacktrace, `cider-stacktrace-analyze-in-region` can be used to
select the stacktrace manually.

== Inspector integration

Within `*cider-error*`, when clicking directly a top-level exception (any of them in the cause chain),
that specific exception will be inspected with the CIDER xref:debugging/inspector.adoc[Inspector].

This allows you to better understand intrincate `ex-data`.

This clicking is defined and customizable in `cider-stacktrace-exception-map`, which has the following defaults:

=== Keybindings

|===
| Action | Description

| kbd:[click] or kbd:[i] or kbd:[p]
| Open the given exception in the Inspector.
|===

0 comments on commit bbef4f4

Please sign in to comment.