mirror of
https://github.com/neovim/neovim
synced 2025-07-17 09:41:46 +00:00
feat(termdebug): improve :Evaluate
floating window (#26621)
Problem: - The :Evaluate result window is not cleaned up when the cursor moves. - This window is not focusable. Solution: Replace the old implementation from autozimu/LanguageClient-neovim with vim.lsp.util.open_floating_preview and implement custom focusing logic. Also remove g:termdebug_useFloatingHover option now that it's working correctly.
This commit is contained in:
@ -240,7 +240,7 @@ gdb window and use a "print" command, e.g.: >
|
|||||||
If mouse pointer movements are working, Vim will also show a balloon when the
|
If mouse pointer movements are working, Vim will also show a balloon when the
|
||||||
mouse rests on text that can be evaluated by gdb.
|
mouse rests on text that can be evaluated by gdb.
|
||||||
You can also use the "K" mapping that will either use Nvim floating windows
|
You can also use the "K" mapping that will either use Nvim floating windows
|
||||||
if available to show the results or print below the status bar.
|
to show the results.
|
||||||
|
|
||||||
Now go back to the source window and put the cursor on the first line after
|
Now go back to the source window and put the cursor on the first line after
|
||||||
the for loop, then type: >
|
the for loop, then type: >
|
||||||
@ -320,6 +320,8 @@ Inspecting variables ~
|
|||||||
|
|
||||||
This is similar to using "print" in the gdb window.
|
This is similar to using "print" in the gdb window.
|
||||||
You can usually shorten `:Evaluate` to `:Ev`.
|
You can usually shorten `:Evaluate` to `:Ev`.
|
||||||
|
The result is displayed in a floating window.
|
||||||
|
You can move the cursor to this window by running `:Evaluate` (or `K`) again.
|
||||||
|
|
||||||
|
|
||||||
Navigating stack frames ~
|
Navigating stack frames ~
|
||||||
@ -475,10 +477,6 @@ If the command needs an argument use a List: >vim
|
|||||||
If there is no g:termdebug_config you can use: >vim
|
If there is no g:termdebug_config you can use: >vim
|
||||||
let g:termdebugger = ['rr', 'replay', '--']
|
let g:termdebugger = ['rr', 'replay', '--']
|
||||||
|
|
||||||
To not use Nvim floating windows for previewing variable evaluation, set the
|
|
||||||
`g:termdebug_useFloatingHover` variable like this: >vim
|
|
||||||
let g:termdebug_useFloatingHover = 0
|
|
||||||
|
|
||||||
If you are a mouse person, you can also define a mapping using your right
|
If you are a mouse person, you can also define a mapping using your right
|
||||||
click to one of the terminal command like evaluate the variable under the
|
click to one of the terminal command like evaluate the variable under the
|
||||||
cursor: >vim
|
cursor: >vim
|
||||||
|
165
runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
vendored
165
runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
vendored
@ -35,21 +35,14 @@
|
|||||||
" The communication with gdb uses GDB/MI. See:
|
" The communication with gdb uses GDB/MI. See:
|
||||||
" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html
|
" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html
|
||||||
"
|
"
|
||||||
" For neovim compatibility, the vim specific calls were replaced with neovim
|
" NEOVIM COMPATIBILITY
|
||||||
" specific calls:
|
|
||||||
" term_start -> termopen
|
|
||||||
" term_sendkeys -> chansend
|
|
||||||
" term_getline -> getbufline
|
|
||||||
" job_info && term_getjob -> using linux command ps to get the tty
|
|
||||||
" balloon -> nvim floating window
|
|
||||||
"
|
"
|
||||||
" The code for opening the floating window was taken from the beautiful
|
" The vim specific functionalities were replaced with neovim specific calls:
|
||||||
" implementation of LanguageClient-Neovim:
|
" - term_start -> termopen
|
||||||
" https://github.com/autozimu/LanguageClient-neovim/blob/0ed9b69dca49c415390a8317b19149f97ae093fa/autoload/LanguageClient.vim#L304
|
" - term_sendkeys -> chansend
|
||||||
"
|
" - term_getline -> getbufline
|
||||||
" Neovim terminal also works seamlessly on windows, which is why the ability
|
" - job_info && term_getjob -> nvim_get_chan_info
|
||||||
" Author: Bram Moolenaar
|
" - balloon -> vim.lsp.util.open_floating_preview
|
||||||
" Copyright: Vim license applies, see ":help license"
|
|
||||||
|
|
||||||
" In case this gets sourced twice.
|
" In case this gets sourced twice.
|
||||||
if exists(':Termdebug')
|
if exists(':Termdebug')
|
||||||
@ -1313,7 +1306,14 @@ endfunc
|
|||||||
|
|
||||||
" :Evaluate - evaluate what is specified / under the cursor
|
" :Evaluate - evaluate what is specified / under the cursor
|
||||||
func s:Evaluate(range, arg)
|
func s:Evaluate(range, arg)
|
||||||
|
if s:eval_float_win_id > 0 && nvim_win_is_valid(s:eval_float_win_id)
|
||||||
|
\ && a:range == 0 && empty(a:arg)
|
||||||
|
call nvim_set_current_win(s:eval_float_win_id)
|
||||||
|
return
|
||||||
|
endif
|
||||||
let expr = s:GetEvaluationExpression(a:range, a:arg)
|
let expr = s:GetEvaluationExpression(a:range, a:arg)
|
||||||
|
let s:evalFromBalloonExpr = 1
|
||||||
|
let s:evalFromBalloonExprResult = ''
|
||||||
let s:ignoreEvalError = 0
|
let s:ignoreEvalError = 0
|
||||||
call s:SendEval(expr)
|
call s:SendEval(expr)
|
||||||
endfunc
|
endfunc
|
||||||
@ -1370,6 +1370,8 @@ let s:ignoreEvalError = 0
|
|||||||
let s:evalFromBalloonExpr = 0
|
let s:evalFromBalloonExpr = 0
|
||||||
let s:evalFromBalloonExprResult = ''
|
let s:evalFromBalloonExprResult = ''
|
||||||
|
|
||||||
|
let s:eval_float_win_id = -1
|
||||||
|
|
||||||
" Handle the result of data-evaluate-expression
|
" Handle the result of data-evaluate-expression
|
||||||
func s:HandleEvaluate(msg)
|
func s:HandleEvaluate(msg)
|
||||||
let value = a:msg
|
let value = a:msg
|
||||||
@ -1392,9 +1394,15 @@ func s:HandleEvaluate(msg)
|
|||||||
let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
|
let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
|
||||||
else
|
else
|
||||||
let s:evalFromBalloonExprResult .= ' = ' . value
|
let s:evalFromBalloonExprResult .= ' = ' . value
|
||||||
endif
|
endif
|
||||||
let s:evalFromBalloonExprResult = split(s:evalFromBalloonExprResult, '\\n')
|
" NEOVIM:
|
||||||
call s:OpenHoverPreview(s:evalFromBalloonExprResult, v:null)
|
" - Result pretty-printing is not implemented. Vim prettifies the result
|
||||||
|
" with balloon_split(), which is not ported to nvim.
|
||||||
|
" - Manually implement window focusing. Sometimes the result of pointer
|
||||||
|
" evaluation arrives in two separate messages, one for the address
|
||||||
|
" itself and the other for the value in that address. So with the stock
|
||||||
|
" focus option, the second message will focus the window containing the
|
||||||
|
" first message.
|
||||||
let s:eval_float_win_id = luaeval('select(2, vim.lsp.util.open_floating_preview(_A))', [s:evalFromBalloonExprResult])
|
let s:eval_float_win_id = luaeval('select(2, vim.lsp.util.open_floating_preview(_A))', [s:evalFromBalloonExprResult])
|
||||||
else
|
else
|
||||||
echomsg '"' . s:evalexpr . '": ' . value
|
echomsg '"' . s:evalexpr . '": ' . value
|
||||||
@ -1403,132 +1411,9 @@ func s:HandleEvaluate(msg)
|
|||||||
if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
|
if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
|
||||||
" Looks like a pointer, also display what it points to.
|
" Looks like a pointer, also display what it points to.
|
||||||
let s:ignoreEvalError = 1
|
let s:ignoreEvalError = 1
|
||||||
call s:SendEval('*' . s:evalexpr)
|
|
||||||
else
|
|
||||||
call s:SendEval('*' . s:evalexpr)
|
call s:SendEval('*' . s:evalexpr)
|
||||||
endif
|
endif
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
function! s:ShouldUseFloatWindow() abort
|
|
||||||
if exists('*nvim_open_win') && (get(g:, 'termdebug_useFloatingHover', 1) == 1)
|
|
||||||
return v:true
|
|
||||||
else
|
|
||||||
return v:false
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:CloseFloatingHoverOnCursorMove(win_id, opened) abort
|
|
||||||
if getpos('.') == a:opened
|
|
||||||
" Just after opening floating window, CursorMoved event is run.
|
|
||||||
" To avoid closing floating window immediately, check the cursor
|
|
||||||
" was really moved
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
autocmd! nvim_termdebug_close_hover
|
|
||||||
let winnr = win_id2win(a:win_id)
|
|
||||||
if winnr == 0
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
call nvim_win_close(a:win_id, v:true)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:CloseFloatingHoverOnBufEnter(win_id, bufnr) abort
|
|
||||||
let winnr = win_id2win(a:win_id)
|
|
||||||
if winnr == 0
|
|
||||||
" Float window was already closed
|
|
||||||
autocmd! nvim_termdebug_close_hover
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
if winnr == winnr()
|
|
||||||
" Cursor is moving into floating window. Do not close it
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
if bufnr('%') == a:bufnr
|
|
||||||
" When current buffer opened hover window, it's not another buffer. Skipped
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
autocmd! nvim_termdebug_close_hover
|
|
||||||
call nvim_win_close(a:win_id, v:true)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" Open preview window. Window is open in:
|
|
||||||
" - Floating window on Neovim (0.4.0 or later)
|
|
||||||
" - Preview window on Neovim (0.3.0 or earlier) or Vim
|
|
||||||
function! s:OpenHoverPreview(lines, filetype) abort
|
|
||||||
" Use local variable since parameter is not modifiable
|
|
||||||
let lines = a:lines
|
|
||||||
let bufnr = bufnr('%')
|
|
||||||
|
|
||||||
let use_float_win = s:ShouldUseFloatWindow()
|
|
||||||
if use_float_win
|
|
||||||
let pos = getpos('.')
|
|
||||||
|
|
||||||
" Calculate width and height
|
|
||||||
let width = 0
|
|
||||||
for index in range(len(lines))
|
|
||||||
let line = lines[index]
|
|
||||||
let lw = strdisplaywidth(line)
|
|
||||||
if lw > width
|
|
||||||
let width = lw
|
|
||||||
endif
|
|
||||||
let lines[index] = line
|
|
||||||
endfor
|
|
||||||
|
|
||||||
let height = len(lines)
|
|
||||||
|
|
||||||
" Calculate anchor
|
|
||||||
" Prefer North, but if there is no space, fallback into South
|
|
||||||
let bottom_line = line('w0') + winheight(0) - 1
|
|
||||||
if pos[1] + height <= bottom_line
|
|
||||||
let vert = 'N'
|
|
||||||
let row = 1
|
|
||||||
else
|
|
||||||
let vert = 'S'
|
|
||||||
let row = 0
|
|
||||||
endif
|
|
||||||
|
|
||||||
" Prefer West, but if there is no space, fallback into East
|
|
||||||
if pos[2] + width <= &columns
|
|
||||||
let hor = 'W'
|
|
||||||
let col = 0
|
|
||||||
else
|
|
||||||
let hor = 'E'
|
|
||||||
let col = 1
|
|
||||||
endif
|
|
||||||
|
|
||||||
let buf = nvim_create_buf(v:false, v:true)
|
|
||||||
call nvim_buf_set_lines(buf, 0, -1, v:true, lines)
|
|
||||||
" using v:true for second argument of nvim_open_win make the floating
|
|
||||||
" window disappear
|
|
||||||
let float_win_id = nvim_open_win(buf, v:false, {
|
|
||||||
\ 'relative': 'cursor',
|
|
||||||
\ 'anchor': vert . hor,
|
|
||||||
\ 'row': row,
|
|
||||||
\ 'col': col,
|
|
||||||
\ 'width': width,
|
|
||||||
\ 'height': height,
|
|
||||||
\ 'style': 'minimal',
|
|
||||||
\ })
|
|
||||||
|
|
||||||
if a:filetype isnot v:null
|
|
||||||
call nvim_set_option_value('filetype', a:filetype, { 'win' : float_win_id })
|
|
||||||
endif
|
|
||||||
|
|
||||||
call nvim_set_option_value('modified', v:false, { 'buf' : buf })
|
|
||||||
call nvim_set_option_value('modifiable', v:false, { 'buf' : buf })
|
|
||||||
|
|
||||||
" Unlike preview window, :pclose does not close window. Instead, close
|
|
||||||
" hover window automatically when cursor is moved.
|
|
||||||
let call_after_move = printf('<SID>CloseFloatingHoverOnCursorMove(%d, %s)', float_win_id, string(pos))
|
|
||||||
let call_on_bufenter = printf('<SID>CloseFloatingHoverOnBufEnter(%d, %d)', float_win_id, bufnr)
|
|
||||||
augroup nvim_termdebug_close_hover
|
|
||||||
execute 'autocmd CursorMoved,CursorMovedI,InsertEnter <buffer> call ' . call_after_move
|
|
||||||
execute 'autocmd BufEnter * call ' . call_on_bufenter
|
|
||||||
augroup END
|
|
||||||
else
|
|
||||||
echomsg a:lines[0]
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" Handle an error.
|
" Handle an error.
|
||||||
func s:HandleError(msg)
|
func s:HandleError(msg)
|
||||||
|
Reference in New Issue
Block a user