Merge pull request #153 from puremourning/conditional-breakpoints

Add basic support for conditional breakpoints
This commit is contained in:
mergify[bot] 2020-04-26 06:44:57 +00:00 committed by GitHub
commit 1b71d84f58
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 49 deletions

View file

@ -74,17 +74,20 @@ But for now, here's a (rather old) screenshot of Vimsepctor debugging Vim:
## Supported debugging features
- flexible configuration syntax that can be checked in to source control
- breakpoints (function, line and exception breakpoints)
- conditional breakpoints (function, line)
- step in/out/over/up, stop, restart
- launch and attach
- remote launch, remote attach
- locals and globals display
- watch expressions
- call stack and navigation
- call stack display and navigation
- variable value display hover
- interactive debug console
- launch debugee within Vim's embedded terminal
- logging/stdout display
- simple stable API for custom tooling (e.g. integrate with language server)
## Supported languages:
@ -427,14 +430,6 @@ than welcome.
The backlog can be [viewed on Trello](https://trello.com/b/yvAKK0rD/vimspector).
In order to use it you have to currently:
- Write a mostly undocumented configuration file that contains essentially
undocumented parameters.
- Accept that it isn't complete yet
- Work around some frustrating bugs in Vim
- Ignore probably many bugs in vimspector!
### Experimental
The plugin is currently _experimental_. That means that any part of it
@ -443,8 +438,9 @@ can (and probably will) change, including things like:
- breaking changes to the configuration
- keys, layout, functionatlity of the UI
If a large number of people start using it then I will do my best to
minimise this, or at least announce on Gitter.
However, I commit to only doing this in the most extreme cases and to annouce
such changes on Gitter well in advance. There's nothing more annoying than stuff
just breaking on you. I get that.
# Mappings
@ -458,12 +454,13 @@ features to set your own mappings. To that end, Vimspector defines the following
* `<Plug>VimspectorRestart`
* `<Plug>VimspectorPause`
* `<Plug>VimspectorToggleBreakpoint`
* `<Plug>VimspectorToggleConditionalBreakpoint`
* `<Plug>VimspectorAddFunctionBreakpoint`
* `<Plug>VimspectorStepOver`
* `<Plug>VimspectorStepInto`
* `<Plug>VimspectorStepOut`
These map 1-1 with the API functions below.
These map roughly 1-1 with the API functions below.
For example, if you want `<F5>` to start/continue debugging, add this to some
appropriate place, such as your `vimrc` (hint: run `:e $MYVIMRC`).
@ -513,17 +510,18 @@ loading vimspector**:
let g:vimspector_enable_mappings = 'HUMAN'
```
| Key | Function | API |
| --- | --- | --- |
| `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` |
| `F3` | Stop debugging. | `vimspector#Stop()` |
| `F4` | Restart debugging with the same configuration. | `vimspector#Restart()` |
| `F6` | Pause debugee. | `vimspector#Pause()` |
| `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` |
| `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '<cexpr>' )` |
| `F10` | Step Over | `vimspector#StepOver()` |
| `F11` | Step Into | `vimspector#StepInto()` |
| `F12` | Step out of current function scope | `vimspector#StepOut()` |
| Key | Function | API |
| --- | --- | --- |
| `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` |
| `F3` | Stop debugging. | `vimspector#Stop()` |
| `F4` | Restart debugging with the same configuration. | `vimspector#Restart()` |
| `F6` | Pause debugee. | `vimspector#Pause()` |
| `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` |
| `<leader>F9` | Toggle conditional line breakpoint on the current line. | `vimspector#ToggleBreakpoint( {condition, hit condition } )` |
| `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '<cexpr>' )` |
| `F10` | Step Over | `vimspector#StepOver()` |
| `F11` | Step Into | `vimspector#StepInto()` |
| `F12` | Step out of current function scope | `vimspector#StepOut()` |
# Usage
@ -564,9 +562,10 @@ debugger](#java---partially-supported)
## Breakpoints
* Use `vimspector#ToggleBreakpoint()` to set/disable/delete a line breakpoint.
* Use `vimspector#AddFunctionBreakpoint( '<name>' )` to add a function
breakpoint.
* Use `vimspector#ToggleBreakpoint([ { 'condition': '<condition>' } ])`
to set/disable/delete a line breakpoint, with optional condition.
* Use `vimspector#AddFunctionBreakpoint( '<name>' [, { 'condition': '<condition>' } ] )`
to add a function breakpoint with optional condition.
## Stepping

View file

@ -42,12 +42,23 @@ function! vimspector#ClearBreakpoints() abort
py3 _vimspector_session.ClearBreakpoints()
endfunction
function! vimspector#ToggleBreakpoint() abort
py3 _vimspector_session.ToggleBreakpoint()
function! vimspector#ToggleBreakpoint( ... ) abort
if a:0 == 0
let options = {}
else
let options = a:1
endif
py3 _vimspector_session.ToggleBreakpoint( vim.eval( 'options' ) )
endfunction
function! vimspector#AddFunctionBreakpoint( function ) abort
py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ) )
function! vimspector#AddFunctionBreakpoint( function, ... ) abort
if a:0 == 0
let options = {}
else
let options = a:1
endif
py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ),
\ vim.eval( 'options' ) )
endfunction
function! vimspector#StepOver() abort

View file

@ -43,6 +43,11 @@ nnoremap <Plug>VimspectorRestart :<c-u>call vimspector#Restart()<CR>
nnoremap <Plug>VimspectorPause :<c-u>call vimspector#Pause()<CR>
nnoremap <Plug>VimspectorToggleBreakpoint
\ :<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
\ :<c-u>call vimspector#AddFunctionBreakpoint( expand( '<cexpr>' ) )<CR>
nnoremap <Plug>VimspectorStepOver :<c-u>call vimspector#StepOver()<CR>
@ -65,6 +70,7 @@ elseif s:mappings ==# 'HUMAN'
nmap <F4> <Plug>VimspectorRestart
nmap <F6> <Plug>VimspectorPause
nmap <F9> <Plug>VimspectorToggleBreakpoint
nmap <leader><F9> <Plug>VimspectorToggleConditionalBreakpoint
nmap <F8> <Plug>VimspectorAddFunctionBreakpoint
nmap <F10> <Plug>VimspectorStepOver
nmap <F11> <Plug>VimspectorStepInto

View file

@ -55,6 +55,9 @@ class ProjectBreakpoints( object ):
if not utils.SignDefined( 'vimspectorBP' ):
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' ):
vim.command( 'sign define vimspectorBPDisabled text=!> texthl=Warning' )
@ -96,8 +99,9 @@ class ProjectBreakpoints( object ):
'col': 1,
'type': 'L',
'valid': 1 if bp[ 'state' ] == 'ENABLED' else 0,
'text': "Line breakpoint - {}".format(
bp[ 'state' ] )
'text': "Line breakpoint - {}: {}".format(
bp[ 'state' ],
json.dumps( bp[ 'options' ] ) )
} )
# I think this shows that the qf list is not right for this.
for bp in self._func_breakpoints:
@ -107,7 +111,8 @@ class ProjectBreakpoints( object ):
'col': 1,
'type': 'F',
'valid': 1,
'text': "Function breakpoint: {}".format( bp[ 'function' ] ),
'text': "Function breakpoint: {}: {}".format( bp[ 'function' ],
bp[ 'options' ] ),
} )
vim.eval( 'setqflist( {} )'.format( json.dumps( qf ) ) )
@ -127,7 +132,7 @@ class ProjectBreakpoints( object ):
self.UpdateUI()
def ToggleBreakpoint( self ):
def ToggleBreakpoint( self, options ):
line, column = vim.current.window.cursor
file_name = vim.current.buffer.name
@ -161,9 +166,10 @@ class ProjectBreakpoints( object ):
self._line_breakpoints[ file_name ].append( {
'state': 'ENABLED',
'line': line,
'options': options,
# 'sign_id': <filled in when placed>,
#
# Used by other breakpoint types:
# Used by other breakpoint types (specified in options):
# 'condition': ...,
# 'hitCondition': ...,
# 'logMessage': ...
@ -171,10 +177,14 @@ class ProjectBreakpoints( object ):
self.UpdateUI()
def AddFunctionBreakpoint( self, function ):
def AddFunctionBreakpoint( self, function, options ):
self._func_breakpoints.append( {
'state': 'ENABLED',
'function': function,
'state': 'ENABLED',
'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
@ -239,7 +249,10 @@ class ProjectBreakpoints( object ):
if bp[ 'state' ] != 'ENABLED':
continue
breakpoints.append( { 'line': bp[ 'line' ] } )
dap_bp = {}
dap_bp.update( bp[ 'options' ] )
dap_bp.update( { 'line': bp[ 'line' ] } )
breakpoints.append( dap_bp )
source = {
'name': os.path.basename( file_name ),
@ -264,15 +277,21 @@ class ProjectBreakpoints( object ):
if self._server_capabilities.get( 'supportsFunctionBreakpoints' ):
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(
lambda msg: response_handler( None, msg ),
{
'command': 'setFunctionBreakpoints',
'arguments': {
'breakpoints': [
{ 'name': bp[ 'function' ] }
for bp in self._func_breakpoints if bp[ 'state' ] == 'ENABLED'
],
'breakpoints': breakpoints,
}
},
failure_handler = lambda *_: response_received()
@ -354,12 +373,15 @@ class ProjectBreakpoints( object ):
bp[ 'sign_id' ] = self._next_sign_id
self._next_sign_id += 1
sign = ( 'vimspectorBPDisabled' if bp[ 'state' ] != 'ENABLED'
else 'vimspectorBPCond' if 'condition' in bp[ 'options' ]
else 'vimspectorBP' )
vim.command(
'sign place {0} group=VimspectorBP line={1} name={2} file={3}'.format(
bp[ 'sign_id' ] ,
bp[ 'line' ],
'vimspectorBP' if bp[ 'state' ] == 'ENABLED'
else 'vimspectorBPDisabled',
sign,
file_name ) )

View file

@ -982,8 +982,8 @@ class DebugSession( object ):
def ListBreakpoints( self ):
return self._breakpoints.ListBreakpoints()
def ToggleBreakpoint( self ):
return self._breakpoints.ToggleBreakpoint()
def ToggleBreakpoint( self, options ):
return self._breakpoints.ToggleBreakpoint( options )
def ClearBreakpoints( self ):
if self._connection:
@ -991,8 +991,8 @@ class DebugSession( object ):
return self._breakpoints.ClearBreakpoints()
def AddFunctionBreakpoint( self, function ):
return self._breakpoints.AddFunctionBreakpoint( function )
def AddFunctionBreakpoint( self, function, options ):
return self._breakpoints.AddFunctionBreakpoint( function, options )
def PathsToAllGadgetConfigs( vimspector_base, current_file ):