From ddee275c69a4264a58d103363579f517d68884aa Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 5 May 2026 10:09:11 +0800 Subject: [PATCH 1/2] vim-patch:9.1.1756: termdebug: Need a few more user commands Problem: termdebug: Need a few more user commands Solution: Add the :RunOrContinue and the :ToggleBreak user commands (bennyyip) closes: vim/vim#18283 https://github.com/vim/vim/commit/c975d62473327d7ca74666d41c99651c23f09498 Co-authored-by: bennyyip --- runtime/doc/terminal.txt | 4 + .../dist/opt/termdebug/plugin/termdebug.vim | 62 +++++++++++--- test/old/testdir/test_plugin_termdebug.vim | 83 +++++++++++++++++++ 3 files changed, 137 insertions(+), 12 deletions(-) diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index 2bcb62571b..46aae00ad3 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -422,12 +422,16 @@ gdb: :Tbreak main < - *:Clear* delete the breakpoint at the cursor position +- *:ToggleBreak* set a breakpoint at the cursor position or delete all + breakpoints at the cursor positoin - *:Step* execute the gdb "step" command - *:Over* execute the gdb "next" command (`:Next` is a Vim command) - *:Until* execute the gdb "until" command - *:Finish* execute the gdb "finish" command - *:Continue* execute the gdb "continue" command +- *:RunOrContinue* execute the gdb "continue" command if program is running, + otherwise run the program - *:Stop* interrupt the program If gdb stops at a source line and there is no window currently showing the diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 093e9cb388..619d56a9cc 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -583,6 +583,25 @@ func s:SendCommand(cmd) endif endfunc +func s:StopCommand() + if s:way == 'prompt' + call s:PromptInterrupt() + else + call s:SendCommand('-exec-interrupt') + endif +endfunc + +func s:ContinueCommand() + if s:way == 'prompt' + call s:SendCommand('continue') + else + " using -exec-continue results in CTRL-C in the gdb window not working, + " communicating via commbuf (= use of SendCommand) has the same result + " call s:SendCommand('-exec-continue') + call chansend(s:gdb_job_id, "continue\r") + endif +endfunc + " This is global so that a user can create their mappings with this. func TermDebugSendCommand(cmd) if s:way == 'prompt' @@ -987,6 +1006,7 @@ func s:InstallCommands() command -nargs=? Break call s:SetBreakpoint() command -nargs=? Tbreak call s:SetBreakpoint(, v:true) + command ToggleBreak call s:ToggleBreak() command Clear call s:ClearBreakpoint() command Step call s:SendResumingCommand('-exec-step') command Over call s:SendResumingCommand('-exec-next') @@ -994,17 +1014,9 @@ func s:InstallCommands() command Finish call s:SendResumingCommand('-exec-finish') command -nargs=* Run call s:Run() command -nargs=* Arguments call s:SendResumingCommand('-exec-arguments ' . ) - - if s:way == 'prompt' - command Stop call s:PromptInterrupt() - command Continue call s:SendCommand('continue') - else - command Stop call s:SendCommand('-exec-interrupt') - " using -exec-continue results in CTRL-C in the gdb window not working, - " communicating via commbuf (= use of SendCommand) has the same result - "command Continue call s:SendCommand('-exec-continue') - command Continue call chansend(s:gdb_job_id, "continue\r") - endif + command Stop call s:StopCommand() + command Continue call s:ContinueCommand() + command RunOrContinue call s:RunOrContinue() command -nargs=* Frame call s:Frame() command -count=1 Up call s:Up() @@ -1115,6 +1127,8 @@ func s:DeleteCommands() delcommand Asm delcommand Var delcommand Winbar + delcommand RunOrContinue + delcommand ToggleBreak if exists('s:saved_K_map') if !empty(s:saved_K_map) && !s:saved_K_map.buffer @@ -1335,6 +1349,19 @@ func s:ClearBreakpoint() endif endfunc +func s:ToggleBreak() + let fname = fnameescape(expand('%:p')) + let lnum = line('.') + let bploc = printf('%s:%d', fname, lnum) + if has_key(s:breakpoint_locations, bploc) + while has_key(s:breakpoint_locations, bploc) + call s:ClearBreakpoint() + endwhile + else + call s:SetBreakpoint("") + endif +endfunc + func s:Run(args) if a:args != '' call s:SendResumingCommand($'-exec-arguments {a:args}') @@ -1342,6 +1369,14 @@ func s:Run(args) call s:SendResumingCommand('-exec-run') endfunc +func s:RunOrContinue() + if s:running + call s:ContinueCommand() + else + call s:Run('') + endif +endfunc + " :Frame - go to a specific frame in the stack func s:Frame(arg) " Note: we explicit do not use mi's command @@ -1839,7 +1874,10 @@ func s:HandleNewBreakpoint(msg, modifiedFlag) if !has_key(s:breakpoint_locations, bploc) let s:breakpoint_locations[bploc] = [] endif - let s:breakpoint_locations[bploc] += [id] + if s:breakpoint_locations[bploc]->index(id) == -1 + " Make sure all ids are unique + let s:breakpoint_locations[bploc] += [id] + endif if bufloaded(fname) call s:PlaceSign(id, subid, entry) diff --git a/test/old/testdir/test_plugin_termdebug.vim b/test/old/testdir/test_plugin_termdebug.vim index 3fbcd8937d..a93ec42e2d 100644 --- a/test/old/testdir/test_plugin_termdebug.vim +++ b/test/old/testdir/test_plugin_termdebug.vim @@ -578,4 +578,87 @@ function Test_termdebug_save_restore_variables() endfunction +func Test_termdebug_toggle_break() + let g:test_is_flaky = 1 + let bin_name = 'XTD_tbreak' + let src_name = bin_name .. '.c' + + eval s:generate_files(bin_name) + + execute 'edit ' .. src_name + execute 'Termdebug ./' .. bin_name + + call WaitForAssert({-> assert_true(get(g:, "termdebug_is_running", v:false))}) + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + let gdb_buf = winbufnr(1) + wincmd b + + let bp_line = 22 " 'return' statement in main + execute "normal! " .. bp_line .. "G" + execute "ToggleBreak" + + call Nterm_wait(gdb_buf) + redraw! + call assert_equal([ + \ {'lnum': bp_line, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + + RunOrContinue + call Nterm_wait(gdb_buf, 400) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': bp_line, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}, + \ {'lnum': bp_line, 'id': 1014, 'name': 'debugBreakpoint1.0', + \ 'priority': 110, 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs->reverse())}) + + " Add one break point + execute "normal! " .. bp_line .. "G" + execute "ToggleBreak" + call Nterm_wait(gdb_buf) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': bp_line, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + " Remove one break point + execute "normal! " .. bp_line .. "G" + execute "ToggleBreak" + call Nterm_wait(gdb_buf) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': bp_line, 'id': 2014, 'name': 'debugBreakpoint2.0', + \ 'priority': 110, 'group': 'TermDebug'}, + \ {'lnum': bp_line, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + " Remove multiple break points + execute "Break" + execute "Break" + execute "Break" + execute "Break" + call Nterm_wait(gdb_buf, 400) + execute "ToggleBreak" + call Nterm_wait(gdb_buf) + redraw! + call WaitForAssert({-> assert_equal([ + \ {'lnum': bp_line, 'id': 12, 'name': 'debugPC', 'priority': 110, + \ 'group': 'TermDebug'}], + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) + + + wincmd t + quit! + redraw! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + call assert_equal([], sign_getplaced('', #{group: 'TermDebug'})[0].signs) + + eval s:cleanup_files(bin_name) + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab From 64249d2f2afc701890871f9b850b57c4b8d9dc0b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 5 May 2026 10:27:11 +0800 Subject: [PATCH 2/2] vim-patch:9.2.0438: tests: test_plugin_termdebug is flaky Problem: Test_termdebug_tbreak(), Test_termdebug_basic(), and Test_termdebug_toggle_break() use synchronous assert_equal() to check breakpoint signs immediately after sending commands to gdb. On slow CI (ASAN, ARM64, macOS) gdb may not have processed the response yet, causing the sign to be missing. Solution: Wrap the three assertions in WaitForAssert() to poll until the signs are placed, matching the pattern already used by the other assertions in the same tests (Jesse Rosenstock). closes: vim/vim#20133 https://github.com/vim/vim/commit/20a124a6e0e2445c500ccc7c496a8fe5d334aed8 Co-authored-by: Jesse Rosenstock Co-authored-by: Gemini --- test/old/testdir/test_plugin_termdebug.vim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/old/testdir/test_plugin_termdebug.vim b/test/old/testdir/test_plugin_termdebug.vim index a93ec42e2d..0dbd16abe4 100644 --- a/test/old/testdir/test_plugin_termdebug.vim +++ b/test/old/testdir/test_plugin_termdebug.vim @@ -182,10 +182,10 @@ func Test_termdebug_basic() Break 9 call Nterm_wait(gdb_buf) redraw! - call assert_equal([ + call WaitForAssert({-> assert_equal([ \ {'lnum': 9, 'id': 1014, 'name': 'debugBreakpoint1.0', \ 'priority': 110, 'group': 'TermDebug'}], - \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) Run call Nterm_wait(gdb_buf, 400) redraw! @@ -344,12 +344,12 @@ func Test_termdebug_tbreak() call Nterm_wait(gdb_buf) redraw! " both temporary and normal breakpoint signs were displayed... - call assert_equal([ + call WaitForAssert({-> assert_equal([ \ {'lnum': temp_bp_line, 'id': 1014, 'name': 'debugBreakpoint1.0', \ 'priority': 110, 'group': 'TermDebug'}, \ {'lnum': bp_line, 'id': 2014, 'name': 'debugBreakpoint2.0', \ 'priority': 110, 'group': 'TermDebug'}], - \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) Run call Nterm_wait(gdb_buf, 400) @@ -599,10 +599,10 @@ func Test_termdebug_toggle_break() call Nterm_wait(gdb_buf) redraw! - call assert_equal([ + call WaitForAssert({-> assert_equal([ \ {'lnum': bp_line, 'id': 1014, 'name': 'debugBreakpoint1.0', \ 'priority': 110, 'group': 'TermDebug'}], - \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) RunOrContinue call Nterm_wait(gdb_buf, 400)