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