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
|
## Supported debugging features
|
||||||
|
|
||||||
|
- flexible configuration syntax that can be checked in to source control
|
||||||
- breakpoints (function, line and exception breakpoints)
|
- breakpoints (function, line and exception breakpoints)
|
||||||
|
- conditional breakpoints (function, line)
|
||||||
- step in/out/over/up, stop, restart
|
- step in/out/over/up, stop, restart
|
||||||
- launch and attach
|
- launch and attach
|
||||||
- remote launch, remote attach
|
- remote launch, remote attach
|
||||||
- locals and globals display
|
- locals and globals display
|
||||||
- watch expressions
|
- watch expressions
|
||||||
- call stack and navigation
|
- call stack display and navigation
|
||||||
- variable value display hover
|
- variable value display hover
|
||||||
- interactive debug console
|
- interactive debug console
|
||||||
- launch debugee within Vim's embedded terminal
|
- launch debugee within Vim's embedded terminal
|
||||||
- logging/stdout display
|
- logging/stdout display
|
||||||
|
- simple stable API for custom tooling (e.g. integrate with language server)
|
||||||
|
|
||||||
## Supported languages:
|
## Supported languages:
|
||||||
|
|
||||||
|
|
@ -427,14 +430,6 @@ than welcome.
|
||||||
|
|
||||||
The backlog can be [viewed on Trello](https://trello.com/b/yvAKK0rD/vimspector).
|
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
|
### Experimental
|
||||||
|
|
||||||
The plugin is currently _experimental_. That means that any part of it
|
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
|
- breaking changes to the configuration
|
||||||
- keys, layout, functionatlity of the UI
|
- keys, layout, functionatlity of the UI
|
||||||
|
|
||||||
If a large number of people start using it then I will do my best to
|
However, I commit to only doing this in the most extreme cases and to annouce
|
||||||
minimise this, or at least announce on Gitter.
|
such changes on Gitter well in advance. There's nothing more annoying than stuff
|
||||||
|
just breaking on you. I get that.
|
||||||
|
|
||||||
# Mappings
|
# Mappings
|
||||||
|
|
||||||
|
|
@ -458,12 +454,13 @@ 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`
|
||||||
* `<Plug>VimspectorStepOut`
|
* `<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
|
For example, if you want `<F5>` to start/continue debugging, add this to some
|
||||||
appropriate place, such as your `vimrc` (hint: run `:e $MYVIMRC`).
|
appropriate place, such as your `vimrc` (hint: run `:e $MYVIMRC`).
|
||||||
|
|
@ -513,17 +510,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 +562,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,23 @@ function! vimspector#ClearBreakpoints() abort
|
||||||
py3 _vimspector_session.ClearBreakpoints()
|
py3 _vimspector_session.ClearBreakpoints()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#ToggleBreakpoint() abort
|
function! vimspector#ToggleBreakpoint( ... ) abort
|
||||||
py3 _vimspector_session.ToggleBreakpoint()
|
if a:0 == 0
|
||||||
|
let options = {}
|
||||||
|
else
|
||||||
|
let options = a:1
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.ToggleBreakpoint( vim.eval( 'options' ) )
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! vimspector#AddFunctionBreakpoint( function ) abort
|
function! vimspector#AddFunctionBreakpoint( function, ... ) abort
|
||||||
py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ) )
|
if a:0 == 0
|
||||||
|
let options = {}
|
||||||
|
else
|
||||||
|
let options = a:1
|
||||||
|
endif
|
||||||
|
py3 _vimspector_session.AddFunctionBreakpoint( vim.eval( 'a:function' ),
|
||||||
|
\ vim.eval( '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