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: