@@ -114,6 +114,8 @@ export class LoggingDebugAdapterTracker implements vscode.DebugAdapterTracker {
114114 return ;
115115 }
116116
117+ this . handleBreakpointFallback ( debugMessage ) ;
118+
117119 if ( debugMessage . event === "exited" && debugMessage . body . exitCode ) {
118120 this . exitCode = debugMessage . body . exitCode ;
119121 this . exitHandler ?.( debugMessage . body . exitCode ) ;
@@ -131,6 +133,102 @@ export class LoggingDebugAdapterTracker implements vscode.DebugAdapterTracker {
131133 }
132134 }
133135
136+ private handleBreakpointFallback ( rawMsg : unknown ) : void {
137+ try {
138+ // Minimal typed view of the event payload we care about.
139+ type FrameLike = { source ?: { path ?: string } ; line ?: number } ;
140+ type BodyLike = {
141+ exitCode ?: number ;
142+ category ?: string ;
143+ output ?: string ;
144+ reason ?: string ;
145+ thread ?: { frames ?: FrameLike [ ] } ;
146+ source ?: { path ?: string } ;
147+ line ?: number ;
148+ } ;
149+
150+ const msg = rawMsg as { type ?: string ; event ?: string ; body ?: BodyLike | undefined } ;
151+ if ( ! msg || msg . type !== "event" ) {
152+ return ;
153+ }
154+
155+ // Case A: stopped event with reason = "breakpoint"
156+ if ( msg . event === "stopped" && msg . body ?. reason === "breakpoint" ) {
157+ const frames = msg . body . thread ?. frames ;
158+ if ( ! Array . isArray ( frames ) || frames . length === 0 ) {
159+ return ;
160+ }
161+
162+ const top = frames [ 0 ] ;
163+ const sourcePath = top ?. source ?. path ;
164+ const line = top ?. line ;
165+ if ( ! sourcePath || typeof line !== "number" ) {
166+ return ;
167+ }
168+
169+ const bpLine0 = line - 1 ; // VS Code uses 0-based lines
170+
171+ const breakpoints = vscode . debug . breakpoints . filter (
172+ b => b instanceof vscode . SourceBreakpoint
173+ ) as vscode . SourceBreakpoint [ ] ;
174+
175+ for ( const bp of breakpoints ) {
176+ const loc = bp . location ;
177+ if ( ! loc ) {
178+ continue ;
179+ }
180+ if ( loc . uri . fsPath !== sourcePath ) {
181+ continue ;
182+ }
183+ if ( loc . range . start . line !== bpLine0 ) {
184+ continue ;
185+ }
186+
187+ // Force a UI refresh so the breakpoint shows as installed.
188+ vscode . debug . removeBreakpoints ( [ bp ] ) ;
189+ vscode . debug . addBreakpoints ( [ bp ] ) ;
190+ break ;
191+ }
192+ return ;
193+ }
194+
195+ // Case B: explicit "breakpoint" event that carries source+line info
196+ if ( msg . event === "breakpoint" && msg . body ) {
197+ const sourcePath = msg . body . source ?. path ;
198+ const line = msg . body . line ;
199+ if ( ! sourcePath || typeof line !== "number" ) {
200+ return ;
201+ }
202+
203+ const bpLine0 = line - 1 ;
204+ const breakpoints = vscode . debug . breakpoints . filter (
205+ b => b instanceof vscode . SourceBreakpoint
206+ ) as vscode . SourceBreakpoint [ ] ;
207+
208+ for ( const bp of breakpoints ) {
209+ const loc = bp . location ;
210+ if ( ! loc ) {
211+ continue ;
212+ }
213+ if ( loc . uri . fsPath !== sourcePath ) {
214+ continue ;
215+ }
216+ if ( loc . range . start . line !== bpLine0 ) {
217+ continue ;
218+ }
219+
220+ vscode . debug . removeBreakpoints ( [ bp ] ) ;
221+ vscode . debug . addBreakpoints ( [ bp ] ) ;
222+ break ;
223+ }
224+ return ;
225+ }
226+ } catch ( err ) {
227+ // eslint-disable-next-line no-console
228+ console . error ( "Breakpoint fallback error:" , err ) ;
229+ }
230+ }
231+
134232 /**
135233 * The debug adapter session is about to be stopped. Delete the session from
136234 * the tracker
0 commit comments