Skip to content

Commit 44a3f5b

Browse files
committed
WIP: Data breakpoints (though can't find a server which supports them)
1 parent e692229 commit 44a3f5b

File tree

5 files changed

+169
-6
lines changed

5 files changed

+169
-6
lines changed

autoload/vimspector.vim

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,14 @@ function! vimspector#ShowDisassembly( ... ) abort
439439
py3 _vimspector_session.ShowDisassembly()
440440
endfunction
441441

442+
function! vimspector#AddDataBreakpoint( ... ) abort
443+
if !s:Enabled()
444+
return
445+
endif
446+
" TODO: how to set options?
447+
py3 _vimspector_session.AddDataBreakpoint( {} )
448+
endfunction
449+
442450
function! vimspector#DeleteWatch() abort
443451
if !s:Enabled()
444452
return

python3/vimspector/breakpoints.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ def __init__( self,
219219
self._func_breakpoints = []
220220
self._exception_breakpoints = None
221221
self._configured_breakpoints = {}
222+
self._data_breakponts = []
222223

223224
self._server_capabilities = {}
224225

@@ -807,7 +808,19 @@ def AddFunctionBreakpoint( self, function, options ):
807808
# 'condition': ...,
808809
# 'hitCondition': ...,
809810
} )
811+
self.UpdateUI()
812+
810813

814+
def AddDataBreakpoint( self,
815+
conn: DebugAdapterConnection,
816+
info,
817+
options ):
818+
self._data_breakponts.append( {
819+
'state': 'ENABLED',
820+
'conn': conn.GetSessionId(),
821+
'info': info,
822+
'options': options
823+
} )
811824
self.UpdateUI()
812825

813826

@@ -1013,6 +1026,40 @@ def response_handler( conn, msg, bp_idxs = [] ):
10131026
failure_handler = response_received
10141027
)
10151028

1029+
if self._data_breakponts and self._server_capabilities[
1030+
'supportsDataBreakpoints' ]:
1031+
connection: DebugAdapterConnection
1032+
for connection in self._connections:
1033+
bp_idxs = []
1034+
breakpoints = []
1035+
for bp in self._data_breakponts:
1036+
if bp[ 'state' ] != 'ENABLED':
1037+
continue
1038+
if bp[ 'conn' ] == connection.GetSessionId():
1039+
continue
1040+
if not bp[ 'info' ].get( 'dataId' ):
1041+
continue
1042+
1043+
data_bp = {}
1044+
data_bp.update( bp[ 'options' ] )
1045+
data_bp[ 'dataId' ] = bp[ 'info' ][ 'dataId' ]
1046+
breakpoints.append( data_bp )
1047+
bp_idxs.append( [ len( breakpoints ), bp ] )
1048+
1049+
if breakpoints:
1050+
self._awaiting_bp_responses += 1
1051+
connection.DoRequest(
1052+
lambda msg, conn=connection, idxs=bp_idxs: response_handler(
1053+
conn,
1054+
msg,
1055+
idxs ),
1056+
{
1057+
'command': 'setDataBreakpoints',
1058+
'arguments': breakpoints,
1059+
},
1060+
failure_handler = response_received
1061+
)
1062+
10161063
if self._exception_breakpoints:
10171064
for connection in self._connections:
10181065
self._awaiting_bp_responses += 1
@@ -1111,6 +1158,11 @@ def Save( self ):
11111158
if bps:
11121159
line[ file_name ] = bps
11131160

1161+
# TODO: Some way to persis data breakpoints? Currently they require
1162+
# variablesReference, which is clearly not something that can be persisted
1163+
#
1164+
# That said, the spec now seems to support data bps on expressions, though i
1165+
# can't see any servers which support that.
11141166
return {
11151167
'line': line,
11161168
'function': self._func_breakpoints,
@@ -1175,6 +1227,11 @@ def _HideBreakpoints( self ):
11751227
signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' )
11761228
del bp[ 'sign_id' ]
11771229

1230+
# TODO could/should we show a sign in the variables view when there's a data
1231+
# brakpoint on the variable? Not sure how best to actually do that, but
1232+
# maybe the variable view can pass that info when calling AddDataBreakpoint,
1233+
# such as the variablesReference/name
1234+
11781235

11791236
def _SignToLine( self, file_name, bp ):
11801237
if bp[ 'is_instruction_breakpoint' ]:

python3/vimspector/debug_session.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,21 @@ def OnDisassemblyWindowScrolled( self, win_id ):
999999

10001000

10011001
@CurrentSession()
1002+
@IfConnected()
1003+
def AddDataBreakpoint( self, opts, buf = None, line_num = None ):
1004+
def add_bp( breakpoint_info ):
1005+
if breakpoint_info[ 'dataId' ] is None:
1006+
utils.UserMessage(
1007+
"Can't set data breakpoint here: { breakpoint_info[ 'description' ] }"
1008+
)
1009+
return
1010+
1011+
# TODO: Ask the user about the possible DataBreakpointAccessType's and add
1012+
# that in to opts here
1013+
self._breakpoints.AddDataBreakpoint( breakpoint_info[ 'dataId' ], opts )
1014+
1015+
self._variablesView.GetDataBreakpointInfo( add_bp, buf, line_num )
1016+
10021017
@IfConnected()
10031018
def AddWatch( self, expression ):
10041019
self._variablesView.AddWatch( self._connection,

python3/vimspector/variables.py

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,17 @@ def IsContained( self ):
6262
def VariablesReference( self ):
6363
assert False
6464

65+
@abc.abstractmethod
66+
def FrameID( self ):
67+
assert False
68+
69+
@abc.abstractmethod
70+
def EvaluateName( self ):
71+
assert False
72+
73+
@abc.abstractmethod
6574
def MemoryReference( self ):
66-
assert None
75+
assert False
6776

6877
@abc.abstractmethod
6978
def HoverText( self ):
@@ -82,6 +91,12 @@ def VariablesReference( self ):
8291
def MemoryReference( self ):
8392
return None
8493

94+
def FrameID( self ):
95+
return None
96+
97+
def EvaluateName( self ):
98+
return self.scope[ 'name' ]
99+
85100
def Update( self, scope ):
86101
self.scope = scope
87102

@@ -91,8 +106,12 @@ def HoverText( self ):
91106

92107
class WatchResult( Expandable ):
93108
"""Holds the result of a Watch expression with expand/collapse."""
94-
def __init__( self, connection: DebugAdapterConnection, result: dict ):
109+
def __init__( self,
110+
connection: DebugAdapterConnection,
111+
watch,
112+
result: dict ):
95113
super().__init__( connection )
114+
self.watch = watch
96115
self.result = result
97116
# A new watch result is marked as changed
98117
self.changed = True
@@ -103,6 +122,12 @@ def VariablesReference( self ):
103122
def MemoryReference( self ):
104123
return self.result.get( 'memoryReference' )
105124

125+
def FrameID( self ):
126+
return self.watch.expression.get( 'frameId' )
127+
128+
def EvaluateName( self ):
129+
return self.watch.expression.get( 'expression' )
130+
106131
def Update( self, result ):
107132
self.changed = False
108133
if self.result[ 'result' ] != result[ 'result' ]:
@@ -129,7 +154,8 @@ class Variable( Expandable ):
129154
"""Holds one level of an expanded value tree. Also itself expandable."""
130155
def __init__( self,
131156
connection: DebugAdapterConnection,
132-
container: Expandable, variable: dict ):
157+
container: Expandable,
158+
variable: dict ):
133159
super().__init__( connection = connection, container = container )
134160
self.variable = variable
135161
# A new variable appearing is marked as changed
@@ -141,6 +167,12 @@ def VariablesReference( self ):
141167
def MemoryReference( self ):
142168
return self.variable.get( 'memoryReference' )
143169

170+
def FrameID( self ):
171+
return self.container.FrameID()
172+
173+
def EvaluateName( self ):
174+
return self.variable.get( 'evaluateName', self.variable[ 'name' ] )
175+
144176
def Update( self, variable ):
145177
self.changed = False
146178
if self.variable[ 'value' ] != variable[ 'value' ]:
@@ -536,7 +568,9 @@ def _UpdateWatchExpression( self, watch: Watch, message: dict ):
536568
if watch.result is not None:
537569
watch.result.Update( message[ 'body' ] )
538570
else:
539-
watch.result = WatchResult( watch.connection, message[ 'body' ] )
571+
watch.result = WatchResult( watch.connection,
572+
watch[ 'frameId' ],
573+
message[ 'body' ] )
540574

541575
if ( watch.result.IsExpandable() and
542576
watch.result.IsExpanded() ):
@@ -668,12 +702,42 @@ def GetMemoryReference( self ):
668702
# Get a memoryReference for use in a ReadMemory request
669703
variable, _ = self._GetVariable( None, None )
670704
if variable is None:
671-
return None
705+
return None, None
672706

673-
# TODO: Return the connection too!
674707
return variable.connection, variable.MemoryReference()
675708

676709

710+
def GetDataBreakpointInfo( self,
711+
then,
712+
buf = None,
713+
line_num = None ):
714+
variable: Expandable
715+
view: View
716+
717+
if not self._server_capabilities.get( 'supportsDataBreakpoints' ):
718+
return None
719+
720+
variable, view = self._GetVariable( buf, line_num )
721+
if variable is None:
722+
return None
723+
724+
arguments = {
725+
'name': variable.EvaluateName()
726+
}
727+
frameId = variable.FrameID()
728+
if frameId:
729+
arguments[ 'frameId' ] = frameId
730+
731+
if variable.IsContained():
732+
arguments[ 'variablesReference' ] = (
733+
variable.container.VariablesReference() )
734+
735+
variable.connection.DoRequest( lambda msg: then( msg[ 'body' ] ), {
736+
'command': 'dataBreakpointInfo',
737+
'arguments': arguments,
738+
} )
739+
740+
677741
def _DrawVariables( self, view, variables, indent_len, is_short = False ):
678742
assert indent_len > 0
679743
for variable in variables:
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
package main
22
import "fmt"
3+
4+
type Toaster struct {
5+
Power int
6+
Colour string
7+
}
8+
9+
type Test struct {
10+
Test string
11+
Toast Toaster
12+
}
13+
314
func main() {
415
var v = "test"
16+
test := Test{
17+
Test: "This is\na\ntest",
18+
Toast: Toaster{
19+
Power: 10,
20+
Colour: "green",
21+
},
22+
}
523
fmt.Println("hello world: " + v)
24+
fmt.Println("Hi " + test.Test)
625
}

0 commit comments

Comments
 (0)