Add basic support for conditional breakpoints
This is the minimal required for a user to use conditional breakpoint - we add an options dict to each breakpoint (line and function) and allow the condition to be supplied. We add a plug mapping and a default shortcut (<leader><F9>) to add one where we ask the user to enter the condition and hit expression. This isn't great but it works. We don't check the capabilities, so they would just be ignored if used on a server that doesn't support them. We also ask for a hit expression which most users won't understand so this isn't ideal either. No tests yet.
This commit is contained in:
parent
aed4144fe5
commit
7a70519b03
5 changed files with 69 additions and 37 deletions
31
README.md
31
README.md
|
|
@ -458,6 +458,7 @@ features to set your own mappings. To that end, Vimspector defines the following
|
||||||
* `<Plug>VimspectorRestart`
|
* `<Plug>VimspectorRestart`
|
||||||
* `<Plug>VimspectorPause`
|
* `<Plug>VimspectorPause`
|
||||||
* `<Plug>VimspectorToggleBreakpoint`
|
* `<Plug>VimspectorToggleBreakpoint`
|
||||||
|
* `<Plug>VimspectorToggleConditionalBreakpoint`
|
||||||
* `<Plug>VimspectorAddFunctionBreakpoint`
|
* `<Plug>VimspectorAddFunctionBreakpoint`
|
||||||
* `<Plug>VimspectorStepOver`
|
* `<Plug>VimspectorStepOver`
|
||||||
* `<Plug>VimspectorStepInto`
|
* `<Plug>VimspectorStepInto`
|
||||||
|
|
@ -513,17 +514,18 @@ loading vimspector**:
|
||||||
let g:vimspector_enable_mappings = 'HUMAN'
|
let g:vimspector_enable_mappings = 'HUMAN'
|
||||||
```
|
```
|
||||||
|
|
||||||
| Key | Function | API |
|
| Key | Function | API |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` |
|
| `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` |
|
||||||
| `F3` | Stop debugging. | `vimspector#Stop()` |
|
| `F3` | Stop debugging. | `vimspector#Stop()` |
|
||||||
| `F4` | Restart debugging with the same configuration. | `vimspector#Restart()` |
|
| `F4` | Restart debugging with the same configuration. | `vimspector#Restart()` |
|
||||||
| `F6` | Pause debugee. | `vimspector#Pause()` |
|
| `F6` | Pause debugee. | `vimspector#Pause()` |
|
||||||
| `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` |
|
| `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` |
|
||||||
| `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '<cexpr>' )` |
|
| `<leader>F9` | Toggle conditional line breakpoint on the current line. | `vimspector#ToggleBreakpoint( {condition, hit condition } )` |
|
||||||
| `F10` | Step Over | `vimspector#StepOver()` |
|
| `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '<cexpr>' )` |
|
||||||
| `F11` | Step Into | `vimspector#StepInto()` |
|
| `F10` | Step Over | `vimspector#StepOver()` |
|
||||||
| `F12` | Step out of current function scope | `vimspector#StepOut()` |
|
| `F11` | Step Into | `vimspector#StepInto()` |
|
||||||
|
| `F12` | Step out of current function scope | `vimspector#StepOut()` |
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
|
|
@ -564,9 +566,10 @@ debugger](#java---partially-supported)
|
||||||
|
|
||||||
## Breakpoints
|
## Breakpoints
|
||||||
|
|
||||||
* Use `vimspector#ToggleBreakpoint()` to set/disable/delete a line breakpoint.
|
* Use `vimspector#ToggleBreakpoint([ { 'condition': '<condition>' } ])`
|
||||||
* Use `vimspector#AddFunctionBreakpoint( '<name>' )` to add a function
|
to set/disable/delete a line breakpoint, with optional condition.
|
||||||
breakpoint.
|
* Use `vimspector#AddFunctionBreakpoint( '<name>' [, { 'condition': '<condition>' } ] )`
|
||||||
|
to add a function breakpoint with optional condition.
|
||||||
|
|
||||||
## Stepping
|
## Stepping
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,12 +42,13 @@ function! vimspector#ClearBreakpoints() abort
|
||||||
py3 _vimspector_session.ClearBreakpoints()
|
py3 _vimspector_session.ClearBreakpoints()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#ToggleBreakpoint() abort
|
function! vimspector#ToggleBreakpoint( options = {} ) abort
|
||||||
py3 _vimspector_session.ToggleBreakpoint()
|
py3 _vimspector_session.ToggleBreakpoint( vim.eval( 'a:options' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#AddFunctionBreakpoint( function ) abort
|
function! vimspector#AddFunctionBreakpoint( function, options = {} ) abort
|
||||||
py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ) )
|
py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ),
|
||||||
|
\ vim.eval( 'a:options' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#StepOver() abort
|
function! vimspector#StepOver() abort
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,11 @@ nnoremap <Plug>VimspectorRestart :<c-u>call vimspector#Restart()<CR>
|
||||||
nnoremap <Plug>VimspectorPause :<c-u>call vimspector#Pause()<CR>
|
nnoremap <Plug>VimspectorPause :<c-u>call vimspector#Pause()<CR>
|
||||||
nnoremap <Plug>VimspectorToggleBreakpoint
|
nnoremap <Plug>VimspectorToggleBreakpoint
|
||||||
\ :<c-u>call vimspector#ToggleBreakpoint()<CR>
|
\ :<c-u>call vimspector#ToggleBreakpoint()<CR>
|
||||||
|
nnoremap <Plug>VimspectorToggleConditionalBreakpoint
|
||||||
|
\ :<c-u>call vimspector#ToggleBreakpoint(
|
||||||
|
\ { 'condition': input( 'Enter condition: ' ),
|
||||||
|
\ 'hitCondition': input( 'Enter hit condition: ' ) }
|
||||||
|
\ )<CR>
|
||||||
nnoremap <Plug>VimspectorAddFunctionBreakpoint
|
nnoremap <Plug>VimspectorAddFunctionBreakpoint
|
||||||
\ :<c-u>call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
|
\ :<c-u>call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
|
||||||
nnoremap <Plug>VimspectorStepOver :<c-u>call vimspector#StepOver()<CR>
|
nnoremap <Plug>VimspectorStepOver :<c-u>call vimspector#StepOver()<CR>
|
||||||
|
|
@ -65,6 +70,7 @@ elseif s:mappings ==# 'HUMAN'
|
||||||
nmap <F4> <Plug>VimspectorRestart
|
nmap <F4> <Plug>VimspectorRestart
|
||||||
nmap <F6> <Plug>VimspectorPause
|
nmap <F6> <Plug>VimspectorPause
|
||||||
nmap <F9> <Plug>VimspectorToggleBreakpoint
|
nmap <F9> <Plug>VimspectorToggleBreakpoint
|
||||||
|
nmap <leader><F9> <Plug>VimspectorToggleConditionalBreakpoint
|
||||||
nmap <F8> <Plug>VimspectorAddFunctionBreakpoint
|
nmap <F8> <Plug>VimspectorAddFunctionBreakpoint
|
||||||
nmap <F10> <Plug>VimspectorStepOver
|
nmap <F10> <Plug>VimspectorStepOver
|
||||||
nmap <F11> <Plug>VimspectorStepInto
|
nmap <F11> <Plug>VimspectorStepInto
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,9 @@ class ProjectBreakpoints( object ):
|
||||||
if not utils.SignDefined( 'vimspectorBP' ):
|
if not utils.SignDefined( 'vimspectorBP' ):
|
||||||
vim.command( 'sign define vimspectorBP text==> texthl=Error' )
|
vim.command( 'sign define vimspectorBP text==> texthl=Error' )
|
||||||
|
|
||||||
|
if not utils.SignDefined( 'vimspectorBPCond' ):
|
||||||
|
vim.command( 'sign define vimspectorBPCond text=?> texthl=Error' )
|
||||||
|
|
||||||
if not utils.SignDefined( 'vimspectorBPDisabled' ):
|
if not utils.SignDefined( 'vimspectorBPDisabled' ):
|
||||||
vim.command( 'sign define vimspectorBPDisabled text=!> texthl=Warning' )
|
vim.command( 'sign define vimspectorBPDisabled text=!> texthl=Warning' )
|
||||||
|
|
||||||
|
|
@ -96,8 +99,9 @@ class ProjectBreakpoints( object ):
|
||||||
'col': 1,
|
'col': 1,
|
||||||
'type': 'L',
|
'type': 'L',
|
||||||
'valid': 1 if bp[ 'state' ] == 'ENABLED' else 0,
|
'valid': 1 if bp[ 'state' ] == 'ENABLED' else 0,
|
||||||
'text': "Line breakpoint - {}".format(
|
'text': "Line breakpoint - {}: {}".format(
|
||||||
bp[ 'state' ] )
|
bp[ 'state' ],
|
||||||
|
json.dumps( bp[ 'options' ] ) )
|
||||||
} )
|
} )
|
||||||
# I think this shows that the qf list is not right for this.
|
# I think this shows that the qf list is not right for this.
|
||||||
for bp in self._func_breakpoints:
|
for bp in self._func_breakpoints:
|
||||||
|
|
@ -107,7 +111,8 @@ class ProjectBreakpoints( object ):
|
||||||
'col': 1,
|
'col': 1,
|
||||||
'type': 'F',
|
'type': 'F',
|
||||||
'valid': 1,
|
'valid': 1,
|
||||||
'text': "Function breakpoint: {}".format( bp[ 'function' ] ),
|
'text': "Function breakpoint: {}: {}".format( bp[ 'function' ],
|
||||||
|
bp[ 'options' ] ),
|
||||||
} )
|
} )
|
||||||
|
|
||||||
vim.eval( 'setqflist( {} )'.format( json.dumps( qf ) ) )
|
vim.eval( 'setqflist( {} )'.format( json.dumps( qf ) ) )
|
||||||
|
|
@ -127,7 +132,7 @@ class ProjectBreakpoints( object ):
|
||||||
|
|
||||||
self.UpdateUI()
|
self.UpdateUI()
|
||||||
|
|
||||||
def ToggleBreakpoint( self ):
|
def ToggleBreakpoint( self, options ):
|
||||||
line, column = vim.current.window.cursor
|
line, column = vim.current.window.cursor
|
||||||
file_name = vim.current.buffer.name
|
file_name = vim.current.buffer.name
|
||||||
|
|
||||||
|
|
@ -161,9 +166,10 @@ class ProjectBreakpoints( object ):
|
||||||
self._line_breakpoints[ file_name ].append( {
|
self._line_breakpoints[ file_name ].append( {
|
||||||
'state': 'ENABLED',
|
'state': 'ENABLED',
|
||||||
'line': line,
|
'line': line,
|
||||||
|
'options': options,
|
||||||
# 'sign_id': <filled in when placed>,
|
# 'sign_id': <filled in when placed>,
|
||||||
#
|
#
|
||||||
# Used by other breakpoint types:
|
# Used by other breakpoint types (specified in options):
|
||||||
# 'condition': ...,
|
# 'condition': ...,
|
||||||
# 'hitCondition': ...,
|
# 'hitCondition': ...,
|
||||||
# 'logMessage': ...
|
# 'logMessage': ...
|
||||||
|
|
@ -171,10 +177,14 @@ class ProjectBreakpoints( object ):
|
||||||
|
|
||||||
self.UpdateUI()
|
self.UpdateUI()
|
||||||
|
|
||||||
def AddFunctionBreakpoint( self, function ):
|
def AddFunctionBreakpoint( self, function, options ):
|
||||||
self._func_breakpoints.append( {
|
self._func_breakpoints.append( {
|
||||||
'state': 'ENABLED',
|
'state': 'ENABLED',
|
||||||
'function': function,
|
'function': function,
|
||||||
|
'options': options,
|
||||||
|
# Specified in options:
|
||||||
|
# 'condition': ...,
|
||||||
|
# 'hitCondition': ...,
|
||||||
} )
|
} )
|
||||||
|
|
||||||
# TODO: We don't really have aanything to update here, but if we're going to
|
# TODO: We don't really have aanything to update here, but if we're going to
|
||||||
|
|
@ -239,7 +249,10 @@ class ProjectBreakpoints( object ):
|
||||||
if bp[ 'state' ] != 'ENABLED':
|
if bp[ 'state' ] != 'ENABLED':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
breakpoints.append( { 'line': bp[ 'line' ] } )
|
dap_bp = {}
|
||||||
|
dap_bp.update( bp[ 'options' ] )
|
||||||
|
dap_bp.update( { 'line': bp[ 'line' ] } )
|
||||||
|
breakpoints.append( dap_bp )
|
||||||
|
|
||||||
source = {
|
source = {
|
||||||
'name': os.path.basename( file_name ),
|
'name': os.path.basename( file_name ),
|
||||||
|
|
@ -264,15 +277,21 @@ class ProjectBreakpoints( object ):
|
||||||
|
|
||||||
if self._server_capabilities.get( 'supportsFunctionBreakpoints' ):
|
if self._server_capabilities.get( 'supportsFunctionBreakpoints' ):
|
||||||
awaiting = awaiting + 1
|
awaiting = awaiting + 1
|
||||||
|
breakpoints = []
|
||||||
|
for bp in self._func_breakpoints:
|
||||||
|
if bp[ 'state' ] != 'ENABLED':
|
||||||
|
continue
|
||||||
|
dap_bp = {}
|
||||||
|
dap_bp.update( bp[ 'options' ] )
|
||||||
|
dap_bp.update( { 'name': bp[ 'function' ] } )
|
||||||
|
breakpoints.append( dap_bp )
|
||||||
|
|
||||||
self._connection.DoRequest(
|
self._connection.DoRequest(
|
||||||
lambda msg: response_handler( None, msg ),
|
lambda msg: response_handler( None, msg ),
|
||||||
{
|
{
|
||||||
'command': 'setFunctionBreakpoints',
|
'command': 'setFunctionBreakpoints',
|
||||||
'arguments': {
|
'arguments': {
|
||||||
'breakpoints': [
|
'breakpoints': breakpoints,
|
||||||
{ 'name': bp[ 'function' ] }
|
|
||||||
for bp in self._func_breakpoints if bp[ 'state' ] == 'ENABLED'
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
failure_handler = lambda *_: response_received()
|
failure_handler = lambda *_: response_received()
|
||||||
|
|
@ -354,12 +373,15 @@ class ProjectBreakpoints( object ):
|
||||||
bp[ 'sign_id' ] = self._next_sign_id
|
bp[ 'sign_id' ] = self._next_sign_id
|
||||||
self._next_sign_id += 1
|
self._next_sign_id += 1
|
||||||
|
|
||||||
|
sign = ( 'vimspectorBPDisabled' if bp[ 'state' ] != 'ENABLED'
|
||||||
|
else 'vimspectorBPCond' if 'condition' in bp[ 'options' ]
|
||||||
|
else 'vimspectorBP' )
|
||||||
|
|
||||||
vim.command(
|
vim.command(
|
||||||
'sign place {0} group=VimspectorBP line={1} name={2} file={3}'.format(
|
'sign place {0} group=VimspectorBP line={1} name={2} file={3}'.format(
|
||||||
bp[ 'sign_id' ] ,
|
bp[ 'sign_id' ] ,
|
||||||
bp[ 'line' ],
|
bp[ 'line' ],
|
||||||
'vimspectorBP' if bp[ 'state' ] == 'ENABLED'
|
sign,
|
||||||
else 'vimspectorBPDisabled',
|
|
||||||
file_name ) )
|
file_name ) )
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -982,8 +982,8 @@ class DebugSession( object ):
|
||||||
def ListBreakpoints( self ):
|
def ListBreakpoints( self ):
|
||||||
return self._breakpoints.ListBreakpoints()
|
return self._breakpoints.ListBreakpoints()
|
||||||
|
|
||||||
def ToggleBreakpoint( self ):
|
def ToggleBreakpoint( self, options ):
|
||||||
return self._breakpoints.ToggleBreakpoint()
|
return self._breakpoints.ToggleBreakpoint( options )
|
||||||
|
|
||||||
def ClearBreakpoints( self ):
|
def ClearBreakpoints( self ):
|
||||||
if self._connection:
|
if self._connection:
|
||||||
|
|
@ -991,8 +991,8 @@ class DebugSession( object ):
|
||||||
|
|
||||||
return self._breakpoints.ClearBreakpoints()
|
return self._breakpoints.ClearBreakpoints()
|
||||||
|
|
||||||
def AddFunctionBreakpoint( self, function ):
|
def AddFunctionBreakpoint( self, function, options ):
|
||||||
return self._breakpoints.AddFunctionBreakpoint( function )
|
return self._breakpoints.AddFunctionBreakpoint( function, options )
|
||||||
|
|
||||||
|
|
||||||
def PathsToAllGadgetConfigs( vimspector_base, current_file ):
|
def PathsToAllGadgetConfigs( vimspector_base, current_file ):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue