vim-patch:9f2962141514

Runtime(termdebug): Add support to view local and argument variables

closes: 12403

9f29621415

Rename the existing "s:running" (#16790) to "s:gdb_running" to not clash with
the "s:running" introduced in this patch (which instead relates to whether the
debugged program is currently running in gdb).

Keep the file `:retab`bed as before.

Co-authored-by: laburnumT <flo.striker@gmail.com>
This commit is contained in:
Sean Dewar 2023-08-21 09:28:58 +01:00
parent 0b0d912763
commit 949dd14d8b
No known key found for this signature in database
GPG Key ID: 08CC2C83AD41B581
2 changed files with 177 additions and 31 deletions

View File

@ -325,7 +325,9 @@ Other commands ~
isn't one isn't one
*:Asm* jump to the window with the disassembly, create it if there *:Asm* jump to the window with the disassembly, create it if there
isn't one 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 ~ Events ~
*termdebug-events* *termdebug-events*
@ -400,6 +402,15 @@ If there is no g:termdebug_config you can use: >vim
let g:termdebug_disasm_window = 15 let g:termdebug_disasm_window = 15
Any value greater than 1 will set the Asm window height to that value. 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: >vim
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: >vim
let g:termdebug_variables_window = 15
Any value greater than 1 will set the Var window height to that value.
Communication ~ Communication ~
*termdebug-communication* *termdebug-communication*
There is another, hidden, buffer, which is used for Vim to communicate with There is another, hidden, buffer, which is used for Vim to communicate with

View File

@ -75,6 +75,7 @@ let s:pc_id = 12
let s:asm_id = 13 let s:asm_id = 13
let s:break_id = 14 " breakpoint number is added to this let s:break_id = 14 " breakpoint number is added to this
let s:stopped = 1 let s:stopped = 1
let s:running = 0
let s:parsing_disasm_msg = 0 let s:parsing_disasm_msg = 0
let s:asm_lines = [] let s:asm_lines = []
@ -151,6 +152,9 @@ func s:StartDebug_internal(dict)
let s:ptywin = 0 let s:ptywin = 0
let s:pid = 0 let s:pid = 0
let s:asmwin = 0 let s:asmwin = 0
let s:asmbuf = 0
let s:varwin = 0
let s:varbuf = 0
if exists('#User#TermdebugStartPre') if exists('#User#TermdebugStartPre')
doauto <nomodeline> User TermdebugStartPre doauto <nomodeline> User TermdebugStartPre
@ -159,7 +163,7 @@ func s:StartDebug_internal(dict)
" Uncomment this line to write logging in "debuglog". " Uncomment this line to write logging in "debuglog".
" call ch_logfile('debuglog', 'w') " 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 " Remember the old value of 'signcolumn' for each buffer that it's set in, so
" that we can restore the value for all buffers. " that we can restore the value for all buffers.
@ -207,11 +211,17 @@ func s:StartDebug_internal(dict)
endif endif
if s:GetDisasmWindow() if s:GetDisasmWindow()
let curwinid = win_getid(winnr()) let curwinid = win_getid()
call s:GotoAsmwinOrCreateIt() call s:GotoAsmwinOrCreateIt()
call win_gotoid(curwinid) call win_gotoid(curwinid)
endif endif
if s:GetVariablesWindow()
let curwinid = win_getid()
call s:GotoVariableswinOrCreateIt()
call win_gotoid(curwinid)
endif
if exists('#User#TermdebugStartPost') if exists('#User#TermdebugStartPost')
doauto <nomodeline> User TermdebugStartPost doauto <nomodeline> User TermdebugStartPost
endif endif
@ -220,11 +230,18 @@ endfunc
" Use when debugger didn't start or ended. " Use when debugger didn't start or ended.
func s:CloseBuffers() func s:CloseBuffers()
exe 'bwipe! ' . s:ptybuf exe 'bwipe! ' . s:ptybuf
if s:asmbuf > 0
exe 'bwipe! ' . s:asmbuf
endif
if s:varbuf > 0
exe 'bwipe! ' . s:varbuf
endif
s:running = 0
unlet! s:gdbwin unlet! s:gdbwin
endfunc endfunc
func s:CheckGdbRunning() func s:CheckGdbRunning()
if !s:running if !s:gdb_running
echoerr string(s:GetCommand()[0]) . ' exited unexpectedly' echoerr string(s:GetCommand()[0]) . ' exited unexpectedly'
call s:CloseBuffers() call s:CloseBuffers()
return '' return ''
@ -246,7 +263,7 @@ func s:StartDebug_term(dict)
let pty_job_info = nvim_get_chan_info(s:pty_job_id) let pty_job_info = nvim_get_chan_info(s:pty_job_id)
let s:ptybuf = pty_job_info['buffer'] let s:ptybuf = pty_job_info['buffer']
let pty = pty_job_info['pty'] let pty = pty_job_info['pty']
let s:ptywin = win_getid(winnr()) let s:ptywin = win_getid()
if s:vertical if s:vertical
" Assuming the source code window will get a signcolumn, use two more " Assuming the source code window will get a signcolumn, use two more
" columns for that, thus one less for the terminal window. " columns for that, thus one less for the terminal window.
@ -317,11 +334,11 @@ func s:StartDebug_term(dict)
call s:CloseBuffers() call s:CloseBuffers()
return return
endif endif
let s:running = v:true let s:gdb_running = v:true
let s:starting = v:true let s:starting = v:true
let gdb_job_info = nvim_get_chan_info(s:gdb_job_id) let gdb_job_info = nvim_get_chan_info(s:gdb_job_id)
let s:gdbbuf = gdb_job_info['buffer'] let s:gdbbuf = gdb_job_info['buffer']
let s:gdbwin = win_getid(winnr()) let s:gdbwin = win_getid()
" Wait for the "startupdone" message before sending any commands. " Wait for the "startupdone" message before sending any commands.
let try_count = 0 let try_count = 0
@ -409,7 +426,7 @@ func s:StartDebug_prompt(dict)
else else
new new
endif endif
let s:gdbwin = win_getid(winnr()) let s:gdbwin = win_getid()
let s:promptbuf = bufnr('') let s:promptbuf = bufnr('')
call prompt_setprompt(s:promptbuf, 'gdb> ') call prompt_setprompt(s:promptbuf, 'gdb> ')
set buftype=prompt set buftype=prompt
@ -476,7 +493,7 @@ func s:StartDebug_prompt(dict)
let pty_job_info = nvim_get_chan_info(s:pty_job_id) let pty_job_info = nvim_get_chan_info(s:pty_job_id)
let s:ptybuf = pty_job_info['buffer'] let s:ptybuf = pty_job_info['buffer']
let pty = pty_job_info['pty'] let pty = pty_job_info['pty']
let s:ptywin = win_getid(winnr()) let s:ptywin = win_getid()
call s:SendCommand('tty ' . pty) call s:SendCommand('tty ' . pty)
" Since GDB runs in a prompt window, the environment has not been set to " Since GDB runs in a prompt window, the environment has not been set to
@ -655,7 +672,7 @@ func s:GdbOutCallback(job_id, msgs, event)
let index += 1 let index += 1
endfor endfor
let curwinid = win_getid(winnr()) let curwinid = win_getid()
call win_gotoid(s:gdbwin) call win_gotoid(s:gdbwin)
" Add the output above the current prompt. " Add the output above the current prompt.
@ -728,7 +745,7 @@ func s:GetAsmAddr(msg)
endfunc endfunc
func s:EndTermDebug(job_id, exit_code, event) func s:EndTermDebug(job_id, exit_code, event)
let s:running = v:false let s:gdb_running = v:false
if s:starting if s:starting
return return
endif endif
@ -737,13 +754,20 @@ func s:EndTermDebug(job_id, exit_code, event)
doauto <nomodeline> User TermdebugStopPre doauto <nomodeline> User TermdebugStopPre
endif endif
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 unlet s:gdbwin
call s:EndDebugCommon() call s:EndDebugCommon()
endfunc endfunc
func s:EndDebugCommon() func s:EndDebugCommon()
let curwinid = win_getid(winnr()) let curwinid = win_getid()
if exists('s:ptybuf') && s:ptybuf if exists('s:ptybuf') && s:ptybuf
exe 'bwipe! ' . s:ptybuf exe 'bwipe! ' . s:ptybuf
@ -785,7 +809,7 @@ func s:EndPromptDebug(job_id, exit_code, event)
doauto <nomodeline> User TermdebugStopPre doauto <nomodeline> User TermdebugStopPre
endif endif
let curwinid = win_getid(winnr()) let curwinid = win_getid()
call win_gotoid(s:gdbwin) call win_gotoid(s:gdbwin)
close close
if curwinid != s:gdbwin if curwinid != s:gdbwin
@ -813,9 +837,9 @@ endfunc
" - CommOutput: ^error,msg="No function contains specified address." " - CommOutput: ^error,msg="No function contains specified address."
func s:HandleDisasmMsg(msg) func s:HandleDisasmMsg(msg)
if a:msg =~ '^\^done' if a:msg =~ '^\^done'
let curwinid = win_getid(winnr()) let curwinid = win_getid()
if win_gotoid(s:asmwin) if win_gotoid(s:asmwin)
silent normal! gg0"_dG silent! %delete _
call setline(1, s:asm_lines) call setline(1, s:asm_lines)
set nomodified set nomodified
set filetype=asm set filetype=asm
@ -857,6 +881,49 @@ func s:HandleDisasmMsg(msg)
endif endif
endfunc 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
func s:CommOutput(job_id, msgs, event) func s:CommOutput(job_id, msgs, event)
for msg in a:msgs for msg in a:msgs
@ -885,6 +952,8 @@ func s:CommOutput(job_id, msgs, event)
elseif msg =~ '^disassemble' elseif msg =~ '^disassemble'
let s:parsing_disasm_msg = 1 let s:parsing_disasm_msg = 1
let s:asm_lines = [] let s:asm_lines = []
elseif msg =~ '^\^done,variables='
call s:HandleVariablesMsg(msg)
endif endif
endif endif
endfor endfor
@ -930,6 +999,7 @@ func s:InstallCommands()
command Program call s:GotoProgram() command Program call s:GotoProgram()
command Source call s:GotoSourcewinOrCreateIt() command Source call s:GotoSourcewinOrCreateIt()
command Asm call s:GotoAsmwinOrCreateIt() command Asm call s:GotoAsmwinOrCreateIt()
command Var call s:GotoVariableswinOrCreateIt()
command Winbar call s:InstallWinbar(1) command Winbar call s:InstallWinbar(1)
let map = 1 let map = 1
@ -984,7 +1054,7 @@ func s:InstallWinbar(force)
" nnoremenu WinBar.Cont :Continue<CR> " nnoremenu WinBar.Cont :Continue<CR>
" nnoremenu WinBar.Stop :Stop<CR> " nnoremenu WinBar.Stop :Stop<CR>
" nnoremenu WinBar.Eval :Evaluate<CR> " nnoremenu WinBar.Eval :Evaluate<CR>
" call add(s:winbar_winids, win_getid(winnr())) " call add(s:winbar_winids, win_getid())
" endif " endif
endfunc endfunc
@ -1005,6 +1075,7 @@ func s:DeleteCommands()
delcommand Program delcommand Program
delcommand Source delcommand Source
delcommand Asm delcommand Asm
delcommand Var
delcommand Winbar delcommand Winbar
if exists('s:k_map_saved') if exists('s:k_map_saved')
@ -1019,7 +1090,7 @@ func s:DeleteCommands()
if has('menu') if has('menu')
" Remove the WinBar entries from all windows where it was added. " 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 " for winid in s:winbar_winids
" if win_gotoid(winid) " if win_gotoid(winid)
" aunmenu WinBar.Step " aunmenu WinBar.Step
@ -1382,7 +1453,7 @@ endfunc
func s:GotoSourcewinOrCreateIt() func s:GotoSourcewinOrCreateIt()
if !win_gotoid(s:sourcewin) if !win_gotoid(s:sourcewin)
new new
let s:sourcewin = win_getid(winnr()) let s:sourcewin = win_getid()
call s:InstallWinbar(0) call s:InstallWinbar(0)
endif endif
endfunc endfunc
@ -1415,19 +1486,21 @@ func s:GotoAsmwinOrCreateIt()
exe 'new' exe 'new'
endif endif
let s:asmwin = win_getid(winnr()) let s:asmwin = win_getid()
setlocal nowrap setlocal nowrap
setlocal number setlocal number
setlocal noswapfile setlocal noswapfile
setlocal buftype=nofile setlocal buftype=nofile
setlocal bufhidden=wipe
setlocal signcolumn=no
setlocal modifiable setlocal modifiable
let asmbuf = bufnr('Termdebug-asm-listing') if s:asmbuf > 0
if asmbuf > 0 exe 'buffer' . s:asmbuf
exe 'buffer' . asmbuf
else else
exe 'file Termdebug-asm-listing' silent file Termdebug-asm-listing
let s:asmbuf = bufnr('Termdebug-asm-listing')
endif endif
if s:GetDisasmWindowHeight() > 0 if s:GetDisasmWindowHeight() > 0
@ -1448,17 +1521,75 @@ func s:GotoAsmwinOrCreateIt()
endif endif
endfunc 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. " Handle stopping and running message from gdb.
" Will update the sign that shows the current position. " Will update the sign that shows the current position.
func s:HandleCursor(msg) func s:HandleCursor(msg)
let wid = win_getid(winnr()) let wid = win_getid()
if a:msg =~ '^\*stopped' if a:msg =~ '^\*stopped'
"call ch_log('program stopped') "call ch_log('program stopped')
let s:stopped = 1 let s:stopped = 1
if a:msg =~ '^\*stopped,reason="exited-normally"'
let s:running = 0
endif
elseif a:msg =~ '^\*running' elseif a:msg =~ '^\*running'
"call ch_log('program running') "call ch_log('program running')
let s:stopped = 0 let s:stopped = 0
let s:running = 1
endif endif
if a:msg =~ 'fullname=' if a:msg =~ 'fullname='
@ -1472,7 +1603,7 @@ func s:HandleCursor(msg)
if asm_addr != '' if asm_addr != ''
let s:asm_addr = asm_addr let s:asm_addr = asm_addr
let curwinid = win_getid(winnr()) let curwinid = win_getid()
if win_gotoid(s:asmwin) if win_gotoid(s:asmwin)
let lnum = search('^' . s:asm_addr) let lnum = search('^' . s:asm_addr)
if lnum == 0 if lnum == 0
@ -1487,6 +1618,10 @@ func s:HandleCursor(msg)
endif endif
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) if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
if lnum =~ '^[0-9]*$' if lnum =~ '^[0-9]*$'
@ -1505,7 +1640,7 @@ func s:HandleCursor(msg)
if &modified if &modified
" TODO: find existing window " TODO: find existing window
exe 'split ' . fnameescape(fname) exe 'split ' . fnameescape(fname)
let s:sourcewin = win_getid(winnr()) let s:sourcewin = win_getid()
call s:InstallWinbar(0) call s:InstallWinbar(0)
else else
exe 'edit ' . fnameescape(fname) exe 'edit ' . fnameescape(fname)