changeset 32921:0305a7f2b874

Runtime(termdebug): Add support to view local and argument variables Commit: https://github.com/vim/vim/commit/9f29621415146abc046471440515e9e34f3e57a1 Author: laburnumT <flo.striker@gmail.com> Date: Sat May 13 16:29:15 2023 +0200 Runtime(termdebug): Add support to view local and argument variables closes: 12403 Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Sun, 20 Aug 2023 20:15:03 +0200
parents 75f46cfabbff
children 397ff3169248
files runtime/doc/terminal.txt runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
diffstat 2 files changed, 169 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/terminal.txt
+++ b/runtime/doc/terminal.txt
@@ -1384,6 +1384,9 @@ Other commands ~
 	     isn't one
  *:Asm*	     jump to the window with the disassembly, create it if there
 	     isn't one
+ *:Var*	     jump to the window with the local and argument variables,
+             create it if there isn't one. This window updates whenever the
+             program is stopped
 
 Events ~
 							*termdebug-events*
@@ -1460,6 +1463,15 @@ If there is no g:termdebug_config you ca
 	let g:termdebug_disasm_window = 15
 Any value greater than 1 will set the Asm window height to that value.
 
+						*termdebug_variables_window*
+If you want the Var window shown by default, set the flag to 1.
+the "variables_window_height" entry can be used to set the window height: >
+	let g:termdebug_config['variables_window'] = 1
+	let g:termdebug_config['variables_window_height'] = 15
+If there is no g:termdebug_config you can use: >
+	let g:termdebug_variables_window = 15
+Any value greater than 1 will set the Var window height to that value.
+
 Communication ~
 						*termdebug-communication*
 There is another, hidden, buffer, which is used for Vim to communicate with
--- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
+++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
@@ -69,6 +69,7 @@ let s:pc_id = 12
 let s:asm_id = 13
 let s:break_id = 14  " breakpoint number is added to this
 let s:stopped = 1
+let s:running = 0
 
 let s:parsing_disasm_msg = 0
 let s:asm_lines = []
@@ -145,6 +146,9 @@ func s:StartDebug_internal(dict)
   let s:ptywin = 0
   let s:pid = 0
   let s:asmwin = 0
+  let s:asmbuf = 0
+  let s:varwin = 0
+  let s:varbuf = 0
 
   if exists('#User#TermdebugStartPre')
     doauto <nomodeline> User TermdebugStartPre
@@ -153,7 +157,7 @@ func s:StartDebug_internal(dict)
   " Uncomment this line to write logging in "debuglog".
   " call ch_logfile('debuglog', 'w')
 
-  let s:sourcewin = win_getid(winnr())
+  let s:sourcewin = win_getid()
 
   " Remember the old value of 'signcolumn' for each buffer that it's set in, so
   " that we can restore the value for all buffers.
@@ -201,11 +205,17 @@ func s:StartDebug_internal(dict)
   endif
 
   if s:GetDisasmWindow()
-    let curwinid = win_getid(winnr())
+    let curwinid = win_getid()
     call s:GotoAsmwinOrCreateIt()
     call win_gotoid(curwinid)
   endif
 
+  if s:GetVariablesWindow()
+    let curwinid = win_getid()
+    call s:GotoVariableswinOrCreateIt()
+    call win_gotoid(curwinid)
+  endif
+
   if exists('#User#TermdebugStartPost')
     doauto <nomodeline> User TermdebugStartPost
   endif
@@ -215,6 +225,13 @@ endfunc
 func s:CloseBuffers()
   exe 'bwipe! ' . s:ptybuf
   exe 'bwipe! ' . s:commbuf
+  if s:asmbuf > 0
+    exe 'bwipe! ' . s:asmbuf
+  endif
+  if s:varbuf > 0
+    exe 'bwipe! ' . s:varbuf
+  endif
+  s:running = 0
   unlet! s:gdbwin
 endfunc
 
@@ -239,7 +256,7 @@ func s:StartDebug_term(dict)
     return
   endif
   let pty = job_info(term_getjob(s:ptybuf))['tty_out']
-  let s:ptywin = win_getid(winnr())
+  let s:ptywin = win_getid()
   if s:vertical
     " Assuming the source code window will get a signcolumn, use two more
     " columns for that, thus one less for the terminal window.
@@ -302,7 +319,7 @@ func s:StartDebug_term(dict)
     call s:CloseBuffers()
     return
   endif
-  let s:gdbwin = win_getid(winnr())
+  let s:gdbwin = win_getid()
 
   " Wait for the "startupdone" message before sending any commands.
   let try_count = 0
@@ -390,7 +407,7 @@ func s:StartDebug_prompt(dict)
   else
     new
   endif
-  let s:gdbwin = win_getid(winnr())
+  let s:gdbwin = win_getid()
   let s:promptbuf = bufnr('')
   call prompt_setprompt(s:promptbuf, 'gdb> ')
   set buftype=prompt
@@ -451,7 +468,7 @@ func s:StartDebug_prompt(dict)
       call job_stop(s:gdbjob)
       return
     endif
-    let s:ptywin = win_getid(winnr())
+    let s:ptywin = win_getid()
     let pty = job_info(term_getjob(s:ptybuf))['tty_out']
     call s:SendCommand('tty ' . pty)
 
@@ -615,7 +632,7 @@ func s:GdbOutCallback(channel, text)
     return
   endif
 
-  let curwinid = win_getid(winnr())
+  let curwinid = win_getid()
   call win_gotoid(s:gdbwin)
 
   " Add the output above the current prompt.
@@ -688,13 +705,20 @@ func s:EndTermDebug(job, status)
   endif
 
   exe 'bwipe! ' . s:commbuf
+  if s:asmbuf > 0
+    exe 'bwipe! ' . s:asmbuf
+  endif
+  if s:varbuf > 0
+    exe 'bwipe! ' . s:varbuf
+  endif
+  let s:running = 0
   unlet s:gdbwin
 
   call s:EndDebugCommon()
 endfunc
 
 func s:EndDebugCommon()
-  let curwinid = win_getid(winnr())
+  let curwinid = win_getid()
 
   if exists('s:ptybuf') && s:ptybuf
     exe 'bwipe! ' . s:ptybuf
@@ -746,7 +770,7 @@ func s:EndPromptDebug(job, status)
     doauto <nomodeline> User TermdebugStopPre
   endif
 
-  let curwinid = win_getid(winnr())
+  let curwinid = win_getid()
   call win_gotoid(s:gdbwin)
   set nomodified
   close
@@ -777,9 +801,9 @@ endfunc
 " - CommOutput: ^error,msg="No function contains specified address."
 func s:HandleDisasmMsg(msg)
   if a:msg =~ '^\^done'
-    let curwinid = win_getid(winnr())
+    let curwinid = win_getid()
     if win_gotoid(s:asmwin)
-      silent normal! gg0"_dG
+      silent! %delete _
       call setline(1, s:asm_lines)
       set nomodified
       set filetype=asm
@@ -822,6 +846,49 @@ func s:HandleDisasmMsg(msg)
   endif
 endfunc
 
+func s:ParseVarinfo(varinfo)
+  let dict = {}
+  let nameIdx = matchstrpos(a:varinfo, '{name="\([^"]*\)"')
+  let dict['name'] = a:varinfo[nameIdx[1] + 7 : nameIdx[2] - 2]
+  let typeIdx = matchstrpos(a:varinfo, ',type="\([^"]*\)"')
+  let dict['type'] = a:varinfo[typeIdx[1] + 7 : typeIdx[2] - 2]
+  let valueIdx = matchstrpos(a:varinfo, ',value="\(.*\)"}')
+  if valueIdx[1] == -1
+    let dict['value'] = 'Complex value'
+  else
+    let dict['value'] = a:varinfo[valueIdx[1] + 8 : valueIdx[2] - 3]
+  endif
+  return dict
+endfunc
+
+func s:HandleVariablesMsg(msg)
+  let curwinid = win_getid()
+  if win_gotoid(s:varwin)
+
+    silent! %delete _
+    let spaceBuffer = 20
+    call setline(1, 'Type' .
+	  \ repeat(' ', 16) .
+	  \ 'Name' .
+	  \ repeat(' ', 16) .
+	  \ 'Value')
+    let cnt = 1
+    let capture = '{name=".\{-}",\%(arg=".\{-}",\)\{0,1\}type=".\{-}"\%(,value=".\{-}"\)\{0,1\}}'
+    let varinfo = matchstr(a:msg, capture, 0, cnt)
+    while varinfo != ''
+      let vardict = s:ParseVarinfo(varinfo)
+      call setline(cnt + 1, vardict['type'] .
+	    \ repeat(' ', max([20 - len(vardict['type']), 1])) .
+	    \ vardict['name'] .
+	    \ repeat(' ', max([20 - len(vardict['name']), 1])) .
+	    \ vardict['value'])
+      let cnt += 1
+      let varinfo = matchstr(a:msg, capture, 0, cnt)
+    endwhile
+  endif
+  call win_gotoid(curwinid)
+endfunc
+
 " Handle a message received from gdb on the GDB/MI interface.
 func s:CommOutput(chan, msg)
   let msgs = split(a:msg, "\r")
@@ -852,6 +919,8 @@ func s:CommOutput(chan, msg)
       elseif msg =~ '^disassemble'
         let s:parsing_disasm_msg = 1
         let s:asm_lines = []
+      elseif msg =~ '^\^done,variables='
+	call s:HandleVariablesMsg(msg)
       endif
     endif
   endfor
@@ -897,6 +966,7 @@ func s:InstallCommands()
   command Program call s:GotoProgram()
   command Source call s:GotoSourcewinOrCreateIt()
   command Asm call s:GotoAsmwinOrCreateIt()
+  command Var call s:GotoVariableswinOrCreateIt()
   command Winbar call s:InstallWinbar(1)
 
   let map = 1
@@ -950,7 +1020,7 @@ func s:InstallWinbar(force)
     nnoremenu WinBar.Cont   :Continue<CR>
     nnoremenu WinBar.Stop   :Stop<CR>
     nnoremenu WinBar.Eval   :Evaluate<CR>
-    call add(s:winbar_winids, win_getid(winnr()))
+    call add(s:winbar_winids, win_getid())
   endif
 endfunc
 
@@ -971,6 +1041,7 @@ func s:DeleteCommands()
   delcommand Program
   delcommand Source
   delcommand Asm
+  delcommand Var
   delcommand Winbar
 
   if exists('s:k_map_saved')
@@ -984,7 +1055,7 @@ func s:DeleteCommands()
 
   if has('menu')
     " Remove the WinBar entries from all windows where it was added.
-    let curwinid = win_getid(winnr())
+    let curwinid = win_getid()
     for winid in s:winbar_winids
       if win_gotoid(winid)
 	aunmenu WinBar.Step
@@ -1240,7 +1311,7 @@ endfunc
 func s:GotoSourcewinOrCreateIt()
   if !win_gotoid(s:sourcewin)
     new
-    let s:sourcewin = win_getid(winnr())
+    let s:sourcewin = win_getid()
     call s:InstallWinbar(0)
   endif
 endfunc
@@ -1273,19 +1344,21 @@ func s:GotoAsmwinOrCreateIt()
       exe 'new'
     endif
 
-    let s:asmwin = win_getid(winnr())
+    let s:asmwin = win_getid()
 
     setlocal nowrap
     setlocal number
     setlocal noswapfile
     setlocal buftype=nofile
+    setlocal bufhidden=wipe
+    setlocal signcolumn=no
     setlocal modifiable
 
-    let asmbuf = bufnr('Termdebug-asm-listing')
-    if asmbuf > 0
-      exe 'buffer' . asmbuf
+    if s:asmbuf > 0
+      exe 'buffer' . s:asmbuf
     else
-      exe 'file Termdebug-asm-listing'
+      silent file Termdebug-asm-listing
+      let s:asmbuf = bufnr('Termdebug-asm-listing')
     endif
 
     if s:GetDisasmWindowHeight() > 0
@@ -1306,17 +1379,75 @@ func s:GotoAsmwinOrCreateIt()
   endif
 endfunc
 
+func s:GetVariablesWindow()
+  if exists('g:termdebug_config')
+    return get(g:termdebug_config, 'variables_window', 0)
+  endif
+  if exists('g:termdebug_disasm_window')
+    return g:termdebug_variables_window
+  endif
+  return 0
+endfunc
+
+func s:GetVariablesWindowHeight()
+  if exists('g:termdebug_config')
+    return get(g:termdebug_config, 'variables_window_height', 0)
+  endif
+  if exists('g:termdebug_variables_window') && g:termdebug_variables_window > 1
+    return g:termdebug_variables_window
+  endif
+  return 0
+endfunc
+
+func s:GotoVariableswinOrCreateIt()
+  if !win_gotoid(s:varwin)
+    if win_gotoid(s:sourcewin)
+      exe 'rightbelow new'
+    else
+      exe 'new'
+    endif
+
+    let s:varwin = win_getid()
+
+    setlocal nowrap
+    setlocal noswapfile
+    setlocal buftype=nofile
+    setlocal bufhidden=wipe
+    setlocal signcolumn=no
+    setlocal modifiable
+
+    if s:varbuf > 0
+      exe 'buffer' . s:varbuf
+    else
+      silent file Termdebug-variables-listing
+      let s:varbuf = bufnr('Termdebug-variables-listing')
+    endif
+
+    if s:GetVariablesWindowHeight() > 0
+      exe 'resize ' .. s:GetVariablesWindowHeight()
+    endif
+  endif
+
+  if s:running
+    call s:SendCommand('-stack-list-variables 2')
+  endif
+endfunc
+
 " Handle stopping and running message from gdb.
 " Will update the sign that shows the current position.
 func s:HandleCursor(msg)
-  let wid = win_getid(winnr())
+  let wid = win_getid()
 
   if a:msg =~ '^\*stopped'
     call ch_log('program stopped')
     let s:stopped = 1
+    if a:msg =~ '^\*stopped,reason="exited-normally"'
+      let s:running = 0
+    endif
   elseif a:msg =~ '^\*running'
     call ch_log('program running')
     let s:stopped = 0
+    let s:running = 1
   endif
 
   if a:msg =~ 'fullname='
@@ -1330,7 +1461,7 @@ func s:HandleCursor(msg)
     if asm_addr != ''
       let s:asm_addr = asm_addr
 
-      let curwinid = win_getid(winnr())
+      let curwinid = win_getid()
       if win_gotoid(s:asmwin)
         let lnum = search('^' . s:asm_addr)
         if lnum == 0
@@ -1345,6 +1476,10 @@ func s:HandleCursor(msg)
     endif
   endif
 
+  if s:running && s:stopped && bufwinnr('Termdebug-variables-listing') != -1
+    call s:SendCommand('-stack-list-variables 2')
+  endif
+
   if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
     let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
     if lnum =~ '^[0-9]*$'
@@ -1363,7 +1498,7 @@ echomsg 'different fname: "' .. expand('
         if &modified
           " TODO: find existing window
           exe 'split ' . fnameescape(fname)
-          let s:sourcewin = win_getid(winnr())
+          let s:sourcewin = win_getid()
           call s:InstallWinbar(0)
         else
           exe 'edit ' . fnameescape(fname)