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>
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 0c1b31a..75359f2 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -10664,6 +10664,7 @@
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*
diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt
index 5020ed5..6b53e02 100644
--- a/runtime/doc/terminal.txt
+++ b/runtime/doc/terminal.txt
@@ -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 @@
<
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 @@
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 @@
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 @@
'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:
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
index 377827e..e7c010d 100644
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -121,7 +121,9 @@
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 @@
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 @@
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 @@
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 @@
#\ ->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 @@
# 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 @@
return ''
endif
evalFromBalloonExpr = true
- evalFromBalloonExprResult = ''
+ evalExprResult = ''
ignoreEvalError = true
var expr = CleanupExpr(v:beval_text)
SendEval(expr)
diff --git a/src/testdir/dumps/Test_termdebug_evaluate_in_popup_01.dump b/src/testdir/dumps/Test_termdebug_evaluate_in_popup_01.dump
new file mode 100644
index 0000000..3dde6cc
--- /dev/null
+++ b/src/testdir/dumps/Test_termdebug_evaluate_in_popup_01.dump
@@ -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| +0fd7ff255@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
diff --git a/src/testdir/dumps/Test_termdebug_evaluate_in_popup_01.vim b/src/testdir/dumps/Test_termdebug_evaluate_in_popup_01.vim
new file mode 100644
index 0000000..959798a
--- /dev/null
+++ b/src/testdir/dumps/Test_termdebug_evaluate_in_popup_01.vim
@@ -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
diff --git a/src/testdir/dumps/Test_termdebug_evaluate_in_popup_02.dump b/src/testdir/dumps/Test_termdebug_evaluate_in_popup_02.dump
new file mode 100644
index 0000000..0e3fa33
--- /dev/null
+++ b/src/testdir/dumps/Test_termdebug_evaluate_in_popup_02.dump
@@ -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| +0fd7ff255@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
diff --git a/src/testdir/dumps/Test_termdebug_evaluate_in_popup_02.vim b/src/testdir/dumps/Test_termdebug_evaluate_in_popup_02.vim
new file mode 100644
index 0000000..959798a
--- /dev/null
+++ b/src/testdir/dumps/Test_termdebug_evaluate_in_popup_02.vim
@@ -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
diff --git a/src/testdir/test_termdebug.vim b/src/testdir/test_termdebug.vim
index b5c12ae..30176cb 100644
--- a/src/testdir/test_termdebug.vim
+++ b/src/testdir/test_termdebug.vim
@@ -1,6 +1,7 @@
" Test for the termdebug plugin
source shared.vim
+source screendump.vim
source check.vim
CheckUnix
@@ -243,6 +244,94 @@
%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())
diff --git a/src/version.c b/src/version.c
index 4831281..90fc3b3 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 817,
+/**/
816,
/**/
815,