Merge pull request #350 from puremourning/nvim-float-window
Replace vim balloons with popups
This commit is contained in:
commit
e70b8f37a3
12 changed files with 784 additions and 113 deletions
54
README.md
54
README.md
|
|
@ -50,6 +50,7 @@ For detailed explanatin of the `.vimspector.json` format, see the
|
|||
* [Run to Cursor](#run-to-cursor)
|
||||
* [Stepping](#stepping)
|
||||
* [Variables and scopes](#variables-and-scopes)
|
||||
* [Variable or selection hover evaluation](#variable-or-selection-hover-evaluation)
|
||||
* [Watches](#watches)
|
||||
* [Watch autocompletion](#watch-autocompletion)
|
||||
* [Stack Traces](#stack-traces)
|
||||
|
|
@ -89,7 +90,7 @@ For detailed explanatin of the `.vimspector.json` format, see the
|
|||
* [Example](#example)
|
||||
* [FAQ](#faq)
|
||||
|
||||
<!-- Added by: ben, at: Sat 9 Jan 2021 13:13:28 GMT -->
|
||||
<!-- Added by: ben, at: Sun 21 Feb 2021 16:59:12 GMT -->
|
||||
|
||||
<!--te-->
|
||||
|
||||
|
|
@ -102,7 +103,7 @@ language that Visual Studio Code supports (but see caveats).
|
|||
The [Vimspector website][website] has an overview of the UI, along with basic
|
||||
instructions for configuration and setup.
|
||||
|
||||
But for now, here's a (rather old) screenshot of Vimsepctor debugging Vim:
|
||||
But for now, here's a (rather old) screenshot of Vimspector debugging Vim:
|
||||
|
||||

|
||||
|
||||
|
|
@ -124,7 +125,7 @@ And a couple of brief demos:
|
|||
- locals and globals display
|
||||
- watch expressions with autocompletion
|
||||
- call stack display and navigation
|
||||
- variable value display hover
|
||||
- hierarchical variable value display popup (see `<Plug>VimspectorBalloonEval`)
|
||||
- interactive debug console with autocompletion
|
||||
- launch debugee within Vim's embedded terminal
|
||||
- logging/stdout display
|
||||
|
|
@ -230,8 +231,8 @@ Why such a new vim ? Well 2 reasons:
|
|||
if you hit them.
|
||||
|
||||
Why is neovim experimental? Because the author doesn't use neovim regularly, and
|
||||
there are no regression tests for vimspector in neovim, so it's likely to break
|
||||
frequently. Issue reports are handled on best-efforts basis, and PRs are
|
||||
there are no regression tests for vimspector in neovim, so it may break
|
||||
occasionally. Issue reports are handled on best-efforts basis, and PRs are
|
||||
welcome to fix bugs. See also the next section descibing differences for neovim
|
||||
vs vim.
|
||||
|
||||
|
|
@ -249,7 +250,8 @@ neovim doesn't implement some features Vimspector relies on:
|
|||
the output window's current output.
|
||||
* Prompt Buffers - used to send commands in the Console and add Watches.
|
||||
(*Note*: prompt buffers are available in neovim nightly)
|
||||
* Balloons - used to display the values of variables when debugging.
|
||||
* Balloons - this allows for the variable evaluation popup to be displayed when
|
||||
hovering the mouse. See below for how to create a keyboard mapping instead.
|
||||
|
||||
Workarounds are in place as follows:
|
||||
|
||||
|
|
@ -258,9 +260,18 @@ Workarounds are in place as follows:
|
|||
[`:VimspectorReset`](#closing-debugger)
|
||||
* Prompt Buffers - There are [`:VimspectorEval`](#console)
|
||||
and [`:VimspectorWatch`](#watches)
|
||||
* Balloons - There is the `<Plug>VimspectorBalloonEval` mapping. There is no
|
||||
default mapping for this, so I recommend something like this to get variable
|
||||
display in a popup:
|
||||
|
||||
There is no workaroud for the lack of balloons; you'll just have to use
|
||||
`:VimspectorEval` or `:VimspectorWatch`, or switch to Vim.
|
||||
```viml
|
||||
" mnemonic 'di' = 'debug inspect' (pick your own, if you prefer!)
|
||||
|
||||
" for normal mode - the word under the cursor
|
||||
nmap <Leader>di <Plug>VimspectorBalloonEval
|
||||
" for visual mode, the visually selected text
|
||||
xmap <Leader>di <Plug>VimspectorBalloonEval
|
||||
```
|
||||
|
||||
## Windows differences
|
||||
|
||||
|
|
@ -653,6 +664,7 @@ features to set your own mappings. To that end, Vimspector defines the following
|
|||
* `<Plug>VimspectorStepInto`
|
||||
* `<Plug>VimspectorStepOut`
|
||||
* `<Plug>VimspectorRunToCursor`
|
||||
* `<Plug>VimspectorBalloonEval`
|
||||
|
||||
These map roughly 1-1 with the API functions below.
|
||||
|
||||
|
|
@ -715,6 +727,18 @@ let g:vimspector_enable_mappings = 'HUMAN'
|
|||
| `F11` | Step Into | `vimspector#StepInto()` |
|
||||
| `F12` | Step out of current function scope | `vimspector#StepOut()` |
|
||||
|
||||
In addition, I recommend adding a mapping to `<Plug>VimspectorBalloonEval`, in
|
||||
normal and visual modes, for example:
|
||||
|
||||
```viml
|
||||
" mnemonic 'di' = 'debug inspect' (pick your own, if you prefer!)
|
||||
|
||||
" for normal mode - the word under the cursor
|
||||
nmap <Leader>di <Plug>VimspectorBalloonEval
|
||||
" for visual mode, the visually selected text
|
||||
xmap <Leader>di <Plug>VimspectorBalloonEval
|
||||
```
|
||||
|
||||
# Usage and API
|
||||
|
||||
This section defines detailed usage instructions, organised by feature. For most
|
||||
|
|
@ -890,6 +914,20 @@ breakpoint when it is hit.
|
|||
|
||||
Scopes and variables are represented by the buffer `vimspector.Variables`.
|
||||
|
||||
## Variable or selection hover evaluation
|
||||
|
||||
All rules for `Variables and scopes` apply plus the following:
|
||||
|
||||
* With mouse enabled, hover over a variable and get the value it evaluates to.
|
||||
* Use your mouse to perform a visual selection of an expression (e.g. `a + b`)
|
||||
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 chose the current selection; `<Esc>`
|
||||
(or leave the tooltip window) to close the tooltip.
|
||||
|
||||

|
||||
|
||||
## Watches
|
||||
|
||||
The watch window is used to inspect variables and expressions. Expressions are
|
||||
|
|
|
|||
|
|
@ -523,6 +523,22 @@ function! vimspector#OnBufferCreated( file_name ) abort
|
|||
py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) )
|
||||
endfunction
|
||||
|
||||
function! vimspector#ShowEvalBalloon( is_visual ) abort
|
||||
if a:is_visual
|
||||
let expr = py3eval( '__import__( "vimspector", fromlist = [ "utils" ] )'
|
||||
\ . '.utils.GetVisualSelection('
|
||||
\ . ' int( vim.eval( "winbufnr( winnr() )" ) ) )' )
|
||||
let expr = join( expr, '\n' )
|
||||
else
|
||||
let expr = expand( '<cexpr>' )
|
||||
endif
|
||||
|
||||
return py3eval( '_vimspector_session.ShowEvalBalloon('
|
||||
\ . ' int( vim.eval( "winnr()" ) ), "'
|
||||
\ . expr
|
||||
\ . '", 0 )' )
|
||||
endfunction
|
||||
|
||||
|
||||
" Boilerplate {{{
|
||||
let &cpoptions=s:save_cpo
|
||||
|
|
|
|||
|
|
@ -19,16 +19,293 @@ let s:save_cpo = &cpoptions
|
|||
set cpoptions&vim
|
||||
" }}}
|
||||
|
||||
" Returns: py.ShowBalloon( winnr, expresssion )
|
||||
function! vimspector#internal#balloon#BalloonExpr() abort
|
||||
" winnr + 1 because for *no good reason* winnr is 0 based here unlike
|
||||
" everywhere else
|
||||
" int() because for *no good reason* winnr is a string.
|
||||
return py3eval('_vimspector_session.ShowBalloon('
|
||||
\ . 'int( vim.eval( "v:beval_winnr" ) ) + 1,'
|
||||
\ . 'vim.eval( "v:beval_text" ) )' )
|
||||
scriptencoding utf-8
|
||||
|
||||
let s:popup_win_id = 0
|
||||
let s:nvim_border_win_id = 0
|
||||
"
|
||||
" tooltip dimensions
|
||||
let s:min_width = 1
|
||||
let s:min_height = 1
|
||||
let s:max_width = 80
|
||||
let s:max_height = 20
|
||||
|
||||
let s:is_neovim = has( 'nvim' )
|
||||
|
||||
|
||||
" This is used as the balloonexpr in vim to show the Tooltip at the hover
|
||||
" position
|
||||
function! vimspector#internal#balloon#HoverTooltip() abort
|
||||
return py3eval( '_vimspector_session.ShowEvalBalloon('
|
||||
\ . ' int( vim.eval( "v:beval_winnr" ) ) + 1,'
|
||||
\ . ' vim.eval( "v:beval_text"),'
|
||||
\ . ' 1 )' )
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#balloon#CreateTooltip( is_hover, ... ) abort
|
||||
let body = []
|
||||
if a:0 > 0
|
||||
let body = a:1
|
||||
endif
|
||||
|
||||
if s:popup_win_id != 0
|
||||
call vimspector#internal#balloon#Close()
|
||||
endif
|
||||
|
||||
if s:is_neovim
|
||||
call s:CreateNeovimTooltip( body )
|
||||
else
|
||||
let config = {
|
||||
\ 'wrap': 0,
|
||||
\ 'filtermode': 'n',
|
||||
\ 'maxwidth': s:max_width,
|
||||
\ 'maxheight': s:max_height,
|
||||
\ 'minwidth': s:min_width,
|
||||
\ 'minheight': s:min_height,
|
||||
\ 'scrollbar': 1,
|
||||
\ 'border': [],
|
||||
\ 'padding': [ 0, 1, 0, 1],
|
||||
\ 'drag': 1,
|
||||
\ 'resize': 1,
|
||||
\ 'close': 'button',
|
||||
\ 'callback': 'vimspector#internal#balloon#CloseCallback'
|
||||
\ }
|
||||
|
||||
" When ambiwidth is single, use prettier characters for the border. This
|
||||
" would look silly when ambiwidth is double.
|
||||
if &ambiwidth ==# 'single' && &encoding ==? 'utf-8'
|
||||
let config[ 'borderchars' ] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ]
|
||||
endif
|
||||
|
||||
if a:is_hover
|
||||
let config[ 'filter' ] = 'vimspector#internal#balloon#MouseFilter'
|
||||
let config[ 'mousemoved' ] = [ 0, 0, 0 ]
|
||||
let s:popup_win_id = popup_beval( body, config )
|
||||
else
|
||||
let config[ 'filter' ] = 'vimspector#internal#balloon#CursorFilter'
|
||||
let config[ 'moved' ] = 'any'
|
||||
let config[ 'cursorline' ] = 1
|
||||
let s:popup_win_id = popup_atcursor( body, config )
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
return s:popup_win_id
|
||||
endfunction
|
||||
|
||||
" Filters for vim {{{
|
||||
function! vimspector#internal#balloon#MouseFilter( winid, key ) abort
|
||||
if a:key ==# "\<Esc>"
|
||||
call vimspector#internal#balloon#Close()
|
||||
return 0
|
||||
endif
|
||||
|
||||
if index( [ "\<leftmouse>", "\<2-leftmouse>" ], a:key ) < 0
|
||||
return 0
|
||||
endif
|
||||
|
||||
let handled = 0
|
||||
let mouse_coords = getmousepos()
|
||||
|
||||
" close the popup if mouse is clicked outside the window
|
||||
if mouse_coords[ 'winid' ] != a:winid
|
||||
call vimspector#internal#balloon#Close()
|
||||
return 0
|
||||
endif
|
||||
|
||||
" place the cursor according to the click
|
||||
call win_execute( a:winid,
|
||||
\ ':call cursor( '
|
||||
\ . mouse_coords[ 'line' ]
|
||||
\ . ', '
|
||||
\ . mouse_coords[ 'column' ]
|
||||
\ . ' )' )
|
||||
|
||||
" 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 )
|
||||
\ . ')' )
|
||||
let handled = 1
|
||||
endif
|
||||
|
||||
return handled
|
||||
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
|
||||
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 )
|
||||
endif
|
||||
|
||||
return popup_filter_menu( a:winid, a:key )
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
|
||||
" Closing {{{
|
||||
|
||||
function! vimspector#internal#balloon#CloseCallback( ... ) abort
|
||||
let s:popup_win_id = 0
|
||||
let s:nvim_border_win_id = 0
|
||||
return py3eval( '_vimspector_session.CleanUpTooltip()' )
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#balloon#Close() abort
|
||||
if s:is_neovim
|
||||
call nvim_win_close( s:popup_win_id, v:true )
|
||||
call nvim_win_close( s:nvim_border_win_id, v:true )
|
||||
|
||||
call vimspector#internal#balloon#CloseCallback()
|
||||
else
|
||||
call popup_close(s:popup_win_id)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
|
||||
" Neovim pollyfill {{{
|
||||
|
||||
function! vimspector#internal#balloon#ResizeTooltip() abort
|
||||
if !s:is_neovim
|
||||
" Vim does this for us
|
||||
return
|
||||
endif
|
||||
|
||||
if s:popup_win_id <= 0 || s:nvim_border_win_id <= 0
|
||||
" nothing to resize
|
||||
return
|
||||
endif
|
||||
|
||||
noautocmd call win_gotoid( s:popup_win_id )
|
||||
let buf_lines = getline( 1, '$' )
|
||||
|
||||
let width = s:min_width
|
||||
let height = min( [ max( [ s:min_height, len( buf_lines ) ] ),
|
||||
\ s:max_height ] )
|
||||
|
||||
" calculate the longest line
|
||||
for l in buf_lines
|
||||
let width = max( [ width, len( l ) ] )
|
||||
endfor
|
||||
|
||||
let width = min( [ width, s:max_width ] )
|
||||
|
||||
let opts = {
|
||||
\ 'width': width,
|
||||
\ 'height': height,
|
||||
\ }
|
||||
|
||||
" resize the content window
|
||||
call nvim_win_set_config( s:popup_win_id, opts )
|
||||
|
||||
" resize the border window
|
||||
let opts[ 'width' ] = width + 4
|
||||
let opts[ 'height' ] = height + 2
|
||||
|
||||
call nvim_win_set_config( s:nvim_border_win_id, opts )
|
||||
call nvim_buf_set_lines( nvim_win_get_buf( s:nvim_border_win_id ),
|
||||
\ 0,
|
||||
\ -1,
|
||||
\ v:true,
|
||||
\ s:GenerateBorder( width, height ) )
|
||||
endfunction
|
||||
|
||||
" neovim doesn't have the border support, so we have to make our own.
|
||||
" FIXME: This will likely break if the user has `ambiwidth=2`
|
||||
function! s:GenerateBorder( width, height ) abort
|
||||
|
||||
let top = '╭' . repeat('─',a:width + 2) . '╮'
|
||||
let mid = '│' . repeat(' ',a:width + 2) . '│'
|
||||
let bot = '╰' . repeat('─',a:width + 2) . '╯'
|
||||
let lines = [ top ] + repeat( [ mid ], a:height ) + [ bot ]
|
||||
|
||||
return lines
|
||||
endfunction
|
||||
|
||||
function! s:CreateNeovimTooltip( body ) abort
|
||||
" generate border for the float window by creating a background buffer and
|
||||
" overlaying the content buffer
|
||||
" see https://github.com/neovim/neovim/issues/9718#issuecomment-546603628
|
||||
let buf_id = nvim_create_buf( v:false, v:true )
|
||||
call nvim_buf_set_lines( buf_id,
|
||||
\ 0,
|
||||
\ -1,
|
||||
\ v:true,
|
||||
\ s:GenerateBorder( s:max_width, s:max_height ) )
|
||||
|
||||
" default the dimensions initially, then we'll calculate the real size and
|
||||
" resize it.
|
||||
let opts = {
|
||||
\ 'relative': 'cursor',
|
||||
\ 'width': s:max_width + 2,
|
||||
\ 'height': s:max_height + 2,
|
||||
\ 'col': 0,
|
||||
\ 'row': 1,
|
||||
\ 'anchor': 'NW',
|
||||
\ 'style': 'minimal'
|
||||
\ }
|
||||
|
||||
" this is the border window
|
||||
let s:nvim_border_win_id = nvim_open_win( buf_id, 0, opts )
|
||||
call nvim_win_set_option( s:nvim_border_win_id, 'signcolumn', 'no' )
|
||||
call nvim_win_set_option( s:nvim_border_win_id, 'relativenumber', v:false )
|
||||
call nvim_win_set_option( s:nvim_border_win_id, 'number', v:false )
|
||||
|
||||
" when calculating where to display the content window, we need to account
|
||||
" for the border
|
||||
let opts.row += 1
|
||||
let opts.height -= 2
|
||||
let opts.col += 2
|
||||
let opts.width -= 4
|
||||
|
||||
" create the content window
|
||||
let buf_id = nvim_create_buf( v:false, v:true )
|
||||
call nvim_buf_set_lines( buf_id, 0, -1, v:true, a:body )
|
||||
call nvim_buf_set_option( buf_id, 'modifiable', v:false )
|
||||
let s:popup_win_id = nvim_open_win( buf_id, v:false, opts )
|
||||
|
||||
call nvim_win_set_option( s:popup_win_id, 'wrap', v:false )
|
||||
call nvim_win_set_option( s:popup_win_id, 'cursorline', v:true )
|
||||
call nvim_win_set_option( s:popup_win_id, 'signcolumn', 'no' )
|
||||
call nvim_win_set_option( s:popup_win_id, 'relativenumber', v:false )
|
||||
call nvim_win_set_option( s:popup_win_id, 'number', v:false )
|
||||
|
||||
" Move the cursor into the popup window, as this is the only way we can
|
||||
" 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>
|
||||
|
||||
" Close the popup whenever we leave this window
|
||||
augroup vimspector#internal#balloon#nvim_float
|
||||
autocmd!
|
||||
autocmd WinLeave <buffer>
|
||||
\ :call vimspector#internal#balloon#Close()
|
||||
\ | autocmd! vimspector#internal#balloon#nvim_float
|
||||
augroup END
|
||||
|
||||
call vimspector#internal#balloon#ResizeTooltip()
|
||||
endfunction
|
||||
|
||||
" }}}
|
||||
|
||||
|
||||
" Boilerplate {{{
|
||||
let &cpoptions=s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
|
|
|||
|
|
@ -60,6 +60,13 @@ nnoremap <silent> <Plug>VimspectorStepOut
|
|||
nnoremap <silent> <Plug>VimspectorRunToCursor
|
||||
\ :<c-u>call vimspector#RunToCursor()<CR>
|
||||
|
||||
" Eval for normal mode
|
||||
nnoremap <silent> <Plug>VimspectorBalloonEval
|
||||
\ :<c-u>call vimspector#ShowEvalBalloon( 0 )<CR>
|
||||
" And for visual modes
|
||||
xnoremap <silent> <Plug>VimspectorBalloonEval
|
||||
\ :<c-u>call vimspector#ShowEvalBalloon( 1 )<CR>
|
||||
|
||||
if s:mappings ==# 'VISUAL_STUDIO'
|
||||
nmap <F5> <Plug>VimspectorContinue
|
||||
nmap <S-F5> <Plug>VimspectorStop
|
||||
|
|
|
|||
|
|
@ -520,8 +520,8 @@ class DebugSession( object ):
|
|||
self._stackTraceView.SetCurrentThread()
|
||||
|
||||
@IfConnected()
|
||||
def ExpandVariable( self ):
|
||||
self._variablesView.ExpandVariable()
|
||||
def ExpandVariable( self, buf = None, line_num = None ):
|
||||
self._variablesView.ExpandVariable( buf, line_num )
|
||||
|
||||
@IfConnected()
|
||||
def AddWatch( self, expression ):
|
||||
|
|
@ -538,13 +538,13 @@ class DebugSession( object ):
|
|||
def DeleteWatch( self ):
|
||||
self._variablesView.DeleteWatch()
|
||||
|
||||
|
||||
@IfConnected()
|
||||
def ShowBalloon( self, winnr, expression ):
|
||||
"""Proxy: ballonexpr -> variables.ShowBallon"""
|
||||
def ShowEvalBalloon( self, winnr, expression, is_hover ):
|
||||
frame = self._stackTraceView.GetCurrentFrame()
|
||||
# Check if RIP is in a frame
|
||||
if frame is None:
|
||||
self._logger.debug( 'Balloon: Not in a stack frame' )
|
||||
self._logger.debug( 'Tooltip: Not in a stack frame' )
|
||||
return ''
|
||||
|
||||
# Check if cursor in code window
|
||||
|
|
@ -555,7 +555,11 @@ class DebugSession( object ):
|
|||
return ''
|
||||
|
||||
# Return variable aware function
|
||||
return self._variablesView.ShowBalloon( frame, expression )
|
||||
return self._variablesView.VariableEval( frame, expression, is_hover )
|
||||
|
||||
|
||||
def CleanUpTooltip( self ):
|
||||
return self._variablesView.CleanUpTooltip()
|
||||
|
||||
@IfConnected()
|
||||
def ExpandFrameOrThread( self ):
|
||||
|
|
@ -679,6 +683,7 @@ class DebugSession( object ):
|
|||
'variables': utils.WindowID( vars_window, self._uiTab ),
|
||||
'watches': utils.WindowID( watch_window, self._uiTab ),
|
||||
'output': utils.WindowID( output_window, self._uiTab ),
|
||||
'eval': None # this is going to be updated every time eval popup is opened
|
||||
}
|
||||
with utils.RestoreCursorPosition():
|
||||
with utils.RestoreCurrentWindow():
|
||||
|
|
|
|||
|
|
@ -634,15 +634,19 @@ def ParseVariables( variables_list,
|
|||
return new_variables
|
||||
|
||||
|
||||
def DisplayBaloon( is_term, display ):
|
||||
def DisplayBalloon( is_term, display, is_hover = False ):
|
||||
if not is_term:
|
||||
display = '\n'.join( display )
|
||||
# To enable the Windows GUI to display the balloon correctly
|
||||
# Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685
|
||||
vim.eval( "balloon_show( '' )" )
|
||||
display = '\n'.join( display )
|
||||
|
||||
vim.eval( "balloon_show( {0} )".format(
|
||||
json.dumps( display ) ) )
|
||||
created_win_id = int( vim.eval(
|
||||
"vimspector#internal#balloon#CreateTooltip({}, {})".format(
|
||||
int( is_hover ), json.dumps( display )
|
||||
)
|
||||
) )
|
||||
|
||||
return created_win_id
|
||||
|
||||
|
||||
def GetBufferFilepath( buf ):
|
||||
|
|
@ -719,6 +723,28 @@ def GetBufferFiletypes( buf ):
|
|||
return ft.split( '.' )
|
||||
|
||||
|
||||
def GetVisualSelection( bufnr ):
|
||||
start_line, start_col = vim.current.buffer.mark( "<" )
|
||||
end_line, end_col = vim.current.buffer.mark( ">" )
|
||||
|
||||
|
||||
# lines are 1 based, but columns are 0 based
|
||||
# don't ask me why...
|
||||
start_line -= 1
|
||||
end_line -= 1
|
||||
|
||||
lines = vim.buffers[ bufnr ][ start_line : end_line + 1 ]
|
||||
# Do end first, in case it's on the same line as start (as doing start first
|
||||
# would change the offset)
|
||||
lines[ -1 ] = lines[ -1 ][ : end_col + 1 ]
|
||||
lines[ 0 ] = lines[ 0 ][ start_col : ]
|
||||
|
||||
_logger.debug( f'Visual selection: { lines } from '
|
||||
f'{ start_line }/{ start_col } -> { end_line }/{ end_col }' )
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
def DisplaySplash( api_prefix, splash, text ):
|
||||
if splash:
|
||||
return Call( f'vimspector#internal#{api_prefix}popup#UpdateSplash',
|
||||
|
|
|
|||
|
|
@ -117,17 +117,36 @@ class Watch:
|
|||
self.expression = expression
|
||||
self.result = None
|
||||
|
||||
@staticmethod
|
||||
def New( frame, expression, context ):
|
||||
watch = {
|
||||
'expression': expression,
|
||||
'context': context,
|
||||
}
|
||||
if frame:
|
||||
watch[ 'frameId' ] = frame[ 'id' ]
|
||||
|
||||
return Watch( watch )
|
||||
|
||||
|
||||
class View:
|
||||
lines: typing.Dict[ int, Expandable ]
|
||||
draw: typing.Callable
|
||||
syntax: str
|
||||
|
||||
def __init__( self, win, lines, draw ):
|
||||
self.lines = lines
|
||||
self.draw = draw
|
||||
self.buf = win.buffer
|
||||
self.syntax = None
|
||||
if win is not None:
|
||||
self.buf = win.buffer
|
||||
utils.SetUpUIWindow( win )
|
||||
|
||||
utils.SetUpUIWindow( win )
|
||||
|
||||
class BufView( View ):
|
||||
def __init__( self, buf, lines, draw ):
|
||||
super().__init__( None, lines, draw )
|
||||
self.buf = buf
|
||||
|
||||
|
||||
class VariablesView( object ):
|
||||
|
|
@ -138,6 +157,9 @@ class VariablesView( object ):
|
|||
self._connection = None
|
||||
self._current_syntax = ''
|
||||
|
||||
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>' )
|
||||
|
|
@ -183,7 +205,9 @@ class VariablesView( object ):
|
|||
'balloonexpr': vim.options[ 'balloonexpr' ],
|
||||
'balloondelay': vim.options[ 'balloondelay' ],
|
||||
}
|
||||
vim.options[ 'balloonexpr' ] = 'vimspector#internal#balloon#BalloonExpr()'
|
||||
vim.options[ 'balloonexpr' ] = ( "vimspector#internal#"
|
||||
"balloon#HoverTooltip()" )
|
||||
|
||||
vim.options[ 'balloondelay' ] = 250
|
||||
|
||||
if has_balloon:
|
||||
|
|
@ -201,6 +225,7 @@ class VariablesView( object ):
|
|||
utils.ClearBuffer( self._vars.buf )
|
||||
with utils.ModifiableScratchBuffer( self._watch.buf ):
|
||||
utils.ClearBuffer( self._watch.buf )
|
||||
self.ClearTooltip()
|
||||
self._current_syntax = ''
|
||||
|
||||
def ConnectionUp( self, connection ):
|
||||
|
|
@ -216,6 +241,8 @@ class VariablesView( object ):
|
|||
|
||||
utils.CleanUpHiddenBuffer( self._vars.buf )
|
||||
utils.CleanUpHiddenBuffer( self._watch.buf )
|
||||
self.ClearTooltip()
|
||||
|
||||
|
||||
def LoadScopes( self, frame ):
|
||||
def scopes_consumer( message ):
|
||||
|
|
@ -267,15 +294,98 @@ class VariablesView( object ):
|
|||
},
|
||||
} )
|
||||
|
||||
def AddWatch( self, frame, expression ):
|
||||
watch = {
|
||||
'expression': expression,
|
||||
'context': 'watch',
|
||||
}
|
||||
if frame:
|
||||
watch[ 'frameId' ] = frame[ 'id' ]
|
||||
def _DrawBalloonEval( self ):
|
||||
watch = self._variable_eval
|
||||
view = self._variable_eval_view
|
||||
|
||||
self._watches.append( Watch( watch ) )
|
||||
with utils.RestoreCursorPosition():
|
||||
with utils.ModifiableScratchBuffer( view.buf ):
|
||||
utils.ClearBuffer( view.buf )
|
||||
# FIXME: This probably doesn't work reliably
|
||||
view.syntax = utils.SetSyntax( None,
|
||||
self._current_syntax,
|
||||
view.buf )
|
||||
|
||||
self._DrawWatchResult( view,
|
||||
0,
|
||||
watch,
|
||||
is_short = True )
|
||||
|
||||
vim.eval( "vimspector#internal#balloon#ResizeTooltip()" )
|
||||
|
||||
def ClearTooltip( self ):
|
||||
# This will actually end up calling CleanUpTooltip via the popup close
|
||||
# callback
|
||||
vim.eval( 'vimspector#internal#balloon#Close()' )
|
||||
|
||||
def CleanUpTooltip( self ) :
|
||||
# remove reference to old tooltip window
|
||||
self._variable_eval_view = None
|
||||
vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = None
|
||||
|
||||
def VariableEval( self, frame, expression, is_hover ):
|
||||
"""Callback to display variable under cursor `:h ballonexpr`"""
|
||||
if not self._connection:
|
||||
return ''
|
||||
|
||||
def handler( message ):
|
||||
|
||||
watch = self._variable_eval
|
||||
if watch.result is None:
|
||||
watch.result = WatchResult( message[ 'body' ] )
|
||||
else:
|
||||
watch.result.Update( message[ 'body' ] )
|
||||
|
||||
popup_win_id = utils.DisplayBalloon( self._is_term, [], is_hover )
|
||||
# record the global eval window id
|
||||
vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( popup_win_id )
|
||||
popup_bufnr = int( vim.eval( "winbufnr({})".format( popup_win_id ) ) )
|
||||
|
||||
# We don't need to do any UI window setup here, as it's already done as
|
||||
# part of the popup creation, so just pass the buffer to the View instance
|
||||
self._variable_eval_view = BufView(
|
||||
vim.buffers[ popup_bufnr ],
|
||||
{},
|
||||
self._DrawBalloonEval
|
||||
)
|
||||
|
||||
if watch.result.IsExpandable():
|
||||
# Always expand the first level
|
||||
watch.result.expanded = Expandable.EXPANDED_BY_US
|
||||
|
||||
if watch.result.IsExpanded():
|
||||
self._connection.DoRequest( partial( self._ConsumeVariables,
|
||||
self._variable_eval_view.draw,
|
||||
watch.result ), {
|
||||
'command': 'variables',
|
||||
'arguments': {
|
||||
'variablesReference': watch.result.VariablesReference(),
|
||||
},
|
||||
} )
|
||||
|
||||
self._DrawBalloonEval()
|
||||
|
||||
def failure_handler( reason, message ):
|
||||
display = [ reason ]
|
||||
float_win_id = utils.DisplayBalloon( self._is_term, display, is_hover )
|
||||
# record the global eval window id
|
||||
vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( float_win_id )
|
||||
|
||||
self._variable_eval = Watch.New( frame,
|
||||
expression,
|
||||
'hover' )
|
||||
|
||||
# Send async request
|
||||
self._connection.DoRequest( handler, {
|
||||
'command': 'evaluate',
|
||||
'arguments': self._variable_eval.expression,
|
||||
}, failure_handler )
|
||||
|
||||
# Return working (meanwhile)
|
||||
return ''
|
||||
|
||||
def AddWatch( self, frame, expression ):
|
||||
self._watches.append( Watch.New( frame, expression, 'watch' ) )
|
||||
self.EvaluateWatches()
|
||||
|
||||
def DeleteWatch( self ):
|
||||
|
|
@ -338,19 +448,27 @@ class VariablesView( object ):
|
|||
watch.result = WatchFailure( reason )
|
||||
self._DrawWatches()
|
||||
|
||||
def ExpandVariable( self ):
|
||||
if vim.current.buffer == self._vars.buf:
|
||||
def ExpandVariable( self, buf = None, line_num = None ):
|
||||
if buf is None:
|
||||
buf = vim.current.buffer
|
||||
|
||||
if line_num is None:
|
||||
line_num = vim.current.window.cursor[ 0 ]
|
||||
|
||||
if buf == self._vars.buf:
|
||||
view = self._vars
|
||||
elif vim.current.buffer == self._watch.buf:
|
||||
elif buf == self._watch.buf:
|
||||
view = self._watch
|
||||
elif ( self._variable_eval_view is not None
|
||||
and buf == self._variable_eval_view.buf ):
|
||||
view = self._variable_eval_view
|
||||
else:
|
||||
return
|
||||
|
||||
current_line = vim.current.window.cursor[ 0 ]
|
||||
if current_line not in view.lines:
|
||||
if line_num not in view.lines:
|
||||
return
|
||||
|
||||
variable = view.lines[ current_line ]
|
||||
variable = view.lines[ line_num ]
|
||||
|
||||
if variable.IsExpanded():
|
||||
# Collapse
|
||||
|
|
@ -371,25 +489,40 @@ class VariablesView( object ):
|
|||
},
|
||||
} )
|
||||
|
||||
def _DrawVariables( self, view, variables, indent ):
|
||||
def _DrawVariables( self, view, variables, indent, is_short = False ):
|
||||
assert indent > 0
|
||||
for variable in variables:
|
||||
line = utils.AppendToBuffer(
|
||||
view.buf,
|
||||
'{indent}{marker}{icon} {name} ({type_}): {value}'.format(
|
||||
text = ''
|
||||
if is_short:
|
||||
text = '{indent}{icon} {name}: {value}'.format(
|
||||
# We borrow 1 space of indent to draw the change marker
|
||||
indent = ' ' * ( indent - 1 ),
|
||||
icon = '+' if ( variable.IsExpandable()
|
||||
and not variable.IsExpanded() ) else '-',
|
||||
name = variable.variable.get( 'name', '' ),
|
||||
value = variable.variable.get( 'value', '<unknown>' )
|
||||
)
|
||||
else:
|
||||
text = '{indent}{marker}{icon} {name} ({type_}): {value}'.format(
|
||||
# We borrow 1 space of indent to draw the change marker
|
||||
indent = ' ' * ( indent - 1 ),
|
||||
marker = '*' if variable.changed else ' ',
|
||||
icon = '+' if ( variable.IsExpandable()
|
||||
and not variable.IsExpanded() ) else '-',
|
||||
name = variable.variable[ 'name' ],
|
||||
name = variable.variable.get( 'name', '' ),
|
||||
type_ = variable.variable.get( 'type', '' ),
|
||||
value = variable.variable.get( 'value',
|
||||
'<unknown>' ) ).split( '\n' ) )
|
||||
value = variable.variable.get( 'value', '<unknown>' )
|
||||
)
|
||||
|
||||
line = utils.AppendToBuffer(
|
||||
view.buf,
|
||||
text.split( '\n' )
|
||||
)
|
||||
|
||||
view.lines[ line ] = variable
|
||||
|
||||
if variable.ShouldDrawDrillDown():
|
||||
self._DrawVariables( view, variable.variables, indent + 2 )
|
||||
self._DrawVariables( view, variable.variables, indent + 2, is_short )
|
||||
|
||||
def _DrawScopes( self ):
|
||||
# FIXME: The drawing is dumb and draws from scratch every time. This is
|
||||
|
|
@ -416,7 +549,7 @@ class VariablesView( object ):
|
|||
'Expression: '
|
||||
+ watch.expression[ 'expression' ] )
|
||||
watch.line = line
|
||||
self._DrawWatchResult( 2, watch )
|
||||
self._DrawWatchResult( self._watch, 2, watch )
|
||||
|
||||
def _DrawScope( self, indent, scope ):
|
||||
icon = '+' if scope.IsExpandable() and not scope.IsExpanded() else '-'
|
||||
|
|
@ -432,27 +565,36 @@ class VariablesView( object ):
|
|||
indent += 2
|
||||
self._DrawVariables( self._vars, scope.variables, indent )
|
||||
|
||||
def _DrawWatchResult( self, indent, watch ):
|
||||
def _DrawWatchResult( self, view, indent, watch, is_short = False ):
|
||||
if not watch.result:
|
||||
return
|
||||
|
||||
assert indent > 0
|
||||
icon = '+' if ( watch.result.IsExpandable() and
|
||||
not watch.result.IsExpanded() ) else '-'
|
||||
assert is_short or indent > 0
|
||||
|
||||
line = '{indent}{marker}{icon} Result: {result}'.format(
|
||||
if is_short:
|
||||
# The first result is always expanded in a hover (short format)
|
||||
icon = ''
|
||||
marker = ''
|
||||
leader = ''
|
||||
else:
|
||||
icon = '+' if ( watch.result.IsExpandable() and
|
||||
not watch.result.IsExpanded() ) else '-'
|
||||
marker = '*' if watch.result.changed else ' '
|
||||
leader = ' Result: '
|
||||
|
||||
line = '{indent}{marker}{icon}{leader}{result}'.format(
|
||||
# We borrow 1 space of indent to draw the change marker
|
||||
indent = ' ' * ( indent - 1 ),
|
||||
marker = '*' if watch.result.changed else ' ',
|
||||
marker = marker,
|
||||
icon = icon,
|
||||
leader = leader,
|
||||
result = watch.result.result.get( 'result', '<unknown>' ) )
|
||||
|
||||
line = utils.AppendToBuffer( self._watch.buf, line.split( '\n' ) )
|
||||
self._watch.lines[ line ] = watch.result
|
||||
line = utils.AppendToBuffer( view.buf, line.split( '\n' ) )
|
||||
view.lines[ line ] = watch.result
|
||||
|
||||
if watch.result.ShouldDrawDrillDown():
|
||||
indent = 4
|
||||
self._DrawVariables( self._watch, watch.result.variables, indent )
|
||||
self._DrawVariables( view, watch.result.variables, indent + 2, is_short )
|
||||
|
||||
def _ConsumeVariables( self, draw, parent, message ):
|
||||
new_variables = []
|
||||
|
|
@ -467,7 +609,6 @@ class VariablesView( object ):
|
|||
variable = v
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
variable = Variable( variable_body )
|
||||
else:
|
||||
|
|
@ -489,47 +630,10 @@ class VariablesView( object ):
|
|||
|
||||
draw()
|
||||
|
||||
def ShowBalloon( self, frame, expression ):
|
||||
"""Callback to display variable under cursor `:h ballonexpr`"""
|
||||
if not self._connection:
|
||||
return ''
|
||||
|
||||
def handler( message ):
|
||||
# TODO: this result count be expandable, but we have no way to allow the
|
||||
# user to interact with the balloon to expand it, unless we use a popup
|
||||
# instead, but even then we don't really want to trap the cursor.
|
||||
body = message[ 'body' ]
|
||||
result = body[ 'result' ]
|
||||
if result is None:
|
||||
result = 'null'
|
||||
display = [
|
||||
'Type: ' + body.get( 'type', '<unknown>' ),
|
||||
'Value: ' + result
|
||||
]
|
||||
utils.DisplayBaloon( self._is_term, display )
|
||||
|
||||
def failure_handler( reason, message ):
|
||||
display = [ reason ]
|
||||
utils.DisplayBaloon( self._is_term, display )
|
||||
|
||||
# Send async request
|
||||
self._connection.DoRequest( handler, {
|
||||
'command': 'evaluate',
|
||||
'arguments': {
|
||||
'expression': expression,
|
||||
'frameId': frame[ 'id' ],
|
||||
'context': 'hover',
|
||||
}
|
||||
}, failure_handler )
|
||||
|
||||
# Return working (meanwhile)
|
||||
return '...'
|
||||
|
||||
|
||||
def SetSyntax( self, syntax ):
|
||||
# TODO: Switch to View.syntax
|
||||
self._current_syntax = utils.SetSyntax( self._current_syntax,
|
||||
syntax,
|
||||
self._vars.buf,
|
||||
self._watch.buf )
|
||||
|
||||
# vim: sw=2
|
||||
|
|
|
|||
|
|
@ -280,8 +280,8 @@ function! Test_Conditional_Line_Breakpoint()
|
|||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 )
|
||||
call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 16 )
|
||||
|
||||
" Add the conditional breakpoint
|
||||
call feedkeys( "\\\<F9>argc==0\<CR>\<CR>", 'xt' )
|
||||
" Add the conditional breakpoint (note , is the mapleader)
|
||||
call feedkeys( ",\<F9>argc==0\<CR>\<CR>", 'xt' )
|
||||
call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP',
|
||||
\ 16,
|
||||
\ 'vimspectorBPCond',
|
||||
|
|
@ -360,8 +360,8 @@ function! Test_Conditional_Line_Breakpoint_Hit()
|
|||
exe 'edit' fn
|
||||
call setpos( '.', [ 0, 14, 1 ] )
|
||||
|
||||
" Add the conditional breakpoint (3 times)
|
||||
call feedkeys( "\\\<F9>\<CR>3\<CR>", 'xt' )
|
||||
" Add the conditional breakpoint (3 times) (note , is the mapleader)
|
||||
call feedkeys( ",\<F9>\<CR>3\<CR>", 'xt' )
|
||||
call vimspector#test#signs#AssertSignGroupSingletonAtLine(
|
||||
\ 'VimspectorBP',
|
||||
\ 14,
|
||||
|
|
|
|||
|
|
@ -293,8 +293,8 @@ function! Test_Conditional_Line_Breakpoint()
|
|||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 )
|
||||
call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 16 )
|
||||
|
||||
" Add the conditional breakpoint
|
||||
call feedkeys( "\\\<F9>argc==0\<CR>\<CR>", 'xt' )
|
||||
" Add the conditional breakpoint (, is mapleader)
|
||||
call feedkeys( ",\<F9>argc==0\<CR>\<CR>", 'xt' )
|
||||
call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP',
|
||||
\ 16,
|
||||
\ 'vimspectorBPCond',
|
||||
|
|
@ -370,8 +370,8 @@ function! Test_Conditional_Line_Breakpoint_Hit()
|
|||
exe 'edit' fn
|
||||
call setpos( '.', [ 0, 14, 1 ] )
|
||||
|
||||
" Add the conditional breakpoint (3 times)
|
||||
call feedkeys( "\\\<F9>\<CR>3\<CR>", 'xt' )
|
||||
" Add the conditional breakpoint (3 times) (, is mapleader)
|
||||
call feedkeys( ",\<F9>\<CR>3\<CR>", 'xt' )
|
||||
call vimspector#test#signs#AssertSignGroupSingletonAtLine(
|
||||
\ 'VimspectorBP',
|
||||
\ 14,
|
||||
|
|
|
|||
|
|
@ -106,9 +106,9 @@ function! Test_Use_Mappings_HUMAN()
|
|||
\ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 16 )
|
||||
\ } )
|
||||
|
||||
" Run to cursor
|
||||
" Run to cursor (note , is the mapleader)
|
||||
call cursor( 9, 1 )
|
||||
call feedkeys( "\\\<F8>", 'xt' )
|
||||
call feedkeys( ",\<F8>", 'xt' )
|
||||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 9, 1 )
|
||||
call WaitForAssert( {->
|
||||
\ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 9 )
|
||||
|
|
|
|||
|
|
@ -591,3 +591,199 @@ function! Test_EvaluateFailure()
|
|||
call vimspector#test#setup#Reset()
|
||||
%bwipe!
|
||||
endfunction
|
||||
|
||||
function! Test_VariableEval()
|
||||
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,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
"Close
|
||||
call feedkeys( "\<Esc>", 'xt' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ assert_equal( v:none, g:vimspector_session_windows.eval )
|
||||
\ } )
|
||||
|
||||
" test selection
|
||||
call setpos( '.', [ 0, 24, 8 ] )
|
||||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 )
|
||||
|
||||
call feedkeys( 'viw,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,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
"Close
|
||||
call feedkeys( "\<Esc>", 'xt' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ assert_equal( v:none, g:vimspector_session_windows.eval )
|
||||
\ } )
|
||||
|
||||
" Get back to normal mode
|
||||
call feedkeys( "\<Esc>", 'xt' )
|
||||
|
||||
" Evaluation error
|
||||
call setpos( '.', [ 0, 25, 1 ] )
|
||||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 25, 1 )
|
||||
call feedkeys( ',d', 'xt' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ assert_notequal( v:none, g:vimspector_session_windows.eval )
|
||||
\ } )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ 'Evaluation error',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.eval ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
"Close
|
||||
call feedkeys( "\<Esc>", 'xt' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ assert_equal( v:none, g:vimspector_session_windows.eval )
|
||||
\ } )
|
||||
|
||||
call vimspector#test#setup#Reset()
|
||||
%bwipe!
|
||||
endfunction
|
||||
|
||||
function! Test_VariableEvalExpand()
|
||||
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,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
" Expand
|
||||
call feedkeys( "jjjj\<CR>", 'xt' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ '{...}',
|
||||
\ ' - i: 0',
|
||||
\ ' - c: 0 ''\\0\{1,3}''',
|
||||
\ ' - fffff: 0',
|
||||
\ ' - another_test: ',
|
||||
\ ' - choo: 0 ''\\0\{1,3}''',
|
||||
\ ' + ints: '
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.eval ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
"Collapse
|
||||
call feedkeys( "\<CR>", 'xt' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ AssertMatchist(
|
||||
\ [
|
||||
\ '{...}',
|
||||
\ ' - i: 0',
|
||||
\ ' - c: 0 ''\\0\{1,3}''',
|
||||
\ ' - fffff: 0',
|
||||
\ ' + another_test: ',
|
||||
\ ],
|
||||
\ getbufline( winbufnr( g:vimspector_session_windows.eval ),
|
||||
\ 1,
|
||||
\ '$' )
|
||||
\ )
|
||||
\ } )
|
||||
|
||||
"Close
|
||||
call feedkeys( "\<Esc>", 'xt' )
|
||||
|
||||
call WaitForAssert( {->
|
||||
\ assert_equal( v:none, g:vimspector_session_windows.eval )
|
||||
\ } )
|
||||
|
||||
call vimspector#test#setup#Reset()
|
||||
%bwipe!
|
||||
endfunction
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
let g:vimspector_test_plugin_path = expand( '<sfile>:p:h:h' )
|
||||
set mouse=a
|
||||
set noequalalways
|
||||
let mapleader = ','
|
||||
let maplocalleader = "\<Space>"
|
||||
|
||||
let &rtp = &rtp . ',' . g:vimspector_test_plugin_path
|
||||
let &runtimepath = &runtimepath . ',' . g:vimspector_test_plugin_path
|
||||
|
||||
filetype plugin indent on
|
||||
syntax enable
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue