patch 9.1.0817: termdebug: cannot evaluate expr in a popup

Commit: 
https://github.com/vim/vim/commit/8f1d09828a92469a7fa824a8653bf689a7864d21
Author: Peter Wolf <pwolf2...@gmail.com>
Date:   Sun Oct 27 21:51:14 2024 +0100

    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 <pwolf2...@gmail.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/tags b/runtime/doc/tags
index 0c1b31a66..75359f269 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 5020ed5b4..6b53e0223 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 377827e85..e7c010d4c 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<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('{', '{
', 'g')
+    ->substitute('}', '
}', 'g')
+    ->substitute(',', ',
', 'g')
+    ->split('
')
+  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="\(.*\)"', ' ', '')
@@ -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 000000000..3dde6cc98
--- /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| +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
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 000000000..959798ac4
--- /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 000000000..0e3fa3339
--- /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| +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
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 000000000..959798ac4
--- /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 b5c12aefe..30176cb75 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\<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 48312818b..90fc3b3e0 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,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1t5AMk-00Btql-NL%40256bit.org.

Raspunde prin e-mail lui