@@ -31,7 +31,7 @@ class PdbAdapter:
31
31
"""Adapter between DAP protocol and MicroPython's sys.settrace functionality."""
32
32
33
33
def __init__ (self ):
34
- self .breakpoints = {} # filename -> {line_no: breakpoint_info}
34
+ self .breakpoints : dict [ str , dict [ int , dict ]] = {} # filename -> {line_no: breakpoint_info} # todo - simplify - reduce info stored
35
35
self .current_frame = None
36
36
self .step_mode = None # None, 'over', 'into', 'out'
37
37
self .step_frame = None
@@ -40,8 +40,8 @@ def __init__(self):
40
40
self .continue_event = False
41
41
self .variables_cache = {} # frameId -> variables
42
42
self .frame_id_counter = 1
43
- self .path_mappings : list [tuple [str ,str ]] = [] # runtime_path -> vscode_path mapping
44
- self .file_mappings : dict [str ,str ] = {} # runtime_path -> vscode_path mapping
43
+ self .path_mappings : list [tuple [str ,str ]] = [] # runtime_path -> vscode_path mapping # todo: move to session level
44
+ self .file_mappings : dict [str ,str ] = {} # runtime_path -> vscode_path mapping # todo : merge with .breakpoints
45
45
46
46
def _debug_print (self , message ):
47
47
"""Print debug message only if debug logging is enabled."""
@@ -69,17 +69,61 @@ def set_trace_function(self, trace_func):
69
69
else :
70
70
raise RuntimeError ("sys.settrace not available" )
71
71
72
- def set_breakpoints (self , filename , breakpoints :list [dict ]):
72
+ def _filename_as_debugee (self , path :str ):
73
+ # check if we have a 1:1 file mapping for this path
74
+ if self .file_mappings .get (path ):
75
+ return self .file_mappings [path ]
76
+ # Check if we have a folder mapping for this path
77
+ for runtime_path , vscode_path in self .path_mappings :
78
+ if path .startswith (vscode_path ):
79
+ path = path .replace (vscode_path , runtime_path , 1 )
80
+ if path .startswith ('//' ):
81
+ path = path [1 :]
82
+ # If no mapping found, return the original path
83
+ return path
84
+
85
+ def _filename_as_debugger (self , path :str ):
86
+ """Convert a file path to the debugger's expected format."""
87
+ path = path or ""
88
+ if not path :
89
+ return path
90
+ if path .startswith ('<' ):
91
+ # Special case for <stdin> or similar
92
+ return path
93
+ # Check if we have a 1:1 file mapping for this path
94
+ for runtime_path , vscode_path in self .path_mappings :
95
+ if path .startswith (runtime_path ):
96
+ path = path .replace (runtime_path , vscode_path , 1 )
97
+ return path
98
+
99
+ # Check if we have a folder mapping for this path
100
+ for runtime_path , vscode_path in self .path_mappings :
101
+ if path .startswith (runtime_path ):
102
+ path = path .replace (runtime_path , vscode_path , 1 )
103
+ if path .startswith ('//' ):
104
+ path = path [1 :]
105
+ # If no mapping found, return the original path
106
+ return path
107
+
108
+ def set_breakpoints (self , filename :str , breakpoints :list [dict ]):
73
109
"""Set breakpoints for a file."""
74
110
self .breakpoints [filename ] = {}
111
+ local_name = self ._filename_as_debugee (filename )
112
+ self .file_mappings [local_name ] = filename
75
113
actual_breakpoints = []
76
-
77
- # Debug log the breakpoint path
78
114
self ._debug_print (f"[PDB] Setting breakpoints for file: { filename } " )
79
115
80
116
for bp in breakpoints :
81
117
line = bp .get ("line" )
82
118
if line :
119
+ if local_name != filename :
120
+ self .breakpoints [local_name ] = {}
121
+ self ._debug_print (f"[>>>] Setting breakpoints for local: { local_name } :{ line } " )
122
+ self .breakpoints [local_name ][line ] = {
123
+ "line" : line ,
124
+ "verified" : True ,
125
+ "source" : {"path" : filename }
126
+ }
83
127
self .breakpoints [filename ][line ] = {
84
128
"line" : line ,
85
129
"verified" : True ,
@@ -91,6 +135,8 @@ def set_breakpoints(self, filename, breakpoints:list[dict]):
91
135
"source" : {"path" : filename }
92
136
})
93
137
138
+ self ._debug_print (f"[PDB] Breakpoints set : { self .breakpoints } " )
139
+
94
140
return actual_breakpoints
95
141
96
142
def should_stop (self , frame , event :str , arg ):
@@ -106,33 +152,18 @@ def should_stop(self, frame, event:str, arg):
106
152
if lineno in self .breakpoints [filename ]:
107
153
self ._debug_print (f"[PDB] HIT BREAKPOINT (exact match) at { filename } :{ lineno } " )
108
154
# Record the path mapping (in this case, they're already the same)
109
- self .file_mappings [filename ] = filename
155
+ self .file_mappings [filename ] = self . _filename_as_debugger ( filename )
110
156
self .hit_breakpoint = True
111
157
return True
112
-
113
- file_basename = basename (filename )
114
- self ._debug_print (f"[PDB] Fallback basename match: '{ file_basename } ' vs available files" )
115
- for bp_file in self .breakpoints :
116
- bp_basename = basename (bp_file )
117
- self ._debug_print (f"[PDB] Comparing '{ file_basename } ' == '{ bp_basename } ' ?" )
118
- if bp_basename == file_basename :
119
- self ._debug_print (f"[PDB] Basename match found! Checking line { lineno } in { list (self .breakpoints [bp_file ].keys ())} " )
120
- if lineno in self .breakpoints [bp_file ]:
121
- self ._debug_print (f"[PDB] HIT BREAKPOINT (fallback basename match) at { filename } :{ lineno } -> { bp_file } " )
122
- # Record the path mapping so we can report the correct path in stack traces
123
- self .file_mappings [filename ] = bp_file
124
- self .hit_breakpoint = True
125
- return True
126
-
127
- # Also check if the runtime path might be relative and the breakpoint path absolute
128
- if ends_with_path (bp_file , filename ):
129
- self ._debug_print (f"[PDB] Relative path match: { bp_file } ends with { filename } " )
130
- if lineno in self .breakpoints [bp_file ]:
131
- self ._debug_print (f"[PDB] HIT BREAKPOINT (relative path match) at { filename } :{ lineno } -> { bp_file } " )
132
- # Record the path mapping so we can report the correct path in stack traces
133
- self .file_mappings [filename ] = bp_file
134
- self .hit_breakpoint = True
135
- return True
158
+ # path/file.py matched - but not the line number - keep running
159
+ else :
160
+ # file not (yet) matched - this is slow so we do not want to do this often.
161
+ # TODO: use builins - sys.path method to find the file
162
+ # if we have a path match , but no breakpoints - add it to the file_mappings dict avoid this check
163
+ self .breakpoints [filename ] = {} # Ensure the filename is in the breakpoints dict
164
+ if not filename in self .file_mappings :
165
+ self .file_mappings [filename ] = self ._filename_as_debugger (filename )
166
+ self ._debug_print (f"[PDB] add mapping for :'{ filename } ' -> '{ self .file_mappings [filename ]} '" )
136
167
137
168
# Check stepping
138
169
if self .step_mode == 'into' :
@@ -216,15 +247,20 @@ def get_stack_trace(self):
216
247
else :
217
248
hint = 'normal'
218
249
250
+ # self._debug_print("=" * 40 )
251
+ # self._debug_print(f"[PDB] file mappings: {repr(self.file_mappings)} " )
252
+ # self._debug_print(f"[PDB] path mappings: {repr(self.path_mappings)}" )
253
+ # self._debug_print("=" * 40 )
254
+
219
255
# Use the VS Code path if we have a mapping, otherwise use the original path
220
- display_path = self .file_mappings . get ( filename , filename )
221
- if filename != display_path :
222
- self ._debug_print (f"[PDB] Stack trace path mapping: { filename } -> { display_path } " )
256
+ debugger_path = self ._filename_as_debugger ( filename )
257
+ if filename != debugger_path :
258
+ self ._debug_print (f"[PDB] Stack trace path mapping: { filename } -> { debugger_path } " )
223
259
# Create StackFrame info
224
260
frames .append ({
225
261
"id" : frame_id ,
226
262
"name" : name ,
227
- "source" : {"path" : display_path },
263
+ "source" : {"path" : debugger_path },
228
264
"line" : line ,
229
265
"column" : 1 ,
230
266
"endLine" : line ,
0 commit comments