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
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    508,
+/**/
     507,
 /**/
     506,