From 6bee102f67461f44a5d539ed1c3904ee304aab66 Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Sat, 29 Apr 2023 15:32:42 +0800 Subject: [PATCH 01/14] Debug stacktrace of value if it's an exception --- src/cider/nrepl/middleware/debug.clj | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/cider/nrepl/middleware/debug.clj b/src/cider/nrepl/middleware/debug.clj index ad51742d..30767737 100644 --- a/src/cider/nrepl/middleware/debug.clj +++ b/src/cider/nrepl/middleware/debug.clj @@ -284,15 +284,18 @@ this map (identified by a key), and will `dissoc` it afterwards."} :rendered pr-str))) (defn- debug-stacktrace - "Create a dummy exception, send its stack." - [] + "Send the stacktrace of `value` if it is an exception. + Otherwise, create a dummy exception to view the call stack at the current location." + [value] (debugger-send {:status :stack - :causes [{:class "StackTrace" - :message "Harmless user-requested stacktrace" - :stacktrace (-> (Exception. "Dummy") - (stacktrace.analyzer/analyze (::print/print-fn *msg*)) - last :stacktrace)}]})) + :causes (if (instance? Throwable value) + (stacktrace.analyzer/analyze value (::print/print-fn *msg*)) + [{:class "StackTrace" + :message "Harmless user-requested stacktrace" + :stacktrace (-> (Exception. "Dummy") + (stacktrace.analyzer/analyze (::print/print-fn *msg*)) + last :stacktrace)}])})) (def debug-commands "An unsorted set of commands supported by the debugger." @@ -365,7 +368,7 @@ this map (identified by a key), and will `dissoc` it afterwards."} value) :here (do (skip-breaks! :before coord (:code dbg-state) force?) value) - :stacktrace (do (debug-stacktrace) + :stacktrace (do (debug-stacktrace value) (recur value dbg-state)) :trace (do (skip-breaks! :trace) value) From 53b2891908855bcd84a246d9ca9cdb80169ae5a6 Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Mon, 8 May 2023 14:50:09 +0800 Subject: [PATCH 02/14] Do not use heuristics on exception breakpoints --- src/cider/nrepl/middleware/debug.clj | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/cider/nrepl/middleware/debug.clj b/src/cider/nrepl/middleware/debug.clj index 30767737..18799ade 100644 --- a/src/cider/nrepl/middleware/debug.clj +++ b/src/cider/nrepl/middleware/debug.clj @@ -597,22 +597,17 @@ this map (identified by a key), and will `dissoc` it afterwards."} (defmacro breakpoint-if-exception "Wrap form in a try-catch that has breakpoint on exception. - Ignores uninteresting forms. - Uninteresting forms are symbols that resolve to `clojure.core` - (taking locals into account), and sexps whose head is present in - `irrelevant-return-value-forms`. Used as :breakfunction in `tag-form`." + Used as :breakfunction in `tag-form`." [form dbg-state original-form] - (if (uninteresting-form? &env form) - form - `(try ~form - (catch Throwable ex# - (let [exn-message# (.getMessage ex#) - break-result# (expand-break exn-message# ~dbg-state ~original-form)] - (if (= exn-message# break-result#) - ;; if they continued then rethrow the exception - (throw ex#) - ;; otherwise return the value they injected - break-result#)))))) + `(try ~form + (catch Throwable ex# + (let [exn-message# (.getMessage ex#) + break-result# (expand-break exn-message# ~dbg-state ~original-form)] + (if (= exn-message# break-result#) + ;; if they continued then rethrow the exception + (throw ex#) + ;; otherwise return the value they injected + break-result#))))) ;;; ## Data readers ;; From bd45364246c8cf67ead0ab35843a6408a34072db Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Mon, 8 May 2023 14:52:55 +0800 Subject: [PATCH 03/14] Return the exception, add :caught-msg to response --- src/cider/nrepl/middleware/debug.clj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cider/nrepl/middleware/debug.clj b/src/cider/nrepl/middleware/debug.clj index 18799ade..aab62dea 100644 --- a/src/cider/nrepl/middleware/debug.clj +++ b/src/cider/nrepl/middleware/debug.clj @@ -601,9 +601,9 @@ this map (identified by a key), and will `dissoc` it afterwards."} [form dbg-state original-form] `(try ~form (catch Throwable ex# - (let [exn-message# (.getMessage ex#) - break-result# (expand-break exn-message# ~dbg-state ~original-form)] - (if (= exn-message# break-result#) + (let [~'STATE__ (assoc-in ~'STATE__ [:msg :caught-msg] (.getMessage ex#)) + break-result# (expand-break ex# ~dbg-state ~original-form)] + (if (= ex# break-result#) ;; if they continued then rethrow the exception (throw ex#) ;; otherwise return the value they injected From 70a39f62a9589cc541c6db26d08efe243bf6d520 Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Sat, 29 Apr 2023 15:43:18 +0800 Subject: [PATCH 04/14] Forms tagged with #break are always interesting --- src/cider/nrepl/middleware/debug.clj | 39 ++++++++++++++++------------ 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/cider/nrepl/middleware/debug.clj b/src/cider/nrepl/middleware/debug.clj index aab62dea..36583567 100644 --- a/src/cider/nrepl/middleware/debug.clj +++ b/src/cider/nrepl/middleware/debug.clj @@ -494,7 +494,7 @@ this map (identified by a key), and will `dissoc` it afterwards."} {:style/indent 1} [form dbg-state original-form] `(with-initial-debug-bindings - (breakpoint-if-interesting + (breakpoint ~form ~dbg-state ~original-form))) (defmacro breakpoint-if-exception-with-initial-debug-bindings @@ -571,29 +571,34 @@ this map (identified by a key), and will `dissoc` it afterwards."} (and (seq? form) (irrelevant-return-value-forms (first form))))) +(defmacro breakpoint + "Wrap form in a breakpoint unconditionally." + [form {:keys [coor] :as dbg-state} original-form] + (let [condition (:break/when (meta form))] + (if condition + ;; If there is a condition and it is falsy, we need to skip + ;; the current level (:deeper than parent coor), but only + ;; once. Next time, we need to test the condition again. + `(let [old-breaks# @*skip-breaks*] + (when-not ~condition + (skip-breaks! :deeper ~(vec (butlast coor)) (:code (:msg ~'STATE__)) false)) + (try + (expand-break ~form ~dbg-state ~original-form) + ;; in case :continue-all was requested in a deeper level + ;; we don't want go back to the old-breaks + (finally (when (not= :all (:mode @*skip-breaks*)) + (reset! *skip-breaks* old-breaks#))))) + `(expand-break ~form ~dbg-state ~original-form)))) + (defmacro breakpoint-if-interesting "Wrap form in a breakpoint if it looks interesting. Uninteresting forms are symbols that resolve to `clojure.core` (taking locals into account), and sexps whose head is present in `irrelevant-return-value-forms`. Used as :breakfunction in `tag-form`." - [form {:keys [coor] :as dbg-state} original-form] + [form dbg-state original-form] (if (uninteresting-form? &env form) form - (let [condition (:break/when (meta form))] - (if condition - ;; If there is a condition and it is falsy, we need to skip - ;; the current level (:deeper than parent coor), but only - ;; once. Next time, we need to test the condition again. - `(let [old-breaks# @*skip-breaks*] - (when-not ~condition - (skip-breaks! :deeper ~(vec (butlast coor)) (:code (:msg ~'STATE__)) false)) - (try - (expand-break ~form ~dbg-state ~original-form) - ;; in case :continue-all was requested in a deeper level - ;; we don't want go back to the old-breaks - (finally (when (not= :all (:mode @*skip-breaks*)) - (reset! *skip-breaks* old-breaks#))))) - `(expand-break ~form ~dbg-state ~original-form))))) + `(breakpoint ~form ~dbg-state ~original-form))) (defmacro breakpoint-if-exception "Wrap form in a try-catch that has breakpoint on exception. From f007e7c427ffda7967bfc041554116338f40cc23 Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Thu, 15 Jun 2023 23:55:44 +0800 Subject: [PATCH 05/14] Use heuristic for instrumenting top level of #dbg Using #dbg on a def/defn should skip the breakpoint since return value is uninteresting. Doing otherwise causes :continue to behave incorrectly on a top-level instrumented form, since the the toplevel breakpoint is registered as the first coor even if it never triggers. --- src/cider/nrepl/middleware/debug.clj | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cider/nrepl/middleware/debug.clj b/src/cider/nrepl/middleware/debug.clj index 36583567..d333d0ef 100644 --- a/src/cider/nrepl/middleware/debug.clj +++ b/src/cider/nrepl/middleware/debug.clj @@ -497,6 +497,13 @@ this map (identified by a key), and will `dissoc` it afterwards."} (breakpoint ~form ~dbg-state ~original-form))) +(defmacro breakpoint-if-interesting-with-initial-debug-bindings + {:style/indent 1} + [form dbg-state original-form] + `(with-initial-debug-bindings + (breakpoint-if-interesting + ~form ~dbg-state ~original-form))) + (defmacro breakpoint-if-exception-with-initial-debug-bindings {:style/indent 1} [form dbg-state original-form] @@ -627,7 +634,7 @@ this map (identified by a key), and will `dissoc` it afterwards."} `form` itself is also marked." [form] (ins/tag-form (ins/tag-form-recursively form #'breakpoint-if-interesting) - #'breakpoint-with-initial-debug-bindings)) + #'breakpoint-if-interesting-with-initial-debug-bindings)) (defn break-on-exception-reader "#exn reader. Wrap `form` in try-catch and break only on exception" From 2024bf484215da2c17205dd6871e94ac8db44c98 Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Wed, 14 Jun 2023 09:41:32 +0800 Subject: [PATCH 06/14] Rename reader macros -> break! / dbg! --- src/cider/nrepl/middleware/debug.clj | 2 +- src/data_readers.clj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cider/nrepl/middleware/debug.clj b/src/cider/nrepl/middleware/debug.clj index d333d0ef..327538c0 100644 --- a/src/cider/nrepl/middleware/debug.clj +++ b/src/cider/nrepl/middleware/debug.clj @@ -676,7 +676,7 @@ this map (identified by a key), and will `dissoc` it afterwards."} fake-reader (fn [x] (reset! has-debug? true) x)] (binding [*ns* (find-ns (symbol (or ns "user"))) *data-readers* (->> (repeat fake-reader) - (interleave '[dbg exn dbgexn break light]) + (interleave '[dbg dbg! break break! light]) (apply assoc *data-readers*))] (try ;; new-line in REPL always throws; skip for debug convenience diff --git a/src/data_readers.clj b/src/data_readers.clj index c865dc09..713b31f8 100644 --- a/src/data_readers.clj +++ b/src/data_readers.clj @@ -1,5 +1,5 @@ {dbg cider.nrepl.middleware.debug/debug-reader break cider.nrepl.middleware.debug/breakpoint-reader - exn cider.nrepl.middleware.debug/break-on-exception-reader - dbgexn cider.nrepl.middleware.debug/debug-on-exception-reader + break! cider.nrepl.middleware.debug/break-on-exception-reader + dbg! cider.nrepl.middleware.debug/debug-on-exception-reader light cider.nrepl.middleware.enlighten/light-reader} From 297b2ccf16a9d263755c776837f71d2ab7c3f6bb Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Sat, 29 Apr 2023 15:39:05 +0800 Subject: [PATCH 07/14] Make the set of debugging reader macros extensible --- src/cider/nrepl/middleware/debug.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cider/nrepl/middleware/debug.clj b/src/cider/nrepl/middleware/debug.clj index 327538c0..5744abc1 100644 --- a/src/cider/nrepl/middleware/debug.clj +++ b/src/cider/nrepl/middleware/debug.clj @@ -665,6 +665,10 @@ this map (identified by a key), and will `dissoc` it afterwards."} (eval form1))) (throw e)))))) +(def ^:dynamic *debug-data-readers* + "Reader macros like #dbg which cause code to be instrumented when present." + '#{dbg exn dbgexn break light}) + ;;; ## Middleware (defn- maybe-debug "Return msg, prepared for debugging if code contains debugging macros." @@ -676,8 +680,8 @@ this map (identified by a key), and will `dissoc` it afterwards."} fake-reader (fn [x] (reset! has-debug? true) x)] (binding [*ns* (find-ns (symbol (or ns "user"))) *data-readers* (->> (repeat fake-reader) - (interleave '[dbg dbg! break break! light]) - (apply assoc *data-readers*))] + (zipmap *debug-data-readers*) + (merge *data-readers*))] (try ;; new-line in REPL always throws; skip for debug convenience (when (> (count code) 3) From df2f6ca176dfa8f932402bdf429345382b96bf85 Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Sat, 29 Apr 2023 15:54:25 +0800 Subject: [PATCH 08/14] Allow wrapping of non-list colls in breakpoints --- src/cider/nrepl/middleware/debug.clj | 4 ++-- src/cider/nrepl/middleware/util/instrument.clj | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/cider/nrepl/middleware/debug.clj b/src/cider/nrepl/middleware/debug.clj index 5744abc1..2c7605ec 100644 --- a/src/cider/nrepl/middleware/debug.clj +++ b/src/cider/nrepl/middleware/debug.clj @@ -627,7 +627,7 @@ this map (identified by a key), and will `dissoc` it afterwards."} (defn breakpoint-reader "#break reader. Mark `form` for breakpointing." [form] - (ins/tag-form form #'breakpoint-with-initial-debug-bindings)) + (ins/tag-form form #'breakpoint-with-initial-debug-bindings true)) (defn debug-reader "#dbg reader. Mark all forms in `form` for breakpointing. @@ -639,7 +639,7 @@ this map (identified by a key), and will `dissoc` it afterwards."} (defn break-on-exception-reader "#exn reader. Wrap `form` in try-catch and break only on exception" [form] - (ins/tag-form form #'breakpoint-if-exception-with-initial-debug-bindings)) + (ins/tag-form form #'breakpoint-if-exception-with-initial-debug-bindings true)) (defn debug-on-exception-reader "#dbgexn reader. Mark all forms in `form` for breakpointing on exception. diff --git a/src/cider/nrepl/middleware/util/instrument.clj b/src/cider/nrepl/middleware/util/instrument.clj index d236e941..a51159f4 100644 --- a/src/cider/nrepl/middleware/util/instrument.clj +++ b/src/cider/nrepl/middleware/util/instrument.clj @@ -225,9 +225,11 @@ ;; Other coll types are safe, so we go inside them and only ;; instrument what's interesting. ;; Do we also need to check for seq? - coll? (doall (instrument-coll form)) + coll? (cond-> (doall (instrument-coll form)) + (::do-break (meta form)) (with-break)) ;; Other things are uninteresting, literals or unreadable objects. - form)) + (cond-> form + (::do-break (meta form)) (with-break)))) ;;;; ## Pre-instrumentation ;;; @@ -294,9 +296,14 @@ "Tag form to be instrumented with breakfunction. This sets the ::breakfunction metadata of form, which can then be used by `instrument-tagged-code`. See this function for the meaning - of breakfunction." - [form breakfunction] - (m/merge-meta form {::breakfunction breakfunction})) + of breakfunction. + When `do-break?` is true it tells the instrumenter to wrap the form + with a breakpoint regardless of other heuristics." + ([form breakfunction] + (tag-form form breakfunction false)) + ([form breakfunction do-break?] + (m/merge-meta form {::breakfunction breakfunction} + (when do-break? {::do-break true})))) (defn tag-form-recursively "Like `tag-form` but also tag all forms inside the given form." From e08bdb3f6bb55fa6483e536d34334bd43f83b32b Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Tue, 13 Jun 2023 22:53:09 +0800 Subject: [PATCH 09/14] Fix some typos --- doc/modules/ROOT/pages/hacking.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/modules/ROOT/pages/hacking.adoc b/doc/modules/ROOT/pages/hacking.adoc index 30471363..b0867554 100644 --- a/doc/modules/ROOT/pages/hacking.adoc +++ b/doc/modules/ROOT/pages/hacking.adoc @@ -1,12 +1,12 @@ = Hacking on cider-nrepl -Hacking on cider-nrepl requires nothing bit a bit of knowledge of Clojure and nREPL. +Hacking on cider-nrepl requires nothing but a bit of knowledge of Clojure and nREPL. In this section we'll take a look at some practical tips to make you more productive while working on the project. == Makefile -cider-nrepl has some pretty complicated Lein profiles, as it has to deal with multiple version of +cider-nrepl has some pretty complicated Lein profiles, as it has to deal with multiple versions of Clojure and ClojureScript, plus dependency inlining with Mr. Anderson. That's why we've added a good old `Makefile` to save you the trouble of having to think about the profiles and just focus on the tasks at hand. From 2de1f779d555555621068e1b44361b462ef2ab32 Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Thu, 15 Jun 2023 22:46:10 +0800 Subject: [PATCH 10/14] Update readme --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2166853f..5bff8cb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ * Bump `cljfmt` to 0.9.2. * Bump `puget` to 1.3.4. +* [#769](https://github.com/clojure-emacs/cider-nrepl/issues/769): Introduce new `#break!` and `#dbg!` reader macros for breaking on exception +* [#772](https://github.com/clojure-emacs/cider-nrepl/issues/772): Improve debugger heuristics on "interesting" breakpoints (Always break on forms tagged with #break, including vectors and maps) +* The `wrap-debug` middleware now adds an optional `:caught-msg` key to the `eval` op response, containing the exception message when caught by the debugger. ## 0.30.0 (2023-01-31) From e381757894d2dc53d8bdbdc99c000587eacaa04f Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Thu, 15 Jun 2023 23:11:11 +0800 Subject: [PATCH 11/14] Fix interop test failure on newer JDKs Test for elements in a known set instead of strict sequence equality, since the newly introduced (?) java.lang.StackTraceElement also has a .length method. --- test/clj/cider/nrepl/middleware/info_test.clj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/clj/cider/nrepl/middleware/info_test.clj b/test/clj/cider/nrepl/middleware/info_test.clj index d02c2504..62ca98a8 100644 --- a/test/clj/cider/nrepl/middleware/info_test.clj +++ b/test/clj/cider/nrepl/middleware/info_test.clj @@ -310,8 +310,8 @@ (testing "java interop method with multiple classes" (let [response (session/message {:op "eldoc" :sym ".length" :ns "cider.nrepl.middleware.info-test"})] - (is (= (:class response) - ["java.lang.String" "java.lang.StringBuffer" "java.lang.CharSequence" "java.lang.StringBuilder"]) + (is (every? (set (:class response)) ;; is a superset of: + ["java.lang.String" "java.lang.StringBuffer" "java.lang.CharSequence" "java.lang.StringBuilder"]) (pr-str response)) (is (= (:member response) "length")) (is (not (contains? response :ns))) From aaac6a9e7a1f08bed01d089474bf4b2c4e5e7bc4 Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Thu, 15 Jun 2023 23:09:41 +0800 Subject: [PATCH 12/14] Fix content-type test failure on macOS Compare the canonical file paths, since Java creates temp files in /var which is a symlink for /private/var. --- test/clj/cider/nrepl/middleware/content_type_test.clj | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/clj/cider/nrepl/middleware/content_type_test.clj b/test/clj/cider/nrepl/middleware/content_type_test.clj index 7c2d02aa..96677f3c 100644 --- a/test/clj/cider/nrepl/middleware/content_type_test.clj +++ b/test/clj/cider/nrepl/middleware/content_type_test.clj @@ -44,14 +44,15 @@ [:body :content-type :content-transfer-encoding :status])))) (testing "java.io.File" - (let [f (java.io.File/createTempFile "foo" ".txt")] + (let [f (java.io.File/createTempFile "foo" ".txt") + path (.getCanonicalPath f)] (is (= {:body "" :content-type ["message/external-body" - {:URL (str "file:" f) :access-type "URL"}] + {:URL (str "file:" path) :access-type "URL"}] :status #{"done"}} (-> {:op "eval" - :code (str "(java.io.File. " (pr-str (str f)) ")") + :code (str "(java.io.File. " (pr-str path) ")") :content-type "true"} session/message (select-keys [:body :content-type :content-transfer-encoding :status])))))) From 40f1aac260ed1852b19b71879010a58dc8cdf050 Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Fri, 16 Jun 2023 00:49:23 +0800 Subject: [PATCH 13/14] Add/update tests for new #break semantics --- .../middleware/debug_integration_test.clj | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/test/clj/cider/nrepl/middleware/debug_integration_test.clj b/test/clj/cider/nrepl/middleware/debug_integration_test.clj index e5b574a8..f4ee21fa 100644 --- a/test/clj/cider/nrepl/middleware/debug_integration_test.clj +++ b/test/clj/cider/nrepl/middleware/debug_integration_test.clj @@ -136,18 +136,59 @@ (f))))) (deftest debug-expression-test + (--> :eval "(ns user.test.debug)") + (<-- {:ns "user.test.debug"}) + (<-- {:status ["done"]}) + (testing "normal eval (no debugging)" (--> :eval "(+ 2 3)") (<-- {:value "5"}) (<-- {:status ["done"]})) - (testing "#break reader, no breakpoints" - ;; This code has only literals and core functions, so it should - ;; not break, but should just return the value + (testing "Top level breakpoints do not trigger" + ;; Since triggering a breakpoint to inspect the toplevel value is no different + ;; from returning the value. (--> :eval "#break (+ 2 3)") (<-- {:value "5"}) (<-- {:status ["done"]})) + (testing "#break on return value of call" + (--> :eval "(do #break (+ 2 3) :ok)") + (<-- {:debug-value "5"}) + (--> :next) + (<-- {:value ":ok"}) + (<-- {:status ["done"]})) + + (testing "#dbg reader on uninteresting forms" + ;; The return value of a def form is uninteresting, as well as the sub forms + ;; which are just core vars and literals. + ;; #dbg should not break on any of them and just return the value. + (--> :eval "(do #dbg (def foo [2 + {:skip \"me\"}]) foo)") + (<-- {:value "[2 #function[clojure.core/+] {:skip \"me\"}]"}) + (<-- {:status ["done"]})) + + (testing "#break reader on uninteresting forms" + ;; #break signifies that the user explicitly wants to set a breakpoint there, + ;; so ignore any of #dbg reader's heuristics on what an interesting form is. + (--> :eval "(let [a 1] #break [2 3], #break {:a a}, #break + #break (def foo 4) foo) ") + (<-- {:debug-value "[2 3]"}) + (--> :next) + (<-- {:debug-value "{:a 1}"}) + (--> :next) + (<-- {:debug-value "#function[clojure.core/+]"}) + (--> :next) + (<-- {:debug-value "#'user.test.debug/foo"}) + (--> :next) + (<-- {:value "4"}) + (<-- {:status ["done"]})) + + (testing "#break reader on literals" + ;; The instrumenter cannot tag literals which don't support metadata, + ;; so #break does not work on them. + (--> :eval "(do #break :kwd, #break 123, #break \"string\" :ok)") + (<-- {:value ":ok"}) + (<-- {:status ["done"]})) + (testing "#dbg reader, with breaks" (--> :eval "#dbg From 705c3a98613ed31586600dd3f6b5f889a436ce30 Mon Sep 17 00:00:00 2001 From: yuhan0 <qythium@gmail.com> Date: Fri, 16 Jun 2023 01:07:00 +0800 Subject: [PATCH 14/14] Fix indentation --- src/cider/nrepl/middleware/util/instrument.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cider/nrepl/middleware/util/instrument.clj b/src/cider/nrepl/middleware/util/instrument.clj index a51159f4..1fb28238 100644 --- a/src/cider/nrepl/middleware/util/instrument.clj +++ b/src/cider/nrepl/middleware/util/instrument.clj @@ -303,7 +303,7 @@ (tag-form form breakfunction false)) ([form breakfunction do-break?] (m/merge-meta form {::breakfunction breakfunction} - (when do-break? {::do-break true})))) + (when do-break? {::do-break true})))) (defn tag-form-recursively "Like `tag-form` but also tag all forms inside the given form."