67
67
; ;; Variables and functions used for navigating between breakpoints.
68
68
(def ^:dynamic *skip-breaks*
69
69
" Map used to determine whether to skip a breakpoint.
70
- Don't set or examine this directly, it is bound in the session
71
- binding map, use `skip-breaks!` and `skip-breaks?` instead.
72
- Its value is discarded at the end each eval session."
70
+ Don't set or examine this directly, it is bound in the session binding map,
71
+ use `skip-breaks!` and `skip-breaks?` instead. Its value is reset at the
72
+ beginning each eval session."
73
73
(atom nil ))
74
74
75
75
(defn- random-uuid-str
90
90
" True if the breakpoint at coordinates should be skipped.
91
91
92
92
The `*skip-breaks*` map stores a `mode`, `coordinates`, the `code` that it
93
- applies to, and a `force?` flag. Behaviour depends on the `mode`:
93
+ applies to, and a `force?` flag.
94
+
95
+ Behaviour depends on the `mode`:
94
96
- :all - return true, skipping all breaks
95
97
- :trace - return false, skip nothing
96
98
- :deeper - return true if the given coordinates are deeper than the
101
103
For :deeper and :before, if we are not in the same code (i.e. we have stepped
102
104
into another instrumented function and code argument doesn't match old code in
103
105
*skip-breaks*), then return the value of `force?`."
104
- [coordinates code]
105
- (if (seq coordinates)
106
- (when-let [{mode :mode skip-coords :coor
107
- code-to-skip :code force? :force? } @*skip-breaks*]
108
- (let [same-defn? (identical? code-to-skip code)]
109
- (case mode
110
- ; ; From :continue, skip everything.
111
- :all true
112
- ; ; From :trace, never skip.
113
- :trace false
114
- ; ; From :out, skip some breaks.
115
- :deeper (if same-defn?
116
- (let [parent (take (count skip-coords) coordinates)]
117
- (and (seq= skip-coords parent)
118
- (> (count coordinates) (count parent))))
119
- force?)
120
- ; ; From :here, skip some breaks.
121
- :before (if same-defn?
122
- (ins/coord< coordinates skip-coords)
123
- force?))))
124
- ; ; We don't breakpoint top-level sexps, because their return value
125
- ; ; is already displayed anyway.
126
- true ))
106
+ [coor STATE__]
107
+ (or (and STATE__ @(:skip STATE__))
108
+ (if (seq coor)
109
+ (when-let [{mode :mode skip-coords :coor
110
+ code-to-skip :code force? :force? } @*skip-breaks*]
111
+ (let [same-defn? (identical? code-to-skip (get-in STATE__ [:msg :code ]))]
112
+ (case mode
113
+ ; ; From :continue, skip everything.
114
+ :all true
115
+ ; ; From :trace, never skip.
116
+ :trace false
117
+ ; ; From :out, skip some breaks.
118
+ :deeper (if same-defn?
119
+ (let [parent (take (count skip-coords) coor)]
120
+ (and (seq= skip-coords parent)
121
+ (> (count coor) (count parent))))
122
+ force?)
123
+ ; ; From :here, skip some breaks.
124
+ :before (if same-defn?
125
+ (ins/coord< coor skip-coords)
126
+ force?))))
127
+ ; ; We don't breakpoint top-level sexps, because their return value
128
+ ; ; is already displayed anyway.
129
+ true )))
127
130
128
131
(defn skip-breaks!
129
132
" Set the value of *skip-breaks* for the top-level breakpoint.
@@ -281,8 +284,6 @@ this map (identified by a key), and will `dissoc` it afterwards."}
281
284
(eval-with-locals (or code (read-debug-input dbg-state :expression prompt))
282
285
dbg-state)))
283
286
284
- (declare read-debug-command )
285
-
286
287
(defn- debug-inspect
287
288
" Inspect `inspect-value`."
288
289
[page-size inspect-value]
@@ -305,6 +306,7 @@ this map (identified by a key), and will `dissoc` it afterwards."}
305
306
306
307
(def debug-commands
307
308
{" c" :continue
309
+ " C" :Continue
308
310
" e" :eval
309
311
" h" :here
310
312
" i" :in
@@ -336,51 +338,58 @@ this map (identified by a key), and will `dissoc` it afterwards."}
336
338
provide additional parameters. For instance, if this map has a :code entry,
337
339
its value is used for operations such as :eval, which would otherwise
338
340
interactively prompt for an expression."
339
- [value dbg-state]
340
- (let [commands (cond-> debug-commands
341
- (not (map? *msg*)) (dissoc " q" )
342
- (nil? (:locals dbg-state)) (dissoc " e" " j" " l" " p" )
343
- (cljs/grab-cljs-env *msg*) identity)
344
- response-raw (read-debug-input dbg-state commands nil )
345
- dbg-state (dissoc dbg-state :inspect )
346
-
347
- {:keys [code coord response page-size force?]
348
- :or {page-size 32 }} (if (map? response-raw)
349
- response-raw
350
- {:response response-raw})]
351
- (reset! step-in-to-next? false )
352
- (case response
353
- :next value
354
- :in (do (reset! step-in-to-next? true )
355
- value)
356
- :continue (do (skip-breaks! :all )
357
- value)
358
- :out (do (skip-breaks! :deeper (butlast (:coor dbg-state)) (:code dbg-state) force?)
359
- value)
360
- :here (do (skip-breaks! :before coord (:code dbg-state) force?)
361
- value)
362
- :stacktrace (do (debug-stacktrace )
363
- (recur value dbg-state))
364
- :trace (do (skip-breaks! :trace )
365
- value)
366
- :locals (->> (debug-inspect page-size (:locals dbg-state))
367
- (assoc dbg-state :inspect )
368
- (recur value))
369
- :inspect (try-if-let [val (read-eval-expression " Inspect value: " dbg-state code)]
370
- (->> (debug-inspect page-size val)
341
+ [coor value locals STATE__]
342
+ (loop [value value
343
+ dbg-state (assoc (:msg STATE__)
344
+ :debug-value (pr-short value)
345
+ :coor coor
346
+ :locals locals)]
347
+ (let [commands (cond-> debug-commands
348
+ (not (map? *msg*)) (dissoc " q" )
349
+ (nil? (:locals dbg-state)) (dissoc " e" " j" " l" " p" )
350
+ (cljs/grab-cljs-env *msg*) identity)
351
+ response-raw (read-debug-input dbg-state commands nil )
352
+ dbg-state (dissoc dbg-state :inspect )
353
+
354
+ {:keys [code coord response page-size force?]
355
+ :or {page-size 32 }} (if (map? response-raw)
356
+ response-raw
357
+ {:response response-raw})]
358
+ (reset! step-in-to-next? false )
359
+ (case response
360
+ :next value
361
+ :in (do (reset! step-in-to-next? true )
362
+ value)
363
+ :continue (do (reset! (:skip STATE__) true )
364
+ value)
365
+ :Continue (do (skip-breaks! :all )
366
+ value)
367
+ :out (do (skip-breaks! :deeper (butlast (:coor dbg-state)) (:code dbg-state) force?)
368
+ value)
369
+ :here (do (skip-breaks! :before coord (:code dbg-state) force?)
370
+ value)
371
+ :stacktrace (do (debug-stacktrace )
372
+ (recur value dbg-state))
373
+ :trace (do (skip-breaks! :trace )
374
+ value)
375
+ :locals (->> (debug-inspect page-size (:locals dbg-state))
371
376
(assoc dbg-state :inspect )
372
377
(recur value))
373
- (recur value dbg-state))
374
- :inject (try-if-let [val (read-eval-expression " Expression to inject: " dbg-state code)]
375
- val
376
- (recur value dbg-state))
377
- :eval (try-if-let [val (read-eval-expression " Expression to evaluate: " dbg-state code)]
378
- (recur value (assoc dbg-state :debug-value (pr-short val)))
379
- (recur value dbg-state))
380
- :quit (abort! )
381
- (do (abort! )
382
- (throw (ex-info " Invalid input from `read-debug-input`."
383
- {:response-raw response-raw}))))))
378
+ :inspect (try-if-let [val (read-eval-expression " Inspect value: " dbg-state code)]
379
+ (->> (debug-inspect page-size val)
380
+ (assoc dbg-state :inspect )
381
+ (recur value))
382
+ (recur value dbg-state))
383
+ :inject (try-if-let [val (read-eval-expression " Expression to inject: " dbg-state code)]
384
+ val
385
+ (recur value dbg-state))
386
+ :eval (try-if-let [val (read-eval-expression " Expression to evaluate: " dbg-state code)]
387
+ (recur value (assoc dbg-state :debug-value (pr-short val)))
388
+ (recur value dbg-state))
389
+ :quit (abort! )
390
+ (do (abort! )
391
+ (throw (ex-info " Invalid input from `read-debug-input`."
392
+ {:response-raw response-raw})))))))
384
393
385
394
(defn print-step-indented [depth form value]
386
395
(print (apply str (repeat (dec depth) " | " )))
@@ -473,6 +482,9 @@ this map (identified by a key), and will `dissoc` it afterwards."}
473
482
; ; top-level sexp, a (= col 1) is much more likely to be
474
483
; ; wrong than right.
475
484
(update :column #(if (= % 1 ) 0 %))))
485
+ ; ; the coor of first form is used as the debugger session id
486
+ :session-id (atom nil )
487
+ :skip (atom false )
476
488
:forms @*tmp-forms*}]
477
489
~@body))
478
490
@@ -481,31 +493,32 @@ this map (identified by a key), and will `dissoc` it afterwards."}
481
493
[form dbg-state original-form]
482
494
`(with-initial-debug-bindings
483
495
(breakpoint-if-interesting
484
- ~form ~dbg-state ~original-form)))
496
+ ~form ~dbg-state ~original-form)))
485
497
486
498
(defn break
487
499
" Breakpoint function.
488
500
Send the result of form and its coordinates to the client and wait for
489
501
response with `read-debug-command`'."
490
502
[coor val locals STATE__]
503
+ (if-let [first-coor @(:session-id STATE__)]
504
+ (when (= first-coor coor)
505
+ (reset! (:skip STATE__) false ))
506
+ (reset! (:session-id STATE__) coor))
491
507
(cond
492
- (skip-breaks? coor ( get-in STATE__ [ :msg :code ]) ) val
508
+ (skip-breaks? coor STATE__) val
493
509
; ; The length of `coor` is a good indicator of current code
494
510
; ; depth.
495
511
(= (:mode @*skip-breaks*) :trace )
496
512
(do (print-step-indented (count coor) (get-in STATE__ [:forms coor]) val)
497
513
val)
498
514
; ; Most common case - ask for input.
499
515
:else
500
- (read-debug-command val (assoc (:msg STATE__)
501
- :debug-value (pr-short val)
502
- :coor coor
503
- :locals locals))))
516
+ (read-debug-command coor val locals STATE__)))
504
517
505
518
(defn apply-instrumented-maybe
506
519
" Apply var-fn or its instrumented version to args."
507
520
[var-fn args coor STATE__]
508
- (let [stepin (step-in? var-fn coor ( get-in STATE__ [ :msg :code ]) )]
521
+ (let [stepin (step-in? var-fn coor STATE__)]
509
522
(apply (if stepin
510
523
(::instrumented (meta var-fn))
511
524
var-fn)
0 commit comments