Merge pull request #24821 from seandewar/vim-9f2962141514

vim-patch:9f2962141514,f6fb52b667ee,19968fc4ec2c,a76f3221cdcf,2ae7ffe0bc3c,3d3a9152fa6d - Termdebug stuff
This commit is contained in:
Sean Dewar
2023-08-25 11:59:51 +01:00
committed by GitHub
2 changed files with 443 additions and 201 deletions

View File

@@ -317,6 +317,18 @@ This is similar to using "print" in the gdb window.
You can usually shorten `:Evaluate` to `:Ev`.
Navigating stack frames ~
*termdebug-frames* *:Frame* *:Up* *:Down*
`:Frame` [frame] select frame [frame], which is a frame number,
address, or function name (default: current frame)
`:Up` [count] go up [count] frames (default: 1; the frame that
called the current)
`+` same (see |termdebug_map_plus| to disable)
`:Down` [count] go down [count] frames (default: 1; the frame called
by the current)
`-` same (see |termdebug_map_minus| to disable)
Other commands ~
*termdebug-commands*
*:Gdb* jump to the gdb window
@@ -325,7 +337,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*
@@ -386,10 +400,18 @@ If there is no g:termdebug_config you can use: >vim
let g:termdebug_use_prompt = 1
<
*termdebug_map_K*
The K key is normally mapped to :Evaluate. If you do not want this use: >vim
The K key is normally mapped to |:Evaluate|. If you do not want this use: >vim
let g:termdebug_config['map_K'] = 0
If there is no g:termdebug_config you can use: >vim
let g:termdebug_map_K = 0
<
*termdebug_map_minus*
The - key is normally mapped to |:Down|. If you do not want this use: >vim
let g:termdebug_config['map_minus'] = 0
<
*termdebug_map_plus*
The + key is normally mapped to |:Up|. If you do not want this use: >vim
let g:termdebug_config['map_plus'] = 0
<
*termdebug_disasm_window*
If you want the Asm window shown by default, set the "disasm_window" flag to
@@ -400,6 +422,15 @@ If there is no g:termdebug_config you can use: >vim
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: >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 ~
*termdebug-communication*
There is another, hidden, buffer, which is used for Vim to communicate with

View File

@@ -2,7 +2,7 @@
"
" Author: Bram Moolenaar
" Copyright: Vim license applies, see ":help license"
" Last Change: 2023 Jun 24
" Last Change: 2023 Aug 23
"
" WORK IN PROGRESS - The basics works stable, more to come
" Note: In general you need at least GDB 7.12 because this provides the
@@ -75,6 +75,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 = []
@@ -127,6 +128,10 @@ func s:GetCommand()
return type(cmd) == v:t_list ? copy(cmd) : [cmd]
endfunc
func s:Echoerr(msg)
echohl ErrorMsg | echom '[termdebug] ' .. a:msg | echohl None
endfunc
func s:StartDebug(bang, ...)
" First argument is the command to debug, second core file or process ID.
call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang})
@@ -139,18 +144,21 @@ endfunc
func s:StartDebug_internal(dict)
if exists('s:gdbwin')
echoerr 'Terminal debugger already running, cannot run two'
call s:Echoerr('Terminal debugger already running, cannot run two')
return
endif
let gdbcmd = s:GetCommand()
if !executable(gdbcmd[0])
echoerr 'Cannot execute debugger program "' .. gdbcmd[0] .. '"'
call s:Echoerr('Cannot execute debugger program "' .. gdbcmd[0] .. '"')
return
endif
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
@@ -159,7 +167,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.
@@ -207,11 +215,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
@@ -220,12 +234,19 @@ endfunc
" Use when debugger didn't start or ended.
func s:CloseBuffers()
exe 'bwipe! ' . s:ptybuf
if s:asmbuf > 0 && bufexists(s:asmbuf)
exe 'bwipe! ' . s:asmbuf
endif
if s:varbuf > 0 && bufexists(s:varbuf)
exe 'bwipe! ' . s:varbuf
endif
let s:running = 0
unlet! s:gdbwin
endfunc
func s:CheckGdbRunning()
if !s:running
echoerr string(s:GetCommand()[0]) . ' exited unexpectedly'
if !s:gdb_running
call s:Echoerr(string(s:GetCommand()[0]) . ' exited unexpectedly')
call s:CloseBuffers()
return ''
endif
@@ -237,16 +258,16 @@ func s:StartDebug_term(dict)
execute s:vertical ? 'vnew' : 'new'
let s:pty_job_id = termopen('tail -f /dev/null;#gdb program')
if s:pty_job_id == 0
echoerr 'invalid argument (or job table is full) while opening terminal window'
call s:Echoerr('Invalid argument (or job table is full) while opening terminal window')
return
elseif s:pty_job_id == -1
echoerr 'Failed to open the program terminal window'
call s:Echoerr('Failed to open the program terminal window')
return
endif
let pty_job_info = nvim_get_chan_info(s:pty_job_id)
let s:ptybuf = pty_job_info['buffer']
let pty = pty_job_info['pty']
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.
@@ -264,11 +285,11 @@ func s:StartDebug_term(dict)
\ })
" hide terminal buffer
if s:comm_job_id == 0
echoerr 'invalid argument (or job table is full) while opening communication terminal window'
call s:Echoerr('Invalid argument (or job table is full) while opening communication terminal window')
exe 'bwipe! ' . s:ptybuf
return
elseif s:comm_job_id == -1
echoerr 'Failed to open the communication terminal window'
call s:Echoerr('Failed to open the communication terminal window')
exe 'bwipe! ' . s:ptybuf
return
endif
@@ -309,19 +330,19 @@ func s:StartDebug_term(dict)
" call ch_log('executing "' . join(gdb_cmd) . '"')
let s:gdb_job_id = termopen(gdb_cmd, {'on_exit': function('s:EndTermDebug')})
if s:gdb_job_id == 0
echoerr 'invalid argument (or job table is full) while opening gdb terminal window'
call s:Echoerr('Invalid argument (or job table is full) while opening gdb terminal window')
exe 'bwipe! ' . s:ptybuf
return
elseif s:gdb_job_id == -1
echoerr 'Failed to open the gdb terminal window'
call s:Echoerr('Failed to open the gdb terminal window')
call s:CloseBuffers()
return
endif
let s:running = v:true
let s:gdb_running = v:true
let s:starting = v:true
let gdb_job_info = nvim_get_chan_info(s:gdb_job_id)
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.
let try_count = 0
@@ -369,7 +390,7 @@ func s:StartDebug_term(dict)
" response can be in the same line or the next line
let response = line1 . line2
if response =~ 'Undefined command'
echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
call s:Echoerr('Sorry, your gdb is too old, gdb 7.12 is required')
" CHECKME: possibly send a "server show version" here
call s:CloseBuffers()
return
@@ -388,7 +409,7 @@ func s:StartDebug_term(dict)
endif
let try_count += 1
if try_count > 100
echoerr 'Cannot check if your gdb works, continuing anyway'
call s:Echoerr('Cannot check if your gdb works, continuing anyway')
break
endif
sleep 10m
@@ -409,7 +430,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
@@ -447,14 +468,15 @@ func s:StartDebug_prompt(dict)
\ 'on_stdout': function('s:JobOutCallback', {'last_line': '', 'real_cb': function('s:GdbOutCallback')}),
\ })
if s:gdbjob == 0
echoerr 'invalid argument (or job table is full) while starting gdb job'
call s:Echoerr('Invalid argument (or job table is full) while starting gdb job')
exe 'bwipe! ' . s:ptybuf
return
elseif s:gdbjob == -1
echoerr 'Failed to start the gdb job'
call s:Echoerr('Failed to start the gdb job')
call s:CloseBuffers()
return
endif
exe $'au BufUnload <buffer={s:promptbuf}> ++once call jobstop(s:gdbjob)'
let s:ptybuf = 0
if has('win32')
@@ -463,20 +485,19 @@ func s:StartDebug_prompt(dict)
else
" Unix: Run the debugged program in a terminal window. Open it below the
" gdb window.
execute 'new'
wincmd x | wincmd j
belowright let s:pty_job_id = termopen('tail -f /dev/null;#gdb program')
belowright new
let s:pty_job_id = termopen('tail -f /dev/null;#gdb program')
if s:pty_job_id == 0
echoerr 'invalid argument (or job table is full) while opening terminal window'
call s:Echoerr('Invalid argument (or job table is full) while opening terminal window')
return
elseif s:pty_job_id == -1
echoerr 'Failed to open the program terminal window'
call s:Echoerr('Failed to open the program terminal window')
return
endif
let pty_job_info = nvim_get_chan_info(s:pty_job_id)
let s:ptybuf = pty_job_info['buffer']
let pty = pty_job_info['pty']
let s:ptywin = win_getid(winnr())
let s:ptywin = win_getid()
call s:SendCommand('tty ' . pty)
" Since GDB runs in a prompt window, the environment has not been set to
@@ -599,12 +620,12 @@ func s:PromptInterrupt()
" Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
" the debugger program so that gdb responds again.
if s:pid == 0
echoerr 'Cannot interrupt gdb, did not find a process ID'
call s:Echoerr('Cannot interrupt gdb, did not find a process ID')
else
call debugbreak(s:pid)
endif
else
call jobstop(s:gdbjob)
call v:lua.vim.uv.kill(jobpid(s:gdbjob), 'sigint')
endif
endfunc
@@ -629,33 +650,39 @@ endfunc
func s:GdbOutCallback(job_id, msgs, event)
"call ch_log('received from gdb: ' . a:text)
" Drop the gdb prompt, we have our own.
" Drop status and echo'd commands.
call filter(a:msgs, { index, val ->
\ val !=# '(gdb)' && val !=# '^done' && val[0] !=# '&'})
let comm_msgs = []
let lines = []
let index = 0
for msg in a:msgs
" Disassembly messages need to be forwarded as-is.
if s:parsing_disasm_msg || msg =~ '^&"disassemble'
call s:CommOutput(a:job_id, [msg], a:event)
continue
endif
" Drop the gdb prompt, we have our own.
" Drop status and echo'd commands.
if msg == '(gdb) ' || msg == '^done' || msg[0] == '&'
continue
endif
if msg =~ '^\^error,msg='
if exists('s:evalexpr')
\ && s:DecodeMessage(msg[11:], v:false)
\ =~ 'A syntax error in expression, near\|No symbol .* in current context'
" Silently drop evaluation errors.
call remove(a:msgs, index)
unlet s:evalexpr
continue
endif
elseif msg[0] == '~'
call add(lines, s:DecodeMessage(msg[1:], v:false))
call remove(a:msgs, index)
continue
endif
let index += 1
call add(comm_msgs, msg)
endfor
let curwinid = win_getid(winnr())
let curwinid = win_getid()
call win_gotoid(s:gdbwin)
" Add the output above the current prompt.
@@ -667,32 +694,34 @@ func s:GdbOutCallback(job_id, msgs, event)
endif
call win_gotoid(curwinid)
call s:CommOutput(a:job_id, a:msgs, a:event)
call s:CommOutput(a:job_id, comm_msgs, a:event)
endfunc
" Decode a message from gdb. "quotedText" starts with a ", return the text up
" to the next ", unescaping characters:
" to the next unescaped ", unescaping characters:
" - remove line breaks (unless "literal" is v:true)
" - change \" to "
" - change \\t to \t (unless "literal" is v:true)
" - change \0xhh to \xhh (disabled for now)
" - change \ooo to octal
" - change \\ to \
func s:DecodeMessage(quotedText, literal)
if a:quotedText[0] != '"'
echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
call s:Echoerr('DecodeMessage(): missing quote in ' . a:quotedText)
return
endif
let msg = a:quotedText
\ ->substitute('^"\|".*', '', 'g')
" multi-byte characters arrive in octal form
" NULL-values must be kept encoded as those break the string otherwise
\ ->substitute('^"\|[^\\]\zs".*', '', 'g')
\ ->substitute('\\"', '"', 'g')
"\ multi-byte characters arrive in octal form
"\ NULL-values must be kept encoded as those break the string otherwise
\ ->substitute('\\000', s:NullRepl, 'g')
\ ->substitute('\\\o\o\o', {-> eval('"' .. submatch(0) .. '"')}, 'g')
" Note: GDB docs also mention hex encodings - the translations below work
" but we keep them out for performance-reasons until we actually see
" those in mi-returns
" \ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g')
" \ ->substitute('\\0x00', s:NullRepl, 'g')
"\ Note: GDB docs also mention hex encodings - the translations below work
"\ but we keep them out for performance-reasons until we actually see
"\ those in mi-returns
"\ \ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g')
"\ \ ->substitute('\\0x00', s:NullRepl, 'g')
\ ->substitute('\\\\', '\', 'g')
\ ->substitute(s:NullRepl, '\\000', 'g')
if !a:literal
@@ -728,7 +757,7 @@ func s:GetAsmAddr(msg)
endfunc
func s:EndTermDebug(job_id, exit_code, event)
let s:running = v:false
let s:gdb_running = v:false
if s:starting
return
endif
@@ -738,16 +767,22 @@ func s:EndTermDebug(job_id, exit_code, event)
endif
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
endif
if s:asmbuf > 0 && bufexists(s:asmbuf)
exe 'bwipe! ' . s:asmbuf
endif
if s:varbuf > 0 && bufexists(s:varbuf)
exe 'bwipe! ' . s:varbuf
endif
let s:running = 0
" Restore 'signcolumn' in all buffers for which it was set.
call win_gotoid(s:sourcewin)
@@ -785,11 +820,8 @@ func s:EndPromptDebug(job_id, exit_code, event)
doauto <nomodeline> User TermdebugStopPre
endif
let curwinid = win_getid(winnr())
call win_gotoid(s:gdbwin)
close
if curwinid != s:gdbwin
call win_gotoid(curwinid)
if bufexists(s:promptbuf)
exe 'bwipe! ' . s:promptbuf
endif
call s:EndDebugCommon()
@@ -797,7 +829,6 @@ func s:EndPromptDebug(job_id, exit_code, event)
"call ch_log("Returning from EndPromptDebug()")
endfunc
" - CommOutput: disassemble $pc
" - CommOutput: &"disassemble $pc\n"
" - CommOutput: ~"Dump of assembler code for function main(int, char**):\n"
" - CommOutput: ~" 0x0000555556466f69 <+0>:\tpush rbp\n"
@@ -807,15 +838,14 @@ endfunc
" - CommOutput: ~"End of assembler dump.\n"
" - CommOutput: ^done
" - CommOutput: disassemble $pc
" - CommOutput: &"disassemble $pc\n"
" - CommOutput: &"No function contains specified address.\n"
" - 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
@@ -839,15 +869,17 @@ func s:HandleDisasmMsg(msg)
call s:SendCommand('disassemble $pc,+100')
endif
let s:parsing_disasm_msg = 0
elseif a:msg =~ '\&\"disassemble \$pc'
elseif a:msg =~ '^&"disassemble \$pc'
if a:msg =~ '+100'
" This is our second disasm attempt
let s:parsing_disasm_msg = 2
endif
else
elseif a:msg !~ '^&"disassemble'
let value = substitute(a:msg, '^\~\"[ ]*', '', '')
let value = substitute(value, '^=>[ ]*', '', '')
let value = substitute(value, '\\n\"\r$', '', '')
" Nvim already trims the final "\r" in s:CommOutput()
" let value = substitute(value, '\\n\"\r$', '', '')
let value = substitute(value, '\\n\"$', '', '')
let value = substitute(value, '\r', '', '')
let value = substitute(value, '\\t', ' ', 'g')
@@ -857,12 +889,54 @@ func s:HandleDisasmMsg(msg)
endif
endfunc
func s:CommOutput(job_id, msgs, event)
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)
for msg in a:msgs
" remove prefixed NL
if msg[0] == "\n"
let msg = msg[1:]
" Nvim job lines are split on "\n", so trim a suffixed CR.
if msg[-1:] == "\r"
let msg = msg[:-2]
endif
if s:parsing_disasm_msg
@@ -882,9 +956,12 @@ func s:CommOutput(job_id, msgs, event)
call s:HandleEvaluate(msg)
elseif msg =~ '^\^error,msg='
call s:HandleError(msg)
elseif msg =~ '^disassemble'
elseif msg =~ '^&"disassemble'
let s:parsing_disasm_msg = 1
let s:asm_lines = []
call s:HandleDisasmMsg(msg)
elseif msg =~ '^\^done,variables='
call s:HandleVariablesMsg(msg)
endif
endif
endfor
@@ -925,11 +1002,16 @@ func s:InstallCommands()
command Continue call chansend(s:gdb_job_id, "continue\r")
endif
command -nargs=* Frame call s:Frame(<q-args>)
command -count=1 Up call s:Up(<count>)
command -count=1 Down call s:Down(<count>)
command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
command Gdb call win_gotoid(s:gdbwin)
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
@@ -939,17 +1021,29 @@ func s:InstallCommands()
let map = g:termdebug_map_K
endif
if map
" let s:k_map_saved = maparg('K', 'n', 0, 1)
let s:k_map_saved = {}
for map in nvim_get_keymap('n')
if map.lhs ==# 'K'
let s:k_map_saved = map
break
endif
endfor
let s:k_map_saved = maparg('K', 'n', 0, 1)
nnoremap K :Evaluate<CR>
endif
let map = 1
if exists('g:termdebug_config')
let map = get(g:termdebug_config, 'map_plus', 1)
endif
if map
let s:plus_map_saved = maparg('+', 'n', 0, 1)
nnoremap <expr> + $'<Cmd>{v:count1}Up<CR>'
endif
let map = 1
if exists('g:termdebug_config')
let map = get(g:termdebug_config, 'map_minus', 1)
endif
if map
let s:minus_map_saved = maparg('-', 'n', 0, 1)
nnoremap <expr> - $'<Cmd>{v:count1}Down<CR>'
endif
if has('menu') && &mouse != ''
call s:InstallWinbar(0)
@@ -984,7 +1078,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
@@ -1000,11 +1094,15 @@ func s:DeleteCommands()
delcommand Arguments
delcommand Stop
delcommand Continue
delcommand Frame
delcommand Up
delcommand Down
delcommand Evaluate
delcommand Gdb
delcommand Program
delcommand Source
delcommand Asm
delcommand Var
delcommand Winbar
if exists('s:k_map_saved')
@@ -1016,10 +1114,28 @@ func s:DeleteCommands()
endif
unlet s:k_map_saved
endif
if exists('s:plus_map_saved')
if empty(s:plus_map_saved)
nunmap +
else
" call mapset(s:plus_map_saved)
call mapset('n', 0, s:plus_map_saved)
endif
unlet s:plus_map_saved
endif
if exists('s:minus_map_saved')
if empty(s:minus_map_saved)
nunmap -
else
" call mapset(s:minus_map_saved)
call mapset('n', 0, s:minus_map_saved)
endif
unlet s:minus_map_saved
endif
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
@@ -1118,7 +1234,7 @@ func s:ClearBreakpoint()
endif
echomsg 'Breakpoint ' . id . ' cleared from line ' . lnum . '.'
else
echoerr 'Internal error trying to remove breakpoint at line ' . lnum . '!'
call s:Echoerr('Internal error trying to remove breakpoint at line ' . lnum . '!')
endif
else
echomsg 'No breakpoint to remove at line ' . lnum . '.'
@@ -1132,6 +1248,37 @@ func s:Run(args)
call s:SendResumingCommand('-exec-run')
endfunc
" :Frame - go to a specfic frame in the stack
func s:Frame(arg)
" Note: we explicit do not use mi's command
" call s:SendCommand('-stack-select-frame "' . a:arg .'"')
" as we only get a "done" mi response and would have to open the file
" 'manually' - using cli command "frame" provides us with the mi response
" already parsed and allows for more formats
if a:arg =~ '^\d\+$' || a:arg == ''
" specify frame by number
call s:SendCommand('-interpreter-exec mi "frame ' . a:arg .'"')
elseif a:arg =~ '^0x[0-9a-fA-F]\+$'
" specify frame by stack address
call s:SendCommand('-interpreter-exec mi "frame address ' . a:arg .'"')
else
" specify frame by function name
call s:SendCommand('-interpreter-exec mi "frame function ' . a:arg .'"')
endif
endfunc
" :Up - go a:count frames in the stack "higher"
func s:Up(count)
" the 'correct' one would be -stack-select-frame N, but we don't know N
call s:SendCommand($'-interpreter-exec console "up {a:count}"')
endfunc
" :Down - go a:count frames in the stack "below"
func s:Down(count)
" the 'correct' one would be -stack-select-frame N, but we don't know N
call s:SendCommand($'-interpreter-exec console "down {a:count}"')
endfunc
func s:SendEval(expr)
" check for "likely" boolean expressions, in which case we take it as lhs
if a:expr =~ "[=!<>]="
@@ -1376,13 +1523,13 @@ func s:HandleError(msg)
let s:evalFromBalloonExpr = 0
return
endif
let msgVal = substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
let msgVal = substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
call s:Echoerr(substitute(msgVal, '\\"', '"', 'g'))
endfunc
func s:GotoSourcewinOrCreateIt()
if !win_gotoid(s:sourcewin)
new
new
let s:sourcewin = win_getid()
call s:InstallWinbar(0)
endif
@@ -1415,19 +1562,21 @@ func s:GotoAsmwinOrCreateIt()
else
exe 'new'
endif
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
if s:asmbuf > 0 && bufexists(s:asmbuf)
exe 'buffer' . s:asmbuf
else
else
silent file Termdebug-asm-listing
let s:asmbuf = bufnr('Termdebug-asm-listing')
endif
@@ -1448,17 +1597,75 @@ func s:GotoAsmwinOrCreateIt()
endif
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 && bufexists(s:varbuf)
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)
func s:HandleCursor(msg)
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
@@ -1472,7 +1679,7 @@ func s:HandleCursor(msg)
let asm_addr = s:GetAsmAddr(a:msg)
if asm_addr != ''
let s:asm_addr = asm_addr
let curwinid = win_getid()
if win_gotoid(s:asmwin)
let lnum = search('^' . s:asm_addr)
@@ -1487,6 +1694,10 @@ func s:HandleCursor(msg)
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)
let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
@@ -1505,7 +1716,7 @@ func s:HandleCursor(msg)
augroup END
if &modified
" TODO: find existing window
exe 'split ' . fnameescape(fname)
exe 'split ' . fnameescape(fname)
let s:sourcewin = win_getid()
call s:InstallWinbar(0)
else