Merge pull request #153 from puremourning/conditional-breakpoints
Add basic support for conditional breakpoints
This commit is contained in:
commit
1b71d84f58
5 changed files with 87 additions and 49 deletions
51
README.md
51
README.md
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ) )
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue