@@ -18,25 +18,28 @@ export function getManagedAdaptersDir(): string {
1818 return join ( home , ".debug-that" , "adapters" ) ;
1919}
2020
21- interface DapBreakpointEntry {
21+ interface DapBreakpointBase {
2222 ref : string ;
23- dapId ?: number ;
24- file : string ;
25- line : number ;
2623 condition ?: string ;
2724 hitCondition ?: string ;
2825 verified : boolean ;
26+ }
27+
28+ interface DapFileBreakpoint extends DapBreakpointBase {
29+ kind : "file" ;
30+ dapId ?: number ;
31+ file : string ;
32+ line : number ;
2933 actualLine ?: number ;
3034}
3135
32- interface DapFunctionBreakpointEntry {
33- ref : string ;
36+ interface DapFunctionBreakpoint extends DapBreakpointBase {
37+ kind : "function" ;
3438 name : string ;
35- condition ?: string ;
36- hitCondition ?: string ;
37- verified : boolean ;
3839}
3940
41+ type DapBreakpoint = DapFileBreakpoint | DapFunctionBreakpoint ;
42+
4043interface DapStackFrame {
4144 id : number ;
4245 name : string ;
@@ -58,10 +61,8 @@ export class DapSession extends BaseSession {
5861 private _stackFrames : DapStackFrame [ ] = [ ] ;
5962 private adapterCapabilities : DebugProtocol . Capabilities = { } ;
6063
61- // Breakpoints: DAP requires sending ALL breakpoints per file at once
62- private breakpoints = new Map < string , DapBreakpointEntry [ ] > ( ) ;
63- private allBreakpoints : DapBreakpointEntry [ ] = [ ] ;
64- private functionBreakpoints : DapFunctionBreakpointEntry [ ] = [ ] ;
64+ // Breakpoints: DAP requires sending ALL breakpoints per file/function at once
65+ private breakpoints : DapBreakpoint [ ] = [ ] ;
6566
6667 // Stored config (applied on launch/restart, and immediately if connected+paused)
6768 private _remaps : [ string , string ] [ ] = [ ] ;
@@ -255,9 +256,7 @@ export class DapSession extends BaseSession {
255256 this . resetState ( ) ;
256257 this . _stackFrames = [ ] ;
257258 this . _isAttached = false ;
258- this . breakpoints . clear ( ) ;
259- this . allBreakpoints = [ ] ;
260- this . functionBreakpoints = [ ] ;
259+ this . breakpoints = [ ] ;
261260 }
262261
263262 // ── Execution control ─────────────────────────────────────────────
@@ -331,7 +330,8 @@ export class DapSession extends BaseSession {
331330 // Resolve short filenames (e.g. "User.java") to full paths via sourcePaths
332331 file = this . resolveSourceFile ( file ) ;
333332
334- const entry : DapBreakpointEntry = {
333+ const entry : DapFileBreakpoint = {
334+ kind : "file" ,
335335 ref : "" , // will be set by RefTable
336336 file,
337337 line,
@@ -340,14 +340,7 @@ export class DapSession extends BaseSession {
340340 verified : false ,
341341 } ;
342342
343- // Add to per-file tracking
344- let fileBreakpoints = this . breakpoints . get ( file ) ;
345- if ( ! fileBreakpoints ) {
346- fileBreakpoints = [ ] ;
347- this . breakpoints . set ( file , fileBreakpoints ) ;
348- }
349- fileBreakpoints . push ( entry ) ;
350- this . allBreakpoints . push ( entry ) ;
343+ this . breakpoints . push ( entry ) ;
351344
352345 // Register ref
353346 const ref = this . refs . addBreakpoint ( `dap-bp:${ file } :${ line } ` , {
@@ -368,40 +361,33 @@ export class DapSession extends BaseSession {
368361 async removeBreakpoint ( ref : string ) : Promise < void > {
369362 this . requireConnected ( ) ;
370363
371- const entry = this . allBreakpoints . find ( ( bp ) => bp . ref === ref ) ;
372- if ( ! entry ) {
364+ const idx = this . breakpoints . findIndex ( ( bp ) => bp . ref === ref ) ;
365+ if ( idx === - 1 ) {
373366 throw new Error ( `Unknown breakpoint ref: ${ ref } ` ) ;
374367 }
375-
376- // Remove from per-file tracking
377- const fileBreakpoints = this . breakpoints . get ( entry . file ) ;
378- if ( fileBreakpoints ) {
379- const idx = fileBreakpoints . indexOf ( entry ) ;
380- if ( idx !== - 1 ) fileBreakpoints . splice ( idx , 1 ) ;
381- if ( fileBreakpoints . length === 0 ) {
382- this . breakpoints . delete ( entry . file ) ;
383- }
384- }
385-
386- // Remove from all-breakpoints list
387- const allIdx = this . allBreakpoints . indexOf ( entry ) ;
388- if ( allIdx !== - 1 ) this . allBreakpoints . splice ( allIdx , 1 ) ;
389-
390- // Remove from ref table
368+ // biome-ignore lint/style/noNonNullAssertion: idx validated above
369+ const entry = this . breakpoints [ idx ] ! ;
370+ this . breakpoints . splice ( idx , 1 ) ;
391371 this . refs . remove ( ref ) ;
392372
393- // Re-sync file breakpoints (or clear them if none left)
394- await this . syncFileBreakpoints ( entry . file ) ;
373+ if ( entry . kind === "file" ) {
374+ await this . syncFileBreakpoints ( entry . file ) ;
375+ } else {
376+ await this . syncFunctionBreakpoints ( ) ;
377+ }
395378 }
396379
397380 async removeAllBreakpoints ( ) : Promise < void > {
398381 this . requireConnected ( ) ;
399382
400- // Clear all files
401- const files = [ ...this . breakpoints . keys ( ) ] ;
402- this . breakpoints . clear ( ) ;
403- this . allBreakpoints = [ ] ;
404- this . functionBreakpoints = [ ] ;
383+ const files = new Set (
384+ this . breakpoints
385+ . filter ( ( bp ) : bp is DapFileBreakpoint => bp . kind === "file" )
386+ . map ( ( bp ) => bp . file ) ,
387+ ) ;
388+ const hadFunctionBps = this . breakpoints . some ( ( bp ) => bp . kind === "function" ) ;
389+
390+ this . breakpoints = [ ] ;
405391
406392 // Remove all BP refs
407393 for ( const entry of this . refs . list ( "BP" ) ) {
@@ -417,7 +403,9 @@ export class DapSession extends BaseSession {
417403 }
418404
419405 // Clear function breakpoints
420- await this . getDap ( ) . send ( "setFunctionBreakpoints" , { breakpoints : [ ] } ) ;
406+ if ( hadFunctionBps ) {
407+ await this . getDap ( ) . send ( "setFunctionBreakpoints" , { breakpoints : [ ] } ) ;
408+ }
421409 }
422410
423411 /** DAP breakpoints are always bound — the pending filter is ignored. */
@@ -428,21 +416,13 @@ export class DapSession extends BaseSession {
428416 line : number ;
429417 condition ?: string ;
430418 } > {
431- const fileBps = this . allBreakpoints . map ( ( bp ) => ( {
432- ref : bp . ref ,
433- type : "BP" as const ,
434- url : bp . file ,
435- line : bp . actualLine ?? bp . line ,
436- condition : bp . condition ,
437- } ) ) ;
438- const fnBps = this . functionBreakpoints . map ( ( bp ) => ( {
419+ return this . breakpoints . map ( ( bp ) => ( {
439420 ref : bp . ref ,
440421 type : "BP" as const ,
441- url : bp . name ,
442- line : 0 ,
422+ url : bp . kind === "file" ? bp . file : bp . name ,
423+ line : bp . kind === "file" ? ( bp . actualLine ?? bp . line ) : 0 ,
443424 condition : bp . condition ,
444425 } ) ) ;
445- return [ ...fileBps , ...fnBps ] ;
446426 }
447427
448428 /**
@@ -455,15 +435,16 @@ export class DapSession extends BaseSession {
455435 ) : Promise < { ref : string } > {
456436 this . requireConnected ( ) ;
457437
458- const entry : DapFunctionBreakpointEntry = {
438+ const entry : DapFunctionBreakpoint = {
439+ kind : "function" ,
459440 ref : "" ,
460441 name,
461442 condition : options ?. condition ,
462443 hitCondition : options ?. hitCount ? String ( options . hitCount ) : undefined ,
463444 verified : false ,
464445 } ;
465446
466- this . functionBreakpoints . push ( entry ) ;
447+ this . breakpoints . push ( entry ) ;
467448
468449 const ref = this . refs . addBreakpoint ( `dap-fn:${ name } ` , {
469450 url : name ,
@@ -475,21 +456,11 @@ export class DapSession extends BaseSession {
475456 return { ref } ;
476457 }
477458
478- async removeFunctionBreakpoint ( ref : string ) : Promise < void > {
479- this . requireConnected ( ) ;
480-
481- const idx = this . functionBreakpoints . findIndex ( ( bp ) => bp . ref === ref ) ;
482- if ( idx === - 1 ) {
483- throw new Error ( `Unknown function breakpoint ref: ${ ref } ` ) ;
484- }
485-
486- this . functionBreakpoints . splice ( idx , 1 ) ;
487- this . refs . remove ( ref ) ;
488- await this . syncFunctionBreakpoints ( ) ;
489- }
490-
491459 private async syncFunctionBreakpoints ( ) : Promise < void > {
492- const dapBps = this . functionBreakpoints . map ( ( bp ) => ( {
460+ const fnBps = this . breakpoints . filter (
461+ ( bp ) : bp is DapFunctionBreakpoint => bp . kind === "function" ,
462+ ) ;
463+ const dapBps = fnBps . map ( ( bp ) => ( {
493464 name : bp . name ,
494465 condition : bp . condition ,
495466 hitCondition : bp . hitCondition ,
@@ -503,8 +474,8 @@ export class DapSession extends BaseSession {
503474 | { breakpoints ?: Array < { id ?: number ; verified ?: boolean } > }
504475 | undefined ;
505476 const resultBps = body ?. breakpoints ?? [ ] ;
506- for ( let i = 0 ; i < this . functionBreakpoints . length ; i ++ ) {
507- const entry = this . functionBreakpoints [ i ] ;
477+ for ( let i = 0 ; i < fnBps . length ; i ++ ) {
478+ const entry = fnBps [ i ] ;
508479 const result = resultBps [ i ] ;
509480 if ( entry && result ) {
510481 entry . verified = result . verified ?? false ;
@@ -791,7 +762,7 @@ export class DapSession extends BaseSession {
791762 }
792763
793764 if ( options . breakpoints !== false ) {
794- snapshot . breakpointCount = this . allBreakpoints . length ;
765+ snapshot . breakpointCount = this . breakpoints . length ;
795766 }
796767
797768 return snapshot ;
@@ -1314,7 +1285,9 @@ export class DapSession extends BaseSession {
13141285 }
13151286
13161287 private async syncFileBreakpoints ( file : string ) : Promise < void > {
1317- const entries = this . breakpoints . get ( file ) ?? [ ] ;
1288+ const entries = this . breakpoints . filter (
1289+ ( bp ) : bp is DapFileBreakpoint => bp . kind === "file" && bp . file === file ,
1290+ ) ;
13181291
13191292 const dapBreakpoints = entries . map ( ( bp ) => {
13201293 const sbp : Record < string , unknown > = { line : bp . line } ;
0 commit comments