Merge pull request #357 from puremourning/set-variable-value

Set variable value
This commit is contained in:
mergify[bot] 2021-02-25 22:39:45 +00:00 committed by GitHub
commit f6517892c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 569 additions and 61 deletions

View file

@ -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.
![variable eval hover](https://puremourning.github.io/vimspector-web/img/vimspector-variable-eval-hover.png)
@ -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>`.
![watch window](https://puremourning.github.io/vimspector-web/img/vimspector-watch-window.png)
@ -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

View file

@ -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

View file

@ -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

View file

@ -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>' ) )

View file

@ -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, {

View file

@ -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 ),

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 )

View file

@ -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

View file

@ -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