@@ -11,6 +11,7 @@ my str $backspaces = "\b" x 80;
1111my $ remote ;
1212my $ default-thread ;
1313my @ user-threads ;
14+ my % breakpoints ;
1415my % abbreviated ;
1516my % reverse-abbreviated ;
1617my $ abbreviate-length ;
@@ -158,8 +159,12 @@ Supported commands:
158159 If suspend is 1, execution of the thread that hit it will stop.
159160 If stacktrace is 1, every hit will send a stack trace along with it.
160161
161- & bold(" clearbp" ) "[file path]" [line number]
162- Clear any breakpoints for a given filename and line number.
162+ & bold(" breakpoints" )
163+ Print all breakpoints.
164+ Synonyms: & bold(" bpl" )
165+
166+ & bold(" clearbp" ) [id] | "[file path]" [line number]
167+ Clear any breakpoints for a given ID or filename and line number.
163168
164169& bold(" assume thread" ) [thread number]
165170 If you don't pass a thread number in future commands, this one will be used.
@@ -326,14 +331,32 @@ sub backtrace($id --> Nil) {
326331sub breakpoint (
327332 Str () $ file , Int () $ line , Bool () $ suspend = False , Bool () $ stacktrace = False
328333--> Nil ) {
334+ if % breakpoints . pairs . first : { . value <file > eq $ file and . value <line > eq $ line } -> $ b {
335+ say " A breakpoint for this file and line (suspend={ {$ b . value <suspend >}} stacktrace={ $ b . value <stacktrace >} ) already exists." ;
336+ say ' Replace it with this new one? (y/n)' ;
337+ while my $ answer = prompt(' > ' ) {
338+ if $ answer eq ' y' { clearbp $ b . key ; last }
339+ elsif $ answer eq ' n' { return }
340+ else { say " It's a y/n answer, but given $ answer" }
341+ }
342+ }
343+
329344 my $ result := remote
330345 " setting breakpoint" ,
331346 { $ remote . breakpoint($ file , $ line , : $ suspend , : $ stacktrace ) }
347+ state $ id = 1 ;
348+ % breakpoints {$ id ++ } = % (: $ file , : $ line , : $ suspend , : $ stacktrace );
332349
333350 output-breakpoint-notifications($ file , $ result <line >, $ _ )
334351 with $ result <notifications >;
335352}
336353
354+ sub breakpoint-list {
355+ table-print " Breakpoints" => ((' id' , ' file' , ' line' , ' suspend' , ' stacktrace' ), | % breakpoints . sort (*. key ). map ({
356+ ($ _ . key , $ _ . value <file line suspend stacktrace >)>>. List . flat if defined $ _ . value
357+ }));
358+ }
359+
337360sub caller (Int () $ handle --> Nil ) {
338361 my $ result := remote
339362 " fetching caller context for handle $ handle" ,
@@ -342,11 +365,22 @@ sub caller(Int() $handle --> Nil) {
342365 say $ result . & to-json (: pretty);
343366}
344367
345- sub clearbp (Str () $ file , Int () $ line --> Nil ) {
368+ multi sub clearbp (Int () $ id --> Nil ) {
369+ say " Breakpoint with this ID ($ id ) does not exist" and return unless defined % breakpoints {$ id };
370+ my ($ file , $ line ) = % breakpoints {$ id }<file line >;
371+ clearbp $ file , $ line , $ id ;
372+ }
373+
374+ multi sub clearbp (Str () $ file , Int () $ line , Int () $ id ? is copy --> Nil ) {
375+ $ id // = % breakpoints . pairs . first ({ . value <file > eq $ file and . value <line > eq $ line }). key
376+ or say " No breakpoint like that ($ file :$ line ) exists" and return ;
377+ % breakpoints {$ id }: delete;
378+
346379 my $ result := remote
347380 " clearing breakpoint for $ file :$ line" ,
348381 { $ remote . clear-breakpoints($ file , $ line ) }
349- say $ result . & to-json (: pretty);
382+
383+ say " Deleted breakpoint for $ file :$ line with ID $ id" ;
350384}
351385
352386sub coderef (Int () $ frame , $ id ) {
@@ -900,12 +934,15 @@ sub MAIN(
900934 when /:s de[cont]? (\d + ) (\d + )? / {
901935 decont $0 , $1 ;
902936 }
903- when /:s clearbp \ "(.*? )\" (\d + ) / {
904- clearbp $0 , $1 ;
937+ when /:s clearbp [( \d + ) | \ "(.*? )\" (\d + )] / {
938+ $0 . Int ?? clearbp $0 !! clearbp $0 , $1 ;
905939 }
906940 when /:s [breakpoint| bp][":" | <.ws >]\"(.*? )\" (\d + ) (\d ? ) (\d ? ) / {
907941 breakpoint $0 , $1 , + $2 , + $3 ;
908942 }
943+ when /:s [breakpoints| bpl] / {
944+ breakpoint-list;
945+ }
909946 when /:s release[handles]? (\d + )+ % \s + / {
910947 release-handles | $0 ;
911948 }
0 commit comments