diff --git a/CHANGELOG.md b/CHANGELOG.md index ade86966e..14fd4a477 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Changes - [#3626](https://github.com/clojure-emacs/cider/issues/3626): `cider-ns-refresh`: jump to the relevant file/line on errors. +- [#3628](https://github.com/clojure-emacs/cider/issues/3628): `cider-ns-refresh`: summarize errors as an overlay. - Bump the injected nREPL to [1.1.1](https://github.com/nrepl/nrepl/blob/v1.1.1/CHANGELOG.md#111-2024-02-20). - Bump the injected `cider-nrepl` to [0.46.0](https://github.com/clojure-emacs/cider-nrepl/blob/1cc9b2/CHANGELOG.md#0460-2024-0305). - Updates [Orchard](https://github.com/clojure-emacs/orchard/blob/v0.23.0/CHANGELOG.md#0230-2024-03-03). diff --git a/cider-ns.el b/cider-ns.el index 81b0cd81b..51ab0ed12 100644 --- a/cider-ns.el +++ b/cider-ns.el @@ -120,7 +120,8 @@ namespace-qualified function of zero arity." (defun cider-ns--present-error (error) "Render the `ERROR' stacktrace, -and jump to the adequate file/line location." +and jump to the adequate file/line location, +presenting the error message as an overlay." (let* ((buf) (jump-args (seq-some (lambda (cause-dict) ;; a dict representing an exception cause (nrepl-dbind-response cause-dict (file-url line column) @@ -132,7 +133,21 @@ and jump to the adequate file/line location." (list buf (cons line column))))) error))) (when jump-args - (apply #'cider-jump-to jump-args)) + (apply #'cider-jump-to jump-args) + (when-let ((message (seq-some (lambda (cause-dict) + (nrepl-dbind-response cause-dict (message) + message)) + ;; `reverse' the causes as the first one typically is a CompilerException, which the second one is the actual exception: + (reverse error)))) + (with-current-buffer buf + (let ((cider-result-use-clojure-font-lock nil) + (trimmed-err (funcall cider-inline-error-message-function message))) + (cider--display-interactive-eval-result trimmed-err + 'error + (save-excursion + (end-of-defun) + (point)) + 'cider-error-overlay-face))))) (cider--render-stacktrace-causes error) ;; Select the window displaying the 'culprit' buffer so that the user can immediately fix it, ;; as most times the displayed stacktrace doesn't need much inspection: diff --git a/test/cider-ns-tests.el b/test/cider-ns-tests.el index e34e0ba64..ab98499be 100644 --- a/test/cider-ns-tests.el +++ b/test/cider-ns-tests.el @@ -34,3 +34,172 @@ (it "raises a user error if cider is not connected" (spy-on 'cider-connected-p :and-return-value nil) (expect (cider-ns-refresh) :to-throw 'user-error))) + +(defvar cider-ns-tests--sample-file-url + "file:test/cider_ns_tests.clj") + +(defvar cider-ns-tests--sample-causes + `((dict "class" "clojure.lang.Compiler$CompilerException" "column" 0 "compile-like" "false" "data" "{:clojure.error/phase :read-source, + :clojure.error/line 23, + :clojure.error/column 0, + :clojure.error/source \"gpml/handler/chat.clj\"}" "file" "gpml/handler/chat.clj" "file-url" ,cider-ns-tests--sample-file-url "line" 23 "location" + (dict "clojure.error/column" 0 "clojure.error/line" 23 "clojure.error/phase" "read-source" "clojure.error/source" "gpml/handler/chat.clj") + "message" "Syntax error reading source at (gpml/handler/chat.clj:23:0)." "path" "gpml/handler/chat.clj" "phase" "read-source" "stacktrace" + ((dict "class" "clojure.lang.Compiler" "file" "Compiler.java" "file-url" nil "flags" + ("tooling" "java") + "line" 7643 "method" "load" "name" "clojure.lang.Compiler/load" "type" "java") + (dict "class" "clojure.lang.RT" "file" "RT.java" "file-url" nil "flags" + ("tooling" "java") + "line" 381 "method" "loadResourceScript" "name" "clojure.lang.RT/loadResourceScript" "type" "java") + (dict "class" "clojure.lang.RT" "file" "RT.java" "file-url" nil "flags" + ("dup" "tooling" "java") + "line" 372 "method" "loadResourceScript" "name" "clojure.lang.RT/loadResourceScript" "type" "java") + (dict "class" "clojure.lang.RT" "file" "RT.java" "file-url" nil "flags" + ("tooling" "java") + "line" 459 "method" "load" "name" "clojure.lang.RT/load" "type" "java") + (dict "class" "clojure.lang.RT" "file" "RT.java" "file-url" nil "flags" + ("dup" "tooling" "java") + "line" 424 "method" "load" "name" "clojure.lang.RT/load" "type" "java") + (dict "class" "clojure.core$load$fn__6924" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load/fn" "line" 6167 "method" "invoke" "name" "clojure.core$load$fn__6924/invoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load") + (dict "class" "clojure.core$load" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load" "line" 6166 "method" "invokeStatic" "name" "clojure.core$load/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load") + (dict "class" "clojure.core$load" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load" "line" 6150 "method" "doInvoke" "name" "clojure.core$load/doInvoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load") + (dict "class" "clojure.lang.RestFn" "file" "RestFn.java" "file-url" nil "flags" + ("tooling" "java") + "line" 411 "method" "invoke" "name" "clojure.lang.RestFn/invoke" "type" "java") + (dict "class" "clojure.core$load_one" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-one" "line" 5939 "method" "invokeStatic" "name" "clojure.core$load_one/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-one") + (dict "class" "clojure.core$load_one" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-one" "line" 5934 "method" "invoke" "name" "clojure.core$load_one/invoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-one") + (dict "class" "clojure.core$load_lib$fn__6866" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-lib/fn" "line" 5981 "method" "invoke" "name" "clojure.core$load_lib$fn__6866/invoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-lib") + (dict "class" "clojure.core$load_lib" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-lib" "line" 5980 "method" "invokeStatic" "name" "clojure.core$load_lib/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-lib") + (dict "class" "clojure.core$load_lib" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-lib" "line" 5959 "method" "doInvoke" "name" "clojure.core$load_lib/doInvoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-lib") + (dict "class" "clojure.lang.RestFn" "file" "RestFn.java" "file-url" nil "flags" + ("tooling" "java") + "line" 145 "method" "applyTo" "name" "clojure.lang.RestFn/applyTo" "type" "java") + (dict "class" "clojure.core$apply" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("tooling" "clj") + "fn" "apply" "line" 669 "method" "invokeStatic" "name" "clojure.core$apply/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/apply") + (dict "class" "clojure.core$load_libs" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-libs" "line" 6022 "method" "invokeStatic" "name" "clojure.core$load_libs/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-libs") + (dict "class" "clojure.core$load_libs" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-libs" "line" 6006 "method" "doInvoke" "name" "clojure.core$load_libs/doInvoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-libs") + (dict "class" "clojure.lang.RestFn" "file" "RestFn.java" "file-url" nil "flags" + ("tooling" "java") + "line" 140 "method" "applyTo" "name" "clojure.lang.RestFn/applyTo" "type" "java") + (dict "class" "clojure.core$apply" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("tooling" "clj") + "fn" "apply" "line" 669 "method" "invokeStatic" "name" "clojure.core$apply/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/apply") + (dict "class" "clojure.core$require" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "require" "line" 6044 "method" "invokeStatic" "name" "clojure.core$require/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/require") + (dict "class" "clojure.core$require" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("dup" "clj") + "fn" "require" "line" 6044 "method" "doInvoke" "name" "clojure.core$require/doInvoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/require") + (dict "class" "clojure.lang.RestFn" "file" "RestFn.java" "file-url" nil "flags" + ("tooling" "java") + "line" 424 "method" "invoke" "name" "clojure.lang.RestFn/invoke" "type" "java"))) + (dict "class" "java.lang.RuntimeException" "compile-like" "false" "message" "Invalid token: :::a" "phase" nil "stacktrace" + ((dict "class" "clojure.lang.Util" "file" "Util.java" "file-url" nil "flags" + ("java") + "line" 221 "method" "runtimeException" "name" "clojure.lang.Util/runtimeException" "type" "java") + (dict "class" "clojure.lang.LispReader" "file" "LispReader.java" "file-url" nil "flags" + ("java") + "line" 412 "method" "interpretToken" "name" "clojure.lang.LispReader/interpretToken" "type" "java") + (dict "class" "clojure.lang.LispReader" "file" "LispReader.java" "file-url" nil "flags" + ("java") + "line" 305 "method" "read" "name" "clojure.lang.LispReader/read" "type" "java") + (dict "class" "clojure.lang.LispReader" "file" "LispReader.java" "file-url" nil "flags" + ("dup" "java") + "line" 216 "method" "read" "name" "clojure.lang.LispReader/read" "type" "java") + (dict "class" "clojure.lang.Compiler" "file" "Compiler.java" "file-url" nil "flags" + ("tooling" "java") + "line" 7631 "method" "load" "name" "clojure.lang.Compiler/load" "type" "java") + (dict "class" "clojure.lang.RT" "file" "RT.java" "file-url" nil "flags" + ("tooling" "java") + "line" 381 "method" "loadResourceScript" "name" "clojure.lang.RT/loadResourceScript" "type" "java") + (dict "class" "clojure.lang.RT" "file" "RT.java" "file-url" nil "flags" + ("dup" "tooling" "java") + "line" 372 "method" "loadResourceScript" "name" "clojure.lang.RT/loadResourceScript" "type" "java") + (dict "class" "clojure.lang.RT" "file" "RT.java" "file-url" nil "flags" + ("tooling" "java") + "line" 459 "method" "load" "name" "clojure.lang.RT/load" "type" "java") + (dict "class" "clojure.lang.RT" "file" "RT.java" "file-url" nil "flags" + ("dup" "tooling" "java") + "line" 424 "method" "load" "name" "clojure.lang.RT/load" "type" "java") + (dict "class" "clojure.core$load$fn__6924" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load/fn" "line" 6167 "method" "invoke" "name" "clojure.core$load$fn__6924/invoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load") + (dict "class" "clojure.core$load" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load" "line" 6166 "method" "invokeStatic" "name" "clojure.core$load/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load") + (dict "class" "clojure.core$load" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load" "line" 6150 "method" "doInvoke" "name" "clojure.core$load/doInvoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load") + (dict "class" "clojure.lang.RestFn" "file" "RestFn.java" "file-url" nil "flags" + ("tooling" "java") + "line" 411 "method" "invoke" "name" "clojure.lang.RestFn/invoke" "type" "java") + (dict "class" "clojure.core$load_one" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-one" "line" 5939 "method" "invokeStatic" "name" "clojure.core$load_one/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-one") + (dict "class" "clojure.core$load_one" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-one" "line" 5934 "method" "invoke" "name" "clojure.core$load_one/invoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-one") + (dict "class" "clojure.core$load_lib$fn__6866" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-lib/fn" "line" 5981 "method" "invoke" "name" "clojure.core$load_lib$fn__6866/invoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-lib") + (dict "class" "clojure.core$load_lib" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-lib" "line" 5980 "method" "invokeStatic" "name" "clojure.core$load_lib/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-lib") + (dict "class" "clojure.core$load_lib" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-lib" "line" 5959 "method" "doInvoke" "name" "clojure.core$load_lib/doInvoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-lib") + (dict "class" "clojure.lang.RestFn" "file" "RestFn.java" "file-url" nil "flags" + ("tooling" "java") + "line" 145 "method" "applyTo" "name" "clojure.lang.RestFn/applyTo" "type" "java") + (dict "class" "clojure.core$apply" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("tooling" "clj") + "fn" "apply" "line" 669 "method" "invokeStatic" "name" "clojure.core$apply/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/apply") + (dict "class" "clojure.core$load_libs" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-libs" "line" 6022 "method" "invokeStatic" "name" "clojure.core$load_libs/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-libs") + (dict "class" "clojure.core$load_libs" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "load-libs" "line" 6006 "method" "doInvoke" "name" "clojure.core$load_libs/doInvoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/load-libs") + (dict "class" "clojure.lang.RestFn" "file" "RestFn.java" "file-url" nil "flags" + ("tooling" "java") + "line" 140 "method" "applyTo" "name" "clojure.lang.RestFn/applyTo" "type" "java") + (dict "class" "clojure.core$apply" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("tooling" "clj") + "fn" "apply" "line" 669 "method" "invokeStatic" "name" "clojure.core$apply/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/apply") + (dict "class" "clojure.core$require" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("clj") + "fn" "require" "line" 6044 "method" "invokeStatic" "name" "clojure.core$require/invokeStatic" "ns" "clojure.core" "type" "clj" "var" "clojure.core/require") + (dict "class" "clojure.core$require" "file" "core.clj" "file-url" "jar:file:/Users/vemv/.m2/repository/org/clojure/clojure/1.12.900/clojure-1.12.900.jar!/clojure/core.clj" "flags" + ("dup" "clj") + "fn" "require" "line" 6044 "method" "doInvoke" "name" "clojure.core$require/doInvoke" "ns" "clojure.core" "type" "clj" "var" "clojure.core/require") + (dict "class" "clojure.lang.RestFn" "file" "RestFn.java" "file-url" nil "flags" + ("tooling" "java") + "line" 424 "method" "invoke" "name" "clojure.lang.RestFn/invoke" "type" "java") )))) + +(describe "cider-ns--present-error" + (it "Works without throwing errors" + (with-clojure-buffer "" + (cider-ns--present-error cider-ns-tests--sample-causes) + (when-let ((b (get-buffer "*cider-error*"))) ;; Clean it up for other tests + (kill-buffer b))))) diff --git a/test/cider_ns_tests.clj b/test/cider_ns_tests.clj new file mode 100644 index 000000000..66c0395a7 --- /dev/null +++ b/test/cider_ns_tests.clj @@ -0,0 +1,2 @@ +(ns cider-ns-tests + "Supports cider-ns-tests.el")