diff --git a/runtime/doc/tags b/runtime/doc/tags index 0c1b31a663..75359f2695 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -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* diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index 5020ed5b45..6b53e0223a 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 @@ 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: diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 377827e857..e7c010d4ce 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -121,7 +121,9 @@ var breakpoint_locations: dict var BreakpointSigns: list 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 + 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) 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 0000000000..3dde6cc98a --- /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 0000000000..959798ac41 --- /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 0000000000..0e3fa33399 --- /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 0000000000..959798ac41 --- /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 b5c12aefe1..30176cb75b 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 @@ 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\") + call TermWait(buf, 400) + call VerifyScreenDump(buf, 'Test_termdebug_evaluate_in_popup_01', {}) + + call term_sendkeys(buf, ":Evaluate p_ptr\") + call TermWait(buf, 400) + call VerifyScreenDump(buf, 'Test_termdebug_evaluate_in_popup_02', {}) + + " Cleanup + call term_sendkeys(buf, ":Gdb") + call term_sendkeys(buf, ":quit!\") + call term_sendkeys(buf, ":qa!\") + 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 48312818bc..90fc3b3e00 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 817, /**/ 816, /**/