Mercurial > vim
changeset 35482:5d823e880ed5 v9.1.0508
patch 9.1.0508: termdebug plugin can be further improved
Commit: https://github.com/vim/vim/commit/f7f8f0b76dc6a3bf5d51825db65245221e5d265e
Author: Ubaldo Tiberi <ubaldo.tiberi@google.com>
Date: Thu Jun 20 22:17:34 2024 +0200
patch 9.1.0508: termdebug plugin can be further improved
Problem: termdebug plugin can be further improved
Solution: add sanity-check, timeout config, change vars to bool
update docs, add more tests (Ubaldo Tiberi)
fixes: #15061
closes: #15057
Signed-off-by: Ubaldo Tiberi <ubaldo.tiberi@google.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Thu, 20 Jun 2024 22:30:09 +0200 |
parents | d139d2462ea0 |
children | b88e0adc731e |
files | runtime/doc/terminal.txt runtime/pack/dist/opt/termdebug/plugin/termdebug.vim src/testdir/test_termdebug.vim src/version.c |
diffstat | 4 files changed, 129 insertions(+), 99 deletions(-) [+] |
line wrap: on
line diff
--- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -1,4 +1,4 @@ -*terminal.txt* For Vim version 9.1. Last change: 2024 Jun 18 +*terminal.txt* For Vim version 9.1. Last change: 2024 Jun 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1283,6 +1283,15 @@ When the debugger ends, typically by typ opened windows are closed. Only one debugger can be active at a time. + + *termdebug-timeout* +Depending on how gdb is launched, termdebug startup time may vary. +To avoid termdebug to get stuck if the startup process of gdb takes too long, +a configurable timeout is included. Such time out is configurable in terms of +multiple of 10ms: > + let g:termdebug_config['timeout'] = 500 # 500 * 10ms = 5 seconds. + +The default timeout is 3000 ms. *:TermdebugCommand* If you want to give specific commands to the command being debugged, you can use the `:TermdebugCommand` command followed by the command name and
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -4,7 +4,7 @@ vim9script # Author: Bram Moolenaar # Copyright: Vim license applies, see ":help license" -# Last Change: 2024 Jun 16 +# Last Change: 2024 Jun 20 # Converted to Vim9: Ubaldo Tiberi <ubaldo.tiberi@gmail.com> # WORK IN PROGRESS - The basics works stable, more to come @@ -50,6 +50,13 @@ if exists('g:termdebug_loaded') finish endif g:termdebug_loaded = true +g:termdebug_is_running = false + + +# The command that starts debugging, e.g. ":Termdebug vim". +# To end type "quit" in the gdb window. +command -nargs=* -complete=file -bang Termdebug StartDebug(<bang>0, <f-args>) +command -nargs=+ -complete=file -bang TermdebugCommand StartDebugCommand(<bang>0, <f-args>) # Script variables declaration. These variables are re-initialized at every # Termdebug instance @@ -198,18 +205,40 @@ def InitScriptVariables() winbar_winids = [] - saved_K_map = maparg('K', 'n', 0, 1) - saved_plus_map = maparg('+', 'n', 0, 1) - saved_minus_map = maparg('-', 'n', 0, 1) + saved_K_map = maparg('K', 'n', false, true) + saved_plus_map = maparg('+', 'n', false, true) + saved_minus_map = maparg('-', 'n', false, true) if has('menu') saved_mousemodel = &mousemodel endif enddef -# The command that starts debugging, e.g. ":Termdebug vim". -# To end type "quit" in the gdb window. -command -nargs=* -complete=file -bang Termdebug StartDebug(<bang>0, <f-args>) -command -nargs=+ -complete=file -bang TermdebugCommand StartDebugCommand(<bang>0, <f-args>) + +def SanityCheck(): bool + var gdb_cmd = GetCommand()[0] + var is_check_ok = true + # Need either the +terminal feature or +channel and the prompt buffer. + # The terminal feature does not work with gdb on win32. + if (way ==# 'prompt') && !has('channel') + err = 'Cannot debug, +channel feature is not supported' + elseif way ==# 'prompt' && !exists('*prompt_setprompt') + err = 'Cannot debug, missing prompt buffer support' + elseif way ==# 'prompt' && !empty(glob(gdb_cmd)) + err = $"You have a file/folder named '{gdb_cmd}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder." + elseif !empty(glob(asmbufname)) + err = $"You have a file/folder named '{asmbufname}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder." + elseif !empty(glob(varbufname)) + err = $"You have a file/folder named '{varbufname}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder." + elseif !executable(gdb_cmd) + err = $"Cannot execute debugger program '{gdb_cmd}'" + endif + + if !empty(err) + Echoerr(err) + is_check_ok = false + endif + return is_check_ok +enddef # Take a breakpoint number as used by GDB and turn it into an integer. @@ -248,18 +277,19 @@ enddef # Get the command to execute the debugger as a list, defaults to ["gdb"]. def GetCommand(): list<string> - var cmd = 'gdb' + var cmd: any if exists('g:termdebug_config') cmd = get(g:termdebug_config, 'command', 'gdb') elseif exists('g:termdebugger') cmd = g:termdebugger + else + cmd = 'gdb' endif return type(cmd) == v:t_list ? copy(cmd) : [cmd] enddef def StartDebug(bang: bool, ...gdb_args: list<string>) - InitScriptVariables() # First argument is the command to debug, second core file or process ID. StartDebug_internal({gdb_args: gdb_args, bang: bang}) enddef @@ -269,15 +299,14 @@ def StartDebugCommand(bang: bool, ...arg StartDebug_internal({gdb_args: [args[0]], proc_args: args[1 : ], bang: bang}) enddef - def StartDebug_internal(dict: dict<any>) - if gdbwin > 0 + if g:termdebug_is_running Echoerr('Terminal debugger already running, cannot run two') return endif - var gdbcmd = GetCommand() - if !executable(gdbcmd[0]) - Echoerr($'Cannot execute debugger program "{gdbcmd[0]}"') + + InitScriptVariables() + if !SanityCheck() return endif @@ -285,6 +314,9 @@ def StartDebug_internal(dict: dict<any>) doauto <nomodeline> User TermdebugStartPre endif + # Uncomment this line to write logging in "debuglog". + # call ch_logfile('debuglog', 'w') + # Assume current window is the source code window sourcewin = win_getid() var wide = 0 @@ -327,6 +359,7 @@ def StartDebug_internal(dict: dict<any>) if exists('#User#TermdebugStartPost') doauto <nomodeline> User TermdebugStartPost endif + g:termdebug_is_running = true enddef # Use when debugger didn't start or ended. @@ -338,7 +371,7 @@ def CloseBuffers() endif endfor - running = 0 + running = false gdbwin = 0 enddef @@ -436,6 +469,10 @@ def StartDebug_term(dict: dict<any>) # Wait for the "startupdone" message before sending any commands. var counter = 0 var counter_max = 300 + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'timeout') + counter_max = g:termdebug_config['timeout'] + endif + var success = false while !success && counter < counter_max if !IsGdbStarted() @@ -524,6 +561,9 @@ enddef # Open a window with a prompt buffer to run gdb in. def StartDebug_prompt(dict: dict<any>) + var gdb_cmd = GetCommand() + gdbbufname = gdb_cmd[0] + if vvertical vertical new else @@ -533,17 +573,7 @@ def StartDebug_prompt(dict: dict<any>) promptbuf = bufnr('') prompt_setprompt(promptbuf, 'gdb> ') set buftype=prompt - - if empty(glob('gdb')) - file gdb - elseif empty(glob('Termdebug-gdb-console')) - file Termdebug-gdb-console - else - Echoerr("You have a file/folder named 'gdb' " .. - "or 'Termdebug-gdb-console'. " .. - "Please exit and rename them because Termdebug may not work " .. - "as expected.") - endif + exe $"file {gdbbufname}" prompt_setcallback(promptbuf, function('PromptCallback')) prompt_setinterrupt(promptbuf, function('PromptInterrupt')) @@ -557,7 +587,6 @@ def StartDebug_prompt(dict: dict<any>) var gdb_args = get(dict, 'gdb_args', []) var proc_args = get(dict, 'proc_args', []) - var gdb_cmd = GetCommand() # Add -quiet to avoid the intro message causing a hit-enter prompt. gdb_cmd += ['-quiet'] # Disable pagination, it causes everything to stop at the gdb, needs to be run early @@ -703,9 +732,9 @@ def g:TermDebugSendCommand(cmd: string) if way == 'prompt' ch_sendraw(gdb_channel, $"{cmd}\n") else - var do_continue = 0 + var do_continue = false if !stopped - do_continue = 1 + do_continue = true StopCommand() sleep 10m endif @@ -918,6 +947,7 @@ def EndDebugCommon() endif au! TermDebug + g:termdebug_is_running = false enddef def EndPromptDebug(job: any, status: any) @@ -1122,11 +1152,11 @@ def InstallCommands() command Source GotoSourcewinOrCreateIt() command Asm GotoAsmwinOrCreateIt() command Var GotoVariableswinOrCreateIt() - command Winbar InstallWinbar(1) + command Winbar InstallWinbar(true) - var map = 1 + var map = true if exists('g:termdebug_config') - map = get(g:termdebug_config, 'map_K', 1) + map = get(g:termdebug_config, 'map_K', true) elseif exists('g:termdebug_map_K') map = g:termdebug_map_K endif @@ -1137,9 +1167,9 @@ def InstallCommands() endif endif - map = 1 + map = true if exists('g:termdebug_config') - map = get(g:termdebug_config, 'map_plus', 1) + map = get(g:termdebug_config, 'map_plus', true) endif if map if !empty(saved_plus_map) && !saved_plus_map.buffer || empty(saved_plus_map) @@ -1147,9 +1177,9 @@ def InstallCommands() endif endif - map = 1 + map = true if exists('g:termdebug_config') - map = get(g:termdebug_config, 'map_minus', 1) + map = get(g:termdebug_config, 'map_minus', true) endif if map if !empty(saved_minus_map) && !saved_minus_map.buffer || empty(saved_minus_map) @@ -1159,11 +1189,11 @@ def InstallCommands() if has('menu') && &mouse != '' - InstallWinbar(0) + InstallWinbar(false) - var pup = 1 + var pup = true if exists('g:termdebug_config') - pup = get(g:termdebug_config, 'popup', 1) + pup = get(g:termdebug_config, 'popup', true) elseif exists('g:termdebug_popup') pup = g:termdebug_popup endif @@ -1181,11 +1211,11 @@ def InstallCommands() enddef # Install the window toolbar in the current window. -def InstallWinbar(force: number) +def InstallWinbar(force: bool) # install the window toolbar by default, can be disabled in the config - var winbar = 1 + var winbar = true if exists('g:termdebug_config') - winbar = get(g:termdebug_config, 'winbar', 1) + winbar = get(g:termdebug_config, 'winbar', true) endif if has('menu') && &mouse != '' && (winbar || force) @@ -1301,9 +1331,9 @@ enddef def SetBreakpoint(at: string, tbreak=false) # Setting a breakpoint may not work while the program is running. # Interrupt to make it work. - var do_continue = 0 + var do_continue = false if !stopped - do_continue = 1 + do_continue = true StopCommand() sleep 10m endif @@ -1539,7 +1569,7 @@ def GotoSourcewinOrCreateIt() if !win_gotoid(sourcewin) new sourcewin = win_getid() - InstallWinbar(0) + InstallWinbar(false) endif enddef @@ -1591,13 +1621,9 @@ def GotoAsmwinOrCreateIt() if asmbufnr > 0 && bufexists(asmbufnr) exe $'buffer {asmbufnr}' - elseif empty(glob('Termdebug-asm-listing')) - silent file Termdebug-asm-listing - asmbufnr = bufnr('Termdebug-asm-listing') else - Echoerr("You have a file/folder named 'Termdebug-asm-listing'. " .. - "Please exit and rename it because Termdebug may not work " .. - "as expected.") + exe $"silent file {asmbufname}" + asmbufnr = bufnr(asmbufname) endif if mdf != 'vert' && GetDisasmWindowHeight() > 0 @@ -1663,15 +1689,12 @@ def GotoVariableswinOrCreateIt() setlocal signcolumn=no setlocal modifiable + # If exists, then open, otherwise create if varbufnr > 0 && bufexists(varbufnr) exe $'buffer {varbufnr}' - elseif empty(glob('Termdebug-variables-listing')) - silent file Termdebug-variables-listing - varbufnr = bufnr('Termdebug-variables-listing') else - Echoerr("You have a file/folder named 'Termdebug-variables-listing'. " .. - "Please exit and rename it because Termdebug may not work " .. - "as expected.") + exe $"silent file {varbufname}" + varbufnr = bufnr(varbufname) endif if mdf != 'vert' && GetVariablesWindowHeight() > 0 @@ -1691,7 +1714,7 @@ def HandleCursor(msg: string) if msg =~ '^\*stopped' ch_log('program stopped') - stopped = 1 + stopped = true if msg =~ '^\*stopped,reason="exited-normally"' running = false endif @@ -1750,7 +1773,7 @@ def HandleCursor(msg: string) # TODO: find existing window exe $'split {fnameescape(fname)}' sourcewin = win_getid() - InstallWinbar(0) + InstallWinbar(false) else exe $'edit {fnameescape(fname)}' endif
--- a/src/testdir/test_termdebug.vim +++ b/src/testdir/test_termdebug.vim @@ -337,44 +337,6 @@ func Test_termdebug_mapping() %bw! endfunc -func Test_termdebug_bufnames() - " Test if user has filename/folders named gdb, Termdebug-gdb-console, - " etc. in the current directory - let g:termdebug_config = {} - let g:termdebug_config['use_prompt'] = 1 - let filename = 'gdb' - let replacement_filename = 'Termdebug-gdb-console' - - call writefile(['This', 'is', 'a', 'test'], filename, 'D') - " Throw away the file once the test has done. - Termdebug - " Once termdebug has completed the startup you should have 3 windows on screen - call WaitForAssert({-> assert_equal(3, winnr('$'))}) - " A file named filename already exists in the working directory, - " hence you must call the newly created buffer differently - call WaitForAssert({-> assert_false(bufexists(filename))}) - call WaitForAssert({-> assert_true(bufexists(replacement_filename))}) - quit! - call WaitForAssert({-> assert_equal(1, winnr('$'))}) - - " Check if error message is in :message - let g:termdebug_config['disasm_window'] = 1 - let filename = 'Termdebug-asm-listing' - call writefile(['This', 'is', 'a', 'test'], filename, 'D') - " Check only the head of the error message - let error_message = "You have a file/folder named '" .. filename .. "'" - Termdebug - " Once termdebug has completed the startup you should have 4 windows on screen - call WaitForAssert({-> assert_equal(4, winnr('$'))}) - call WaitForAssert({-> assert_notequal(-1, stridx(execute('messages'), error_message))}) - quit! - wincmd b - wincmd q - call WaitForAssert({-> assert_equal(1, winnr('$'))}) - - unlet g:termdebug_config -endfunc - function Test_termdebug_save_restore_variables() " saved mousemodel let &mousemodel='' @@ -413,5 +375,39 @@ function Test_termdebug_save_restore_var unlet g:termdebug_config endfunction +function Test_termdebug_sanity_check() + " Test if user has filename/folders with wrong names + let g:termdebug_config = {} + let s:dict = {'disasm_window': 'Termdebug-asm-listing', 'use_prompt': 'gdb', 'variables_window': 'Termdebug-variables-listing'} + + for key in keys(s:dict) + let s:filename = s:dict[key] + let g:termdebug_config[key] = 1 + let s:error_message = "You have a file/folder named '" .. s:filename .. "'" + + " Write dummy file with bad name + call writefile(['This', 'is', 'a', 'test'], s:filename) + Termdebug + call WaitForAssert({-> assert_true(execute('messages') =~ s:error_message)}) + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + + call delete(s:filename) + call remove(g:termdebug_config, key) + endfor + + unlet g:termdebug_config +endfunction + +function Test_termdebug_double_termdebug_instances() + let s:error_message = 'Terminal debugger already running, cannot run two' + Termdebug + call WaitForAssert({-> assert_equal(3, winnr('$'))}) + Termdebug + call WaitForAssert({-> assert_true(execute('messages') =~ s:error_message)}) + wincmd t + quit! + call WaitForAssert({-> assert_equal(1, winnr('$'))}) + :%bw! +endfunction " vim: shiftwidth=2 sts=2 expandtab