patch 9.1.0817: termdebug: cannot evaluate expr in a popup

Problem:  termdebug: cannot evaluate expr in a popup
Solution: enhance termdebug plugin and allow to evaluate expressions in
          a popup window, add a unit test (Peter Wolf).

fixes: #15877
closes: #15933

Signed-off-by: Peter Wolf <pwolf2310@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Peter Wolf
2024-10-27 21:51:14 +01:00
committed by Christian Brabandt
parent 2abec431e1
commit 8f1d09828a
9 changed files with 230 additions and 12 deletions

View File

@ -10664,6 +10664,7 @@ termdebug-stepping terminal.txt /*termdebug-stepping*
termdebug-timeout terminal.txt /*termdebug-timeout*
termdebug-variables terminal.txt /*termdebug-variables*
termdebug_disasm_window terminal.txt /*termdebug_disasm_window*
termdebug_evaluate_in_popup terminal.txt /*termdebug_evaluate_in_popup*
termdebug_map_K terminal.txt /*termdebug_map_K*
termdebug_map_minus terminal.txt /*termdebug_map_minus*
termdebug_map_plus terminal.txt /*termdebug_map_plus*

View File

@ -1,4 +1,4 @@
*terminal.txt* For Vim version 9.1. Last change: 2024 Jul 28
*terminal.txt* For Vim version 9.1. Last change: 2024 Oct 27
VIM REFERENCE MANUAL by Bram Moolenaar
@ -1537,6 +1537,7 @@ If there is no g:termdebug_config you can use: >
<
However, the latter form will be deprecated in future releases.
Mappings ~
The termdebug plugin enables a few default mappings. All those mappings
are reset to their original values once the termdebug session concludes.
@ -1591,6 +1592,7 @@ If the current window has enough horizontal space, it will be vertically split
and the Var window will be shown side by side with the source code window (and
the height options won't be used).
Communication ~
*termdebug-communication*
There is another, hidden, buffer, which is used for Vim to communicate with
@ -1675,10 +1677,11 @@ If there is no g:termdebug_config you can use: >
However, the latter form will be deprecated in future releases.
Change default signs ~
*termdebug_signs*
Termdebug uses the hex number of the breakpoint ID in the signcolumn to
represent breakpoints. if it is greater than "0xFF", then it will be displayed
represent breakpoints. If it is greater than "0xFF", then it will be displayed
as "F+", due to we really only have two screen cells for the sign.
If you want to customize the breakpoint signs: >
@ -1716,4 +1719,18 @@ Set the wide value to 1 to use a vertical split without ever changing
'columns'. This is useful when the terminal can't be resized by Vim.
Evaluate in Popup Window at Cursor ~
*termdebug_evaluate_in_popup*
By default |:Evaluate| will simply echo its output. For larger entities this
might become difficult to read or even truncated.
Alternatively, the evaluation result may be output into a popup window at the
current cursor position: >
let g:termdebug_config['evaluate_in_popup'] = v:true
This can also be used in a "one-shot" manner: >
func OnCursorHold()
let g:termdebug_config['evaluate_in_popup'] = v:true
:Evaluate
let g:termdebug_config['evaluate_in_popup'] = v:false
endfunc
<
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -121,7 +121,9 @@ var breakpoint_locations: dict<any>
var BreakpointSigns: list<string>
var evalFromBalloonExpr: bool
var evalFromBalloonExprResult: string
var evalInPopup: bool
var evalPopupId: number
var evalExprResult: string
var ignoreEvalError: bool
var evalexpr: string
# Remember the old value of 'signcolumn' for each buffer that it's set in, so
@ -202,7 +204,9 @@ def InitScriptVariables()
BreakpointSigns = []
evalFromBalloonExpr = false
evalFromBalloonExprResult = ''
evalInPopup = false
evalPopupId = -1
evalExprResult = ''
ignoreEvalError = false
evalexpr = ''
# Remember the old value of 'signcolumn' for each buffer that it's set in, so
@ -1478,10 +1482,23 @@ def SendEval(expr: string)
evalexpr = exprLHS
enddef
# Returns whether to evaluate in a popup or not, defaults to false.
def EvaluateInPopup(): bool
if exists('g:termdebug_config')
return get(g:termdebug_config, 'evaluate_in_popup', false)
endif
return false
enddef
# :Evaluate - evaluate what is specified / under the cursor
def Evaluate(range: number, arg: string)
var expr = GetEvaluationExpression(range, arg)
echom $"expr: {expr}"
if EvaluateInPopup()
evalInPopup = true
evalExprResult = ''
else
echomsg $'expr: {expr}'
endif
ignoreEvalError = false
SendEval(expr)
enddef
@ -1541,6 +1558,37 @@ def Balloon_show(expr: string)
endif
enddef
def Popup_format(expr: string): list<string>
var lines = expr
->substitute('{', '{\n', 'g')
->substitute('}', '\n}', 'g')
->substitute(',', ',\n', 'g')
->split('\n')
var indentation = 0
var formatted_lines = []
for line in lines
var stripped = line->substitute('^\s\+', '', '')
if stripped =~ '^}'
indentation -= 2
endif
formatted_lines->add(repeat(' ', indentation) .. stripped)
if stripped =~ '{$'
indentation += 2
endif
endfor
return formatted_lines
enddef
def Popup_show(expr: string)
var formatted = Popup_format(expr)
if evalPopupId != -1
popup_close(evalPopupId)
endif
# Specifying the line is necessary, as the winbar seems to cause issues
# otherwise. I.e., the popup would be shown one line too high.
evalPopupId = popup_atcursor(formatted, {'line': 'cursor-1'})
enddef
def HandleEvaluate(msg: string)
var value = msg
->substitute('.*value="\(.*\)"', '\1', '')
@ -1555,13 +1603,12 @@ def HandleEvaluate(msg: string)
#\ ->substitute('\\0x00', NullRep, 'g')
#\ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g')
->substitute(NullRepl, '\\000', 'g')
if evalFromBalloonExpr
if empty(evalFromBalloonExprResult)
evalFromBalloonExprResult = $'{evalexpr}: {value}'
if evalFromBalloonExpr || evalInPopup
if empty(evalExprResult)
evalExprResult = $'{evalexpr}: {value}'
else
evalFromBalloonExprResult ..= $' = {value}'
evalExprResult ..= $' = {value}'
endif
Balloon_show(evalFromBalloonExprResult)
else
echomsg $'"{evalexpr}": {value}'
endif
@ -1570,8 +1617,12 @@ def HandleEvaluate(msg: string)
# Looks like a pointer, also display what it points to.
ignoreEvalError = true
SendEval($'*{evalexpr}')
else
elseif evalFromBalloonExpr
Balloon_show(evalExprResult)
evalFromBalloonExpr = false
elseif evalInPopup
Popup_show(evalExprResult)
evalInPopup = false
endif
enddef
@ -1588,7 +1639,7 @@ def TermDebugBalloonExpr(): string
return ''
endif
evalFromBalloonExpr = true
evalFromBalloonExprResult = ''
evalExprResult = ''
ignoreEvalError = true
var expr = CleanupExpr(v:beval_text)
SendEval(expr)

View File

@ -0,0 +1,20 @@
|U+0&#ffffff0|s|i|n|g| |h|o|s|t| |l|i|b|t|h|r|e|a|d|_|d|b| |l|i|b|r|a|r|y| |"|/+0#00e0003&|l|i|b|/|x|8|6|_|6|4|-|l|i|n|u|x|-|g|n|u|/|l|i|b|t|h|r|e|a|d|_|d|b|.|s|o|.|1|"+0#0000000&|.|
@75
|B|r|e|a|k|p|o|i|n|t| |1|,| |m+0#e0e0004&|a|i|n| +0#0000000&|(|a+0#00e0e07&|r|g|c|=+0#0000000&|1|,| |a+0#00e0e07&|r|g|v|=+0#0000000&|0|x|7|f@6|d|e|f|8|)| @26
@4|a|t| |X+0#00e0003&|T|D|_|e|v|a|l|u|a|t|e|_|i|n|_|p|o|p|u|p|.|c|:+0#0000000&|9| @42
|9| @8|r+2#0000e05&|e|t|u|r|n| +0#0000000&|0+0#e000e06&|;+0#e000002&| +0#0000000&@55
@75
|g+0#ffffff16#00e0003|d|b| |[|r|u|n@1|i|n|g|]| @43|1|,|1| @11|T|o|p
| +0#0000000#ffffff0@74
@75
@75
@75
@75
|d+0#ffffff16#00e0003|e|b|u|p+0#0000001#ffd7ff255|:| |{| @3|g+0#ffffff16#00e0003|r|a|m| |[|a|c|t|i|v|e|]| @31|0|,|0|-|1| @9|A|l@1
| +0#0000000#e0e0e08| +2#ffffff16#6c6c6c255|S|t| +0#0000001#ffd7ff255@1|x| |=| |1|,|x+2#ffffff16#6c6c6c255|t| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|F|i|n|i|s|h| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|C|o|n|t| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|S|t|o|p| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|E|v|a|l| | +0#0000000#e0e0e08@25
| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@1| +0#0000001#ffd7ff255@1|y| |=| |2| |o+0#0000000#ffffff0|i|n|t| |p| |=| |{|a|r|g|c|,| |2+0#e000002&|}+0#0000000&|;| @43
| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@1|}+0#0000001#ffd7ff255| @6|o+0#0000000#ffffff0|i|n|t|*| |p|_|p|t|r| |=| |&|p|;| @45
|0+0&#ff404010|1| +0&#5fd7ff255@1>r+0#af5f00255&|e|t|u|r|n| +0#0000000&|0+0#e000002&|;+0#0000000&| @61
| +0#0000e05#a8a8a8255@1|}+0#0000000#ffffff0| @71
|X+3&&|T|D|_|e|v|a|l|u|a|t|e|_|i|n|_|p|o|p|u|p|.|c| @33|9|,|3| @11|B|o|t
|:+0&&|E|v|a|l|u|a|t|e| |p| @63

View File

@ -0,0 +1,9 @@
" replace hex addresses with |0|x|f@12|
:%s/|0|x|\(\(\w\|@\)\+|\)\+/|0|x|f@12|/g
" Only keep screen lines relevant to the actual popup and evaluation.
" Especially the top lines are too instable and cause flakiness between
" different systems and tool versions.
normal! G
normal! 8k
normal! dgg

View File

@ -0,0 +1,20 @@
|U+0&#ffffff0|s|i|n|g| |h|o|s|t| |l|i|b|t|h|r|e|a|d|_|d|b| |l|i|b|r|a|r|y| |"|/+0#00e0003&|l|i|b|/|x|8|6|_|6|4|-|l|i|n|u|x|-|g|n|u|/|l|i|b|t|h|r|e|a|d|_|d|b|.|s|o|.|1|"+0#0000000&|.|
@75
|B|r|e|a|k|p|o|i|n|t| |1|,| |m+0#e0e0004&|a|i|n| +0#0000000&|(|a+0#00e0e07&|r|g|c|=+0#0000000&|1|,| |a+0#00e0e07&|r|g|v|=+0#0000000&|0|x|7|f@6|d|e|f|8|)| @26
@4|a|t| |X+0#00e0003&|T|D|_|e|v|a|l|u|a|t|e|_|i|n|_|p|o|p|u|p|.|c|:+0#0000000&|9| @42
|9| @8|r+2#0000e05&|e|t|u|r|n| +0#0000000&|0+0#e000e06&|;+0#e000002&| +0#0000000&@55
@75
|g+0#ffffff16#00e0003|d|b| |[|r|u|n@1|i|n|g|]| @43|1|,|1| @11|T|o|p
| +0#0000000#ffffff0@74
@75
@75
@75
@75
|d+0#ffffff16#00e0003|e|b|u|p+0#0000001#ffd7ff255|_|p|t|r|:| |0|x|7|f@6|d@1|c|0| |=| |{| +0#ffffff16#00e0003@27|0|,|0|-|1| @9|A|l@1
| +0#0000000#e0e0e08| +2#ffffff16#6c6c6c255|S|t| +0#0000001#ffd7ff255@1|x| |=| |1|,| @16|o+2#ffffff16#6c6c6c255|n|t| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|S|t|o|p| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|E|v|a|l| | +0#0000000#e0e0e08@25
| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@1| +0#0000001#ffd7ff255@1|y| |=| |2| @17|}+0#0000000#ffffff0|;| @43
| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@1|}+0#0000001#ffd7ff255| @23| +0#0000000#ffffff0@45
|0+0&#ff404010|1| +0&#5fd7ff255@1>r+0#af5f00255&|e|t|u|r|n| +0#0000000&|0+0#e000002&|;+0#0000000&| @61
| +0#0000e05#a8a8a8255@1|}+0#0000000#ffffff0| @71
|X+3&&|T|D|_|e|v|a|l|u|a|t|e|_|i|n|_|p|o|p|u|p|.|c| @33|9|,|3| @11|B|o|t
|:+0&&|E|v|a|l|u|a|t|e| |p|_|p|t|r| @59

View File

@ -0,0 +1,9 @@
" replace hex addresses with |0|x|f@12|
:%s/|0|x|\(\(\w\|@\)\+|\)\+/|0|x|f@12|/g
" Only keep screen lines relevant to the actual popup and evaluation.
" Especially the top lines are too instable and cause flakiness between
" different systems and tool versions.
normal! G
normal! 8k
normal! dgg

View File

@ -1,6 +1,7 @@
" Test for the termdebug plugin
source shared.vim
source screendump.vim
source check.vim
CheckUnix
@ -243,6 +244,94 @@ func Test_termdebug_tbreak()
%bw!
endfunc
func Test_termdebug_evaluate()
let bin_name = 'XTD_evaluate'
let src_name = bin_name .. '.c'
call s:generate_files(bin_name)
edit XTD_evaluate.c
Termdebug ./XTD_evaluate
call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))})
call WaitForAssert({-> assert_equal(3, winnr('$'))})
let gdb_buf = winbufnr(1)
wincmd b
" return stmt in main
Break 22
call term_wait(gdb_buf)
Run
call term_wait(gdb_buf, 400)
redraw!
" Evaluate an expression
Evaluate n
call term_wait(gdb_buf)
call assert_equal(execute('1messages')->trim(), '"n": 7')
Evaluate argc
call term_wait(gdb_buf)
call assert_equal(execute('1messages')->trim(), '"argc": 1')
Evaluate isprime(n)
call term_wait(gdb_buf)
call assert_equal(execute('1messages')->trim(), '"isprime(n)": 1')
wincmd t
quit!
redraw!
call s:cleanup_files(bin_name)
%bw!
endfunc
func Test_termdebug_evaluate_in_popup()
CheckScreendump
let bin_name = 'XTD_evaluate_in_popup'
let src_name = bin_name .. '.c'
let code =<< trim END
struct Point {
int x;
int y;
};
int main(int argc, char* argv[]) {
struct Point p = {argc, 2};
struct Point* p_ptr = &p;
return 0;
}
END
call writefile(code, src_name, 'D')
call system($'{g:GCC} -g -o {bin_name} {src_name}')
let lines =<< trim END
edit XTD_evaluate_in_popup.c
packadd termdebug
let g:termdebug_config = {}
let g:termdebug_config['evaluate_in_popup'] = v:true
Termdebug ./XTD_evaluate_in_popup
wincmd b
Break 9
Run
END
call writefile(lines, 'Xscript', 'D')
let buf = RunVimInTerminal('-S Xscript', {})
call TermWait(buf, 400)
call term_sendkeys(buf, ":Evaluate p\<CR>")
call TermWait(buf, 400)
call VerifyScreenDump(buf, 'Test_termdebug_evaluate_in_popup_01', {})
call term_sendkeys(buf, ":Evaluate p_ptr\<CR>")
call TermWait(buf, 400)
call VerifyScreenDump(buf, 'Test_termdebug_evaluate_in_popup_02', {})
" Cleanup
call term_sendkeys(buf, ":Gdb")
call term_sendkeys(buf, ":quit!\<CR>")
call term_sendkeys(buf, ":qa!\<CR>")
call StopVimInTerminal(buf)
call delete(bin_name)
%bw!
endfunc
func Test_termdebug_mapping()
%bw!
call assert_true(maparg('K', 'n', 0, 1)->empty())

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
817,
/**/
816,
/**/