Skip to content

Commit 309c0de

Browse files
committed
Allow editing of breakpoint options in breakpoints window
Tidy up the breakpoints window winbar to allow more buttons. Add ability to reset exception breakpoints. Fix winbar in neovim (which wasn't updated in the recent patches).
1 parent 39657c1 commit 309c0de

File tree

7 files changed

+229
-47
lines changed

7 files changed

+229
-47
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -910,12 +910,14 @@ The following mappings apply by default in the breakpoints window:
910910
* `t`, `<F9>` - toggle, i.e. enable/disable breakpoint
911911
* `T` - toggle, i.e. enable/disable ALL breakpoints
912912
* `dd`, `<Del>` - delete the current breakpoint
913+
* `cc`, `C` - edit the current breakpoint options
913914
* `i`, `a`, `o` - add a new line breakpoint
914915
* `I`, `A`, `O` - add a new function breakpoint
915916
* `<Enter>` or double-click - jump to the line breakpoint
916917

917918
A WinBar is provided (where supported) too. This adds functions like
918-
saving/restoring sessions and clearing all breakpoints too.
919+
saving/restoring sessions, clearing all breakpoints, and resetting the exception
920+
breakpoints options.
919921

920922
### Line breakpoints
921923

autoload/vimspector.vim

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -173,33 +173,18 @@ function! vimspector#ClearBreakpoints() abort
173173
py3 _vimspector_session.ClearBreakpoints()
174174
endfunction
175175

176-
let s:extended_breakpoint_properties = [
177-
\ { 'prop': 'condition', 'msg': 'Enter condition expression' },
178-
\ { 'prop': 'hitCondition', 'msg': 'Enter hit count expression' },
179-
\ { 'prop': 'logMessage',
180-
\ 'msg': 'Enter log expression (to make log point)' },
181-
\ ]
182-
183-
function! s:AskForInput( ... ) abort
184-
return py3eval( '__import__( "vimspector", fromlist=[ "utils" ] )'
185-
\ . '.utils.AskForInput( *vim.eval( "a:000" ) )' )
176+
function! vimspector#ResetExceptionBreakpoints() abort
177+
if !s:Enabled()
178+
return
179+
endif
180+
py3 _vimspector_session.ResetExceptionBreakpoints()
186181
endfunction
187182

188-
function! s:GetAdvancedBreakpointOptions() abort
189-
let options = {}
190-
for spec in s:extended_breakpoint_properties
191-
let response = s:AskForInput( spec.msg . ': ' )
192-
if response is s:None
193-
return s:None
194-
elseif response !=# ''
195-
let options[ spec.prop ] = response
196-
endif
197-
endfor
198-
199-
return options
183+
function! s:GetAdvancedBreakpointOptions( ... ) abort
184+
return py3eval( '__import__( "vimspector", fromlist=[ "breakpoints" ] )'
185+
\ . '.breakpoints.GetAdvancedBreakpointOptions()' )
200186
endfunction
201187

202-
203188
function! vimspector#ToggleAdvancedBreakpoint() abort
204189
let options = s:GetAdvancedBreakpointOptions()
205190
if options is s:None
@@ -220,6 +205,11 @@ function! vimspector#ToggleBreakpoint( ... ) abort
220205
py3 _vimspector_session.ToggleBreakpoint( vim.eval( 'options' ) )
221206
endfunction
222207

208+
function! s:AskForInput( ... ) abort
209+
return py3eval( '__import__( "vimspector", fromlist=[ "utils" ] )'
210+
\ . '.utils.AskForInput( *vim.eval( "a:000" ) )' )
211+
endfunction
212+
223213
function! vimspector#SetAdvancedLineBreakpoint() abort
224214
if !s:Enabled()
225215
return
@@ -592,6 +582,13 @@ function! vimspector#JumpToBreakpointViewBreakpoint() abort
592582
py3 _vimspector_session.JumpToBreakpointViewBreakpoint()
593583
endfunction
594584

585+
function! vimspector#EditBreakpointOptionsViewBreakpoint() abort
586+
if !s:Enabled()
587+
return
588+
endif
589+
py3 _vimspector_session.EditBreakpointOptionsViewBreakpoint()
590+
endfunction
591+
595592
function! vimspector#JumpToNextBreakpoint() abort
596593
if !s:Enabled()
597594
return

python3/vimspector/breakpoints.py

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,10 @@ def _UpdateView( self, breakpoint_list, show=True ):
8181
'toggle': 'ToggleBreakpointViewBreakpoint',
8282
'toggle_all': 'ToggleAllBreakpointsViewBreakpoint',
8383
'delete': 'DeleteBreakpointViewBreakpoint',
84+
'edit': 'EditBreakpointOptionsViewBreakpoint',
8485
'jump_to': 'JumpToBreakpointViewBreakpoint',
8586
'add_line': 'SetAdvancedLineBreakpoint',
86-
'add_func': 'AddAdvancedFunctionBreakpoint'
87+
'add_func': 'AddAdvancedFunctionBreakpoint',
8788
}
8889
for key, func in groups.items():
8990
for mapping in utils.GetVimList( mappings, key ):
@@ -100,27 +101,7 @@ def _UpdateView( self, breakpoint_list, show=True ):
100101

101102
utils.SetSyntax( '', 'vimspector-breakpoints', self._buffer )
102103

103-
if utils.UseWinBar():
104-
vim.command( 'nnoremenu <silent> 1.1 WinBar.Delete '
105-
':call vimspector#DeleteBreakpointViewBreakpoint()<CR>' )
106-
vim.command( 'nnoremenu <silent> 1.2 WinBar.Toggle '
107-
':call vimspector#ToggleBreakpointViewBreakpoint()<CR>' )
108-
vim.command( 'nnoremenu <silent> 1.2 WinBar.*Toggle '
109-
':call'
110-
' vimspector#ToggleAllBreakpointsViewBreakpoint()<CR>' )
111-
vim.command( 'nnoremenu <silent> 1.3 WinBar.Jump\\ To '
112-
':call vimspector#JumpToBreakpointViewBreakpoint()<CR>' )
113-
# TODO: Add tests for this function
114-
vim.command( 'nnoremenu <silent> 1.4 WinBar.+Line '
115-
':call vimspector#SetAdvancedLineBreakpoint()<CR>' )
116-
vim.command( 'nnoremenu <silent> 1.4 WinBar.+Function '
117-
':call vimspector#AddAdvancedFunctionBreakpoint()<CR>' )
118-
vim.command( 'nnoremenu <silent> 1.4 WinBar.Clear '
119-
':call vimspector#ClearBreakpoints()<CR>' )
120-
vim.command( 'nnoremenu <silent> 1.4 WinBar.Save '
121-
':call vimspector#WriteSessionFile()<CR>' )
122-
vim.command( 'nnoremenu <silent> 1.4 WinBar.Load '
123-
':call vimspector#ReadSessionFile()<CR>' )
104+
self._RenderWinBar()
124105

125106
# we want to maintain the height of the window
126107
self._win.options[ "winfixheight" ] = True
@@ -141,6 +122,28 @@ def FormatEntry( el ):
141122
utils.SetBufferContents( self._buffer,
142123
list( map( FormatEntry, breakpoint_list ) ) )
143124

125+
126+
def _RenderWinBar( self ):
127+
if not utils.UseWinBar():
128+
return
129+
130+
if not self._HasWindow():
131+
return
132+
133+
with utils.LetCurrentWindow( self._win ):
134+
utils.SetWinBar(
135+
( 'Del', 'vimspector#DeleteBreakpointViewBreakpoint()' ),
136+
( 'On/Off', 'vimspector#ToggleBreakpointViewBreakpoint()' ),
137+
( 'Edit', 'vimspector#EditBreakpointOptionsViewBreakpoint()' ),
138+
( '+Line', 'vimspector#SetAdvancedLineBreakpoint()' ),
139+
( '+Func', 'vimspector#AddAdvancedFunctionBreakpoint()' ),
140+
( 'Clr All', 'vimspector#ClearBreakpoints()' ),
141+
( 'Clr Excp', 'vimspector#ResetExceptionBreakpoints()' ),
142+
( 'Save', 'vimspector#WriteSessionFile()' ),
143+
( 'Load', 'vimspector#ReadSessionFile()' ),
144+
)
145+
146+
144147
def CloseBreakpoints( self ):
145148
if not self._HasWindow():
146149
return
@@ -336,6 +339,26 @@ def JumpToBreakpointViewBreakpoint( self ):
336339

337340
_JumpToBreakpoint( bp )
338341

342+
def EditBreakpointOptionsViewBreakpoint( self ):
343+
vbp = self._breakpoints_view.GetBreakpointForLine()
344+
if not vbp:
345+
return
346+
347+
# Try to find the actual breakpoint
348+
bp, index = self._FindLineBreakpoint( vbp.get( 'filename' ),
349+
vbp.get( 'lnum' ) )
350+
351+
if not bp:
352+
return
353+
354+
options = GetAdvancedBreakpointOptions( bp[ 'options' ] )
355+
if options is None:
356+
return
357+
358+
self.SetLineBreakpoint( vbp[ 'filename' ], vbp[ 'lnum' ], options )
359+
utils.UserMessage( "Breakpoint updated." )
360+
361+
339362
def JumpToNextBreakpoint( self, reverse=False ):
340363
bps = self._breakpoints_view._breakpoint_list
341364
if not bps:
@@ -440,6 +463,12 @@ def ClearBreakpoints( self ):
440463
self.UpdateUI()
441464

442465

466+
def ResetExceptionBreakpoints( self ):
467+
# TODO: Should exceptoni breakpoints be per-session!?
468+
self._exception_breakpoints = None
469+
self.UpdateUI()
470+
471+
443472
def _FindLineBreakpoint( self, file_name, line ):
444473
for bp, index in self._AllBreakpointsOnLine( file_name, line ):
445474
return bp, index
@@ -1172,3 +1201,27 @@ def _SignToLine( self, file_name, bp ):
11721201
bp[ 'line' ] = int( signs[ 0 ][ 'signs' ][ 0 ][ 'lnum' ] )
11731202

11741203
return
1204+
1205+
1206+
_extended_breakpoint_properties = [
1207+
{ 'prop': 'condition', 'msg': 'Enter condition expression' },
1208+
{ 'prop': 'hitCondition', 'msg': 'Enter hit count expression' },
1209+
{ 'prop': 'logMessage',
1210+
'msg': 'Enter log expression (to make log point)' },
1211+
]
1212+
1213+
1214+
def GetAdvancedBreakpointOptions( existing_options = None ):
1215+
options = {}
1216+
if existing_options:
1217+
options.update( existing_options )
1218+
1219+
for spec in _extended_breakpoint_properties:
1220+
response = utils.AskForInput( spec[ 'msg' ] + ': ',
1221+
options.get( spec[ 'prop' ] ) )
1222+
if response is None:
1223+
return None
1224+
elif response:
1225+
options[ spec[ 'prop' ] ] = response
1226+
1227+
return options

python3/vimspector/debug_session.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,9 @@ def DeleteBreakpointViewBreakpoint( self ):
20932093
def JumpToBreakpointViewBreakpoint( self ):
20942094
self._breakpoints.JumpToBreakpointViewBreakpoint()
20952095

2096+
def EditBreakpointOptionsViewBreakpoint( self ):
2097+
self._breakpoints.EditBreakpointOptionsViewBreakpoint()
2098+
20962099
def JumpToNextBreakpoint( self ):
20972100
self._breakpoints.JumpToNextBreakpoint()
20982101

@@ -2170,6 +2173,9 @@ def ClearLineBreakpoint( self, file_name, line_num ):
21702173
def ClearBreakpoints( self ):
21712174
return self._breakpoints.ClearBreakpoints()
21722175

2176+
def ResetExceptionBreakpoints( self ):
2177+
return self._breakpoints.ResetExceptionBreakpoints()
2178+
21732179
def AddFunctionBreakpoint( self, function, options ):
21742180
return self._breakpoints.AddFunctionBreakpoint( function, options )
21752181

python3/vimspector/settings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,10 @@
100100
'toggle': [ 't', '<F9>' ],
101101
'toggle_all': [ 'T' ],
102102
'delete': [ 'dd', '<Del>' ],
103+
'edit': [ 'cc', 'C' ],
103104
'add_line': [ 'i', 'a', 'o', '<Insert>' ],
104105
'add_func': [ 'I', 'A', 'O', '<leader><Insert>' ],
105-
'jump_to': [ '<2-LeftMouse>', '<Enter>' ]
106+
'jump_to': [ '<2-LeftMouse>', '<Enter>' ],
106107
}
107108
},
108109

support/test/python/simple_python/.vimspector.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@
126126
}
127127
}
128128
},
129+
"run - no exception bp override": {
130+
"adapter": "debugpy",
131+
"configuration": {
132+
"request": "launch",
133+
"type": "python",
134+
"cwd": "${workspaceRoot}",
135+
"program": "${file}",
136+
"stopOnEntry": false,
137+
"console": "integratedTerminal"
138+
}
139+
},
129140
"run - debugpy-python2": {
130141
"adapter": "debugpy-python2",
131142
"configuration": {

tests/breakpoints.test.vim

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,118 @@ function! Test_Conditional_Line_Breakpoint()
569569
%bwipeout!
570570
endfunction
571571

572+
function! Test_Conditional_Line_Breakpoint_Edit()
573+
lcd testdata/cpp/simple
574+
edit simple.cpp
575+
call setpos( '.', [ 0, 16, 1 ] )
576+
577+
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 )
578+
call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 16 )
579+
580+
" Add breakpoint using API:
581+
" - add it using a condition which doesn't match (argc == 0)
582+
" - then edit it to use a condition which matches (argc == 1)
583+
call vimspector#SetLineBreakpoint( 'simple.cpp', 17,
584+
\ { 'condition': 'argc == 0' } )
585+
call vimspector#test#signs#AssertSignGroupSingletonAtLine(
586+
\ 'VimspectorBP',
587+
\ 17,
588+
\ 'vimspectorBPCond',
589+
\ 9 )
590+
591+
call vimspector#ListBreakpoints()
592+
call s:CheckBreakpointView( [
593+
\ 'simple.cpp:17 Line breakpoint - ENABLED: {"condition": "argc == 0"}'
594+
\ ] )
595+
let bname = bufname( winbufnr( g:vimspector_session_windows.breakpoints ) )
596+
call vimspector#test#signs#AssertCursorIsAtLineInBuffer(
597+
\ bname,
598+
\ 1,
599+
\ 1 )
600+
601+
call feedkeys( 'cc'
602+
\ .. "\<C-u>argc == 1\<CR>"
603+
\ .. "1\<CR>"
604+
\ .. "\<CR>",
605+
\ 'xt' )
606+
607+
call s:CheckBreakpointView( [
608+
\ 'simple.cpp:17 Line breakpoint - ENABLED: {"condition": "argc == 1", "hitCondition": "1"}'
609+
\ ] )
610+
611+
wincmd p
612+
call vimspector#ListBreakpoints()
613+
call setpos( '.', [ 0, 1, 1 ] )
614+
615+
" Start debugging
616+
call vimspector#LaunchWithSettings( #{ configuration: 'run-to-breakpoint' } )
617+
618+
" Condition matches on line 17
619+
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 17, 1 )
620+
621+
call vimspector#test#setup#Reset()
622+
623+
lcd -
624+
%bwipeout!
625+
endfunction
626+
627+
function! Test_Conditional_Line_Breakpoint_Edit_While_Connected()
628+
lcd testdata/cpp/simple
629+
edit simple.cpp
630+
call setpos( '.', [ 0, 16, 1 ] )
631+
632+
call vimspector#Launch()
633+
" break on main
634+
call vimspector#test#signs#AssertCursorIsAtLineInBuffer(
635+
\ 'simple.cpp',
636+
\ s:break_main_line, 1 )
637+
638+
" Add breakpoint using API:
639+
" - add it using a condition which doesn't match (argc == 0)
640+
" - then edit it to use a condition which matches (argc == 1)
641+
call vimspector#SetLineBreakpoint( 'simple.cpp', 17,
642+
\ { 'condition': 'argc == 0' } )
643+
call WaitForAssert( { ->
644+
\ vimspector#test#signs#AssertSignGroupSingletonAtLine(
645+
\ 'VimspectorBP',
646+
\ 17,
647+
\ 'vimspectorBPCond',
648+
\ 9 ) } )
649+
650+
call vimspector#ListBreakpoints()
651+
call s:CheckBreakpointView( [
652+
\ 'simple.cpp:17 Line breakpoint - VERIFIED: {"condition": "argc == 0"}'
653+
\ ] )
654+
let bname = bufname( winbufnr( g:vimspector_session_windows.breakpoints ) )
655+
call vimspector#test#signs#AssertCursorIsAtLineInBuffer(
656+
\ bname,
657+
\ 1,
658+
\ 1 )
659+
660+
call feedkeys( 'cc'
661+
\ .. "\<C-u>argc == 1\<CR>"
662+
\ .. "1\<CR>"
663+
\ .. "\<CR>",
664+
\ 'xt' )
665+
666+
call s:CheckBreakpointView( [
667+
\ 'simple.cpp:17 Line breakpoint - VERIFIED: {"condition": "argc == 1", "hitCondition": "1"}'
668+
\ ] )
669+
670+
wincmd p
671+
call vimspector#ListBreakpoints()
672+
call setpos( '.', [ 0, 1, 1 ] )
673+
674+
" Condition matches on line 17
675+
call vimspector#Continue()
676+
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 17, 1 )
677+
call vimspector#test#setup#Reset()
678+
679+
lcd -
680+
%bwipeout!
681+
endfunction
682+
683+
572684
function! SetUp_Test_Conditional_Line_Breakpoint_Disable()
573685
let g:vimspector_enable_mappings = 'HUMAN'
574686
call s:PushSetting( 'vimspector_toggle_disables_breakpoint', 1 )

0 commit comments

Comments
 (0)