@@ -100,6 +100,36 @@ def write_message(self, message: dict, incoming: bool):
100
100
)
101
101
102
102
103
+ # Debuggers communicate optional feature support.
104
+ class DAPDebuggerCapabilities :
105
+ def __init__ (self ):
106
+ self .supportsConfigurationDoneRequest : bool = False
107
+ self .supportsFunctionBreakpoints : bool = False
108
+ self .supportsConditionalBreakpoints : bool = False
109
+ self .supportsHitConditionalBreakpoints : bool = False
110
+ self .supportsEvaluateForHovers : bool = False
111
+ self .supportsSetVariable : bool = False
112
+ self .supportsStepInTargetsRequest : bool = False
113
+ self .supportsModulesRequest : bool = False
114
+ self .supportsValueFormattingOptions : bool = False
115
+ self .supportsLogPoints : bool = False
116
+ self .supportsSetExpression : bool = False
117
+ self .supportsDataBreakpoints : bool = False
118
+ self .supportsReadMemoryRequest : bool = False
119
+ self .supportsWriteMemoryRequest : bool = False
120
+ self .supportsDisassembleRequest : bool = False
121
+ self .supportsCancelRequest : bool = False
122
+ self .supportsSteppingGranularity : bool = False
123
+ self .supportsInstructionBreakpoints : bool = False
124
+
125
+ def update (self , logger : Logger , feature_dict : dict ):
126
+ for k , v in feature_dict .items ():
127
+ if hasattr (self , k ):
128
+ setattr (self , k , v )
129
+ else :
130
+ logger .warning (f"DAP: Unknown support flag: { k } " )
131
+
132
+
103
133
# As DAP does not give us a trivially query-able process, we are responsible for maintaining our own state information,
104
134
# including what breakpoints are currently set, and whether the debugger is running or stopped.
105
135
# This class holds all state that is set based on events sent by the debug adapter; most responses are forwarded through
@@ -142,6 +172,9 @@ def __init__(self):
142
172
# Map of DAP breakpoint IDs to resolved instruction addresses.
143
173
self .bp_addr_map = {}
144
174
175
+ # DAP features supported by the debugger.
176
+ self .capabilities = DAPDebuggerCapabilities ()
177
+
145
178
def set_response (self , req_id : int , response : dict ):
146
179
if len (self .responses ) > req_id :
147
180
self .responses [req_id ] = response
@@ -315,6 +348,9 @@ def _handle_message(
315
348
and debugger_state .thread is None
316
349
):
317
350
debugger_state .thread = event_details ["threadId" ]
351
+ elif event_type == "capabilities" :
352
+ # Unchanged capabilites may not be included.
353
+ debugger_state .capabilities .update (logger , event_details )
318
354
# There are many events we do not care about, just skip processing them.
319
355
else :
320
356
pass
@@ -338,6 +374,12 @@ def _handle_message(
338
374
debugger_state .frame_map = [
339
375
stackframe ["id" ] for stackframe in message ["body" ]["stackFrames" ]
340
376
]
377
+ # The debugger communicates which optional DAP features are
378
+ # supported in its initalize response.
379
+ if message ["command" ] == "initialize" and message ["success" ] == True :
380
+ body = message .get ("body" )
381
+ if body :
382
+ debugger_state .capabilities .update (logger , body )
341
383
342
384
def _colorize_dap_message (message : dict ) -> dict :
343
385
colorized_message = copy .deepcopy (message )
0 commit comments