Skip to content

Remove features that relied on printed exception parsing #3793

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
### Changes

- [#3782](https://github.com/clojure-emacs/cider/issues/3782): **(Breaking)** Drop official support for Emacs 26.
- [#3793](https://github.com/clojure-emacs/cider/issues/3793): **(Breaking)** Remove features that relied on printed exception parsing:
- `cider-stacktrace-analyze-string` and `cider-stacktrace-analyze-at-point` functions.
- Automatic stacktrace parsing in log viewer.
- Bump the injected `cider-nrepl` to [0.53.1](https://github.com/clojure-emacs/cider-nrepl/blob/master/CHANGELOG.md#0531-2025-03-26).
- Info: recognize printed Java classes/methods and munged Clojure functions in stacktrace outputs.
- Inspector: add dedicated view for Exceptions.
Expand Down
48 changes: 6 additions & 42 deletions cider-log.el
Original file line number Diff line number Diff line change
Expand Up @@ -215,15 +215,6 @@ It will not be used if the package hasn't been installed."
"filters" ,(cider-log-consumer-filters consumer))
(cider-nrepl-send-request callback)))

(defun cider-request:log-analyze-stacktrace (framework appender event &optional callback)
"Analyze the EVENT stacktrace of the APPENDER of FRAMEWORK and call CALLBACK."
(cider-ensure-op-supported "cider/log-analyze-stacktrace")
(thread-first `("op" "cider/log-analyze-stacktrace"
"framework" ,(cider-log-framework-id framework)
"appender" ,(cider-log-appender-id appender)
"event" ,(cider-log-event-id event))
(cider-nrepl-send-request callback)))

(defun cider-sync-request:log-update-consumer (framework appender consumer)
"Add CONSUMER to the APPENDER of FRAMEWORK and call CALLBACK on log events."
(cider-ensure-op-supported "cider/log-update-consumer")
Expand Down Expand Up @@ -690,24 +681,6 @@ The KEYS are used to lookup the values and are joined by SEPARATOR."
(seq-doseq (window windows)
(set-window-point window (point-max))))))

(defun cider-log-event--show-stacktrace (framework appender event)
"Show the stacktrace of the log EVENT of FRAMEWORK and APPENDER."
(when (and framework appender event (cider-log-event-exception event))
(let ((auto-select-buffer cider-auto-select-error-buffer)
(causes nil))
(cider-request:log-analyze-stacktrace
framework appender event
(lambda (response)
(nrepl-dbind-response response (class status)
(cond (class (setq causes (cons response causes)))
(status (when causes
(cider-stacktrace-render
(cider-popup-buffer cider-error-buffer
auto-select-buffer
#'cider-stacktrace-mode
'ancillary)
(reverse causes)))))))))))

(defun cider-log-event-next-line (&optional n)
"Move N lines forward."
(interactive "p")
Expand All @@ -719,8 +692,6 @@ The KEYS are used to lookup the values and are joined by SEPARATOR."
(event (cider-log-event-at-point)))
(let ((cider-auto-select-error-buffer nil))
(save-window-excursion
(when (get-buffer-window cider-error-buffer)
(cider-log-event--show-stacktrace framework appender event))
(when (get-buffer-window cider-inspector-buffer)
(cider-log-event--inspect framework appender event))
(when (get-buffer-window cider-log-event-buffer)
Expand Down Expand Up @@ -856,7 +827,6 @@ The KEYS are used to lookup the values and are joined by SEPARATOR."
(define-key map (kbd "C-c M-l f") #'cider-log-framework)
(define-key map (kbd "C-c M-l i") #'cider-log-info)
(define-key map (kbd "C-c M-l l") #'cider-log)
(define-key map (kbd "E") 'cider-log-show-stacktrace)
(define-key map (kbd "F") 'cider-log-print-event)
(define-key map (kbd "I") 'cider-log-inspect-event)
(define-key map (kbd "RET") 'cider-log-inspect-event)
Expand Down Expand Up @@ -1173,16 +1143,12 @@ the CIDER Inspector and the CIDER stacktrace mode.
(cider-log-appender-display-name appender)))

;; Event actions

(transient-define-suffix cider-log-show-stacktrace (framework appender event)
"Show the stacktrace of the log EVENT of FRAMEWORK and APPENDER."
:description "Show log event stacktrace"
:if #'cider-log-event-at-point
:inapt-if-not (lambda ()
(when-let (event (cider-log-event-at-point))
(cider-log-event-exception event)))
(interactive (list (cider-log--framework) (cider-log--appender) (cider-log-event-at-point)))
(cider-log-event--show-stacktrace framework appender event))
(defun cider-log-show-stacktrace (&rest _)
"Removed."
(interactive)
(message "This function has been removed.
You can jump to functions and methods directly from the printed stacktrace now."))
(make-obsolete 'cider-log-show-stacktrace nil "1.18")

(transient-define-suffix cider-log-print-event (framework appender event)
"Format the log EVENT of FRAMEWORK and APPENDER."
Expand Down Expand Up @@ -1439,7 +1405,6 @@ the CIDER Inspector and the CIDER stacktrace mode.
(cider-log--threads-option)]
["Actions"
("c" cider-log-clear-event-buffer)
("e" cider-log-show-stacktrace)
("i" cider-log-inspect-event)
("p" cider-log-print-event)
("s" cider-log--do-search-events)]
Expand Down Expand Up @@ -1497,7 +1462,6 @@ based on `transient-mode'."
["Event Actions"
("eb" cider-log-switch-to-buffer)
("ec" cider-log-clear-event-buffer)
("ee" cider-log-show-stacktrace)
("ei" cider-log-inspect-event)
("ep" cider-log-print-event)
("es" "Search log events" cider-log-event-search
Expand Down
50 changes: 11 additions & 39 deletions cider-stacktrace.el
Original file line number Diff line number Diff line change
Expand Up @@ -930,47 +930,19 @@ through the `cider-stacktrace-suppressed-errors' variable."
(cider-stacktrace-initialize causes)
(font-lock-refresh-defaults)))

(defun cider-stacktrace--analyze-stacktrace-op (stacktrace)
"Return the Cider NREPL op to analyze STACKTRACE."
(list "op" "analyze-stacktrace" "stacktrace" stacktrace))

(defun cider-stacktrace--analyze-render (causes)
"Render the CAUSES of the stacktrace analysis result."
(let ((buffer (get-buffer-create cider-error-buffer)))
(with-current-buffer buffer
(cider-stacktrace-mode)
(cider-stacktrace-render buffer (reverse causes))
(display-buffer buffer cider-jump-to-pop-to-buffer-actions))))

(defun cider-stacktrace-analyze-string (stacktrace)
"Analyze the STACKTRACE string and show the result."
(when (stringp stacktrace)
(set-text-properties 0 (length stacktrace) nil stacktrace))
(let (causes)
(cider-nrepl-send-request
`("op" "analyze-stacktrace"
"stacktrace" ,stacktrace
,@(cider--nrepl-print-request-plist fill-column))
(lambda (response)
(setq causes (nrepl-dbind-response response (class status)
(cond (class (cons response causes))
((and (member "done" status) causes)
(cider-stacktrace--analyze-render causes)))))))))

(defun cider-stacktrace-analyze-at-point ()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's good to replace these obsolete commands with some message explaining why there were removed. I don't think they had many users, being relatively new, but adding a message like "Use x instead" is never a bad idea IMO.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Will do.

"Analyze the stacktrace at point."
"Removed."
(interactive)
(message "This function has been removed.
You can jump to functions and methods directly from the printed stacktrace now."))
(make-obsolete 'cider-stacktrace-analyze-at-point nil "1.18")

(defun cider-stacktrace-analyze-in-region (&rest _)
"Removed."
(interactive)
(cond ((thing-at-point 'sentence)
(cider-stacktrace-analyze-string (thing-at-point 'sentence)))
((thing-at-point 'paragraph)
(cider-stacktrace-analyze-string (thing-at-point 'paragraph)))
(t (cider-stacktrace-analyze-in-region (region-beginning) (region-end)))))

(defun cider-stacktrace-analyze-in-region (beg end)
"Analyze the stacktrace in the region between BEG and END."
(interactive (list (region-beginning) (region-end)))
(let ((stacktrace (buffer-substring beg end)))
(cider-stacktrace-analyze-string stacktrace)))
(message "This function has been removed.
You can jump to functions and methods directly from the printed stacktrace now."))
(make-obsolete 'cider-stacktrace-analyze-in-region nil "1.18")

(provide 'cider-stacktrace)

Expand Down
21 changes: 6 additions & 15 deletions doc/modules/ROOT/pages/debugging/logging.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,22 @@
CIDER Log Mode allows you to capture, debug, inspect and view log
events emitted by Java logging frameworks. The captured log events can
be searched, streamed to the client, pretty-printed, and are integrated
with the CIDER link:inspector.html[Inspector] and
link:../usage/dealing_with_errors.html[Stacktrace Mode]. Here is a
with the CIDER link:inspector.html[Inspector]. Here is a
screenshot of CIDER Log Mode in action.

image::cider-log.png[CIDER Log]

NOTE: The screenshot displays the list of log events in the
`+*cider-log*+` buffer on the left. To the right, a log event is
visible in the `+*cider-inspect*+` buffer, where the exception of the
event is also displayed in the CIDER Stacktrace Mode. From the
Stacktrace Mode buffer you can jump to the source of each frame. At
the bottom the CIDER log menu is shown from which you can perform
logging related actions.
NOTE: The screenshot displays the list of log events in the `+*cider-log*+`
buffer on the left. To the right, a log event is visible in the
`+*cider-inspect*+` buffer. At the bottom the CIDER log menu is shown from which
you can perform logging related actions.

== Features

- Browse Javadocs and website of the given log framework.
- Search log events and show them in buffers.
- link:../usage/pretty_printing.html[Pretty-print] log events.
- Show log events in the CIDER link:inspector.html[Inspector].
- Show log event exceptions in the CIDER link:../usage/dealing_with_errors.html[Stacktrace Mode].
- Integration with https://github.com/doublep/logview[logview].

== Dependencies
Expand Down Expand Up @@ -278,7 +273,7 @@ The following keybindings can be used to interact with log consumers.
== Log Event

Log events can be searched, streamed to a client or viewed in CIDER's
Inspector and Stacktrace Mode. When searching log events the user can
Inspector Mode. When searching log events the user can
specify a set of filters. Events that match the filters are shown in
the `+*cider-log*+` buffer. Additionally a log consumer will be
attached to the appender to receive log events matching the search
Expand All @@ -301,10 +296,6 @@ The following keybindings can be used to interact with log events.
| kbd:[C-c M-l e c]
| Clear all events from the log event buffer.

| `cider-log-show-stacktrace`
| kbd:[C-c M-l e e]
| Show the stacktrace of the log event at point in the CIDER Stacktrace Mode.

| `cider-log-inspect-event`
| kbd:[C-c M-l e i]
| Show the log event in the CIDER Inspector.
Expand Down
86 changes: 0 additions & 86 deletions doc/modules/ROOT/pages/usage/dealing_with_errors.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -210,92 +210,6 @@ for instance:
(setq cider-stacktrace-fill-column 80)
----

=== Inspecting printed stacktraces

Some of the errors you encounter as a Clojurists aren't necessarily
evaluation errors that happened in your REPL. Many times, you see
errors printed in a textual representation in other buffers as well,
like log files or the REPL for example. Cider can parse and analyze
some of those printed errors as well and show them in
`cider-stacktrace-mode` with the following commands:

* The `cider-stacktrace-analyze-at-point` command uses the `thingatpt`
library to extract the current stacktrace at point. It sends the
extracted stacktrace to the middleware in order to parse and analyze
it, and then shows the result in Cider's `cider-stacktrace-mode`.

* The `cider-stacktrace-analyze-in-region` command does the same as
`cider-stacktrace-analyze-at-point`, but uses the current region to
extract the stacktrace.

==== Examples

Here is an example of a stacktrace printed with the Java
`printStackTrace` method:

[source,text]
----
clojure.lang.ExceptionInfo: BOOM-1 {:boom "1"}
at java.base/java.lang.Thread.run(Thread.java:829)
----

To open this stacktrace in the Cider stacktrace inspector, move point
somewhere over the exception and run `M-x
cider-stacktrace-analyze-at-point`.

This also works to some extent for exceptions that are buried inside a
string like the following exception:

[source,text]
----
"clojure.lang.ExceptionInfo: BOOM-1 {:boom \"1\"}\n at java.base/java.lang.Thread.run(Thread.java:829)"
----

Those exceptions are often hard to read. The Cider stacktrace
inspector can help you navigating exceptions even in those cases.

==== Supported formats

Cider recognizes stacktraces printed in the following formats:

- `Aviso` - Exceptions printed with the
https://ioavisopretty.readthedocs.io/en/latest/exceptions.html[write-exception]
function of the https://github.com/AvisoNovate/pretty[Aviso]
library.

- `clojure.repl` - Exceptions printed with the
https://clojure.github.io/clojure/branch-master/clojure.repl-api.html#clojure.repl/pst[clojure.repl/pst]
function.

- `clojure.stacktrace` - Exceptions printed with the
https://clojure.github.io/clojure/branch-master/clojure.stacktrace-api.html#clojure.stacktrace/print-cause-trace[clojure.stacktrace/print-cause-trace]
function.

- `Java` - Exceptions printed with the
https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html#printStackTrace--[Throwable/printStackTrace]
method.

- `Tagged Literal` - Exceptions printed with the
https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/pr[clojure.core/pr]
function.

==== Limitations

- Cider only recognizes stacktraces that have been printed in one of
the supported formats.

- Stacktraces are analyzed with the classpath of the Cider session the
buffer is associated with. If the stacktrace contains references to
classes not on this classpath, some information might be missing
from the analysis.

- The `cider-stacktrace-analyze-at-point` function might not detect
the stacktrace at point in every situation. The thing at point might
be different depending on which major mode is active in a
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),
Expand Down
54 changes: 0 additions & 54 deletions test/cider-stacktrace-tests.el
Original file line number Diff line number Diff line change
Expand Up @@ -257,57 +257,3 @@
:to-be-truthy)
(expect (or both shown1 shown2)
:to-be nil))))

(defun cider-stacktrace-tests--analyze-at-point (stacktrace pos)
"Test `cider-stacktrace-analyze-at-point' with STACKTRACE at POS."
(with-temp-buffer
(erase-buffer)
(insert stacktrace)
(goto-char pos)
(cider-stacktrace-analyze-at-point)))

(describe "cider-stacktrace-analyze-at-point"
:var (cider-stacktrace-analyze-string)
(before-each (spy-on 'cider-stacktrace-analyze-string))

(it "should analyze the Aviso stacktrace with point at beginning"
(cider-stacktrace-tests--analyze-at-point cider-stacktrace-tests-boom-aviso 0)
(expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-aviso))

(it "should analyze the Clojure stacktrace with point at beginning"
(cider-stacktrace-tests--analyze-at-point cider-stacktrace-tests-boom-clojure 0)
(expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-clojure))

(it "should analyze the Java stacktrace with point at beginning"
(cider-stacktrace-tests--analyze-at-point cider-stacktrace-tests-boom-java 0)
(expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-java))

(it "should analyze the Clojure stacktrace with point inside"
(cider-stacktrace-tests--analyze-at-point cider-stacktrace-tests-boom-clojure 10)
(expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-clojure))

(it "should analyze the Java stacktrace with point inside"
(cider-stacktrace-tests--analyze-at-point cider-stacktrace-tests-boom-java 10)
(expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-java)))

(defun cider-stacktrace-tests--analyze-in-region (stacktrace)
"Test `cider-stacktrace-analyze-in-region' with STACKTRACE."
(with-temp-buffer
(insert stacktrace)
(cider-stacktrace-analyze-in-region (point-min) (point-max))))

(describe "cider-stacktrace-analyze-in-region"
:var (cider-stacktrace-analyze-string)
(before-each (spy-on 'cider-stacktrace-analyze-string))

(it "should analyze the Aviso stacktrace in region"
(cider-stacktrace-tests--analyze-in-region cider-stacktrace-tests-boom-aviso)
(expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-aviso))

(it "should analyze the Clojure stacktrace in region"
(cider-stacktrace-tests--analyze-in-region cider-stacktrace-tests-boom-clojure)
(expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-clojure))

(it "should analyze the Java stacktrace in region"
(cider-stacktrace-tests--analyze-in-region cider-stacktrace-tests-boom-java)
(expect 'cider-stacktrace-analyze-string :to-have-been-called-with cider-stacktrace-tests-boom-java)))