From 7c4eef909626e6cc1e9fdf9baba201e42210e7d0 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 29 Nov 2020 00:53:11 -0500 Subject: [PATCH 01/61] finally got float window working with variable evaluation --- autoload/vimspector/internal/balloon.vim | 12 ++++++++++++ autoload/vimspector/internal/state.vim | 22 ++++++++++++++++++++++ python3/vimspector/utils.py | 4 ++++ 3 files changed, 38 insertions(+) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 360ed08..bdceec4 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -29,6 +29,18 @@ function! vimspector#internal#balloon#BalloonExpr() abort \ . 'vim.eval( "v:beval_text" ) )' ) endfunction +" Returns: py.ShowBalloon( winnr, expresssion ) +function! vimspector#internal#balloon#Tooltip() 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( "expand(\"\")" ) )' ) +endfunction + + + " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index f1e690a..d9d8cd2 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -47,6 +47,28 @@ function! vimspector#internal#state#GetAPIPrefix() abort return s:prefix endfunction +" REMOVEME: this is just a temporary thing to get float window working +" Returns: py.ShowBalloon( winnr, expresssion ) +function! vimspector#internal#state#Tooltip() 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( "winnr()" ) ) ,' + \ . 'vim.eval( "expand(\"\")" ) )' ) +endfunction + +function! vimspector#internal#state#TooltipExec(body) abort + let buf = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(buf, 0, -1, v:true, a:body) + let opts = { 'relative': 'cursor', 'width': 40, 'height': 2, 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } + let g:float_win = nvim_open_win(buf, 0, opts) + + augroup vimspector#internal#balloon#nvim_float + autocmd! + autocmd CursorMoved * :call nvim_win_close(g:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float + augroup END +endfunction " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index e621d33..576c493 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -635,6 +635,10 @@ def ParseVariables( variables_list, def DisplayBaloon( is_term, display ): + if int(vim.eval("has('nvim')")): + vim.eval("vimspector#internal#state#TooltipExec({})".format(display)) + return + if not is_term: display = '\n'.join( display ) # To enable the Windows GUI to display the balloon correctly From 4c0ba92ac61db2ea422270d9b997d0099116825d Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 8 Dec 2020 21:03:40 -0500 Subject: [PATCH 02/61] working on variables view --- autoload/vimspector/internal/state.vim | 20 +++++- python3/vimspector/variables.py | 91 ++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index d9d8cd2..ade14bb 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -58,10 +58,28 @@ function! vimspector#internal#state#Tooltip() abort \ . 'vim.eval( "expand(\"\")" ) )' ) endfunction +function! vimspector#internal#state#CreateTooltip() abort + +endfunction + function! vimspector#internal#state#TooltipExec(body) abort let buf = nvim_create_buf(v:false, v:true) call nvim_buf_set_lines(buf, 0, -1, v:true, a:body) - let opts = { 'relative': 'cursor', 'width': 40, 'height': 2, 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } + + " get the max width on a line + let width = 0 + let maxWidth = winwidth() + + for w in a:body + let width = max(len(w), width) + " reached the max size, no point in looping more + if width > maxWidth + let width = maxWidth + break + endif + endfor + + let opts = { 'relative': 'cursor', 'width': width, 'height': len(a:body), 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } let g:float_win = nvim_open_win(buf, 0, opts) augroup vimspector#internal#balloon#nvim_float diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 4028722..a67c06e 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -371,6 +371,10 @@ class VariablesView( object ): }, } ) + + + + def _DrawVariables( self, view, variables, indent ): assert indent > 0 for variable in variables: @@ -531,5 +535,92 @@ class VariablesView( object ): syntax, self._vars.buf, self._watch.buf ) +class VariableEval + def __init__(self, stackTrace): + self.stackTrace = stackTrace + self._connection = None + self.variable = Variable() + def _ConsumeVariables(self, parent, message): + new_variables = [] + for variable_body in message[ 'body' ][ 'variables' ]: + if parent.variables is None: + parent.variables = [] + + # Find the variable in parent + found = False + for index, v in enumerate( parent.variables ): + if v.variable[ 'name' ] == variable_body[ 'name' ]: + variable = v + found = True + break + + if not found: + variable = Variable( variable_body ) + else: + variable.Update( variable_body ) + + new_variables.append( variable ) + + if variable.IsExpandable() and variable.IsExpanded(): + self._connection.DoRequest( partial( self._ConsumeVariables, + draw, + variable ), { + 'command': 'variables', + 'arguments': { + 'variablesReference': variable.VariablesReference() + }, + } ) + + parent.variables = new_variables + + def _DrawVariables( self, variables, indent ): + assert indent > 0 + for variable in variables: + line = utils.AppendToBuffer( + view.buf, + '{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' ], + type_ = variable.variable.get( 'type', '' ), + value = variable.variable.get( 'value', + '' ) ).split( '\n' ) ) + view.lines[ line ] = variable + + if variable.ShouldDrawDrillDown(): + self._DrawVariables( view, variable.variables, indent + 2 ) + + def AddVariableEval( self, bufid ): + current_symbol = vim.eval("expand('')") + + 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. + _ConsumeVariables(self, self.variable, message) + variableEvalView = View( variables_win, {}, self._DrawScopes ) + _DrawVariables(self, self.variable, 0) + + + def failure_handler(reason, message): + vim.eval("echom {}".format(message)) + + self._connection.DoRequest( handler, { + 'command': 'evaluate', + 'arguments': { + 'expression': expression, + 'frameId': frame[ 'id' ], + 'context': 'hover', + } + }, failure_handler ) + + def ConnectionUp( self, connection ): + self._connection = connection + + def ConnectionClosed(self): + self._connection = None # vim: sw=2 From 07858cc250291f18b5621a88615fa6061c65302b Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 8 Dec 2020 23:00:13 -0500 Subject: [PATCH 03/61] adding on the fly eval of inside a floating window --- autoload/vimspector/internal/state.vim | 12 +- python3/vimspector/debug_session.py | 20 +++ python3/vimspector/variables.py | 165 +++++++++++-------------- 3 files changed, 103 insertions(+), 94 deletions(-) diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index ade14bb..6d3aad4 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -53,7 +53,7 @@ function! vimspector#internal#state#Tooltip() 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(' + return py3eval('_vimspector_session.ShowTooltip(' \ . 'int( vim.eval( "winnr()" ) ) ,' \ . 'vim.eval( "expand(\"\")" ) )' ) endfunction @@ -62,16 +62,20 @@ function! vimspector#internal#state#CreateTooltip() abort endfunction +function! vimspector#internal#state#ShowTooltip() abort + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ) )') +endfunction + function! vimspector#internal#state#TooltipExec(body) abort let buf = nvim_create_buf(v:false, v:true) call nvim_buf_set_lines(buf, 0, -1, v:true, a:body) " get the max width on a line let width = 0 - let maxWidth = winwidth() + let maxWidth = winwidth(0) for w in a:body - let width = max(len(w), width) + let width = max([len(w), width]) " reached the max size, no point in looping more if width > maxWidth let width = maxWidth @@ -79,8 +83,10 @@ function! vimspector#internal#state#TooltipExec(body) abort endif endfor + let opts = { 'relative': 'cursor', 'width': width, 'height': len(a:body), 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } let g:float_win = nvim_open_win(buf, 0, opts) + call setwinvar(g:float_win, '&wrap', 0) augroup vimspector#internal#balloon#nvim_float autocmd! diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index ea72651..e741d88 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -538,6 +538,26 @@ class DebugSession( object ): def DeleteWatch( self ): self._variablesView.DeleteWatch() + + @IfConnected() + def ShowTooltip(self, winnr, expression): + """Proxy: ballonexpr -> variables.ShowBallon""" + frame = self._stackTraceView.GetCurrentFrame() + # Check if RIP is in a frame + if frame is None: + self._logger.debug( 'Balloon: Not in a stack frame' ) + return '' + + # Check if cursor in code window + if winnr != int( self._codeView._window.number ): + self._logger.debug( 'Winnr %s is not the code window %s', + winnr, + self._codeView._window.number ) + return '' + + # Return variable aware function + return self._variablesView.VariableEval(frame, expression) + @IfConnected() def ShowBalloon( self, winnr, expression ): """Proxy: ballonexpr -> variables.ShowBallon""" diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index a67c06e..ec18a71 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -125,9 +125,9 @@ class View: def __init__( self, win, lines, draw ): self.lines = lines self.draw = draw - self.buf = win.buffer - - utils.SetUpUIWindow( win ) + if (win is not None): + self.buf = win.buffer + utils.SetUpUIWindow( win ) class VariablesView( object ): @@ -138,6 +138,8 @@ class VariablesView( object ): self._connection = None self._current_syntax = '' + self._variable_eval = None + def AddExpandMappings(): vim.command( 'nnoremap ' ':call vimspector#ExpandVariable()' ) @@ -267,6 +269,75 @@ class VariablesView( object ): }, } ) + def _DrawEval(self): + # TODO: create vim functin that creates a floating window and returns window id + # use that window id to retrieve the buffer + # use that buffer in order to populate the results of the query + indent = 0 + icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' + + line = utils.AppendToBuffer( self._vars.buf, + '{0}{1} Scope: {2}'.format( + ' ' * indent, + icon, + self._variable_eval.scope[ 'name' ] ) ) + self._vars.lines[ line ] = self._variable_eval + + if self._variable_eval.ShouldDrawDrillDown(): + indent += 2 + # replace with a newly created view for the purposes of evaluation + self._DrawVariables( self._vars, self._variable_eval.variables, indent ) + + # vim.eval("vimspector#internal#state#TooltipExec({})".format([self._variable_eval])) + + + def VariableEval(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', '' ), + 'Value: ' + result + ] + + self._variable_eval = Scope(body) + + self._connection.DoRequest( partial( self._ConsumeVariables, + self._DrawEval, + self._variable_eval ), { + 'command': 'variables', + 'arguments': { + 'variablesReference': self._variable_eval.VariablesReference(), + }, + } ) + + + 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 AddWatch( self, frame, expression ): watch = { 'expression': expression, @@ -535,92 +606,4 @@ class VariablesView( object ): syntax, self._vars.buf, self._watch.buf ) -class VariableEval - def __init__(self, stackTrace): - self.stackTrace = stackTrace - self._connection = None - self.variable = Variable() - - def _ConsumeVariables(self, parent, message): - new_variables = [] - for variable_body in message[ 'body' ][ 'variables' ]: - if parent.variables is None: - parent.variables = [] - - # Find the variable in parent - found = False - for index, v in enumerate( parent.variables ): - if v.variable[ 'name' ] == variable_body[ 'name' ]: - variable = v - found = True - break - - if not found: - variable = Variable( variable_body ) - else: - variable.Update( variable_body ) - - new_variables.append( variable ) - - if variable.IsExpandable() and variable.IsExpanded(): - self._connection.DoRequest( partial( self._ConsumeVariables, - draw, - variable ), { - 'command': 'variables', - 'arguments': { - 'variablesReference': variable.VariablesReference() - }, - } ) - - parent.variables = new_variables - - def _DrawVariables( self, variables, indent ): - assert indent > 0 - for variable in variables: - line = utils.AppendToBuffer( - view.buf, - '{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' ], - type_ = variable.variable.get( 'type', '' ), - value = variable.variable.get( 'value', - '' ) ).split( '\n' ) ) - view.lines[ line ] = variable - - if variable.ShouldDrawDrillDown(): - self._DrawVariables( view, variable.variables, indent + 2 ) - - def AddVariableEval( self, bufid ): - current_symbol = vim.eval("expand('')") - - 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. - _ConsumeVariables(self, self.variable, message) - variableEvalView = View( variables_win, {}, self._DrawScopes ) - _DrawVariables(self, self.variable, 0) - - - def failure_handler(reason, message): - vim.eval("echom {}".format(message)) - - self._connection.DoRequest( handler, { - 'command': 'evaluate', - 'arguments': { - 'expression': expression, - 'frameId': frame[ 'id' ], - 'context': 'hover', - } - }, failure_handler ) - - def ConnectionUp( self, connection ): - self._connection = connection - - def ConnectionClosed(self): - self._connection = None # vim: sw=2 From 7432be95329c0fe1ed531c75acb672be4646e835 Mon Sep 17 00:00:00 2001 From: dsych Date: Wed, 9 Dec 2020 23:19:32 -0500 Subject: [PATCH 04/61] implemented dynamic float windows for nvim --- autoload/vimspector/internal/state.vim | 27 ++++++---- python3/vimspector/variables.py | 74 +++++++++++++------------- 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index 6d3aad4..b80a4b7 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -47,19 +47,24 @@ function! vimspector#internal#state#GetAPIPrefix() abort return s:prefix endfunction -" REMOVEME: this is just a temporary thing to get float window working -" Returns: py.ShowBalloon( winnr, expresssion ) -function! vimspector#internal#state#Tooltip() 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.ShowTooltip(' - \ . 'int( vim.eval( "winnr()" ) ) ,' - \ . 'vim.eval( "expand(\"\")" ) )' ) -endfunction - function! vimspector#internal#state#CreateTooltip() abort + let buf = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(buf, 0, -1, v:true, []) + " default the dimensions for now. they can be easily overwritten later + let opts = { 'relative': 'cursor', 'width': 50, 'height': 2, 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } + let g:float_win = nvim_open_win(buf, 0, opts) + call setwinvar(g:float_win, '&wrap', 0) + + call win_gotoid(g:float_win) + + " make sure we clean up the float after it loses focus + augroup vimspector#internal#balloon#nvim_float + autocmd! + autocmd WinLeave * :call nvim_win_close(g:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float + augroup END + + return g:float_win endfunction function! vimspector#internal#state#ShowTooltip() abort diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index ec18a71..7019168 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -139,6 +139,8 @@ class VariablesView( object ): self._current_syntax = '' self._variable_eval = None + self._variable_eval_view = None + self._variable_eval_win = None def AddExpandMappings(): vim.command( 'nnoremap ' @@ -270,25 +272,12 @@ class VariablesView( object ): } ) def _DrawEval(self): - # TODO: create vim functin that creates a floating window and returns window id - # use that window id to retrieve the buffer - # use that buffer in order to populate the results of the query - indent = 0 - icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' + self._variable_eval_win.height = min(5, len(self._variable_eval.variables)) + with utils.RestoreCursorPosition(): + utils.ClearBuffer( self._variable_eval_view.buf ) + icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' - line = utils.AppendToBuffer( self._vars.buf, - '{0}{1} Scope: {2}'.format( - ' ' * indent, - icon, - self._variable_eval.scope[ 'name' ] ) ) - self._vars.lines[ line ] = self._variable_eval - - if self._variable_eval.ShouldDrawDrillDown(): - indent += 2 - # replace with a newly created view for the purposes of evaluation - self._DrawVariables( self._vars, self._variable_eval.variables, indent ) - - # vim.eval("vimspector#internal#state#TooltipExec({})".format([self._variable_eval])) + self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 ) def VariableEval(self, frame, expression): @@ -297,28 +286,38 @@ class VariablesView( object ): 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', '' ), - 'Value: ' + result - ] self._variable_eval = Scope(body) - self._connection.DoRequest( partial( self._ConsumeVariables, - self._DrawEval, - self._variable_eval ), { - 'command': 'variables', - 'arguments': { - 'variablesReference': self._variable_eval.VariablesReference(), - }, - } ) + float_win_id = vim.eval("vimspector#internal#state#CreateTooltip()") + self._variable_eval_win = vim.current.window + + with utils.LetCurrentWindow( self._variable_eval_win ): + vim.command( 'nnoremap ' + ':call vimspector#ExpandVariable()' ) + vim.command( 'nnoremap ' + ':quit' ) + vim.command( 'nnoremap <2-LeftMouse> ' + ':call vimspector#ExpandVariable()' ) + + + if(self._variable_eval.VariablesReference() > 0): + self._variable_eval_view = View(self._variable_eval_win, {}, self._DrawEval) + self._connection.DoRequest( partial( self._ConsumeVariables, + self._DrawEval, + self._variable_eval ), { + 'command': 'variables', + 'arguments': { + 'variablesReference': self._variable_eval.VariablesReference(), + }, + } ) + else: + # in case that there is nothing to expand, we need to simulate a response from 'variables' request + # it returns [Variable] + self._variable_eval_view = View(self._variable_eval_win, {}, self._DrawEval) + self._variable_eval.variables = [Variable({'name': expression, 'value': body['result']})] + self._DrawEval() def failure_handler( reason, message ): @@ -414,6 +413,8 @@ class VariablesView( object ): view = self._vars elif vim.current.buffer == self._watch.buf: view = self._watch + elif vim.current.buffer == self._variable_eval_view.buf: + view = self._variable_eval_view else: return @@ -542,7 +543,6 @@ class VariablesView( object ): variable = v found = True break - if not found: variable = Variable( variable_body ) else: From 4e994968ffff31cdf30030f66b94a36b861468d7 Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 12 Dec 2020 17:48:50 -0500 Subject: [PATCH 05/61] added popup support for vim, still need to figure out how to expand variables --- autoload/vimspector/internal/state.vim | 97 +++++++++++++++++++++----- python3/vimspector/variables.py | 21 ++---- 2 files changed, 88 insertions(+), 30 deletions(-) diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index b80a4b7..bf2abde 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -47,24 +47,89 @@ function! vimspector#internal#state#GetAPIPrefix() abort return s:prefix endfunction +let s:float_win = 0 + function! vimspector#internal#state#CreateTooltip() abort - let buf = nvim_create_buf(v:false, v:true) - call nvim_buf_set_lines(buf, 0, -1, v:true, []) + if has('nvim') + let buf = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(buf, 0, -1, v:true, []) - " default the dimensions for now. they can be easily overwritten later - let opts = { 'relative': 'cursor', 'width': 50, 'height': 2, 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } - let g:float_win = nvim_open_win(buf, 0, opts) - call setwinvar(g:float_win, '&wrap', 0) + " default the dimensions for now. they can be easily overwritten later + let opts = { + \ 'relative': 'cursor', + \ 'width': 50, + \ 'height': 2, + \ 'col': 0, + \ 'row': 1, + \ 'anchor': 'NW', + \ 'style': 'minimal' + \ } + let s:float_win = nvim_open_win(buf, 0, opts) + call setwinvar(s:float_win, '&wrap', 1) + call setwinvar(s:float_win, '&cursorline', 1) - call win_gotoid(g:float_win) + call win_gotoid(s:float_win) - " make sure we clean up the float after it loses focus - augroup vimspector#internal#balloon#nvim_float - autocmd! - autocmd WinLeave * :call nvim_win_close(g:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float - augroup END + nnoremap :call vimspector#ExpandVariable() + nnoremap :quit + nnoremap <2-LeftMouse>:call vimspector#ExpandVariable() - return g:float_win + " make sure we clean up the float after it loses focus + augroup vimspector#internal#balloon#nvim_float + autocmd! + autocmd WinLeave * :call nvim_win_close(s:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float + augroup END + + else + " assume we are inside vim + func! MyFilter(winid, key) + if index(['j', 'k', 'h', 'l'], a:key) >= 0 + call win_execute(a:winid, ':normal '.a:key) + " do something + return 1 + elseif a:key == "\" + echo 'pressed left mouse' + let mouse_coords = getmousepos() + " close the popup if mouse is clicked outside the window + if mouse_coords['winid'] != a:winid + call popup_close(a:winid) + endif + + echo 'clicked line '.mouse_coords['line'] + call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") + return 1 + elseif a:key == "\" + echo 'pressed enter at line '.line(".", a:winid) + echo 'here' + call vimspector#ExpandVariable() + + return 1 + elseif a:key == "\" + call popup_close(a:winid) + let s:float_win = 0 + return 1 + endif + return 0 + endfunc + + if s:float_win != 0 + popup_close(s:float_win) + endif + + let s:float_win = popup_create([], #{ + \ filter: "MyFilter", + \ cursorline: 1, + \ wrap: 1, + \ filtermode: "n", + \ maxwidth: 50, + \ maxheight: 5, + \ scrollbar: 1, + \ moved: "any", + \ }) + + endif + + return s:float_win endfunction function! vimspector#internal#state#ShowTooltip() abort @@ -90,12 +155,12 @@ function! vimspector#internal#state#TooltipExec(body) abort let opts = { 'relative': 'cursor', 'width': width, 'height': len(a:body), 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } - let g:float_win = nvim_open_win(buf, 0, opts) - call setwinvar(g:float_win, '&wrap', 0) + let s:float_win = nvim_open_win(buf, 0, opts) + call setwinvar(s:float_win, '&wrap', 0) augroup vimspector#internal#balloon#nvim_float autocmd! - autocmd CursorMoved * :call nvim_win_close(g:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float + autocmd CursorMoved * :call nvim_win_close(s:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float augroup END endfunction " Boilerplate {{{ diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 7019168..f7f8f02 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -140,7 +140,6 @@ class VariablesView( object ): self._variable_eval = None self._variable_eval_view = None - self._variable_eval_win = None def AddExpandMappings(): vim.command( 'nnoremap ' @@ -272,7 +271,6 @@ class VariablesView( object ): } ) def _DrawEval(self): - self._variable_eval_win.height = min(5, len(self._variable_eval.variables)) with utils.RestoreCursorPosition(): utils.ClearBuffer( self._variable_eval_view.buf ) icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' @@ -291,19 +289,14 @@ class VariablesView( object ): self._variable_eval = Scope(body) float_win_id = vim.eval("vimspector#internal#state#CreateTooltip()") - self._variable_eval_win = vim.current.window - - with utils.LetCurrentWindow( self._variable_eval_win ): - vim.command( 'nnoremap ' - ':call vimspector#ExpandVariable()' ) - vim.command( 'nnoremap ' - ':quit' ) - vim.command( 'nnoremap <2-LeftMouse> ' - ':call vimspector#ExpandVariable()' ) + float_buf_nr = int(vim.eval("winbufnr({})".format(float_win_id))) + # since vim's popup cant be focused there is no way + # to get a reference to its window + # we will emulate python's window object overselves + self._variable_eval_view = View(type('__vim__window__', (object,), {'options': {}, 'buffer': vim.buffers[float_buf_nr]}), {}, self._DrawEval) if(self._variable_eval.VariablesReference() > 0): - self._variable_eval_view = View(self._variable_eval_win, {}, self._DrawEval) self._connection.DoRequest( partial( self._ConsumeVariables, self._DrawEval, self._variable_eval ), { @@ -315,7 +308,6 @@ class VariablesView( object ): else: # in case that there is nothing to expand, we need to simulate a response from 'variables' request # it returns [Variable] - self._variable_eval_view = View(self._variable_eval_win, {}, self._DrawEval) self._variable_eval.variables = [Variable({'name': expression, 'value': body['result']})] self._DrawEval() @@ -418,7 +410,8 @@ class VariablesView( object ): else: return - current_line = vim.current.window.cursor[ 0 ] + current_line = int(vim.eval("getbufinfo({})['lnum']".format(view.buf.name))) + print(current_line) if current_line not in view.lines: return From 819d6366aca4808e2b9fe8be562fa1adf6b64ae2 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 13 Dec 2020 20:52:16 -0500 Subject: [PATCH 06/61] fully implemented the hover function for vim --- autoload/vimspector.vim | 4 + autoload/vimspector/internal/balloon.vim | 93 ++++++++++++++++++ autoload/vimspector/internal/state.vim | 116 ----------------------- python3/vimspector/debug_session.py | 7 +- python3/vimspector/variables.py | 21 ++-- 5 files changed, 112 insertions(+), 129 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 35ecd03..fd4b0c5 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -523,6 +523,10 @@ function! vimspector#OnBufferCreated( file_name ) abort py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) ) endfunction +function! vimspector#ShowTooltip() abort + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ) )') +endfunction + " Boilerplate {{{ let &cpoptions=s:save_cpo diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index bdceec4..dc34261 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -40,6 +40,99 @@ function! vimspector#internal#balloon#Tooltip() abort endfunction +let s:float_win = 0 + +function! vimspector#internal#balloon#closeCallback() abort + let s:float_win = 0 + return py3eval('_vimspector_session._CleanUpTooltip()') +endfunction + +function! vimspector#internal#balloon#CreateTooltip() abort + if has('nvim') + let buf = nvim_create_buf(v:false, v:true) + " call nvim_buf_set_option(buf, 'modifiable', v:false) + call nvim_buf_set_lines(buf, 0, -1, v:true, []) + + " default the dimensions for now. they can be easily overwritten later + let opts = #{ + \ relative: 'cursor', + \ width: 50, + \ height: 5, + \ col: 0, + \ row: 1, + \ anchor: 'NW', + \ style: 'minimal' + \ } + let s:float_win = nvim_open_win(buf, 0, opts) + call nvim_win_set_option(s:float_win, 'wrap', v:true) + call nvim_win_set_option(s:float_win, 'cursorline', v:true) + call nvim_win_set_option(s:float_win, 'signcolumn', 'no') + call nvim_win_set_option(s:float_win, 'relativenumber', v:false) + call nvim_win_set_option(s:float_win, 'number', v:false) + + noa call win_gotoid(s:float_win) + + nnoremap :call vimspector#ExpandVariable() + nnoremap :quit + nnoremap <2-LeftMouse>:call vimspector#ExpandVariable() + + " make sure we clean up the float after it loses focus + augroup vimspector#internal#balloon#nvim_float + autocmd! + autocmd WinLeave * :call nvim_win_close(s:float_win, 1) | :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float + augroup END + + else + " assume we are inside vim + func! MyFilter(winid, key) + if index(['j', 'k'], a:key) >= 0 + call win_execute(a:winid, ':normal '.a:key) + " do something + return 1 + elseif a:key == "\" + let mouse_coords = getmousepos() + " close the popup if mouse is clicked outside the window + if mouse_coords['winid'] != a:winid + call popup_close(a:winid) + endif + + call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") + return 1 + elseif a:key == "\" + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") + + return 1 + elseif a:key == "\" + call popup_close(a:winid) + call vimspector#internal#balloon#closeCallback() + return 1 + endif + return 0 + endfunc + + if s:float_win != 0 + call popup_close(s:float_win) + call vimspector#internal#balloon#closeCallback() + endif + + let s:float_win = popup_create([], #{ + \ filter: "MyFilter", + \ cursorline: 1, + \ wrap: 1, + \ filtermode: "n", + \ maxwidth: 50, + \ maxheight: 5, + \ scrollbar: 1, + \ moved: "any", + \ }) + " call setbufvar(winbufnr(s:float_win), '&buflisted', 1) + + endif + + return s:float_win +endfunction " Boilerplate {{{ let &cpoptions=s:save_cpo diff --git a/autoload/vimspector/internal/state.vim b/autoload/vimspector/internal/state.vim index bf2abde..f1e690a 100644 --- a/autoload/vimspector/internal/state.vim +++ b/autoload/vimspector/internal/state.vim @@ -47,122 +47,6 @@ function! vimspector#internal#state#GetAPIPrefix() abort return s:prefix endfunction -let s:float_win = 0 - -function! vimspector#internal#state#CreateTooltip() abort - if has('nvim') - let buf = nvim_create_buf(v:false, v:true) - call nvim_buf_set_lines(buf, 0, -1, v:true, []) - - " default the dimensions for now. they can be easily overwritten later - let opts = { - \ 'relative': 'cursor', - \ 'width': 50, - \ 'height': 2, - \ 'col': 0, - \ 'row': 1, - \ 'anchor': 'NW', - \ 'style': 'minimal' - \ } - let s:float_win = nvim_open_win(buf, 0, opts) - call setwinvar(s:float_win, '&wrap', 1) - call setwinvar(s:float_win, '&cursorline', 1) - - call win_gotoid(s:float_win) - - nnoremap :call vimspector#ExpandVariable() - nnoremap :quit - nnoremap <2-LeftMouse>:call vimspector#ExpandVariable() - - " make sure we clean up the float after it loses focus - augroup vimspector#internal#balloon#nvim_float - autocmd! - autocmd WinLeave * :call nvim_win_close(s:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float - augroup END - - else - " assume we are inside vim - func! MyFilter(winid, key) - if index(['j', 'k', 'h', 'l'], a:key) >= 0 - call win_execute(a:winid, ':normal '.a:key) - " do something - return 1 - elseif a:key == "\" - echo 'pressed left mouse' - let mouse_coords = getmousepos() - " close the popup if mouse is clicked outside the window - if mouse_coords['winid'] != a:winid - call popup_close(a:winid) - endif - - echo 'clicked line '.mouse_coords['line'] - call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") - return 1 - elseif a:key == "\" - echo 'pressed enter at line '.line(".", a:winid) - echo 'here' - call vimspector#ExpandVariable() - - return 1 - elseif a:key == "\" - call popup_close(a:winid) - let s:float_win = 0 - return 1 - endif - return 0 - endfunc - - if s:float_win != 0 - popup_close(s:float_win) - endif - - let s:float_win = popup_create([], #{ - \ filter: "MyFilter", - \ cursorline: 1, - \ wrap: 1, - \ filtermode: "n", - \ maxwidth: 50, - \ maxheight: 5, - \ scrollbar: 1, - \ moved: "any", - \ }) - - endif - - return s:float_win -endfunction - -function! vimspector#internal#state#ShowTooltip() abort - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ) )') -endfunction - -function! vimspector#internal#state#TooltipExec(body) abort - let buf = nvim_create_buf(v:false, v:true) - call nvim_buf_set_lines(buf, 0, -1, v:true, a:body) - - " get the max width on a line - let width = 0 - let maxWidth = winwidth(0) - - for w in a:body - let width = max([len(w), width]) - " reached the max size, no point in looping more - if width > maxWidth - let width = maxWidth - break - endif - endfor - - - let opts = { 'relative': 'cursor', 'width': width, 'height': len(a:body), 'col': 0, 'row': 1, 'anchor': 'NW', 'style': 'minimal' } - let s:float_win = nvim_open_win(buf, 0, opts) - call setwinvar(s:float_win, '&wrap', 0) - - augroup vimspector#internal#balloon#nvim_float - autocmd! - autocmd CursorMoved * :call nvim_win_close(s:float_win, 1) | autocmd! vimspector#internal#balloon#nvim_float - augroup END -endfunction " Boilerplate {{{ let &cpoptions=s:save_cpo unlet s:save_cpo diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index e741d88..8b2196f 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -520,8 +520,8 @@ class DebugSession( object ): self._stackTraceView.SetCurrentThread() @IfConnected() - def ExpandVariable( self ): - self._variablesView.ExpandVariable() + def ExpandVariable( self, lineNum = -1 ): + self._variablesView.ExpandVariable(lineNum) @IfConnected() def AddWatch( self, expression ): @@ -558,6 +558,9 @@ class DebugSession( object ): # Return variable aware function return self._variablesView.VariableEval(frame, expression) + def _CleanUpTooltip(self): + return self._variablesView._CleanUpTooltip() + @IfConnected() def ShowBalloon( self, winnr, expression ): """Proxy: ballonexpr -> variables.ShowBallon""" diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index f7f8f02..2e9d2b2 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -122,7 +122,7 @@ class View: lines: typing.Dict[ int, Expandable ] draw: typing.Callable - def __init__( self, win, lines, draw ): + def __init__( self, win, lines, draw): self.lines = lines self.draw = draw if (win is not None): @@ -277,6 +277,10 @@ class VariablesView( object ): self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 ) + def _CleanUpTooltip(self): + # remove reference to old tooltip window + self._variable_eval_view = None + return '' def VariableEval(self, frame, expression): """Callback to display variable under cursor `:h ballonexpr`""" @@ -288,12 +292,12 @@ class VariablesView( object ): self._variable_eval = Scope(body) - float_win_id = vim.eval("vimspector#internal#state#CreateTooltip()") + float_win_id = vim.eval("vimspector#internal#balloon#CreateTooltip()") float_buf_nr = int(vim.eval("winbufnr({})".format(float_win_id))) # since vim's popup cant be focused there is no way # to get a reference to its window - # we will emulate python's window object overselves + # we will emulate python's window object ourselves self._variable_eval_view = View(type('__vim__window__', (object,), {'options': {}, 'buffer': vim.buffers[float_buf_nr]}), {}, self._DrawEval) if(self._variable_eval.VariablesReference() > 0): @@ -400,18 +404,17 @@ class VariablesView( object ): watch.result = WatchFailure( reason ) self._DrawWatches() - def ExpandVariable( self ): + def ExpandVariable( self, lineNum = -1 ): if vim.current.buffer == self._vars.buf: view = self._vars elif vim.current.buffer == self._watch.buf: view = self._watch - elif vim.current.buffer == self._variable_eval_view.buf: + elif self._variable_eval_view is not None: view = self._variable_eval_view else: return - current_line = int(vim.eval("getbufinfo({})['lnum']".format(view.buf.name))) - print(current_line) + current_line = vim.current.window.cursor[0] if lineNum <= 0 else lineNum if current_line not in view.lines: return @@ -436,10 +439,6 @@ class VariablesView( object ): }, } ) - - - - def _DrawVariables( self, view, variables, indent ): assert indent > 0 for variable in variables: From d2990d7baed2c31aaf79a777f5b337d6d0b16064 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 13 Dec 2020 22:25:27 -0500 Subject: [PATCH 07/61] making sure to open popup relative to cursor --- autoload/vimspector.vim | 2 +- autoload/vimspector/internal/balloon.vim | 31 ++++++++++++++---------- python3/vimspector/debug_session.py | 4 +-- python3/vimspector/utils.py | 12 +++------ python3/vimspector/variables.py | 11 ++++----- 5 files changed, 29 insertions(+), 31 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index fd4b0c5..d8018a0 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -524,7 +524,7 @@ function! vimspector#OnBufferCreated( file_name ) abort endfunction function! vimspector#ShowTooltip() abort - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ) )') + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ), 0)') endfunction diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index dc34261..89f2b11 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -30,13 +30,8 @@ function! vimspector#internal#balloon#BalloonExpr() abort endfunction " Returns: py.ShowBalloon( winnr, expresssion ) -function! vimspector#internal#balloon#Tooltip() 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( "expand(\"\")" ) )' ) +function! vimspector#internal#balloon#HoverTooltip() abort + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') endfunction @@ -47,11 +42,16 @@ function! vimspector#internal#balloon#closeCallback() abort return py3eval('_vimspector_session._CleanUpTooltip()') endfunction -function! vimspector#internal#balloon#CreateTooltip() abort +function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) + let body = [] + if a:0 > 0 + let body = a:1 + endif + if has('nvim') let buf = nvim_create_buf(v:false, v:true) " call nvim_buf_set_option(buf, 'modifiable', v:false) - call nvim_buf_set_lines(buf, 0, -1, v:true, []) + call nvim_buf_set_lines(buf, 0, -1, v:true, body) " default the dimensions for now. they can be easily overwritten later let opts = #{ @@ -117,7 +117,7 @@ function! vimspector#internal#balloon#CreateTooltip() abort call vimspector#internal#balloon#closeCallback() endif - let s:float_win = popup_create([], #{ + let config = #{ \ filter: "MyFilter", \ cursorline: 1, \ wrap: 1, @@ -125,9 +125,14 @@ function! vimspector#internal#balloon#CreateTooltip() abort \ maxwidth: 50, \ maxheight: 5, \ scrollbar: 1, - \ moved: "any", - \ }) - " call setbufvar(winbufnr(s:float_win), '&buflisted', 1) + \ } + if a:is_hover + let config['mousemoved'] = "word" + let s:float_win = popup_beval(body, config) + else + let config['moved'] = "any" + let s:float_win = popup_atcursor(body, config) + endif endif diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 8b2196f..de5a7c6 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -540,7 +540,7 @@ class DebugSession( object ): @IfConnected() - def ShowTooltip(self, winnr, expression): + def ShowTooltip(self, winnr, expression, is_hover): """Proxy: ballonexpr -> variables.ShowBallon""" frame = self._stackTraceView.GetCurrentFrame() # Check if RIP is in a frame @@ -556,7 +556,7 @@ class DebugSession( object ): return '' # Return variable aware function - return self._variablesView.VariableEval(frame, expression) + return self._variablesView.VariableEval(frame, expression, is_hover) def _CleanUpTooltip(self): return self._variablesView._CleanUpTooltip() diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 576c493..788f03e 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -634,19 +634,13 @@ def ParseVariables( variables_list, return new_variables -def DisplayBaloon( is_term, display ): - if int(vim.eval("has('nvim')")): - vim.eval("vimspector#internal#state#TooltipExec({})".format(display)) - return - +def DisplayBaloon( 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 ) ) ) + return int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format(is_hover, json.dumps( display )) ) ) def GetBufferFilepath( buf ): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 2e9d2b2..71abc45 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -186,7 +186,7 @@ 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: @@ -282,7 +282,7 @@ class VariablesView( object ): self._variable_eval_view = None return '' - def VariableEval(self, frame, expression): + def VariableEval(self, frame, expression, is_hover): """Callback to display variable under cursor `:h ballonexpr`""" if not self._connection: return '' @@ -291,8 +291,7 @@ class VariablesView( object ): body = message[ 'body' ] self._variable_eval = Scope(body) - - float_win_id = vim.eval("vimspector#internal#balloon#CreateTooltip()") + float_win_id = utils.DisplayBaloon(self._is_term, [], is_hover) float_buf_nr = int(vim.eval("winbufnr({})".format(float_win_id))) # since vim's popup cant be focused there is no way @@ -318,7 +317,7 @@ class VariablesView( object ): def failure_handler( reason, message ): display = [ reason ] - utils.DisplayBaloon( self._is_term, display ) + utils.DisplayBaloon( self._is_term, display, is_hover ) # Send async request self._connection.DoRequest( handler, { @@ -331,7 +330,7 @@ class VariablesView( object ): }, failure_handler ) # Return working (meanwhile) - return '...' + return '' def AddWatch( self, frame, expression ): watch = { From 747ad9b8042a9d0af3635628e1285381b1413fac Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 22 Dec 2020 13:03:37 -0500 Subject: [PATCH 08/61] removing literal dictionary syntax, as it is not included in nvim-4.4 --- autoload/vimspector/internal/balloon.vim | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 89f2b11..866eb65 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -54,14 +54,14 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) call nvim_buf_set_lines(buf, 0, -1, v:true, body) " default the dimensions for now. they can be easily overwritten later - let opts = #{ - \ relative: 'cursor', - \ width: 50, - \ height: 5, - \ col: 0, - \ row: 1, - \ anchor: 'NW', - \ style: 'minimal' + let opts = { + \ 'relative': 'cursor', + \ 'width': 50, + \ 'height': 5, + \ 'col': 0, + \ 'row': 1, + \ 'anchor': 'NW', + \ 'style': 'minimal' \ } let s:float_win = nvim_open_win(buf, 0, opts) call nvim_win_set_option(s:float_win, 'wrap', v:true) @@ -117,14 +117,14 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) call vimspector#internal#balloon#closeCallback() endif - let config = #{ - \ filter: "MyFilter", - \ cursorline: 1, - \ wrap: 1, - \ filtermode: "n", - \ maxwidth: 50, - \ maxheight: 5, - \ scrollbar: 1, + let config = { + \ 'filter': "MyFilter", + \ 'cursorline': 1, + \ 'wrap': 1, + \ 'filtermode': "n", + \ 'maxwidth': 50, + \ 'maxheight': 5, + \ 'scrollbar': 1, \ } if a:is_hover let config['mousemoved'] = "word" From 0ced5eb1d42269c03ef9aa3ad7ff8a58e68857d7 Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 22 Dec 2020 13:57:25 -0500 Subject: [PATCH 09/61] splitting mouse and cursor filtering to avoid trapping keyboard for hover --- autoload/vimspector/internal/balloon.vim | 34 ++++++++++++++++++------ 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 866eb65..007c8b0 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -84,19 +84,34 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) else " assume we are inside vim - func! MyFilter(winid, key) - if index(['j', 'k'], a:key) >= 0 - call win_execute(a:winid, ':normal '.a:key) - " do something - return 1 - elseif a:key == "\" + func! MouseFilter(winid, key) + if index(["\", "\<2-leftmouse>"], a:key) >= 0 let mouse_coords = getmousepos() " close the popup if mouse is clicked outside the window if mouse_coords['winid'] != a:winid call popup_close(a:winid) + call vimspector#internal#balloon#closeCallback() 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>" && mouse_coords['winid'] == a:winid + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") + endif + + return 1 + endif + return 0 + endfunc + + func! CursorFiler(winid, key) + if index(['j', 'k'], a:key) >= 0 + call win_execute(a:winid, ':normal '.a:key) + return 1 elseif a:key == "\" " forward line number to python, since vim does not allow us to focus @@ -107,8 +122,10 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) elseif a:key == "\" call popup_close(a:winid) call vimspector#internal#balloon#closeCallback() + return 1 endif + return 0 endfunc @@ -118,7 +135,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endif let config = { - \ 'filter': "MyFilter", \ 'cursorline': 1, \ 'wrap': 1, \ 'filtermode': "n", @@ -127,9 +143,11 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'scrollbar': 1, \ } if a:is_hover - let config['mousemoved'] = "word" + let config['filter'] = "MouseFilter" + let config['mousemoved'] = [0, 0, 0] let s:float_win = popup_beval(body, config) else + let config['filter'] = "CursorFiler" let config['moved'] = "any" let s:float_win = popup_atcursor(body, config) endif From 4617250f412017a38afe7b2f77ecfa962e17bada Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 22 Dec 2020 23:56:18 -0500 Subject: [PATCH 10/61] adding border to popup --- autoload/vimspector/internal/balloon.vim | 63 +++++++++++++++++++----- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 007c8b0..e2f041d 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -32,13 +32,23 @@ endfunction " Returns: py.ShowBalloon( winnr, expresssion ) function! vimspector#internal#balloon#HoverTooltip() abort return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') + endfunction let s:float_win = 0 +let s:nvim_related_win = 0 function! vimspector#internal#balloon#closeCallback() abort + if has('nvim') + call nvim_win_close(s:float_win, v:true) + call nvim_win_close(s:nvim_related_win, v:true) + else + call popup_close(s:float_win) + endif + let s:float_win = 0 + let s:nvim_related_win = 0 return py3eval('_vimspector_session._CleanUpTooltip()') endfunction @@ -48,22 +58,53 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let body = a:1 endif + " tooltip dimensions + let max_height = 5 + let max_width = 50 + if has('nvim') - let buf = nvim_create_buf(v:false, v:true) - " call nvim_buf_set_option(buf, 'modifiable', v:false) - call nvim_buf_set_lines(buf, 0, -1, v:true, body) + " 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 top = "╭" . repeat("─", max_width) . "╮" + let mid = "│" . repeat(" ", max_width) . "│" + let bot = "╰" . repeat("─", max_width) . "╯" + let lines = [top] + repeat([mid], max_height) + [bot] + + let buf_id = nvim_create_buf(v:false, v:true) + call nvim_buf_set_lines(buf_id, 0, -1, v:true, lines) " default the dimensions for now. they can be easily overwritten later let opts = { \ 'relative': 'cursor', - \ 'width': 50, - \ 'height': 5, + \ 'width': max_width + 2, + \ 'height': max_height + 2, \ 'col': 0, \ 'row': 1, \ 'anchor': 'NW', \ 'style': 'minimal' \ } - let s:float_win = nvim_open_win(buf, 0, opts) + " this is the border window + let s:nvim_related_win = nvim_open_win(buf_id, 0, opts) + call nvim_win_set_option(s:nvim_related_win, 'wrap', v:true) + call nvim_win_set_option(s:nvim_related_win, 'cursorline', v:true) + call nvim_win_set_option(s:nvim_related_win, 'signcolumn', 'no') + call nvim_win_set_option(s:nvim_related_win, 'relativenumber', v:false) + call nvim_win_set_option(s:nvim_related_win, 'number', v:false) + + " when calculating where to display the content window, we need to account + " for the border + set winhl=Normal:Floating + 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, body) + let s:float_win = nvim_open_win(buf_id, v:false, opts) + call nvim_win_set_option(s:float_win, 'wrap', v:true) call nvim_win_set_option(s:float_win, 'cursorline', v:true) call nvim_win_set_option(s:float_win, 'signcolumn', 'no') @@ -79,7 +120,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) " make sure we clean up the float after it loses focus augroup vimspector#internal#balloon#nvim_float autocmd! - autocmd WinLeave * :call nvim_win_close(s:float_win, 1) | :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float + autocmd WinLeave * :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float augroup END else @@ -89,7 +130,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let mouse_coords = getmousepos() " close the popup if mouse is clicked outside the window if mouse_coords['winid'] != a:winid - call popup_close(a:winid) call vimspector#internal#balloon#closeCallback() endif @@ -120,7 +160,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) return 1 elseif a:key == "\" - call popup_close(a:winid) call vimspector#internal#balloon#closeCallback() return 1 @@ -130,7 +169,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endfunc if s:float_win != 0 - call popup_close(s:float_win) call vimspector#internal#balloon#closeCallback() endif @@ -138,9 +176,10 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'cursorline': 1, \ 'wrap': 1, \ 'filtermode': "n", - \ 'maxwidth': 50, - \ 'maxheight': 5, + \ 'maxwidth': max_width, + \ 'maxheight': max_height, \ 'scrollbar': 1, + \ 'border': [] \ } if a:is_hover let config['filter'] = "MouseFilter" From b95ae00054accd645dba06affc026dad85c331d7 Mon Sep 17 00:00:00 2001 From: dsych Date: Wed, 23 Dec 2020 00:17:19 -0500 Subject: [PATCH 11/61] enabling a close button for popup --- autoload/vimspector/internal/balloon.vim | 1 + 1 file changed, 1 insertion(+) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index e2f041d..fd37e79 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -184,6 +184,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) if a:is_hover let config['filter'] = "MouseFilter" let config['mousemoved'] = [0, 0, 0] + let config['close'] = "button" let s:float_win = popup_beval(body, config) else let config['filter'] = "CursorFiler" From f60b259dbc499b25cb5c8549cd365e22e2e8f653 Mon Sep 17 00:00:00 2001 From: dsych Date: Wed, 23 Dec 2020 23:12:57 -0500 Subject: [PATCH 12/61] adding type information for simple types --- python3/vimspector/variables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 71abc45..f5930f8 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -311,7 +311,7 @@ class VariablesView( object ): else: # in case that there is nothing to expand, we need to simulate a response from 'variables' request # it returns [Variable] - self._variable_eval.variables = [Variable({'name': expression, 'value': body['result']})] + self._variable_eval.variables = [Variable({'name': expression, 'type': body['type'], 'value': body['result']})] self._DrawEval() From 04a5e889f95d0f09615d782b94661e615f578447 Mon Sep 17 00:00:00 2001 From: dsych Date: Wed, 23 Dec 2020 23:32:37 -0500 Subject: [PATCH 13/61] making sure that float window is not modifiable inside nvim --- autoload/vimspector/internal/balloon.vim | 1 + python3/vimspector/variables.py | 1 + 2 files changed, 2 insertions(+) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index fd37e79..467de90 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -103,6 +103,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) " 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, body) + call nvim_buf_set_option(buf_id, 'modifiable', v:false) let s:float_win = nvim_open_win(buf_id, v:false, opts) call nvim_win_set_option(s:float_win, 'wrap', v:true) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index f5930f8..a81991a 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -272,6 +272,7 @@ class VariablesView( object ): def _DrawEval(self): with utils.RestoreCursorPosition(): + with utils.ModifiableScratchBuffer( self._variable_eval_view.buf ): utils.ClearBuffer( self._variable_eval_view.buf ) icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' From b9ed7f34b46f30d451a174005b64fd151ac14764 Mon Sep 17 00:00:00 2001 From: dsych Date: Thu, 24 Dec 2020 00:08:02 -0500 Subject: [PATCH 14/61] removing redundant name for simple types, shortening variable type to 20 char and variable value to 100 chars to avoid overflowing tooltip window if the wrapped line is longer than the max size of the tooltip --- python3/vimspector/variables.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index a81991a..b30c473 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -276,7 +276,7 @@ class VariablesView( object ): utils.ClearBuffer( self._variable_eval_view.buf ) icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' - self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 ) + self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 , True) def _CleanUpTooltip(self): # remove reference to old tooltip window @@ -312,7 +312,7 @@ class VariablesView( object ): else: # in case that there is nothing to expand, we need to simulate a response from 'variables' request # it returns [Variable] - self._variable_eval.variables = [Variable({'name': expression, 'type': body['type'], 'value': body['result']})] + self._variable_eval.variables = [Variable({ 'type': body['type'], 'value': body['result']})] self._DrawEval() @@ -439,9 +439,18 @@ 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: + type_ = variable.variable.get( 'type', '' ) + value = variable.variable.get( 'value', '' ) + + if is_short: + if( len(type_) > 20): + type_ = type_[0:16] + " ..." + if( len(value) > 100 ): + value = value[0:96] + " ..." + line = utils.AppendToBuffer( view.buf, '{indent}{marker}{icon} {name} ({type_}): {value}'.format( @@ -450,14 +459,13 @@ class VariablesView( object ): marker = '*' if variable.changed else ' ', icon = '+' if ( variable.IsExpandable() and not variable.IsExpanded() ) else '-', - name = variable.variable[ 'name' ], - type_ = variable.variable.get( 'type', '' ), - value = variable.variable.get( 'value', - '' ) ).split( '\n' ) ) + name = variable.variable.get( 'name', '' ), + type_ = type_, + value = value ).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 From cb441be7bc1a4479cffb7a22d890e685cc6dcbe6 Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 2 Jan 2021 12:27:46 -0500 Subject: [PATCH 15/61] allowing resizing and dragging with a mouse --- autoload/vimspector/internal/balloon.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 467de90..48e1d05 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -175,7 +175,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let config = { \ 'cursorline': 1, - \ 'wrap': 1, + \ 'wrap': 0, \ 'filtermode': "n", \ 'maxwidth': max_width, \ 'maxheight': max_height, @@ -186,6 +186,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let config['filter'] = "MouseFilter" let config['mousemoved'] = [0, 0, 0] let config['close'] = "button" + let config['drag'] = 1 + let config['resize'] = 1 let s:float_win = popup_beval(body, config) else let config['filter'] = "CursorFiler" From 2f1c93a2ace0feffde737ddffa3dad3ef7df866a Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 2 Jan 2021 12:29:18 -0500 Subject: [PATCH 16/61] only diplaying relevant info like name and value for nested types and value only for simple types --- python3/vimspector/variables.py | 44 +++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index b30c473..4f6e4ff 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -274,9 +274,15 @@ class VariablesView( object ): with utils.RestoreCursorPosition(): with utils.ModifiableScratchBuffer( self._variable_eval_view.buf ): utils.ClearBuffer( self._variable_eval_view.buf ) - icon = '+' if self._variable_eval.IsExpandable() and not self._variable_eval.IsExpanded() else '-' - - self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 , True) + # if we only have a single non-expandable variable, it means we ran into a simple type + # hence, there is no need to draw additional fluff around the value + if(len(self._variable_eval.variables) == 1 and self._variable_eval.variables[0].IsExpandable() == False): + utils.AppendToBuffer( + self._variable_eval_view.buf, + self._variable_eval.variables[0].variable.get( 'value', '' ) + ) + else: + self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 , True) def _CleanUpTooltip(self): # remove reference to old tooltip window @@ -442,26 +448,32 @@ class VariablesView( object ): def _DrawVariables( self, view, variables, indent, is_short = False ): assert indent > 0 for variable in variables: - type_ = variable.variable.get( 'type', '' ) - value = variable.variable.get( 'value', '' ) - + text = '' if is_short: - if( len(type_) > 20): - type_ = type_[0:16] + " ..." - if( len(value) > 100 ): - value = value[0:96] + " ..." - - line = utils.AppendToBuffer( - view.buf, - '{indent}{marker}{icon} {name} ({type_}): {value}'.format( + 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', '' ) + ) + 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.get( 'name', '' ), - type_ = type_, - value = value ).split( '\n' )) + type_ = variable.variable.get( 'type', '' ), + value = variable.variable.get( 'value', '' ) + ) + + line = utils.AppendToBuffer( + view.buf, + text.split( '\n' )) + view.lines[ line ] = variable if variable.ShouldDrawDrillDown(): From 3c857cebf41f554e80330c710140adcb7ab0600b Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 2 Jan 2021 15:55:52 -0500 Subject: [PATCH 17/61] dynamically adjusting window size for nvim's floating window based on the buffer size --- autoload/vimspector/internal/balloon.vim | 68 +++++++++++++++++++----- python3/vimspector/utils.py | 6 ++- python3/vimspector/variables.py | 2 + 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 48e1d05..3c0f02e 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -38,6 +38,12 @@ endfunction let s:float_win = 0 let s:nvim_related_win = 0 +" +" tooltip dimensions +let s:min_width = 1 +let s:min_height = 1 +let s:max_width = 50 +let s:max_height = 5 function! vimspector#internal#balloon#closeCallback() abort if has('nvim') @@ -52,33 +58,66 @@ function! vimspector#internal#balloon#closeCallback() abort return py3eval('_vimspector_session._CleanUpTooltip()') endfunction +function! vimspector#internal#balloon#nvim_generate_border(width, height) + 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! vimspector#internal#balloon#nvim_resize_tooltip() + if !has('nvim') || s:float_win <= 0 || s:nvim_related_win <= 0 + return + endif + + noa call win_gotoid(s:float_win) + 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:float_win, opts) + + " resize the border window + let opts['width'] = width + 4 + let opts['height'] = height + 2 + call nvim_win_set_config(s:nvim_related_win, opts) + call nvim_buf_set_lines(nvim_win_get_buf(s:nvim_related_win), 0, -1, v:true, vimspector#internal#balloon#nvim_generate_border(width, height)) + +endfunction + function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let body = [] if a:0 > 0 let body = a:1 endif - " tooltip dimensions - let max_height = 5 - let max_width = 50 - if has('nvim') " 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 top = "╭" . repeat("─", max_width) . "╮" - let mid = "│" . repeat(" ", max_width) . "│" - let bot = "╰" . repeat("─", max_width) . "╯" - let lines = [top] + repeat([mid], max_height) + [bot] - let buf_id = nvim_create_buf(v:false, v:true) - call nvim_buf_set_lines(buf_id, 0, -1, v:true, lines) + call nvim_buf_set_lines(buf_id, 0, -1, v:true, vimspector#internal#balloon#nvim_generate_border(s:max_width, s:max_height)) " default the dimensions for now. they can be easily overwritten later let opts = { \ 'relative': 'cursor', - \ 'width': max_width + 2, - \ 'height': max_height + 2, + \ 'width': s:max_width + 2, + \ 'height': s:max_height + 2, \ 'col': 0, \ 'row': 1, \ 'anchor': 'NW', @@ -122,6 +161,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) augroup vimspector#internal#balloon#nvim_float autocmd! autocmd WinLeave * :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float + autocmd BufModifiedSet * :echo execute(eval("nvim_win_get_buf(".s:float_win.")")."bufdo echom &modified") augroup END else @@ -177,8 +217,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'cursorline': 1, \ 'wrap': 0, \ 'filtermode': "n", - \ 'maxwidth': max_width, - \ 'maxheight': max_height, + \ 'maxwidth': s:max_width, + \ 'maxheight': s:max_height, \ 'scrollbar': 1, \ 'border': [] \ } diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 788f03e..05df29c 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -640,7 +640,11 @@ def DisplayBaloon( is_term, display, is_hover = False ): # Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685 display = '\n'.join( display ) - return int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format(is_hover, json.dumps( display )) ) ) + rc = int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format(is_hover, json.dumps( display )) ) ) + + vim.eval("vimspector#internal#balloon#nvim_resize_tooltip()") + + return rc def GetBufferFilepath( buf ): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 4f6e4ff..205efe7 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -284,6 +284,8 @@ class VariablesView( object ): else: self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 , True) + vim.eval("vimspector#internal#balloon#nvim_resize_tooltip()") + def _CleanUpTooltip(self): # remove reference to old tooltip window self._variable_eval_view = None From e0b1d6ed8164b42cebd8900a039dc644129405e6 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 3 Jan 2021 00:42:06 -0500 Subject: [PATCH 18/61] fixing linting errors in python files --- python3/vimspector/debug_session.py | 8 ++-- python3/vimspector/utils.py | 8 +++- python3/vimspector/variables.py | 70 +++++++++++++++++++---------- 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index de5a7c6..9ebc6ab 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -521,7 +521,7 @@ class DebugSession( object ): @IfConnected() def ExpandVariable( self, lineNum = -1 ): - self._variablesView.ExpandVariable(lineNum) + self._variablesView.ExpandVariable( lineNum ) @IfConnected() def AddWatch( self, expression ): @@ -540,7 +540,7 @@ class DebugSession( object ): @IfConnected() - def ShowTooltip(self, winnr, expression, is_hover): + def ShowTooltip( self, winnr, expression, is_hover ): """Proxy: ballonexpr -> variables.ShowBallon""" frame = self._stackTraceView.GetCurrentFrame() # Check if RIP is in a frame @@ -556,9 +556,9 @@ class DebugSession( object ): return '' # Return variable aware function - return self._variablesView.VariableEval(frame, expression, is_hover) + return self._variablesView.VariableEval( frame, expression, is_hover ) - def _CleanUpTooltip(self): + def _CleanUpTooltip( self ): return self._variablesView._CleanUpTooltip() @IfConnected() diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 05df29c..0fbe03a 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -640,9 +640,13 @@ def DisplayBaloon( is_term, display, is_hover = False ): # Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685 display = '\n'.join( display ) - rc = int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format(is_hover, json.dumps( display )) ) ) + rc = int( vim.eval( + "vimspector#internal#balloon#CreateTooltip({}, {})".format( + is_hover, json.dumps( display ) + ) + ) ) - vim.eval("vimspector#internal#balloon#nvim_resize_tooltip()") + vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) return rc diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 205efe7..234fb16 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -122,10 +122,10 @@ class View: lines: typing.Dict[ int, Expandable ] draw: typing.Callable - def __init__( self, win, lines, draw): + def __init__( self, win, lines, draw ): self.lines = lines self.draw = draw - if (win is not None): + if ( win is not None ): self.buf = win.buffer utils.SetUpUIWindow( win ) @@ -138,8 +138,8 @@ class VariablesView( object ): self._connection = None self._current_syntax = '' - self._variable_eval = None - self._variable_eval_view = None + self._variable_eval: Scope = None + self._variable_eval_view: View = None def AddExpandMappings(): vim.command( 'nnoremap ' @@ -186,7 +186,9 @@ class VariablesView( object ): 'balloonexpr': vim.options[ 'balloonexpr' ], 'balloondelay': vim.options[ 'balloondelay' ], } - vim.options[ 'balloonexpr' ] = 'vimspector#internal#balloon#HoverTooltip()' + vim.options[ 'balloonexpr' ] = "vimspector#internal#" + "balloon#HoverTooltip()" + vim.options[ 'balloondelay' ] = 250 if has_balloon: @@ -270,28 +272,38 @@ class VariablesView( object ): }, } ) - def _DrawEval(self): + def _DrawEval( self ): with utils.RestoreCursorPosition(): with utils.ModifiableScratchBuffer( self._variable_eval_view.buf ): utils.ClearBuffer( self._variable_eval_view.buf ) - # if we only have a single non-expandable variable, it means we ran into a simple type + # if we only have a single non-expandable variable, + # it means we ran into a simple type # hence, there is no need to draw additional fluff around the value - if(len(self._variable_eval.variables) == 1 and self._variable_eval.variables[0].IsExpandable() == False): + if( + len( self._variable_eval.variables ) == 1 + and self._variable_eval.variables[ 0 ].IsExpandable() is False + ): utils.AppendToBuffer( self._variable_eval_view.buf, - self._variable_eval.variables[0].variable.get( 'value', '' ) - ) + self._variable_eval.variables[ 0 ] + .variable.get( 'value', '' ) + ) else: - self._DrawVariables( self._variable_eval_view, self._variable_eval.variables, 2 , True) + self._DrawVariables( + self._variable_eval_view, + self._variable_eval.variables, + 2, + True + ) - vim.eval("vimspector#internal#balloon#nvim_resize_tooltip()") + vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) - def _CleanUpTooltip(self): + def _CleanUpTooltip( self ) : # remove reference to old tooltip window self._variable_eval_view = None return '' - def VariableEval(self, frame, expression, is_hover): + def VariableEval( self, frame, expression, is_hover ): """Callback to display variable under cursor `:h ballonexpr`""" if not self._connection: return '' @@ -299,16 +311,24 @@ class VariablesView( object ): def handler( message ): body = message[ 'body' ] - self._variable_eval = Scope(body) - float_win_id = utils.DisplayBaloon(self._is_term, [], is_hover) - float_buf_nr = int(vim.eval("winbufnr({})".format(float_win_id))) + self._variable_eval = Scope( body ) + float_win_id = utils.DisplayBaloon( self._is_term, [], is_hover ) + float_buf_nr = int( vim.eval( "winbufnr({})".format( float_win_id ) ) ) # since vim's popup cant be focused there is no way # to get a reference to its window # we will emulate python's window object ourselves - self._variable_eval_view = View(type('__vim__window__', (object,), {'options': {}, 'buffer': vim.buffers[float_buf_nr]}), {}, self._DrawEval) + self._variable_eval_view = View( + type( + '__vim__window__', + ( object, ), + { 'options': {}, 'buffer': vim.buffers[ float_buf_nr ] } + ), + {}, + self._DrawEval + ) - if(self._variable_eval.VariablesReference() > 0): + if( self._variable_eval.VariablesReference() > 0 ): self._connection.DoRequest( partial( self._ConsumeVariables, self._DrawEval, self._variable_eval ), { @@ -318,9 +338,12 @@ class VariablesView( object ): }, } ) else: - # in case that there is nothing to expand, we need to simulate a response from 'variables' request + # in case that there is nothing to expand, + # we need to simulate a response from 'variables' request # it returns [Variable] - self._variable_eval.variables = [Variable({ 'type': body['type'], 'value': body['result']})] + self._variable_eval.variables = [ + Variable( { 'type': body[ 'type' ], 'value': body[ 'result' ] } ) + ] self._DrawEval() @@ -422,7 +445,7 @@ class VariablesView( object ): else: return - current_line = vim.current.window.cursor[0] if lineNum <= 0 else lineNum + current_line = vim.current.window.cursor[ 0 ] if lineNum <= 0 else lineNum if current_line not in view.lines: return @@ -474,7 +497,8 @@ class VariablesView( object ): line = utils.AppendToBuffer( view.buf, - text.split( '\n' )) + text.split( '\n' ) + ) view.lines[ line ] = variable From 93568ba05d2c69abfff18eea66c39b75bdf49a54 Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 5 Jan 2021 22:13:20 -0500 Subject: [PATCH 19/61] removing test code --- autoload/vimspector/internal/balloon.vim | 1 - 1 file changed, 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 3c0f02e..dc8f543 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -161,7 +161,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) augroup vimspector#internal#balloon#nvim_float autocmd! autocmd WinLeave * :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float - autocmd BufModifiedSet * :echo execute(eval("nvim_win_get_buf(".s:float_win.")")."bufdo echom &modified") augroup END else From 052d63dee5929dbfbf91ee7d761c9eaf30010895 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 5 Jan 2021 22:11:48 +0000 Subject: [PATCH 20/61] Fix mouse interraction with the popup - don't handle events outside the window --- autoload/vimspector/internal/balloon.vim | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index dc8f543..7575be4 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -166,29 +166,29 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) else " assume we are inside vim func! MouseFilter(winid, key) + let handled = 0 if index(["\", "\<2-leftmouse>"], a:key) >= 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#closeCallback() + else + " 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>" && mouse_coords['winid'] == a:winid + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") + let handled = 1 + endif 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>" && mouse_coords['winid'] == a:winid - " forward line number to python, since vim does not allow us to focus - " the correct window - call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") - endif - - return 1 endif - return 0 + return handled endfunc - func! CursorFiler(winid, key) + func! CursorFilter(winid, key) if index(['j', 'k'], a:key) >= 0 call win_execute(a:winid, ':normal '.a:key) @@ -229,7 +229,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let config['resize'] = 1 let s:float_win = popup_beval(body, config) else - let config['filter'] = "CursorFiler" + let config['filter'] = "CursorFilter" let config['moved'] = "any" let s:float_win = popup_atcursor(body, config) endif From 0895c5e82900bd20de97d80668a28d7723a09c77 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 5 Jan 2021 22:12:14 +0000 Subject: [PATCH 21/61] Add 1 cell of padding to the x direction which makes the popup more readable --- autoload/vimspector/internal/balloon.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 7575be4..0b37c68 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -219,7 +219,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'maxwidth': s:max_width, \ 'maxheight': s:max_height, \ 'scrollbar': 1, - \ 'border': [] + \ 'border': [], + \ 'padding': [ 0, 1, 0, 1] \ } if a:is_hover let config['filter'] = "MouseFilter" From 64e38b57abfee39cb232fa9f013bf7b45b685085 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Tue, 5 Jan 2021 22:13:25 +0000 Subject: [PATCH 22/61] Fix crash; enable syntax highlighting in hover popup; use a Watch for the popup and re-use existing drawing code --- python3/vimspector/variables.py | 117 +++++++++++++++++--------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 234fb16..797a6a9 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -117,14 +117,27 @@ 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.syntax = None if ( win is not None ): self.buf = win.buffer utils.SetUpUIWindow( win ) @@ -186,8 +199,8 @@ class VariablesView( object ): 'balloonexpr': vim.options[ 'balloonexpr' ], 'balloondelay': vim.options[ 'balloondelay' ], } - vim.options[ 'balloonexpr' ] = "vimspector#internal#" - "balloon#HoverTooltip()" + vim.options[ 'balloonexpr' ] = ( "vimspector#internal#" + "balloon#HoverTooltip()" ) vim.options[ 'balloondelay' ] = 250 @@ -273,28 +286,25 @@ class VariablesView( object ): } ) def _DrawEval( self ): + watch = self._variable_eval + view = self._variable_eval_view + with utils.RestoreCursorPosition(): - with utils.ModifiableScratchBuffer( self._variable_eval_view.buf ): - utils.ClearBuffer( self._variable_eval_view.buf ) - # if we only have a single non-expandable variable, - # it means we ran into a simple type - # hence, there is no need to draw additional fluff around the value - if( - len( self._variable_eval.variables ) == 1 - and self._variable_eval.variables[ 0 ].IsExpandable() is False - ): - utils.AppendToBuffer( - self._variable_eval_view.buf, - self._variable_eval.variables[ 0 ] - .variable.get( 'value', '' ) - ) - else: - self._DrawVariables( - self._variable_eval_view, - self._variable_eval.variables, - 2, - True - ) + with utils.ModifiableScratchBuffer( view.buf ): + utils.ClearBuffer( view.buf ) + # FIXME: This probably doesn't work reliably + view.syntax = utils.SetSyntax( view.syntax, + self._current_syntax, + view.buf ) + + utils.AppendToBuffer( + view.buf, + f"Expression: { watch.expression[ 'expression' ] }" ) + + self._DrawWatchResult( view, + 2, + watch, + is_short = True ) vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) @@ -309,9 +319,13 @@ class VariablesView( object ): return '' def handler( message ): - body = message[ 'body' ] - self._variable_eval = Scope( body ) + watch = self._variable_eval + if watch.result is None: + watch.result = WatchResult( message[ 'body' ] ) + else: + watch.result.Update( message[ 'body' ] ) + float_win_id = utils.DisplayBaloon( self._is_term, [], is_hover ) float_buf_nr = int( vim.eval( "winbufnr({})".format( float_win_id ) ) ) @@ -328,51 +342,41 @@ class VariablesView( object ): self._DrawEval ) - if( self._variable_eval.VariablesReference() > 0 ): + 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._DrawEval, - self._variable_eval ), { + self._variable_eval_view.draw, + watch.result ), { 'command': 'variables', 'arguments': { - 'variablesReference': self._variable_eval.VariablesReference(), + 'variablesReference': watch.result.VariablesReference(), }, } ) - else: - # in case that there is nothing to expand, - # we need to simulate a response from 'variables' request - # it returns [Variable] - self._variable_eval.variables = [ - Variable( { 'type': body[ 'type' ], 'value': body[ 'result' ] } ) - ] - self._DrawEval() + self._DrawEval() def failure_handler( reason, message ): display = [ reason ] utils.DisplayBaloon( self._is_term, display, is_hover ) + self._variable_eval = Watch.New( frame, + expression, + 'hover' ) + # Send async request self._connection.DoRequest( handler, { 'command': 'evaluate', - 'arguments': { - 'expression': expression, - 'frameId': frame[ 'id' ], - 'context': 'hover', - } + 'arguments': self._variable_eval.expression, }, failure_handler ) # Return working (meanwhile) return '' def AddWatch( self, frame, expression ): - watch = { - 'expression': expression, - 'context': 'watch', - } - if frame: - watch[ 'frameId' ] = frame[ 'id' ] - - self._watches.append( Watch( watch ) ) + self._watches.append( Watch.New( frame, expression, 'watch' ) ) self.EvaluateWatches() def DeleteWatch( self ): @@ -470,7 +474,7 @@ class VariablesView( object ): }, } ) - def _DrawVariables( self, view, variables, indent, is_short = False ): + def _DrawVariables( self, view, variables, indent, is_short = False ): assert indent > 0 for variable in variables: text = '' @@ -530,7 +534,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 '-' @@ -546,7 +550,7 @@ 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 @@ -561,12 +565,12 @@ class VariablesView( object ): icon = icon, result = watch.result.result.get( 'result', '' ) ) - 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, is_short ) def _ConsumeVariables( self, draw, parent, message ): new_variables = [] @@ -640,6 +644,7 @@ class VariablesView( object ): def SetSyntax( self, syntax ): + # TODO: Switch to View.syntax self._current_syntax = utils.SetSyntax( self._current_syntax, syntax, self._vars.buf, From 0b4da4c82ba9f1be03aca66a62fffadd71e49624 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Jan 2021 14:34:37 +0000 Subject: [PATCH 23/61] Remove more text from the hover popup --- python3/vimspector/variables.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 797a6a9..d8f2f53 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -297,12 +297,8 @@ class VariablesView( object ): self._current_syntax, view.buf ) - utils.AppendToBuffer( - view.buf, - f"Expression: { watch.expression[ 'expression' ] }" ) - self._DrawWatchResult( view, - 2, + 0, watch, is_short = True ) @@ -554,15 +550,25 @@ class VariablesView( object ): 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', '' ) ) line = utils.AppendToBuffer( view.buf, line.split( '\n' ) ) From cfae062da14bf3af6c9a177501eaa94bf1cfab41 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Jan 2021 14:48:00 +0000 Subject: [PATCH 24/61] Disable cursorLine for the hover-only popup --- autoload/vimspector/internal/balloon.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 0b37c68..6007c31 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -213,7 +213,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endif let config = { - \ 'cursorline': 1, \ 'wrap': 0, \ 'filtermode': "n", \ 'maxwidth': s:max_width, @@ -232,6 +231,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) else let config['filter'] = "CursorFilter" let config['moved'] = "any" + let config['cursorline'] = 1 let s:float_win = popup_atcursor(body, config) endif From f3c39e12ab3449b74603c025133445441d8cb490 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 6 Jan 2021 14:51:35 +0000 Subject: [PATCH 25/61] FixUp: Display in watch window, and indent in popup --- python3/vimspector/variables.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index d8f2f53..e657824 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -560,7 +560,7 @@ class VariablesView( object ): else: icon = '+' if ( watch.result.IsExpandable() and not watch.result.IsExpanded() ) else '-' - marker = '*' if watch.result.changed else ' ', + marker = '*' if watch.result.changed else ' ' leader = ' Result: ' line = '{indent}{marker}{icon}{leader}{result}'.format( @@ -575,8 +575,7 @@ class VariablesView( object ): view.lines[ line ] = watch.result if watch.result.ShouldDrawDrillDown(): - indent = 4 - self._DrawVariables( view, watch.result.variables, indent, is_short ) + self._DrawVariables( view, watch.result.variables, indent + 2, is_short ) def _ConsumeVariables( self, draw, parent, message ): new_variables = [] From 0d703779dc58cb1e11d09c0a74348c0995df1661 Mon Sep 17 00:00:00 2001 From: dsych Date: Thu, 7 Jan 2021 00:20:23 -0500 Subject: [PATCH 26/61] evaluating select range --- autoload/vimspector.vim | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index d8018a0..7f752e4 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -523,8 +523,39 @@ function! vimspector#OnBufferCreated( file_name ) abort py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) ) endfunction -function! vimspector#ShowTooltip() abort - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"\")" ), 0)') +function! vimspector#ShowTooltipForSelection() range + let [start, end] = [[line("'<"), col("'<") - 1], [line("'>"), col("'>")]] + " restor cursor position, since command mode puts it to the begining of the + " current line when invoked from visual mode + call cursor(end) + + " retrive the lines selected in visual mode + let lines = getbufline(bufnr('%'), start[0], end[0]) + + " make sure the leave only the parts we care about if multiple lines are + " selected + let lines[0] = strcharpart(lines[0], start[1]) + let lines_len = len(lines) - 1 + + if len(lines) == 1 + let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1] - start[1]) + else + let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1]) + endif + + let str = join(lines) + + call vimspector#ShowTooltip(str) +endfunction + +function! vimspector#ShowTooltip(...) abort + let str = "" + + if a:0 > 0 + let str = a:1 + endif + + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"'.str.'\")" ), 0)') endfunction From 2d082cc923c104650224a75af8a2cfb815e6c698 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 21:28:48 +0000 Subject: [PATCH 27/61] Use a more generous maximum size for the popup (TODO: option for this ?) --- autoload/vimspector/internal/balloon.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 6007c31..d5f0b6c 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -42,8 +42,8 @@ let s:nvim_related_win = 0 " tooltip dimensions let s:min_width = 1 let s:min_height = 1 -let s:max_width = 50 -let s:max_height = 5 +let s:max_width = 80 +let s:max_height = 20 function! vimspector#internal#balloon#closeCallback() abort if has('nvim') From cb174c176d75ee22e4858c139355a11e29205732 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 21:58:45 +0000 Subject: [PATCH 28/61] Disable wrapping long lines in neovim popup too --- autoload/vimspector/internal/balloon.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index d5f0b6c..4f4e064 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -145,7 +145,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) call nvim_buf_set_option(buf_id, 'modifiable', v:false) let s:float_win = nvim_open_win(buf_id, v:false, opts) - call nvim_win_set_option(s:float_win, 'wrap', v:true) + call nvim_win_set_option(s:float_win, 'wrap', v:false) call nvim_win_set_option(s:float_win, 'cursorline', v:true) call nvim_win_set_option(s:float_win, 'signcolumn', 'no') call nvim_win_set_option(s:float_win, 'relativenumber', v:false) From 64f2c8eb01d4d446b571c7366d356fff92aacd17 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 21:59:02 +0000 Subject: [PATCH 29/61] Use a fancy single line border in vim popup when we can --- autoload/vimspector/internal/balloon.vim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 4f4e064..47544c5 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -221,6 +221,11 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'border': [], \ 'padding': [ 0, 1, 0, 1] \ } + + if &ambiwidth ==# 'single' && &encoding == 'utf-8' + let config['borderchars'] = [ '─', '│', '─', '│', '╭', '╮', '╯', '╰' ] + endif + if a:is_hover let config['filter'] = "MouseFilter" let config['mousemoved'] = [0, 0, 0] From 672ac78fefdea0d5597e6f6b4a2e565e3083aa20 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 21:59:18 +0000 Subject: [PATCH 30/61] Fix flaky syntax highlighting toggling on/off --- python3/vimspector/variables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index e657824..138453e 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -293,7 +293,7 @@ class VariablesView( object ): with utils.ModifiableScratchBuffer( view.buf ): utils.ClearBuffer( view.buf ) # FIXME: This probably doesn't work reliably - view.syntax = utils.SetSyntax( view.syntax, + view.syntax = utils.SetSyntax( None, self._current_syntax, view.buf ) From fccafd6739e1a31905ea454fbe14aa537f3bc672 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:32:05 +0000 Subject: [PATCH 31/61] FixUp: border characters - indicate that the corner can be dragged --- autoload/vimspector/internal/balloon.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 47544c5..d9861cb 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -19,6 +19,8 @@ let s:save_cpo = &cpoptions set cpoptions&vim " }}} +scriptencoding utf-8 + " Returns: py.ShowBalloon( winnr, expresssion ) function! vimspector#internal#balloon#BalloonExpr() abort " winnr + 1 because for *no good reason* winnr is 0 based here unlike @@ -223,7 +225,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ } if &ambiwidth ==# 'single' && &encoding == 'utf-8' - let config['borderchars'] = [ '─', '│', '─', '│', '╭', '╮', '╯', '╰' ] + let config['borderchars'] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ] endif if a:is_hover From d2ed8a828c65eaeaca10fcfec2fd8ce5e8bbedfc Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:32:52 +0000 Subject: [PATCH 32/61] Use popup_filter_menu for the keyboard-popup rather than 100% our own --- autoload/vimspector/internal/balloon.vim | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index d9861cb..88c6dd0 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -179,7 +179,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) 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>" && mouse_coords['winid'] == a:winid + 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(".line('.', a:winid).")") @@ -191,23 +191,16 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endfunc func! CursorFilter(winid, key) - if index(['j', 'k'], a:key) >= 0 - call win_execute(a:winid, ':normal '.a:key) - - return 1 - elseif a:key == "\" + if a:key == "\" " forward line number to python, since vim does not allow us to focus " the correct window call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") - - return 1 - elseif a:key == "\" - call vimspector#internal#balloon#closeCallback() - return 1 + elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 + return MouseFilter( a:winid, a:key ) endif - return 0 + return popup_filter_menu( a:winid, a:key ) endfunc if s:float_win != 0 From bc1146df3bb8d0ba925d0f8e42c49935dd19850c Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:33:31 +0000 Subject: [PATCH 33/61] Use normal highlighting for the popup as it's probably more readable (debatable?) --- autoload/vimspector/internal/balloon.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 88c6dd0..1803a35 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -214,7 +214,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'maxheight': s:max_height, \ 'scrollbar': 1, \ 'border': [], - \ 'padding': [ 0, 1, 0, 1] + \ 'padding': [ 0, 1, 0, 1], + \ 'highlight': 'Normal', \ } if &ambiwidth ==# 'single' && &encoding == 'utf-8' From 322b7e0a383d26635c563b9e33331d412d2bedaf Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:33:52 +0000 Subject: [PATCH 34/61] Enable mouse interraction for the both popups in vim --- autoload/vimspector/internal/balloon.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 1803a35..caf906f 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -216,6 +216,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'border': [], \ 'padding': [ 0, 1, 0, 1], \ 'highlight': 'Normal', + \ 'drag': 1, + \ 'resize': 1 \ } if &ambiwidth ==# 'single' && &encoding == 'utf-8' @@ -226,8 +228,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let config['filter'] = "MouseFilter" let config['mousemoved'] = [0, 0, 0] let config['close'] = "button" - let config['drag'] = 1 - let config['resize'] = 1 let s:float_win = popup_beval(body, config) else let config['filter'] = "CursorFilter" From 894ca522d305ac2cf929d0aef2480e6fe1f7dae1 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:48:20 +0000 Subject: [PATCH 35/61] Use the popup callback rather than manually trying to do it --- autoload/vimspector/internal/balloon.vim | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index caf906f..491acf5 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -47,14 +47,19 @@ let s:min_height = 1 let s:max_width = 80 let s:max_height = 20 -function! vimspector#internal#balloon#closeCallback() abort +function! vimspector#internal#balloon#Close() abort if has('nvim') call nvim_win_close(s:float_win, v:true) call nvim_win_close(s:nvim_related_win, v:true) + + call vimspector#internal#balloon#CloseCallback() else call popup_close(s:float_win) endif +endfunction + +function! vimspector#internal#balloon#CloseCallback( ... ) abort let s:float_win = 0 let s:nvim_related_win = 0 return py3eval('_vimspector_session._CleanUpTooltip()') @@ -162,7 +167,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) " make sure we clean up the float after it loses focus augroup vimspector#internal#balloon#nvim_float autocmd! - autocmd WinLeave * :call vimspector#internal#balloon#closeCallback() | autocmd! vimspector#internal#balloon#nvim_float + autocmd WinLeave * :call vimspector#internal#balloon#Close() | autocmd! vimspector#internal#balloon#nvim_float augroup END else @@ -173,7 +178,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let mouse_coords = getmousepos() " close the popup if mouse is clicked outside the window if mouse_coords['winid'] != a:winid - call vimspector#internal#balloon#closeCallback() + call vimspector#internal#balloon#Close() else " place the cursor according to the click call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") @@ -204,7 +209,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endfunc if s:float_win != 0 - call vimspector#internal#balloon#closeCallback() + call vimspector#internal#balloon#Close() endif let config = { @@ -217,7 +222,8 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'padding': [ 0, 1, 0, 1], \ 'highlight': 'Normal', \ 'drag': 1, - \ 'resize': 1 + \ 'resize': 1, + \ 'callback': 'vimspector#internal#balloon#CloseCallback' \ } if &ambiwidth ==# 'single' && &encoding == 'utf-8' From e5e13ffcdd09bfae55335c57626a93b0ce4d5afc Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Fri, 8 Jan 2021 22:48:34 +0000 Subject: [PATCH 36/61] Fix typo: Baloon -> Balloon --- python3/vimspector/utils.py | 2 +- python3/vimspector/variables.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 0fbe03a..2bd3c08 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -634,7 +634,7 @@ def ParseVariables( variables_list, return new_variables -def DisplayBaloon( is_term, display, is_hover = False ): +def DisplayBalloon( is_term, display, is_hover = False ): if not is_term: # To enable the Windows GUI to display the balloon correctly # Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685 diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 138453e..c9b263c 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -322,7 +322,7 @@ class VariablesView( object ): else: watch.result.Update( message[ 'body' ] ) - float_win_id = utils.DisplayBaloon( self._is_term, [], is_hover ) + float_win_id = utils.DisplayBalloon( self._is_term, [], is_hover ) float_buf_nr = int( vim.eval( "winbufnr({})".format( float_win_id ) ) ) # since vim's popup cant be focused there is no way @@ -356,7 +356,7 @@ class VariablesView( object ): def failure_handler( reason, message ): display = [ reason ] - utils.DisplayBaloon( self._is_term, display, is_hover ) + utils.DisplayBalloon( self._is_term, display, is_hover ) self._variable_eval = Watch.New( frame, expression, @@ -628,11 +628,11 @@ class VariablesView( object ): 'Type: ' + body.get( 'type', '' ), 'Value: ' + result ] - utils.DisplayBaloon( self._is_term, display ) + utils.DisplayBalloon( self._is_term, display ) def failure_handler( reason, message ): display = [ reason ] - utils.DisplayBaloon( self._is_term, display ) + utils.DisplayBalloon( self._is_term, display ) # Send async request self._connection.DoRequest( handler, { From 31e44548d38a28169bd3a36f6f4dca660179bd1f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 9 Jan 2021 10:45:27 +0000 Subject: [PATCH 37/61] Tidy up MouseFilter --- autoload/vimspector/internal/balloon.vim | 42 +++++++++++++----------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 491acf5..c292d5f 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -171,27 +171,31 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) augroup END else - " assume we are inside vim func! MouseFilter(winid, key) - let handled = 0 - if index(["\", "\<2-leftmouse>"], a:key) >= 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() - else - " 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(".line('.', a:winid).")") - let handled = 1 - endif - endif + if index(["\", "\<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(".line('.', a:winid).")") + let handled = 1 + endif + return handled endfunc From 3239963893b285eaa456a2421c8a48e5fba39a51 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sat, 9 Jan 2021 11:21:53 +0000 Subject: [PATCH 38/61] Set the correct highlights in both popups in neovim --- autoload/vimspector/internal/balloon.vim | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index c292d5f..0f0c8d9 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -140,7 +140,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) " when calculating where to display the content window, we need to account " for the border - set winhl=Normal:Floating let opts.row += 1 let opts.height -= 2 let opts.col += 2 @@ -158,7 +157,16 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) call nvim_win_set_option(s:float_win, 'relativenumber', v:false) call nvim_win_set_option(s:float_win, 'number', v:false) - noa call win_gotoid(s:float_win) + let old_curwin = win_getid() + try + noautocmd call win_gotoid(s:nvim_related_win) + set winhl=Normal:Floating + finally + noautocmd call win_gotoid(old_curwin) + endtry + + noautocmd call win_gotoid(s:float_win) + set winhl=Normal:Floating nnoremap :call vimspector#ExpandVariable() nnoremap :quit From 91bebc182602a1c98d17a0ae039f1a0ad95a6362 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 10 Jan 2021 14:56:23 -0500 Subject: [PATCH 39/61] adding a close button for keyboard triggered popup, since drag and resize are also enabled. removing wrap from border. --- autoload/vimspector/internal/balloon.vim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 0f0c8d9..1cb91c8 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -132,7 +132,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ } " this is the border window let s:nvim_related_win = nvim_open_win(buf_id, 0, opts) - call nvim_win_set_option(s:nvim_related_win, 'wrap', v:true) call nvim_win_set_option(s:nvim_related_win, 'cursorline', v:true) call nvim_win_set_option(s:nvim_related_win, 'signcolumn', 'no') call nvim_win_set_option(s:nvim_related_win, 'relativenumber', v:false) @@ -229,12 +228,15 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ '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], \ 'highlight': 'Normal', \ 'drag': 1, \ 'resize': 1, + \ 'close': 'button', \ 'callback': 'vimspector#internal#balloon#CloseCallback' \ } @@ -245,7 +247,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) if a:is_hover let config['filter'] = "MouseFilter" let config['mousemoved'] = [0, 0, 0] - let config['close'] = "button" let s:float_win = popup_beval(body, config) else let config['filter'] = "CursorFilter" From 13102dc7117d988415b17f78b48f39aecb725696 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 10 Jan 2021 15:25:02 -0500 Subject: [PATCH 40/61] reverting back the highlight group --- autoload/vimspector/internal/balloon.vim | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 1cb91c8..b7d101b 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -132,7 +132,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ } " this is the border window let s:nvim_related_win = nvim_open_win(buf_id, 0, opts) - call nvim_win_set_option(s:nvim_related_win, 'cursorline', v:true) call nvim_win_set_option(s:nvim_related_win, 'signcolumn', 'no') call nvim_win_set_option(s:nvim_related_win, 'relativenumber', v:false) call nvim_win_set_option(s:nvim_related_win, 'number', v:false) @@ -156,16 +155,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) call nvim_win_set_option(s:float_win, 'relativenumber', v:false) call nvim_win_set_option(s:float_win, 'number', v:false) - let old_curwin = win_getid() - try - noautocmd call win_gotoid(s:nvim_related_win) - set winhl=Normal:Floating - finally - noautocmd call win_gotoid(old_curwin) - endtry - noautocmd call win_gotoid(s:float_win) - set winhl=Normal:Floating nnoremap :call vimspector#ExpandVariable() nnoremap :quit @@ -233,7 +223,6 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'scrollbar': 1, \ 'border': [], \ 'padding': [ 0, 1, 0, 1], - \ 'highlight': 'Normal', \ 'drag': 1, \ 'resize': 1, \ 'close': 'button', From 4466fce20b15df37ad1463676f09e3d0825235f0 Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 10 Jan 2021 15:37:17 -0500 Subject: [PATCH 41/61] fixing styling inside vim files --- autoload/vimspector.vim | 4 +-- autoload/vimspector/internal/balloon.vim | 36 ++++++++++++------------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 7f752e4..941ec40 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -523,7 +523,7 @@ function! vimspector#OnBufferCreated( file_name ) abort py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) ) endfunction -function! vimspector#ShowTooltipForSelection() range +function! vimspector#ShowTooltipForSelection() range abort let [start, end] = [[line("'<"), col("'<") - 1], [line("'>"), col("'>")]] " restor cursor position, since command mode puts it to the begining of the " current line when invoked from visual mode @@ -549,7 +549,7 @@ function! vimspector#ShowTooltipForSelection() range endfunction function! vimspector#ShowTooltip(...) abort - let str = "" + let str = '' if a:0 > 0 let str = a:1 diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index b7d101b..b922397 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -65,16 +65,16 @@ function! vimspector#internal#balloon#CloseCallback( ... ) abort return py3eval('_vimspector_session._CleanUpTooltip()') endfunction -function! vimspector#internal#balloon#nvim_generate_border(width, height) - let top = "╭" . repeat("─",a:width + 2) . "╮" - let mid = "│" . repeat(" ",a:width + 2) . "│" - let bot = "╰" . repeat("─",a:width + 2) . "╯" +function! vimspector#internal#balloon#nvim_generate_border(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! vimspector#internal#balloon#nvim_resize_tooltip() +function! vimspector#internal#balloon#nvim_resize_tooltip() abort if !has('nvim') || s:float_win <= 0 || s:nvim_related_win <= 0 return endif @@ -107,7 +107,7 @@ function! vimspector#internal#balloon#nvim_resize_tooltip() endfunction -function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) +function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) abort let body = [] if a:0 > 0 let body = a:1 @@ -168,7 +168,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) augroup END else - func! MouseFilter(winid, key) + func! MouseFilter(winid, key) abort if index(["\", "\<2-leftmouse>"], a:key) < 0 return 0 endif @@ -183,24 +183,24 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) endif " place the cursor according to the click - call win_execute(a:winid, ":call cursor(".mouse_coords['line'].", ".mouse_coords['column'].")") + 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>" + 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(".line('.', a:winid).")") + call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') let handled = 1 endif return handled endfunc - func! CursorFilter(winid, key) - if a:key == "\" + func! CursorFilter(winid, key) abort + if a:key ==? "\" " forward line number to python, since vim does not allow us to focus " the correct window - call py3eval("_vimspector_session.ExpandVariable(".line('.', a:winid).")") + call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') return 1 elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 return MouseFilter( a:winid, a:key ) @@ -215,7 +215,7 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) let config = { \ 'wrap': 0, - \ 'filtermode': "n", + \ 'filtermode': 'n', \ 'maxwidth': s:max_width, \ 'maxheight': s:max_height, \ 'minwidth': s:min_width, @@ -229,17 +229,17 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) \ 'callback': 'vimspector#internal#balloon#CloseCallback' \ } - if &ambiwidth ==# 'single' && &encoding == 'utf-8' + if &ambiwidth ==# 'single' && &encoding ==? 'utf-8' let config['borderchars'] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ] endif if a:is_hover - let config['filter'] = "MouseFilter" + let config['filter'] = 'MouseFilter' let config['mousemoved'] = [0, 0, 0] let s:float_win = popup_beval(body, config) else - let config['filter'] = "CursorFilter" - let config['moved'] = "any" + let config['filter'] = 'CursorFilter' + let config['moved'] = 'any' let config['cursorline'] = 1 let s:float_win = popup_atcursor(body, config) endif From 789377eab47ba3dee165443693146068853aa11a Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 00:35:24 -0500 Subject: [PATCH 42/61] adding the first test! --- python3/vimspector/debug_session.py | 1 + python3/vimspector/variables.py | 3 ++ tests/variables.test.vim | 64 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 9ebc6ab..18c4726 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -702,6 +702,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(): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index c9b263c..e96a585 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -307,6 +307,7 @@ class VariablesView( object ): def _CleanUpTooltip( self ) : # remove reference to old tooltip window self._variable_eval_view = None + vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = None return '' def VariableEval( self, frame, expression, is_hover ): @@ -323,6 +324,8 @@ class VariablesView( object ): watch.result.Update( message[ 'body' ] ) float_win_id = utils.DisplayBalloon( self._is_term, [], is_hover ) + # record the global eval window id + vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( float_win_id ) float_buf_nr = int( vim.eval( "winbufnr({})".format( float_win_id ) ) ) # since vim's popup cant be focused there is no way diff --git a/tests/variables.test.vim b/tests/variables.test.vim index 9152875..5cf575f 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -591,3 +591,67 @@ 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 ) + + "evaluate the prev line + " call win_gotoid( g:vimspector_session_windows.code ) + call setpos('.', [ 0, 24, 8 ]) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) + call vimspector#ShowTooltip() + + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '{...}', + \ ' - i: 0', + \ ' - c: 0 ''\\0\{1,3}''', + \ ' - fffff: 0', + \ ' + another_test: ', + \ ], + \ getbufline( winbufnr( g:vimspector_session_windows.eval ), + \ 1, + \ '$' ) + \ ) + \ } ) + + " Expand + call win_execute( g:vimspector_session_windows.eval, [ + \ 'call feedkeys( ''jjjj'', ''xt'' )', + \ 'call feedkeys( "\", ''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, + \ '$' ) + \ ) + \ } ) + + "Close + call win_execute( g:vimspector_session_windows.eval, 'call feedkeys( "\", ''xt'')') + + call WaitForAssert( {-> + \ assert_equal( 0, g:vimspector_session_windows.eval ) + \ } ) + + call vimspector#test#setup#Reset() + %bwipe! +endfunction From 0c79384529f413ee58d453a4c6ec41a5cde8b15d Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 08:50:39 -0500 Subject: [PATCH 43/61] there is no need to execute feedkeys inside a popup context, since keys are added into the global input buffer --- tests/variables.test.vim | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index 5cf575f..c587dea 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -623,10 +623,8 @@ function! Test_VariableEval() \ } ) " Expand - call win_execute( g:vimspector_session_windows.eval, [ - \ 'call feedkeys( ''jjjj'', ''xt'' )', - \ 'call feedkeys( "\", ''xt'' )' - \ ] ) + call feedkeys( 'jjjj', 'xt' ) + call feedkeys( "\", 'xt' ) call WaitForAssert( {-> \ AssertMatchist( @@ -646,11 +644,9 @@ function! Test_VariableEval() \ } ) "Close - call win_execute( g:vimspector_session_windows.eval, 'call feedkeys( "\", ''xt'')') + call feedkeys( "\", 'xt' ) - call WaitForAssert( {-> - \ assert_equal( 0, g:vimspector_session_windows.eval ) - \ } ) + call assert_equal( v:none, g:vimspector_session_windows.eval ) call vimspector#test#setup#Reset() %bwipe! From 0ff9dc5f9e6677288c4a015bc550270e184955dc Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 23:06:35 -0500 Subject: [PATCH 44/61] making sure that select marks were set before attempting to extract text --- autoload/vimspector.vim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 941ec40..1458060 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -532,6 +532,10 @@ function! vimspector#ShowTooltipForSelection() range abort " retrive the lines selected in visual mode let lines = getbufline(bufnr('%'), start[0], end[0]) + if(len(lines) < 1) + return + endif + " make sure the leave only the parts we care about if multiple lines are " selected let lines[0] = strcharpart(lines[0], start[1]) From e0b0a7f3d2edfaf447788adc744856b120b57995 Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 23:07:01 -0500 Subject: [PATCH 45/61] recording popup win id on the failed evaluation --- python3/vimspector/variables.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index e96a585..7666954 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -359,7 +359,9 @@ class VariablesView( object ): def failure_handler( reason, message ): display = [ reason ] - utils.DisplayBalloon( self._is_term, display, is_hover ) + 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, From 5a23ec5bebd1a0b527ffd27b70990fc5221429c4 Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 23:07:37 -0500 Subject: [PATCH 46/61] adding tests for select range, invalid eval and expand/collapse --- tests/variables.test.vim | 130 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 3 deletions(-) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index c587dea..caf2a67 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -602,11 +602,118 @@ function! Test_VariableEval() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) "evaluate the prev line - " call win_gotoid( g:vimspector_session_windows.code ) call setpos('.', [ 0, 24, 8 ]) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) call vimspector#ShowTooltip() + 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( "\", 'xt' ) + + call assert_equal( v:none, g:vimspector_session_windows.eval ) + + " test selection + call setpos('.', [ 0, 24, 8 ]) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) + + " enter visual mode + " this is a hack, since usually, when user enters command mode from inside + " visual mode, the latter is immediately interrupted and the '<' '>' marks are + " set. for some odd reason, visual mode is not interupted from the script, + " so we need to manually escape and re-trigger previous visual selection + call execute('normal v') + call feedkeys("lllll\", 'xt') + call execute("normal gv") + + call vimspector#ShowTooltipForSelection() + + 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( "\", 'xt' ) + + call assert_equal( v:none, g:vimspector_session_windows.eval ) + + " Evaluation error + call setpos('.', [ 0, 25, 1 ]) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 25, 1 ) + call vimspector#ShowTooltip() + + 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( "\", 'xt' ) + + 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 ) + + "evaluate the prev line + call setpos('.', [ 0, 24, 8 ]) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) + call vimspector#ShowTooltip() + + call WaitForAssert( {-> + \ assert_notequal( v:none, g:vimspector_session_windows.eval ) + \ } ) + call WaitForAssert( {-> \ AssertMatchist( \ [ @@ -623,8 +730,7 @@ function! Test_VariableEval() \ } ) " Expand - call feedkeys( 'jjjj', 'xt' ) - call feedkeys( "\", 'xt' ) + call feedkeys( "jjjj\", 'xt' ) call WaitForAssert( {-> \ AssertMatchist( @@ -643,6 +749,24 @@ function! Test_VariableEval() \ ) \ } ) + "Collapse + call feedkeys( "\", '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( "\", 'xt' ) From 639e89f5db9e750fdbac45bccb3268aa798fb1cf Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 23:16:02 -0500 Subject: [PATCH 47/61] fixing linting --- autoload/vimspector.vim | 2 +- tests/variables.test.vim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 1458060..75ed961 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -541,7 +541,7 @@ function! vimspector#ShowTooltipForSelection() range abort let lines[0] = strcharpart(lines[0], start[1]) let lines_len = len(lines) - 1 - if len(lines) == 1 + if len( lines ) == 1 let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1] - start[1]) else let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1]) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index caf2a67..1e4a9ec 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -641,7 +641,7 @@ function! Test_VariableEval() " so we need to manually escape and re-trigger previous visual selection call execute('normal v') call feedkeys("lllll\", 'xt') - call execute("normal gv") + call execute('normal gv') call vimspector#ShowTooltipForSelection() From 8c39a861bdba156d6897a18aeef6594be7dba304 Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 15 Jan 2021 23:36:26 -0500 Subject: [PATCH 48/61] hopefully fixing tests on mac --- tests/variables.test.vim | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index 1e4a9ec..ae51ad8 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -628,7 +628,9 @@ function! Test_VariableEval() "Close call feedkeys( "\", 'xt' ) - call assert_equal( v:none, g:vimspector_session_windows.eval ) + call WaitForAssert( {-> + \ assert_equal( v:none, g:vimspector_session_windows.eval ) + \ } ) " test selection call setpos('.', [ 0, 24, 8 ]) @@ -665,9 +667,13 @@ function! Test_VariableEval() \ } ) "Close - call feedkeys( "\", 'xt' ) + " we need to send esc twice because of the weird interactions between visual + " mode and tests + call feedkeys( "\\", 'xt' ) - call assert_equal( v:none, g:vimspector_session_windows.eval ) + call WaitForAssert( {-> + \ assert_equal( v:none, g:vimspector_session_windows.eval ) + \ } ) " Evaluation error call setpos('.', [ 0, 25, 1 ]) @@ -692,6 +698,10 @@ function! Test_VariableEval() "Close call feedkeys( "\", 'xt' ) + call WaitForAssert( {-> + \ assert_equal( v:none, g:vimspector_session_windows.eval ) + \ } ) + call vimspector#test#setup#Reset() %bwipe! endfunction @@ -770,7 +780,9 @@ function! Test_VariableEvalExpand() "Close call feedkeys( "\", 'xt' ) - call assert_equal( v:none, g:vimspector_session_windows.eval ) + call WaitForAssert( {-> + \ assert_equal( v:none, g:vimspector_session_windows.eval ) + \ } ) call vimspector#test#setup#Reset() %bwipe! From 47680565c433be8947fa3b44de2b0e5474d611ac Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 2 Feb 2021 20:53:31 -0500 Subject: [PATCH 49/61] adding docs --- README.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8e7a036..a18a89f 100644 --- a/README.md +++ b/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/selection hover evaluation](#variable-eval) * [Watches](#watches) * [Watch autocompletion](#watch-autocompletion) * [Stack Traces](#stack-traces) @@ -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: ![vimspector-vim-screenshot](https://puremourning.github.io/vimspector-web/img/vimspector-overview.png) @@ -249,7 +250,7 @@ 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. +* Tooltips - used to display the values of variables when debugging. Workarounds are in place as follows: @@ -258,9 +259,9 @@ Workarounds are in place as follows: [`:VimspectorReset`](#closing-debugger) * Prompt Buffers - There are [`:VimspectorEval`](#console) and [`:VimspectorWatch`](#watches) - -There is no workaroud for the lack of balloons; you'll just have to use -`:VimspectorEval` or `:VimspectorWatch`, or switch to Vim. +* Functions - There are + [`:call vimspector#ShowTooltip()`](#variable-eval) and + [`:call vimspector#ShowTooltipForSelection()`](#variable-eval) ## Windows differences @@ -890,6 +891,17 @@ breakpoint when it is hit. Scopes and variables are represented by the buffer `vimspector.Variables`. +## Variable/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 hightlight a expression (e.g. a + b) and the result of the expression. +* Call `vimspector#ShowTooltip()` or `vimspector#ShowTooltipForSelection()` to evaluate expressions without mouse (the only way to use this feature in nvim). +* Use regular nagivation keys to chose the current selection and `` (or leave the tooltip window) to close the tooltip. + +Note: using a selection evaluation might lead to undesired consequences, since **the expression is actually executed**. E.g. `c = a + b;` once this entire line is evaluated, value of `c` becomes the sum of `a` and `b`. + + ## Watches The watch window is used to inspect variables and expressions. Expressions are From d6c68d691cecb5326b2686712ee04c67f62947e8 Mon Sep 17 00:00:00 2001 From: dsych Date: Tue, 2 Feb 2021 21:03:57 -0500 Subject: [PATCH 50/61] streamlining the docs --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a18a89f..a7519e8 100644 --- a/README.md +++ b/README.md @@ -50,7 +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/selection hover evaluation](#variable-eval) + * [Variable/selection hover evaluation](#variable-or-selection-hover-evaluation) * [Watches](#watches) * [Watch autocompletion](#watch-autocompletion) * [Stack Traces](#stack-traces) @@ -260,8 +260,8 @@ Workarounds are in place as follows: * Prompt Buffers - There are [`:VimspectorEval`](#console) and [`:VimspectorWatch`](#watches) * Functions - There are - [`:call vimspector#ShowTooltip()`](#variable-eval) and - [`:call vimspector#ShowTooltipForSelection()`](#variable-eval) + [`:call vimspector#ShowTooltip()`](#variable-or-selection-hover-evaluation) and + [`:call vimspector#ShowTooltipForSelection()`](#variable-or-selection-hover-evaluation) ## Windows differences @@ -891,13 +891,13 @@ breakpoint when it is hit. Scopes and variables are represented by the buffer `vimspector.Variables`. -## Variable/selection hover evaluation +## 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 hightlight a expression (e.g. a + b) and the result of the expression. +* Use your mouse to perform a visual selection of an expression (e.g. `a + b`) and get its result. * Call `vimspector#ShowTooltip()` or `vimspector#ShowTooltipForSelection()` to evaluate expressions without mouse (the only way to use this feature in nvim). -* Use regular nagivation keys to chose the current selection and `` (or leave the tooltip window) to close the tooltip. +* Use regular nagivation keys to chose the current selection; `` (or leave the tooltip window) to close the tooltip. Note: using a selection evaluation might lead to undesired consequences, since **the expression is actually executed**. E.g. `c = a + b;` once this entire line is evaluated, value of `c` becomes the sum of `a` and `b`. From ae137ecdd0a5daf47280e1672ff4fd4294fb0cde Mon Sep 17 00:00:00 2001 From: dsych Date: Sun, 7 Feb 2021 16:41:05 -0500 Subject: [PATCH 51/61] removing old balloon code --- autoload/vimspector/internal/balloon.vim | 17 ----------- python3/vimspector/debug_session.py | 23 ++------------- python3/vimspector/variables.py | 37 ------------------------ 3 files changed, 2 insertions(+), 75 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index b922397..86611f6 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -21,23 +21,6 @@ set cpoptions&vim scriptencoding utf-8 -" 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" ) )' ) -endfunction - -" Returns: py.ShowBalloon( winnr, expresssion ) -function! vimspector#internal#balloon#HoverTooltip() abort - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') - -endfunction - - let s:float_win = 0 let s:nvim_related_win = 0 " diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 18c4726..e6fff61 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -541,11 +541,11 @@ class DebugSession( object ): @IfConnected() def ShowTooltip( self, winnr, expression, is_hover ): - """Proxy: ballonexpr -> variables.ShowBallon""" + """Proxy: ballonexpr -> variables.ShowTooltip""" 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 @@ -561,25 +561,6 @@ class DebugSession( object ): def _CleanUpTooltip( self ): return self._variablesView._CleanUpTooltip() - @IfConnected() - def ShowBalloon( self, winnr, expression ): - """Proxy: ballonexpr -> variables.ShowBallon""" - frame = self._stackTraceView.GetCurrentFrame() - # Check if RIP is in a frame - if frame is None: - self._logger.debug( 'Balloon: Not in a stack frame' ) - return '' - - # Check if cursor in code window - if winnr != int( self._codeView._window.number ): - self._logger.debug( 'Winnr %s is not the code window %s', - winnr, - self._codeView._window.number ) - return '' - - # Return variable aware function - return self._variablesView.ShowBalloon( frame, expression ) - @IfConnected() def ExpandFrameOrThread( self ): self._stackTraceView.ExpandFrameOrThread() diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 7666954..c0cb2fd 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -616,43 +616,6 @@ 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', '' ), - 'Value: ' + result - ] - utils.DisplayBalloon( self._is_term, display ) - - def failure_handler( reason, message ): - display = [ reason ] - utils.DisplayBalloon( 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, From 51d551fe52ad5d12d22f81d9d110fc7cb506e243 Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 19 Feb 2021 23:39:54 -0500 Subject: [PATCH 52/61] replacing function calls with plug command --- autoload/vimspector.vim | 40 ++++-------------------- autoload/vimspector/internal/balloon.vim | 5 +++ plugin/vimspector.vim | 6 ++++ python3/vimspector/debug_session.py | 19 +++++++++++ python3/vimspector/utils.py | 23 +++++++++++++- 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 75ed961..9ac7ff1 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -523,43 +523,15 @@ function! vimspector#OnBufferCreated( file_name ) abort py3 _vimspector_session.RefreshSigns( vim.eval( 'a:file_name' ) ) endfunction -function! vimspector#ShowTooltipForSelection() range abort - let [start, end] = [[line("'<"), col("'<") - 1], [line("'>"), col("'>")]] - " restor cursor position, since command mode puts it to the begining of the - " current line when invoked from visual mode - call cursor(end) - - " retrive the lines selected in visual mode - let lines = getbufline(bufnr('%'), start[0], end[0]) - - if(len(lines) < 1) - return - endif - - " make sure the leave only the parts we care about if multiple lines are - " selected - let lines[0] = strcharpart(lines[0], start[1]) - let lines_len = len(lines) - 1 - - if len( lines ) == 1 - let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1] - start[1]) +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) else - let lines[lines_len] = strcharpart(lines[lines_len], 0, end[1]) + let expr = expand('') endif - let str = join(lines) - - call vimspector#ShowTooltip(str) -endfunction - -function! vimspector#ShowTooltip(...) abort - let str = '' - - if a:0 > 0 - let str = a:1 - endif - - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "winnr()" ) ) ,vim.eval( "expand(\"'.str.'\")" ), 0)') + return py3eval( '_vimspector_session.ShowEvalBalloon( int( vim.eval( "winnr()" ) ), "'.expr.'", 0 )' ) endfunction diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 86611f6..bc445ba 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -21,6 +21,11 @@ set cpoptions&vim scriptencoding utf-8 +" Returns: py.ShowBalloon( winnr, expresssion ) +function! vimspector#internal#balloon#HoverTooltip() abort + return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') +endfunction + let s:float_win = 0 let s:nvim_related_win = 0 " diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 05245cd..6465c21 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -60,6 +60,12 @@ nnoremap VimspectorStepOut nnoremap VimspectorRunToCursor \ :call vimspector#RunToCursor() +nnoremap VimspectorBalloonEval + \ :call vimspector#ShowEvalBalloon(0) + +xnoremap VimspectorBalloonEval + \ :call vimspector#ShowEvalBalloon(1) + if s:mappings ==# 'VISUAL_STUDIO' nmap VimspectorContinue nmap VimspectorStop diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index e6fff61..db14ebc 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -539,6 +539,25 @@ class DebugSession( object ): self._variablesView.DeleteWatch() + @IfConnected() + 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( 'Tooltip: Not in a stack frame' ) + return '' + + # Check if cursor in code window + if winnr != int( self._codeView._window.number ): + self._logger.debug( 'Winnr %s is not the code window %s', + winnr, + self._codeView._window.number ) + return '' + + # Return variable aware function + return self._variablesView.VariableEval( frame, expression, is_hover ) + + @IfConnected() def ShowTooltip( self, winnr, expression, is_hover ): """Proxy: ballonexpr -> variables.ShowTooltip""" diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 2bd3c08..287f919 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -34,6 +34,12 @@ _log_handler = logging.FileHandler( LOG_FILE, mode = 'w' ) _log_handler.setFormatter( logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) ) +# this is the "large number" that vim returns for col num, when getting '> mark +# with getpos() for line wise visual selection(V) +# see https://github.com/vim/vim/blob/eed9d46293f0842aad0d50ff3a526f9a48b12421/src/evalfunc.c#L4077 +# and https://github.com/vim/vim/blob/064095012c0b8e4e43e75834b337115950898fbf/src/vim.h#L1699 +MAX_COL = 2147483647 + def SetUpLogging( logger ): logger.setLevel( logging.DEBUG ) @@ -642,7 +648,7 @@ def DisplayBalloon( is_term, display, is_hover = False ): rc = int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format( - is_hover, json.dumps( display ) + int( is_hover ), json.dumps( display ) ) ) ) @@ -725,6 +731,21 @@ 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 as me why... + lines = vim.buffers[ bufnr ][ start_line - 1 : end_line ] + lines[ 0 ] = lines[ 0 ][ start_col : ] + # only trim the end if we are not in line-wise select mode + if( end_col < MAX_COL ): + lines[ -1 ] = lines[ -1 ][ : end_col + 1 ] + + return lines + + def DisplaySplash( api_prefix, splash, text ): if splash: return Call( f'vimspector#internal#{api_prefix}popup#UpdateSplash', From 0313efa06f009d1bb814c115b11bb0afa7f2db4d Mon Sep 17 00:00:00 2001 From: dsych Date: Fri, 19 Feb 2021 23:44:20 -0500 Subject: [PATCH 53/61] removing redundant check for array bounds --- python3/vimspector/utils.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 287f919..d03a344 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -34,13 +34,6 @@ _log_handler = logging.FileHandler( LOG_FILE, mode = 'w' ) _log_handler.setFormatter( logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) ) -# this is the "large number" that vim returns for col num, when getting '> mark -# with getpos() for line wise visual selection(V) -# see https://github.com/vim/vim/blob/eed9d46293f0842aad0d50ff3a526f9a48b12421/src/evalfunc.c#L4077 -# and https://github.com/vim/vim/blob/064095012c0b8e4e43e75834b337115950898fbf/src/vim.h#L1699 -MAX_COL = 2147483647 - - def SetUpLogging( logger ): logger.setLevel( logging.DEBUG ) if _log_handler not in logger.handlers: @@ -739,9 +732,7 @@ def GetVisualSelection( bufnr ): # don't as me why... lines = vim.buffers[ bufnr ][ start_line - 1 : end_line ] lines[ 0 ] = lines[ 0 ][ start_col : ] - # only trim the end if we are not in line-wise select mode - if( end_col < MAX_COL ): - lines[ -1 ] = lines[ -1 ][ : end_col + 1 ] + lines[ -1 ] = lines[ -1 ][ : end_col + 1 ] return lines From 44711899cbb68c6f2b10b11ef3a842b7659d8f14 Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 20 Feb 2021 00:15:31 -0500 Subject: [PATCH 54/61] moving stuff around --- autoload/vimspector/internal/balloon.vim | 90 ++++++++++++------------ python3/vimspector/debug_session.py | 19 ----- 2 files changed, 45 insertions(+), 64 deletions(-) diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index bc445ba..16ae4c8 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -21,9 +21,8 @@ set cpoptions&vim scriptencoding utf-8 -" Returns: py.ShowBalloon( winnr, expresssion ) function! vimspector#internal#balloon#HoverTooltip() abort - return py3eval('_vimspector_session.ShowTooltip(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') + return py3eval('_vimspector_session.ShowEvalBalloon(int( vim.eval( "v:beval_winnr" ) ) + 1 ,vim.eval( "v:beval_text"), 1)') endfunction let s:float_win = 0 @@ -35,6 +34,47 @@ let s:min_height = 1 let s:max_width = 80 let s:max_height = 20 +function! vimspector#internal#balloon#MouseFilter(winid, key) abort + if index(["\", "\<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('.line('.', a:winid).')') + let handled = 1 + endif + + return handled +endfunction + +function! vimspector#internal#balloon#CursorFilter(winid, key) abort + if a:key ==? "\" + " forward line number to python, since vim does not allow us to focus + " the correct window + call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') + return 1 + elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 + return vimspector#internal#balloon#MouseFilter( a:winid, a:key ) + endif + + return popup_filter_menu( a:winid, a:key ) +endfunction + function! vimspector#internal#balloon#Close() abort if has('nvim') call nvim_win_close(s:float_win, v:true) @@ -152,50 +192,10 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) abort " make sure we clean up the float after it loses focus augroup vimspector#internal#balloon#nvim_float autocmd! - autocmd WinLeave * :call vimspector#internal#balloon#Close() | autocmd! vimspector#internal#balloon#nvim_float + autocmd BufLeave * :call vimspector#internal#balloon#Close() | autocmd! vimspector#internal#balloon#nvim_float augroup END else - func! MouseFilter(winid, key) abort - if index(["\", "\<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('.line('.', a:winid).')') - let handled = 1 - endif - - return handled - endfunc - - func! CursorFilter(winid, key) abort - if a:key ==? "\" - " forward line number to python, since vim does not allow us to focus - " the correct window - call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') - return 1 - elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 - return MouseFilter( a:winid, a:key ) - endif - - return popup_filter_menu( a:winid, a:key ) - endfunc if s:float_win != 0 call vimspector#internal#balloon#Close() @@ -222,11 +222,11 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) abort endif if a:is_hover - let config['filter'] = 'MouseFilter' + let config['filter'] = 'vimspector#internal#balloon#MouseFilter' let config['mousemoved'] = [0, 0, 0] let s:float_win = popup_beval(body, config) else - let config['filter'] = 'CursorFilter' + let config['filter'] = 'vimspector#internal#balloon#CursorFilter' let config['moved'] = 'any' let config['cursorline'] = 1 let s:float_win = popup_atcursor(body, config) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index db14ebc..c91caa4 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -558,25 +558,6 @@ class DebugSession( object ): return self._variablesView.VariableEval( frame, expression, is_hover ) - @IfConnected() - def ShowTooltip( self, winnr, expression, is_hover ): - """Proxy: ballonexpr -> variables.ShowTooltip""" - frame = self._stackTraceView.GetCurrentFrame() - # Check if RIP is in a frame - if frame is None: - self._logger.debug( 'Tooltip: Not in a stack frame' ) - return '' - - # Check if cursor in code window - if winnr != int( self._codeView._window.number ): - self._logger.debug( 'Winnr %s is not the code window %s', - winnr, - self._codeView._window.number ) - return '' - - # Return variable aware function - return self._variablesView.VariableEval( frame, expression, is_hover ) - def _CleanUpTooltip( self ): return self._variablesView._CleanUpTooltip() From 7dcb15f11ccb89f3a0a29df8976d2cf9bfe71e0b Mon Sep 17 00:00:00 2001 From: dsych Date: Sat, 20 Feb 2021 00:27:11 -0500 Subject: [PATCH 55/61] cleaning up --- python3/vimspector/utils.py | 4 ++-- python3/vimspector/variables.py | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index d03a344..8010fb9 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -639,7 +639,7 @@ def DisplayBalloon( is_term, display, is_hover = False ): # Refer https://github.com/vim/vim/issues/1512#issuecomment-492070685 display = '\n'.join( display ) - rc = int( vim.eval( + created_win_id = int( vim.eval( "vimspector#internal#balloon#CreateTooltip({}, {})".format( int( is_hover ), json.dumps( display ) ) @@ -647,7 +647,7 @@ def DisplayBalloon( is_term, display, is_hover = False ): vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) - return rc + return created_win_id def GetBufferFilepath( buf ): diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index c0cb2fd..7890e1b 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -138,7 +138,7 @@ class View: self.lines = lines self.draw = draw self.syntax = None - if ( win is not None ): + if win is not None: self.buf = win.buffer utils.SetUpUIWindow( win ) @@ -285,7 +285,7 @@ class VariablesView( object ): }, } ) - def _DrawEval( self ): + def _DrawBalloonEval( self ): watch = self._variable_eval view = self._variable_eval_view @@ -308,7 +308,6 @@ class VariablesView( object ): # remove reference to old tooltip window self._variable_eval_view = None vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = None - return '' def VariableEval( self, frame, expression, is_hover ): """Callback to display variable under cursor `:h ballonexpr`""" @@ -338,7 +337,7 @@ class VariablesView( object ): { 'options': {}, 'buffer': vim.buffers[ float_buf_nr ] } ), {}, - self._DrawEval + self._DrawBalloonEval ) if watch.result.IsExpandable(): @@ -355,7 +354,7 @@ class VariablesView( object ): }, } ) - self._DrawEval() + self._DrawBalloonEval() def failure_handler( reason, message ): display = [ reason ] From cc84e159329fbed068754b0bb6d3237f0ce5d14d Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 00:34:51 +0000 Subject: [PATCH 56/61] Tidy up, refactor and fix some bugs --- autoload/vimspector.vim | 13 +- autoload/vimspector/internal/balloon.vim | 411 +++++++++++++---------- plugin/vimspector.vim | 7 +- python3/vimspector/debug_session.py | 8 +- python3/vimspector/utils.py | 17 +- python3/vimspector/variables.py | 61 ++-- 6 files changed, 307 insertions(+), 210 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 9ac7ff1..d4bcfa7 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -525,13 +525,18 @@ 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) + let expr = py3eval( '__import__( "vimspector", fromlist = [ "utils" ] )' + \ . '.utils.GetVisualSelection(' + \ . ' int( vim.eval( "winbufnr( winnr() )" ) ) )' ) + let expr = join( expr, '\n' ) else - let expr = expand('') + let expr = expand( '' ) endif - return py3eval( '_vimspector_session.ShowEvalBalloon( int( vim.eval( "winnr()" ) ), "'.expr.'", 0 )' ) + return py3eval( '_vimspector_session.ShowEvalBalloon(' + \ . ' int( vim.eval( "winnr()" ) ), "' + \ . expr + \ . '", 0 )' ) endfunction diff --git a/autoload/vimspector/internal/balloon.vim b/autoload/vimspector/internal/balloon.vim index 16ae4c8..5f25f3a 100644 --- a/autoload/vimspector/internal/balloon.vim +++ b/autoload/vimspector/internal/balloon.vim @@ -21,12 +21,8 @@ set cpoptions&vim scriptencoding utf-8 -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 - -let s:float_win = 0 -let s:nvim_related_win = 0 +let s:popup_win_id = 0 +let s:nvim_border_win_id = 0 " " tooltip dimensions let s:min_width = 1 @@ -34,173 +30,31 @@ let s:min_height = 1 let s:max_width = 80 let s:max_height = 20 -function! vimspector#internal#balloon#MouseFilter(winid, key) abort - if index(["\", "\<2-leftmouse>"], a:key) < 0 - return 0 - endif +let s:is_neovim = has( 'nvim' ) - 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('.line('.', a:winid).')') - let handled = 1 - endif - - return handled +" 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#CursorFilter(winid, key) abort - if a:key ==? "\" - " forward line number to python, since vim does not allow us to focus - " the correct window - call py3eval('_vimspector_session.ExpandVariable('.line('.', a:winid).')') - return 1 - elseif index( [ "\", "\<2-LeftMouse>" ], a:key ) >= 0 - return vimspector#internal#balloon#MouseFilter( a:winid, a:key ) - endif - - return popup_filter_menu( a:winid, a:key ) -endfunction - -function! vimspector#internal#balloon#Close() abort - if has('nvim') - call nvim_win_close(s:float_win, v:true) - call nvim_win_close(s:nvim_related_win, v:true) - - call vimspector#internal#balloon#CloseCallback() - else - call popup_close(s:float_win) - endif - -endfunction - -function! vimspector#internal#balloon#CloseCallback( ... ) abort - let s:float_win = 0 - let s:nvim_related_win = 0 - return py3eval('_vimspector_session._CleanUpTooltip()') -endfunction - -function! vimspector#internal#balloon#nvim_generate_border(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! vimspector#internal#balloon#nvim_resize_tooltip() abort - if !has('nvim') || s:float_win <= 0 || s:nvim_related_win <= 0 - return - endif - - noa call win_gotoid(s:float_win) - 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:float_win, opts) - - " resize the border window - let opts['width'] = width + 4 - let opts['height'] = height + 2 - call nvim_win_set_config(s:nvim_related_win, opts) - call nvim_buf_set_lines(nvim_win_get_buf(s:nvim_related_win), 0, -1, v:true, vimspector#internal#balloon#nvim_generate_border(width, height)) - -endfunction - -function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) abort +function! vimspector#internal#balloon#CreateTooltip( is_hover, ... ) abort let body = [] if a:0 > 0 let body = a:1 endif - if has('nvim') - " 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, vimspector#internal#balloon#nvim_generate_border(s:max_width, s:max_height)) - - " default the dimensions for now. they can be easily overwritten later - 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_related_win = nvim_open_win(buf_id, 0, opts) - call nvim_win_set_option(s:nvim_related_win, 'signcolumn', 'no') - call nvim_win_set_option(s:nvim_related_win, 'relativenumber', v:false) - call nvim_win_set_option(s:nvim_related_win, '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, body) - call nvim_buf_set_option(buf_id, 'modifiable', v:false) - let s:float_win = nvim_open_win(buf_id, v:false, opts) - - call nvim_win_set_option(s:float_win, 'wrap', v:false) - call nvim_win_set_option(s:float_win, 'cursorline', v:true) - call nvim_win_set_option(s:float_win, 'signcolumn', 'no') - call nvim_win_set_option(s:float_win, 'relativenumber', v:false) - call nvim_win_set_option(s:float_win, 'number', v:false) - - noautocmd call win_gotoid(s:float_win) - - nnoremap :call vimspector#ExpandVariable() - nnoremap :quit - nnoremap <2-LeftMouse>:call vimspector#ExpandVariable() - - " make sure we clean up the float after it loses focus - augroup vimspector#internal#balloon#nvim_float - autocmd! - autocmd BufLeave * :call vimspector#internal#balloon#Close() | autocmd! vimspector#internal#balloon#nvim_float - augroup END + if s:popup_win_id != 0 + call vimspector#internal#balloon#Close() + endif + if s:is_neovim + call s:CreateNeovimTooltip( body ) else - - if s:float_win != 0 - call vimspector#internal#balloon#Close() - endif - let config = { \ 'wrap': 0, \ 'filtermode': 'n', @@ -217,26 +71,241 @@ function! vimspector#internal#balloon#CreateTooltip(is_hover, ...) abort \ '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'] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ] + let config[ 'borderchars' ] = [ '─', '│', '─', '│', '╭', '╮', '┛', '╰' ] endif if a:is_hover - let config['filter'] = 'vimspector#internal#balloon#MouseFilter' - let config['mousemoved'] = [0, 0, 0] - let s:float_win = popup_beval(body, config) + 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:float_win = popup_atcursor(body, config) + 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:float_win + return s:popup_win_id endfunction +" Filters for vim {{{ +function! vimspector#internal#balloon#MouseFilter( winid, key ) abort + if a:key ==# "\" + call vimspector#internal#balloon#Close() + return 0 + endif + + if index( [ "\", "\<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 ==? "\" + " 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( [ "\", "\<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 + \ call vimspector#ExpandVariable() + nnoremap + \ quit + nnoremap <2-LeftMouse> + \ call vimspector#ExpandVariable() + + " Close the popup whenever we leave this window + augroup vimspector#internal#balloon#nvim_float + autocmd! + autocmd WinLeave + \ :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 diff --git a/plugin/vimspector.vim b/plugin/vimspector.vim index 6465c21..6691a2a 100644 --- a/plugin/vimspector.vim +++ b/plugin/vimspector.vim @@ -60,11 +60,12 @@ nnoremap VimspectorStepOut nnoremap VimspectorRunToCursor \ :call vimspector#RunToCursor() +" Eval for normal mode nnoremap VimspectorBalloonEval - \ :call vimspector#ShowEvalBalloon(0) - + \ :call vimspector#ShowEvalBalloon( 0 ) +" And for visual modes xnoremap VimspectorBalloonEval - \ :call vimspector#ShowEvalBalloon(1) + \ :call vimspector#ShowEvalBalloon( 1 ) if s:mappings ==# 'VISUAL_STUDIO' nmap VimspectorContinue diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index c91caa4..36e6feb 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -520,8 +520,8 @@ class DebugSession( object ): self._stackTraceView.SetCurrentThread() @IfConnected() - def ExpandVariable( self, lineNum = -1 ): - self._variablesView.ExpandVariable( lineNum ) + def ExpandVariable( self, buf = None, line_num = None ): + self._variablesView.ExpandVariable( buf, line_num ) @IfConnected() def AddWatch( self, expression ): @@ -558,8 +558,8 @@ class DebugSession( object ): return self._variablesView.VariableEval( frame, expression, is_hover ) - def _CleanUpTooltip( self ): - return self._variablesView._CleanUpTooltip() + def CleanUpTooltip( self ): + return self._variablesView.CleanUpTooltip() @IfConnected() def ExpandFrameOrThread( self ): diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index 8010fb9..e2f96b0 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -645,8 +645,6 @@ def DisplayBalloon( is_term, display, is_hover = False ): ) ) ) - vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) - return created_win_id @@ -728,11 +726,20 @@ 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 as me why... - lines = vim.buffers[ bufnr ][ start_line - 1 : end_line ] - lines[ 0 ] = lines[ 0 ][ start_col : ] + # 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 diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 7890e1b..74626ac 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -143,6 +143,12 @@ class View: utils.SetUpUIWindow( win ) +class BufView( View ): + def __init__( self, buf, lines, draw ): + super().__init__( None, lines, draw ) + self.buf = buf + + class VariablesView( object ): def __init__( self, variables_win, watches_win ): self._logger = logging.getLogger( __name__ ) @@ -219,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 ): @@ -234,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 ): @@ -302,9 +311,14 @@ class VariablesView( object ): watch, is_short = True ) - vim.eval( "vimspector#internal#balloon#nvim_resize_tooltip()" ) + vim.eval( "vimspector#internal#balloon#ResizeTooltip()" ) - def _CleanUpTooltip( self ) : + 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 @@ -322,22 +336,17 @@ class VariablesView( object ): else: watch.result.Update( message[ 'body' ] ) - float_win_id = utils.DisplayBalloon( self._is_term, [], is_hover ) + popup_win_id = utils.DisplayBalloon( self._is_term, [], is_hover ) # record the global eval window id - vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( float_win_id ) - float_buf_nr = int( vim.eval( "winbufnr({})".format( float_win_id ) ) ) + vim.vars[ 'vimspector_session_windows' ][ 'eval' ] = int( popup_win_id ) + popup_bufnr = int( vim.eval( "winbufnr({})".format( popup_win_id ) ) ) - # since vim's popup cant be focused there is no way - # to get a reference to its window - # we will emulate python's window object ourselves - self._variable_eval_view = View( - type( - '__vim__window__', - ( object, ), - { 'options': {}, 'buffer': vim.buffers[ float_buf_nr ] } - ), - {}, - self._DrawBalloonEval + # 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(): @@ -439,21 +448,27 @@ class VariablesView( object ): watch.result = WatchFailure( reason ) self._DrawWatches() - def ExpandVariable( self, lineNum = -1 ): - 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: + 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 lineNum <= 0 else lineNum - 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 From 5a1eb9250a16491b520d980659c39944ebb65943 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 16:26:48 +0000 Subject: [PATCH 57/61] Fix test now that we're using a mapping --- tests/variables.test.vim | 40 ++++++++++++++++++++-------------------- tests/vimrc | 4 +++- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/tests/variables.test.vim b/tests/variables.test.vim index ae51ad8..a43fafd 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -601,10 +601,14 @@ function! Test_VariableEval() call vimspector#StepOver() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) + " leader is , + xmap d VimspectorBalloonEval + nmap d VimspectorBalloonEval + "evaluate the prev line - call setpos('.', [ 0, 24, 8 ]) + call setpos( '.', [ 0, 24, 8 ] ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) - call vimspector#ShowTooltip() + call feedkeys( ',d', 'xt' ) call WaitForAssert( {-> \ assert_notequal( v:none, g:vimspector_session_windows.eval ) @@ -633,19 +637,10 @@ function! Test_VariableEval() \ } ) " test selection - call setpos('.', [ 0, 24, 8 ]) + call setpos( '.', [ 0, 24, 8 ] ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) - " enter visual mode - " this is a hack, since usually, when user enters command mode from inside - " visual mode, the latter is immediately interrupted and the '<' '>' marks are - " set. for some odd reason, visual mode is not interupted from the script, - " so we need to manually escape and re-trigger previous visual selection - call execute('normal v') - call feedkeys("lllll\", 'xt') - call execute('normal gv') - - call vimspector#ShowTooltipForSelection() + call feedkeys( 'viw,d', 'xt' ) call WaitForAssert( {-> \ assert_notequal( v:none, g:vimspector_session_windows.eval ) @@ -667,18 +662,19 @@ function! Test_VariableEval() \ } ) "Close - " we need to send esc twice because of the weird interactions between visual - " mode and tests - call feedkeys( "\\", 'xt' ) + call feedkeys( "\", 'xt' ) call WaitForAssert( {-> \ assert_equal( v:none, g:vimspector_session_windows.eval ) \ } ) + " Get back to normal mode + call feedkeys( "\", 'xt' ) + " Evaluation error - call setpos('.', [ 0, 25, 1 ]) + call setpos( '.', [ 0, 25, 1 ] ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 25, 1 ) - call vimspector#ShowTooltip() + call feedkeys( ',d', 'xt' ) call WaitForAssert( {-> \ assert_notequal( v:none, g:vimspector_session_windows.eval ) @@ -715,10 +711,14 @@ function! Test_VariableEvalExpand() call vimspector#StepOver() call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 26, 1 ) + " leader is , + xmap d VimspectorBalloonEval + nmap d VimspectorBalloonEval + "evaluate the prev line - call setpos('.', [ 0, 24, 8 ]) + call setpos( '.', [ 0, 24, 8 ] ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 24, 8 ) - call vimspector#ShowTooltip() + call feedkeys( ',d', 'xt' ) call WaitForAssert( {-> \ assert_notequal( v:none, g:vimspector_session_windows.eval ) diff --git a/tests/vimrc b/tests/vimrc index e362824..c65efa8 100644 --- a/tests/vimrc +++ b/tests/vimrc @@ -1,8 +1,10 @@ let g:vimspector_test_plugin_path = expand( ':p:h:h' ) set mouse=a set noequalalways +let mapleader = ',' +let maplocalleader = "\" -let &rtp = &rtp . ',' . g:vimspector_test_plugin_path +let &runtimepath = &runtimepath . ',' . g:vimspector_test_plugin_path filetype plugin indent on syntax enable From 323e22b8a97a1d226d6e076c5476eda075a99ad5 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 16:58:45 +0000 Subject: [PATCH 58/61] Update readme --- README.md | 50 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a7519e8..8475ab2 100644 --- a/README.md +++ b/README.md @@ -125,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 `VimspectorBalloonEval`) - interactive debug console with autocompletion - launch debugee within Vim's embedded terminal - logging/stdout display @@ -231,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. @@ -250,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) -* Tooltips - 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: @@ -259,9 +260,18 @@ Workarounds are in place as follows: [`:VimspectorReset`](#closing-debugger) * Prompt Buffers - There are [`:VimspectorEval`](#console) and [`:VimspectorWatch`](#watches) -* Functions - There are - [`:call vimspector#ShowTooltip()`](#variable-or-selection-hover-evaluation) and - [`:call vimspector#ShowTooltipForSelection()`](#variable-or-selection-hover-evaluation) +* Balloons - There is the `VimspectorBalloonEval` mapping. There is no +default mapping for this, so I recommend something like this to get variable +display in a popup: + +```viml +" mnemonic 'di' = 'debug inspect' (pick your own, if you prefer!) + +" for normal mode - the word under the cursor +nmap di VimspectorBalloonEval +" for visual mode, the visually selected text +xmap di VimspectorBalloonEval +``` ## Windows differences @@ -654,6 +664,7 @@ features to set your own mappings. To that end, Vimspector defines the following * `VimspectorStepInto` * `VimspectorStepOut` * `VimspectorRunToCursor` +* `VimspectorBalloonEval` These map roughly 1-1 with the API functions below. @@ -716,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 `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 di VimspectorBalloonEval +" for visual mode, the visually selected text +xmap di VimspectorBalloonEval +``` + # Usage and API This section defines detailed usage instructions, organised by feature. For most @@ -894,13 +917,16 @@ 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. -* Call `vimspector#ShowTooltip()` or `vimspector#ShowTooltipForSelection()` to evaluate expressions without mouse (the only way to use this feature in nvim). -* Use regular nagivation keys to chose the current selection; `` (or leave the tooltip window) to close the tooltip. - -Note: using a selection evaluation might lead to undesired consequences, since **the expression is actually executed**. E.g. `c = a + b;` once this entire line is evaluated, value of `c` becomes the sum of `a` and `b`. +* 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 + `VimspectorBalloonEval` to manually trigger the popup. +* Use regular nagivation keys (`j`, `k`) to chose the current selection; `` + (or leave the tooltip window) to close the tooltip. +![variable eval hover](https://puremourning.github.io/vimspector-web/img/vimspector-variable-eval-hover.png) ## Watches From 6b546cd621d171883d152f01c46ce6c8fcc1c77b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 16:59:41 +0000 Subject: [PATCH 59/61] Update TOC --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8475ab2..b3088b7 100644 --- a/README.md +++ b/README.md @@ -50,7 +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/selection hover evaluation](#variable-or-selection-hover-evaluation) + * [Variable or selection hover evaluation](#variable-or-selection-hover-evaluation) * [Watches](#watches) * [Watch autocompletion](#watch-autocompletion) * [Stack Traces](#stack-traces) @@ -90,7 +90,7 @@ For detailed explanatin of the `.vimspector.json` format, see the * [Example](#example) * [FAQ](#faq) - + From 4958de92d39809236433792197470cc672453e2b Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 18:05:59 +0000 Subject: [PATCH 60/61] Fix flake8 error --- python3/vimspector/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index e2f96b0..4022315 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -34,6 +34,7 @@ _log_handler = logging.FileHandler( LOG_FILE, mode = 'w' ) _log_handler.setFormatter( logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) ) + def SetUpLogging( logger ): logger.setLevel( logging.DEBUG ) if _log_handler not in logger.handlers: From 5754e96067a5048308b84f402847776d1cac3d59 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 21 Feb 2021 18:17:14 +0000 Subject: [PATCH 61/61] FixUp: Change of mapleader --- tests/breakpoints.test.vim | 8 ++++---- tests/breakpoints_doublewidth.test.vim | 8 ++++---- tests/mappings.test.vim | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/breakpoints.test.vim b/tests/breakpoints.test.vim index 2e3eef8..877c1cf 100644 --- a/tests/breakpoints.test.vim +++ b/tests/breakpoints.test.vim @@ -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( "\\\argc==0\\", 'xt' ) + " Add the conditional breakpoint (note , is the mapleader) + call feedkeys( ",\argc==0\\", '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( "\\\\3\", 'xt' ) + " Add the conditional breakpoint (3 times) (note , is the mapleader) + call feedkeys( ",\\3\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 14, diff --git a/tests/breakpoints_doublewidth.test.vim b/tests/breakpoints_doublewidth.test.vim index c646c3e..4bc0571 100644 --- a/tests/breakpoints_doublewidth.test.vim +++ b/tests/breakpoints_doublewidth.test.vim @@ -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( "\\\argc==0\\", 'xt' ) + " Add the conditional breakpoint (, is mapleader) + call feedkeys( ",\argc==0\\", '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( "\\\\3\", 'xt' ) + " Add the conditional breakpoint (3 times) (, is mapleader) + call feedkeys( ",\\3\", 'xt' ) call vimspector#test#signs#AssertSignGroupSingletonAtLine( \ 'VimspectorBP', \ 14, diff --git a/tests/mappings.test.vim b/tests/mappings.test.vim index fcd19fe..0f9995d 100644 --- a/tests/mappings.test.vim +++ b/tests/mappings.test.vim @@ -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( "\\\", 'xt' ) + call feedkeys( ",\", 'xt' ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 9, 1 ) call WaitForAssert( {-> \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 9 )