Merge pull request #357 from puremourning/set-variable-value
Set variable value
This commit is contained in:
commit
f6517892c1
12 changed files with 569 additions and 61 deletions
13
README.md
13
README.md
|
|
@ -125,6 +125,8 @@ And a couple of brief demos:
|
|||
- remote launch, remote attach
|
||||
- locals and globals display
|
||||
- watch expressions with autocompletion
|
||||
- variable inspection tooltip on hover
|
||||
- set variable value in locals, watch and hover windows
|
||||
- call stack display and navigation
|
||||
- hierarchical variable value display popup (see `<Plug>VimspectorBalloonEval`)
|
||||
- interactive debug console with autocompletion
|
||||
|
|
@ -912,6 +914,8 @@ breakpoint when it is hit.
|
|||
|
||||
* Current scope shows values of locals.
|
||||
* Use `<CR>`, or double-click with left mouse to expand/collapse (+, -).
|
||||
* Set the value of the variable with `<C-CR>` (control + `<CR>`) or
|
||||
`<leader><CR>` (if `modifyOtherKeys` doesn't work for you)
|
||||
* When changing the stack frame the locals window updates.
|
||||
* While paused, hover to see values
|
||||
|
||||
|
|
@ -928,8 +932,10 @@ All rules for `Variables and scopes` apply plus the following:
|
|||
and get its result.
|
||||
* Make a normal mode (`nmap`) and visual mode (`xmap`) mapping to
|
||||
`<Plug>VimspectorBalloonEval` to manually trigger the popup.
|
||||
* Use regular nagivation keys (`j`, `k`) to choose the current selection; `<Esc>`
|
||||
(or leave the tooltip window) to close the tooltip.
|
||||
* Set the value of the variable with `<C-CR>` (control + `<CR>`) or
|
||||
`<leader><CR>` (if `modifyOtherKeys` doesn't work for you)
|
||||
* Use regular nagivation keys (`j`, `k`) to choose the current selection; `<Esc>`
|
||||
(or leave the tooltip window) to close the tooltip.
|
||||
|
||||

|
||||
|
||||
|
|
@ -946,6 +952,8 @@ to add a new watch expression.
|
|||
* Alternatively, use `:VimspectorWatch <expression>`. Tab-completion for
|
||||
expression is available in some debug adapters.
|
||||
* Expand result with `<CR>`, or double-click with left mouse.
|
||||
* Set the value of the variable with `<C-CR>` (control + `<CR>`) or
|
||||
`<leader><CR>` (if `modifyOtherKeys` doesn't work for you)
|
||||
* Delete with `<DEL>`.
|
||||
|
||||

|
||||
|
|
@ -2049,3 +2057,4 @@ hi link jsonComment Comment
|
|||
[debugpy]: https://github.com/microsoft/debugpy
|
||||
[YouCompleteMe]: https://github.com/ycm-core/YouCompleteMe#java-semantic-completion
|
||||
[remote-debugging]: https://puremourning.github.io/vimspector/configuration.html#remote-debugging-support
|
||||
[YcmJava]: https://github.com/ycm-core/YouCompleteMe#java-semantic-completion
|
||||
|
|
|
|||
|
|
@ -209,6 +209,17 @@ function! vimspector#ExpandVariable() abort
|
|||
py3 _vimspector_session.ExpandVariable()
|
||||
endfunction
|
||||
|
||||
function! vimspector#SetVariableValue( ... ) abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
endif
|
||||
if a:0 == 0
|
||||
py3 _vimspector_session.SetVariableValue()
|
||||
else
|
||||
py3 _vimspector_session.SetVariableValue( new_value = vim.eval( 'a:1' ) )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vimspector#DeleteWatch() abort
|
||||
if !s:Enabled()
|
||||
return
|
||||
|
|
@ -228,7 +239,9 @@ function! vimspector#AddWatch( ... ) abort
|
|||
return
|
||||
endif
|
||||
if a:0 == 0
|
||||
let expr = input( 'Enter watch expression: ' )
|
||||
let expr = input( 'Enter watch expression: ',
|
||||
\ '',
|
||||
\ 'custom,vimspector#CompleteExpr' )
|
||||
else
|
||||
let expr = a:1
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -124,8 +124,6 @@ function! vimspector#internal#balloon#MouseFilter( winid, key ) abort
|
|||
|
||||
" expand the variable if we got double click
|
||||
if a:key ==? "\<2-leftmouse>"
|
||||
" forward line number to python, since vim does not allow us to focus
|
||||
" the correct window
|
||||
call py3eval( '_vimspector_session.ExpandVariable('
|
||||
\ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],'
|
||||
\ . 'line_num = ' . line( '.', a:winid )
|
||||
|
|
@ -136,17 +134,49 @@ function! vimspector#internal#balloon#MouseFilter( winid, key ) abort
|
|||
return handled
|
||||
endfunction
|
||||
|
||||
function! s:MatchKey( key, candidates ) abort
|
||||
for candidate in a:candidates
|
||||
" If the mapping string looks like a special character, then try and
|
||||
" expand it. This is... a hack. The whole thing only works if the mapping
|
||||
" is a single key (anyway), and so we assume any string starting with < is a
|
||||
" special key (which will be the common case) and try and map it. If it
|
||||
" fails... it fails.
|
||||
if candidate[ 0 ] == '<'
|
||||
try
|
||||
execute 'let candidate = "\' . candidate . '"'
|
||||
endtry
|
||||
endif
|
||||
|
||||
if candidate ==# a:key
|
||||
return v:true
|
||||
endif
|
||||
endfor
|
||||
|
||||
return v:false
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#balloon#CursorFilter( winid, key ) abort
|
||||
if a:key ==? "\<CR>"
|
||||
" forward line number to python, since vim does not allow us to focus
|
||||
" the correct window
|
||||
let mappings = py3eval(
|
||||
\ "__import__( 'vimspector',"
|
||||
\." fromlist = [ 'settings' ] ).settings.Dict("
|
||||
\." 'mappings' )[ 'variables' ]" )
|
||||
|
||||
if index( [ "\<LeftMouse>", "\<2-LeftMouse>" ], a:key ) >= 0
|
||||
return vimspector#internal#balloon#MouseFilter( a:winid, a:key )
|
||||
endif
|
||||
|
||||
if s:MatchKey( a:key, mappings.expand_collapse )
|
||||
call py3eval( '_vimspector_session.ExpandVariable('
|
||||
\ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],'
|
||||
\ . 'line_num = ' . line( '.', a:winid )
|
||||
\ . ')' )
|
||||
return 1
|
||||
elseif index( [ "\<LeftMouse>", "\<2-LeftMouse>" ], a:key ) >= 0
|
||||
return vimspector#internal#balloon#MouseFilter( a:winid, a:key )
|
||||
elseif s:MatchKey( a:key, mappings.set_value )
|
||||
call py3eval( '_vimspector_session.SetVariableValue('
|
||||
\ . 'buf = vim.buffers[ ' . winbufnr( a:winid ) . ' ],'
|
||||
\ . 'line_num = ' . line( '.', a:winid )
|
||||
\ . ')' )
|
||||
return 1
|
||||
endif
|
||||
|
||||
return popup_filter_menu( a:winid, a:key )
|
||||
|
|
@ -291,12 +321,10 @@ function! s:CreateNeovimTooltip( body ) abort
|
|||
" interract with the popup in neovim
|
||||
noautocmd call win_gotoid( s:popup_win_id )
|
||||
|
||||
nnoremap <silent> <buffer> <CR>
|
||||
\ <cmd>call vimspector#ExpandVariable()<CR>
|
||||
nnoremap <silent> <buffer> <Esc>
|
||||
\ <cmd>quit<CR>
|
||||
nnoremap <silent> <buffer> <2-LeftMouse>
|
||||
\ <cmd>call vimspector#ExpandVariable()<CR>
|
||||
nnoremap <silent> <buffer> <Esc> <cmd>quit<CR>
|
||||
call py3eval( "__import__( 'vimspector', "
|
||||
\." fromlist = [ 'variables' ] )."
|
||||
\.' variables.AddExpandMappings()' )
|
||||
|
||||
" Close the popup whenever we leave this window
|
||||
augroup vimspector#internal#balloon#nvim_float
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ augroup VimspectorUserAutoCmds
|
|||
autocmd user VimspectorDebugEnded silent
|
||||
augroup END
|
||||
|
||||
" FIXME: Only register this _while_ debugging is active
|
||||
augroup Vimspector
|
||||
autocmd!
|
||||
autocmd BufNew * call vimspector#OnBufferCreated( expand( '<afile>' ) )
|
||||
|
|
|
|||
|
|
@ -525,6 +525,10 @@ class DebugSession( object ):
|
|||
def ExpandVariable( self, buf = None, line_num = None ):
|
||||
self._variablesView.ExpandVariable( buf, line_num )
|
||||
|
||||
@IfConnected()
|
||||
def SetVariableValue( self, new_value = None, buf = None, line_num = None ):
|
||||
self._variablesView.SetVariableValue( new_value, buf, line_num )
|
||||
|
||||
@IfConnected()
|
||||
def AddWatch( self, expression ):
|
||||
self._variablesView.AddWatch( self._stackTraceView.GetCurrentFrame(),
|
||||
|
|
@ -999,6 +1003,7 @@ class DebugSession( object ):
|
|||
def handle_initialize_response( msg ):
|
||||
self._server_capabilities = msg.get( 'body' ) or {}
|
||||
self._breakpoints.SetServerCapabilities( self._server_capabilities )
|
||||
self._variablesView.SetServerCapabilities( self._server_capabilities )
|
||||
self._Launch()
|
||||
|
||||
self._connection.DoRequest( handle_initialize_response, {
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ class OutputView( object ):
|
|||
raise
|
||||
|
||||
vim.command(
|
||||
"nnoremenu 1.{0} WinBar.{1}{2} "
|
||||
"nnoremenu <silent> 1.{0} WinBar.{1}{2} "
|
||||
":call vimspector#ShowOutputInWindow( {3}, '{1}' )<CR>".format(
|
||||
tab_buffer.index,
|
||||
utils.Escape( category ),
|
||||
|
|
|
|||
|
|
@ -38,6 +38,19 @@ DEFAULTS = {
|
|||
|
||||
# Installer
|
||||
'install_gadgets': [],
|
||||
|
||||
# Mappings
|
||||
'mappings': {
|
||||
'variables': {
|
||||
'expand_collapse': [ '<CR>', '<2-LeftMouse>' ],
|
||||
'delete': [ '<Del>' ],
|
||||
'set_value': [ '<C-CR>', '<leader><CR>' ]
|
||||
},
|
||||
'stack_trace': {
|
||||
'expand_or_jump': [ '<CR>', '<2-LeftMouse>' ],
|
||||
'focus_thread': [ '<leader><CR>' ],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -69,9 +82,44 @@ if hasattr( vim, 'Dictionary' ):
|
|||
DICT_TYPE = vim.Dictionary
|
||||
|
||||
|
||||
def Dict( option: str ):
|
||||
d = DICT_TYPE( DEFAULTS.get( option, {} ) )
|
||||
d.update( utils.GetVimValue( vim.vars,
|
||||
f'vimspector_{ option }',
|
||||
{} ) )
|
||||
return d
|
||||
def Dict( option ):
|
||||
return _UpdateDict( DICT_TYPE( DEFAULTS.get( option, {} ) ),
|
||||
vim.vars.get( f'vimspector_{ option }', DICT_TYPE() ) )
|
||||
|
||||
|
||||
def _UpdateDict( target, override ):
|
||||
"""Apply the updates in |override| to the dict |target|. This is like
|
||||
dict.update, but recursive. i.e. if the existing element is a dict, then
|
||||
override elements of the sub-dict rather than wholesale replacing.
|
||||
e.g.
|
||||
UpdateDict(
|
||||
{
|
||||
'outer': { 'inner': { 'key': 'oldValue', 'existingKey': True } }
|
||||
},
|
||||
{
|
||||
'outer': { 'inner': { 'key': 'newValue' } },
|
||||
'newKey': { 'newDict': True },
|
||||
}
|
||||
)
|
||||
yields:
|
||||
{
|
||||
'outer': {
|
||||
'inner': {
|
||||
'key': 'newValue',
|
||||
'existingKey': True
|
||||
}
|
||||
},
|
||||
'newKey': { newDict: True }
|
||||
}
|
||||
"""
|
||||
|
||||
for key, value in override.items():
|
||||
current_value = target.get( key )
|
||||
if not isinstance( current_value, DICT_TYPE ):
|
||||
target[ key ] = value
|
||||
elif isinstance( value, DICT_TYPE ):
|
||||
target[ key ] = _UpdateDict( current_value, value )
|
||||
else:
|
||||
target[ key ] = value
|
||||
|
||||
return target
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import os
|
|||
import logging
|
||||
import typing
|
||||
|
||||
from vimspector import utils, signs
|
||||
from vimspector import utils, signs, settings
|
||||
|
||||
|
||||
class Thread:
|
||||
|
|
@ -107,20 +107,23 @@ class StackTraceView( object ):
|
|||
utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' )
|
||||
utils.SetUpUIWindow( win )
|
||||
|
||||
mappings = settings.Dict( 'mappings' )[ 'stack_trace' ]
|
||||
|
||||
with utils.LetCurrentWindow( win ):
|
||||
vim.command( 'nnoremap <silent> <buffer> <CR> '
|
||||
':<C-U>call vimspector#GoToFrame()<CR>' )
|
||||
vim.command( 'nnoremap <silent> <buffer> <leader><CR> '
|
||||
':<C-U>call vimspector#SetCurrentThread()<CR>' )
|
||||
vim.command( 'nnoremap <silent> <buffer> <2-LeftMouse> '
|
||||
':<C-U>call vimspector#GoToFrame()<CR>' )
|
||||
for mapping in utils.GetVimList( mappings, 'expand_or_jump' ):
|
||||
vim.command( f'nnoremap <silent> <buffer> { mapping } '
|
||||
':<C-U>call vimspector#GoToFrame()<CR>' )
|
||||
|
||||
for mapping in utils.GetVimList( mappings, 'focus_thread' ):
|
||||
vim.command( f'nnoremap <silent> <buffer> { mapping } '
|
||||
':<C-U>call vimspector#SetCurrentThread()<CR>' )
|
||||
|
||||
if utils.UseWinBar():
|
||||
vim.command( 'nnoremenu 1.1 WinBar.Pause/Continue '
|
||||
vim.command( 'nnoremenu <silent> 1.1 WinBar.Pause/Continue '
|
||||
':call vimspector#PauseContinueThread()<CR>' )
|
||||
vim.command( 'nnoremenu 1.2 WinBar.Expand/Collapse '
|
||||
vim.command( 'nnoremenu <silent> 1.2 WinBar.Expand/Collapse '
|
||||
':call vimspector#GoToFrame()<CR>' )
|
||||
vim.command( 'nnoremenu 1.3 WinBar.Focus '
|
||||
vim.command( 'nnoremenu <silent> 1.3 WinBar.Focus '
|
||||
':call vimspector#SetCurrentThread()<CR>' )
|
||||
|
||||
win.options[ 'cursorline' ] = False
|
||||
|
|
|
|||
|
|
@ -356,16 +356,21 @@ def SelectFromList( prompt, options ):
|
|||
return None
|
||||
|
||||
|
||||
def AskForInput( prompt, default_value = None ):
|
||||
def AskForInput( prompt, default_value = None, completion = None ):
|
||||
if default_value is None:
|
||||
default_option = ''
|
||||
else:
|
||||
default_option = ", '{}'".format( Escape( default_value ) )
|
||||
default_value = ''
|
||||
|
||||
args = [ prompt, default_value ]
|
||||
|
||||
if completion is not None:
|
||||
if completion == 'expr':
|
||||
args.append( 'custom,vimspector#CompleteExpr' )
|
||||
else:
|
||||
args.append( completion )
|
||||
|
||||
with InputSave():
|
||||
try:
|
||||
return vim.eval( "input( '{}' {} )".format( Escape( prompt ),
|
||||
default_option ) )
|
||||
return Call( 'input', *args )
|
||||
except ( KeyboardInterrupt, vim.error ):
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import logging
|
|||
from functools import partial
|
||||
import typing
|
||||
|
||||
from vimspector import utils
|
||||
from vimspector import utils, settings
|
||||
|
||||
|
||||
class Expandable:
|
||||
|
|
@ -32,8 +32,9 @@ class Expandable:
|
|||
a 'variablesReference' to be resolved by the 'variables' request. Records the
|
||||
current state expanded/collapsed. Implementations just implement
|
||||
VariablesReference to get the variables."""
|
||||
def __init__( self ):
|
||||
def __init__( self, container: 'Expandable' = None ):
|
||||
self.variables: typing.List[ 'Variable' ] = None
|
||||
self.container: Expandable = container
|
||||
# None is Falsy and represents collapsed _by default_. WHen set to False,
|
||||
# this means the user explicitly collapsed it. When True, the user expanded
|
||||
# it (or we expanded it by default).
|
||||
|
|
@ -48,6 +49,9 @@ class Expandable:
|
|||
def IsExpandable( self ):
|
||||
return self.VariablesReference() > 0
|
||||
|
||||
def IsContained( self ):
|
||||
return self.container is not None
|
||||
|
||||
@abc.abstractmethod
|
||||
def VariablesReference( self ):
|
||||
assert False
|
||||
|
|
@ -92,8 +96,8 @@ class WatchFailure( WatchResult ):
|
|||
|
||||
class Variable( Expandable ):
|
||||
"""Holds one level of an expanded value tree. Also itself expandable."""
|
||||
def __init__( self, variable: dict ):
|
||||
super().__init__()
|
||||
def __init__( self, container: Expandable, variable: dict ):
|
||||
super().__init__( container = container )
|
||||
self.variable = variable
|
||||
# A new variable appearing is marked as changed
|
||||
self.changed = True
|
||||
|
|
@ -149,6 +153,18 @@ class BufView( View ):
|
|||
self.buf = buf
|
||||
|
||||
|
||||
def AddExpandMappings( mappings = None ):
|
||||
if mappings is None:
|
||||
mappings = settings.Dict( 'mappings' )[ 'variables' ]
|
||||
for mapping in utils.GetVimList( mappings, 'expand_collapse' ):
|
||||
vim.command( f'nnoremap <silent> <buffer> { mapping } '
|
||||
':<C-u>call vimspector#ExpandVariable()<CR>' )
|
||||
|
||||
for mapping in utils.GetVimList( mappings, 'set_value' ):
|
||||
vim.command( f'nnoremap <silent> <buffer> { mapping } '
|
||||
':<C-u>call vimspector#SetVariableValue()<CR>' )
|
||||
|
||||
|
||||
class VariablesView( object ):
|
||||
def __init__( self, variables_win, watches_win ):
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
|
|
@ -156,22 +172,22 @@ class VariablesView( object ):
|
|||
|
||||
self._connection = None
|
||||
self._current_syntax = ''
|
||||
self._server_capabilities = None
|
||||
|
||||
self._variable_eval: Scope = None
|
||||
self._variable_eval_view: View = None
|
||||
|
||||
def AddExpandMappings():
|
||||
vim.command( 'nnoremap <silent> <buffer> <CR> '
|
||||
':<C-u>call vimspector#ExpandVariable()<CR>' )
|
||||
vim.command( 'nnoremap <silent> <buffer> <2-LeftMouse> '
|
||||
':<C-u>call vimspector#ExpandVariable()<CR>' )
|
||||
mappings = settings.Dict( 'mappings' )[ 'variables' ]
|
||||
|
||||
# Set up the "Variables" buffer in the variables_win
|
||||
self._scopes: typing.List[ Scope ] = []
|
||||
self._vars = View( variables_win, {}, self._DrawScopes )
|
||||
utils.SetUpHiddenBuffer( self._vars.buf, 'vimspector.Variables' )
|
||||
with utils.LetCurrentWindow( variables_win ):
|
||||
AddExpandMappings()
|
||||
if utils.UseWinBar():
|
||||
vim.command( 'nnoremenu <silent> 1.1 WinBar.Set '
|
||||
':call vimspector#SetVariableValue()<CR>' )
|
||||
AddExpandMappings( mappings )
|
||||
|
||||
# Set up the "Watches" buffer in the watches_win (and create a WinBar in
|
||||
# there)
|
||||
|
|
@ -183,17 +199,20 @@ class VariablesView( object ):
|
|||
'vimspector#AddWatchPrompt',
|
||||
'vimspector#OmniFuncWatch' )
|
||||
with utils.LetCurrentWindow( watches_win ):
|
||||
AddExpandMappings()
|
||||
vim.command(
|
||||
'nnoremap <buffer> <DEL> :call vimspector#DeleteWatch()<CR>' )
|
||||
AddExpandMappings( mappings )
|
||||
for mapping in utils.GetVimList( mappings, 'delete' ):
|
||||
vim.command(
|
||||
f'nnoremap <buffer> { mapping } :call vimspector#DeleteWatch()<CR>' )
|
||||
|
||||
if utils.UseWinBar():
|
||||
vim.command( 'nnoremenu 1.1 WinBar.New '
|
||||
vim.command( 'nnoremenu <silent> 1.1 WinBar.New '
|
||||
':call vimspector#AddWatch()<CR>' )
|
||||
vim.command( 'nnoremenu 1.2 WinBar.Expand/Collapse '
|
||||
vim.command( 'nnoremenu <silent> 1.2 WinBar.Expand/Collapse '
|
||||
':call vimspector#ExpandVariable()<CR>' )
|
||||
vim.command( 'nnoremenu 1.3 WinBar.Delete '
|
||||
vim.command( 'nnoremenu <silent> 1.3 WinBar.Delete '
|
||||
':call vimspector#DeleteWatch()<CR>' )
|
||||
vim.command( 'nnoremenu <silent> 1.1 WinBar.Set '
|
||||
':call vimspector#SetVariableValue()<CR>' )
|
||||
|
||||
# Set the (global!) balloon expr if supported
|
||||
has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) )
|
||||
|
|
@ -231,11 +250,17 @@ class VariablesView( object ):
|
|||
def ConnectionUp( self, connection ):
|
||||
self._connection = connection
|
||||
|
||||
def SetServerCapabilities( self, capabilities ):
|
||||
self._server_capabilities = capabilities
|
||||
|
||||
def ConnectionClosed( self ):
|
||||
self.Clear()
|
||||
self._connection = None
|
||||
self._server_capabilities = None
|
||||
|
||||
def Reset( self ):
|
||||
self._server_capabilities = None
|
||||
|
||||
for k, v in self._oldoptions.items():
|
||||
vim.options[ k ] = v
|
||||
|
||||
|
|
@ -447,7 +472,7 @@ class VariablesView( object ):
|
|||
watch.result = WatchFailure( reason )
|
||||
self._DrawWatches()
|
||||
|
||||
def ExpandVariable( self, buf = None, line_num = None ):
|
||||
def _GetVariable( self, buf = None, line_num = None ):
|
||||
if buf is None:
|
||||
buf = vim.current.buffer
|
||||
|
||||
|
|
@ -462,12 +487,17 @@ class VariablesView( object ):
|
|||
and buf == self._variable_eval_view.buf ):
|
||||
view = self._variable_eval_view
|
||||
else:
|
||||
return
|
||||
return None
|
||||
|
||||
if line_num not in view.lines:
|
||||
return
|
||||
return None
|
||||
|
||||
variable = view.lines[ line_num ]
|
||||
return view.lines[ line_num ], view
|
||||
|
||||
def ExpandVariable( self, buf = None, line_num = None ):
|
||||
variable, view = self._GetVariable( buf, line_num )
|
||||
if variable is None:
|
||||
return
|
||||
|
||||
if variable.IsExpanded():
|
||||
# Collapse
|
||||
|
|
@ -488,6 +518,67 @@ class VariablesView( object ):
|
|||
},
|
||||
} )
|
||||
|
||||
def SetVariableValue( self, new_value = None, buf = None, line_num = None ):
|
||||
variable: Variable
|
||||
view: View
|
||||
|
||||
if not self._server_capabilities.get( 'supportsSetVariable' ):
|
||||
return
|
||||
|
||||
variable, view = self._GetVariable( buf, line_num )
|
||||
if variable is None:
|
||||
return
|
||||
|
||||
if not variable.IsContained():
|
||||
return
|
||||
|
||||
if new_value is None:
|
||||
new_value = utils.AskForInput( 'New Value: ',
|
||||
variable.variable.get( 'value', '' ),
|
||||
completion = 'expr' )
|
||||
|
||||
if new_value is None:
|
||||
return
|
||||
|
||||
|
||||
def handler( message ):
|
||||
# Annoyingly the response to setVariable request doesn't return a
|
||||
# Variable, but some part of it, so take a copy of the existing Variable
|
||||
# dict and update it, then call its update method with the updated copy.
|
||||
new_variable = dict( variable.variable )
|
||||
new_variable.update( message[ 'body' ] )
|
||||
|
||||
# Clear any existing known children (FIXME: Is this the right thing to do)
|
||||
variable.variables = None
|
||||
|
||||
# If the variable is expanded, re-request its children
|
||||
if variable.IsExpanded():
|
||||
self._connection.DoRequest( partial( self._ConsumeVariables,
|
||||
view.draw,
|
||||
variable ), {
|
||||
'command': 'variables',
|
||||
'arguments': {
|
||||
'variablesReference': variable.VariablesReference()
|
||||
},
|
||||
} )
|
||||
|
||||
variable.Update( new_variable )
|
||||
view.draw()
|
||||
|
||||
def failure_handler( reason, message ):
|
||||
utils.UserMessage( f'Cannot set value: { reason }', error = True )
|
||||
|
||||
self._connection.DoRequest( handler, {
|
||||
'command': 'setVariable',
|
||||
'arguments': {
|
||||
'variablesReference': variable.container.VariablesReference(),
|
||||
'name': variable.variable[ 'name' ],
|
||||
'value': new_value
|
||||
},
|
||||
}, failure_handler = failure_handler )
|
||||
|
||||
|
||||
|
||||
def _DrawVariables( self, view, variables, indent, is_short = False ):
|
||||
assert indent > 0
|
||||
for variable in variables:
|
||||
|
|
@ -609,7 +700,7 @@ class VariablesView( object ):
|
|||
found = True
|
||||
break
|
||||
if not found:
|
||||
variable = Variable( variable_body )
|
||||
variable = Variable( parent, variable_body )
|
||||
else:
|
||||
variable.Update( variable_body )
|
||||
|
||||
|
|
|
|||
|
|
@ -105,7 +105,10 @@ function! s:OnJumpToFrame() abort
|
|||
nmap <silent> <buffer> <LocalLeader>di <Plug>VimspectorBalloonEval
|
||||
xmap <silent> <buffer> <LocalLeader>di <Plug>VimspectorBalloonEval
|
||||
|
||||
let s:mapped[ string( bufnr() ) ] = 1
|
||||
let s:mapped[ string( bufnr() ) ] = { 'modifiable': &modifiable }
|
||||
|
||||
setlocal nomodifiable
|
||||
|
||||
endfunction
|
||||
|
||||
function! s:OnDebugEnd() abort
|
||||
|
|
@ -124,6 +127,8 @@ function! s:OnDebugEnd() abort
|
|||
silent! nunmap <buffer> <LocalLeader>dc
|
||||
silent! nunmap <buffer> <LocalLeader>di
|
||||
silent! xunmap <buffer> <LocalLeader>di
|
||||
|
||||
let &l:modifiable = s:mapped[ bufnr ][ 'modifiable' ]
|
||||
endtry
|
||||
endfor
|
||||
finally
|
||||
|
|
@ -142,4 +147,15 @@ augroup END
|
|||
|
||||
" }}}
|
||||
|
||||
" Custom mappings for special buffers {{{
|
||||
|
||||
let g:vimspector_mappings = {
|
||||
\ 'stack_trace': {},
|
||||
\ 'variables': {
|
||||
\ 'set_value': [ '<Tab>', '<C-CR>', 'C' ],
|
||||
\ }
|
||||
\ }
|
||||
|
||||
" }}}
|
||||
|
||||
" vim: foldmethod=marker
|
||||
|
|
|
|||
|
|
@ -830,3 +830,292 @@ function! Test_VariableEvalExpand()
|
|||
call vimspector#test#setup#Reset()
|
||||
%bwipe!
|
||||
endfunction
|
||||
|
||||
function! Test_SetVariableValue_Local()
|
||||
let fn = 'testdata/cpp/simple/struct.cpp'
|
||||
call s:StartDebugging( #{ fn: fn, line: 24, col: 1, launch: #{
|
||||
\ configuration: 'run-to-breakpoint'
|
||||
\ } } )
|
||||
|
||||
" Make sure the Test t is initialised
|
||||
call vimspector#StepOver()
|
||||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ assert_equal(
|
||||
\ [
|
||||
\ '- Scope: Locals',
|
||||
\ ' *+ t (Test): {...}',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.variables ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
call assert_equal( 'cpp',
|
||||
\ getbufvar(
|
||||
\ winbufnr( g:vimspector_session_windows.variables ),
|
||||
\ '&syntax' ) )
|
||||
|
||||
" Expand
|
||||
call win_gotoid( g:vimspector_session_windows.variables )
|
||||
call setpos( '.', [ 0, 2, 1 ] )
|
||||
call feedkeys( "\<CR>", 'xt' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ '- Scope: Locals',
|
||||
\ ' \*- t (Test): {...}',
|
||||
\ ' \*- i (int): 0',
|
||||
\ ' \*- c (char): 0 ''\\0\{1,3}''',
|
||||
\ ' \*- fffff (float): 0',
|
||||
\ ' \*+ another_test (AnotherTest):\( {...}\)\?',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.variables ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
call setpos( '.', [ 0, 3, 1 ] )
|
||||
|
||||
" We can't just fire the keys to the inpit prompt because we use inputsave()
|
||||
" and inputrestore(), so mock that out and fire away.
|
||||
py3 <<EOF
|
||||
from unittest import mock
|
||||
with mock.patch( 'vimspector.utils.InputSave' ):
|
||||
vim.eval( 'feedkeys( "\<C-CR>\<C-u>100\<CR>", "xt" )' )
|
||||
EOF
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ '- Scope: Locals',
|
||||
\ ' \*- t (Test): {...}',
|
||||
\ ' \*- i (int): 100',
|
||||
\ ' \*- c (char): 0 ''\\0\{1,3}''',
|
||||
\ ' \*- fffff (float): 0',
|
||||
\ ' \*+ another_test (AnotherTest):\( {...}\)\?',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.variables ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
" Now set it via the more comforable scripting interface
|
||||
call vimspector#SetVariableValue( '1234' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ '- Scope: Locals',
|
||||
\ ' \*- t (Test): {...}',
|
||||
\ ' \*- i (int): 1234',
|
||||
\ ' \*- c (char): 0 ''\\0\{1,3}''',
|
||||
\ ' \*- fffff (float): 0',
|
||||
\ ' \*+ another_test (AnotherTest):\( {...}\)\?',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.variables ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
" Something fails
|
||||
call vimspector#SetVariableValue( 'this is invalid' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ '- Scope: Locals',
|
||||
\ ' \*- t (Test): {...}',
|
||||
\ ' \*- i (int): 1234',
|
||||
\ ' \*- c (char): 0 ''\\0\{1,3}''',
|
||||
\ ' \*- fffff (float): 0',
|
||||
\ ' \*+ another_test (AnotherTest):\( {...}\)\?',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.variables ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
|
||||
call vimspector#test#setup#Reset()
|
||||
%bwipe!
|
||||
endfunction
|
||||
|
||||
function! Test_SetVariableValue_Watch()
|
||||
let fn = 'testdata/cpp/simple/struct.cpp'
|
||||
call s:StartDebugging( #{ fn: fn, line: 24, col: 1, launch: #{
|
||||
\ configuration: 'run-to-breakpoint'
|
||||
\ } } )
|
||||
|
||||
" Make sure the Test t is initialised
|
||||
call vimspector#StepOver()
|
||||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 )
|
||||
|
||||
call win_gotoid( g:vimspector_session_windows.watches )
|
||||
call feedkeys( "it\<CR>", 'xt' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ assert_equal(
|
||||
\ [
|
||||
\ 'Watches: ----',
|
||||
\ 'Expression: t',
|
||||
\ ' *+ Result: {...}',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.watches ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
call assert_equal( 'cpp',
|
||||
\ getbufvar(
|
||||
\ winbufnr( g:vimspector_session_windows.watches ),
|
||||
\ '&syntax' ) )
|
||||
|
||||
" Expand
|
||||
call win_gotoid( g:vimspector_session_windows.watches )
|
||||
call setpos( '.', [ 0, 3, 1 ] )
|
||||
call feedkeys( "\<CR>", 'xt' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ 'Watches: ----',
|
||||
\ 'Expression: t',
|
||||
\ ' \*- Result: {...}',
|
||||
\ ' \*- i (int): 0',
|
||||
\ ' \*- c (char): 0 ''\\0\{1,3}''',
|
||||
\ ' \*- fffff (float): 0',
|
||||
\ ' \*+ another_test (AnotherTest):\( {...}\)\?',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.watches ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
call setpos( '.', [ 0, 4, 1 ] )
|
||||
|
||||
" We can't just fire the keys to the inpit prompt because we use inputsave()
|
||||
" and inputrestore(), so mock that out and fire away.
|
||||
" Note: mapleder is ,
|
||||
py3 <<EOF
|
||||
from unittest import mock
|
||||
with mock.patch( 'vimspector.utils.InputSave' ):
|
||||
vim.eval( 'feedkeys( ",\<CR>\<C-u>100\<CR>", "xt" )' )
|
||||
EOF
|
||||
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ 'Watches: ----',
|
||||
\ 'Expression: t',
|
||||
\ ' \*- Result: {...}',
|
||||
\ ' \*- i (int): 100',
|
||||
\ ' \*- c (char): 0 ''\\0\{1,3}''',
|
||||
\ ' \*- fffff (float): 0',
|
||||
\ ' \*+ another_test (AnotherTest):\( {...}\)\?',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.watches ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
" Now set it via the more comforable scripting interface
|
||||
call vimspector#SetVariableValue( '1234' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ 'Watches: ----',
|
||||
\ 'Expression: t',
|
||||
\ ' \*- Result: {...}',
|
||||
\ ' \*- i (int): 1234',
|
||||
\ ' \*- c (char): 0 ''\\0\{1,3}''',
|
||||
\ ' \*- fffff (float): 0',
|
||||
\ ' \*+ another_test (AnotherTest):\( {...}\)\?',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.watches ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
call vimspector#test#setup#Reset()
|
||||
%bwipe!
|
||||
endfunction
|
||||
|
||||
function! Test_SetVariableValue_Balloon()
|
||||
let fn = 'testdata/cpp/simple/struct.cpp'
|
||||
call s:StartDebugging( #{ fn: fn, line: 24, col: 1, launch: #{
|
||||
\ configuration: 'run-to-breakpoint'
|
||||
\ } } )
|
||||
|
||||
call vimspector#StepOver()
|
||||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 )
|
||||
|
||||
" leader is ,
|
||||
xmap <buffer> <Leader>d <Plug>VimspectorBalloonEval
|
||||
nmap <buffer> <Leader>d <Plug>VimspectorBalloonEval
|
||||
|
||||
"evaluate the prev line
|
||||
call setpos( '.', [ 0, 24, 8 ] )
|
||||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 )
|
||||
call feedkeys( ',d', 'xt' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ assert_notequal( v:none, g:vimspector_session_windows.eval )
|
||||
\ } )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ '{...}',
|
||||
\ ' - i: 0',
|
||||
\ ' - c: 0 ''\\0\{1,3}''',
|
||||
\ ' - fffff: 0',
|
||||
\ ' + another_test: ',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.eval ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
" Move down to the ffff line
|
||||
|
||||
call feedkeys( 'jjj', 'xt' )
|
||||
" We can't just fire the keys to the inpit prompt because we use inputsave()
|
||||
" and inputrestore(), so mock that out and fire away.
|
||||
" Note: mapleder is ,
|
||||
py3 <<EOF
|
||||
from unittest import mock
|
||||
with mock.patch( 'vimspector.utils.InputSave' ):
|
||||
vim.eval( 'feedkeys( "\<C-CR>\<C-u>100\<CR>", "xt" )' )
|
||||
EOF
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ '{...}',
|
||||
\ ' - i: 0',
|
||||
\ ' - c: 0 ''\\0\{1,3}''',
|
||||
\ ' - fffff: 100',
|
||||
\ ' + another_test: ',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.eval ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
call vimspector#test#setup#Reset()
|
||||
%bwipe!
|
||||
endfunction
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue