1fe386641SBram Moolenaar" Debugger plugin using gdb. 2c572da5fSBram Moolenaar" 3b3307b5eSBram Moolenaar" Author: Bram Moolenaar 4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license" 5*cb80aa2dSBram Moolenaar" Last Change: 2020 Oct 25 6c572da5fSBram Moolenaar" 7b3307b5eSBram Moolenaar" WORK IN PROGRESS - Only the basics work 8b3307b5eSBram Moolenaar" Note: On MS-Windows you need a recent version of gdb. The one included with 9b3307b5eSBram Moolenaar" MingW is too old (7.6.1). 10b3307b5eSBram Moolenaar" I used version 7.12 from http://www.equation.com/servlet/equation.cmd?fa=gdb 11fe386641SBram Moolenaar" 12b3307b5eSBram Moolenaar" There are two ways to run gdb: 13b3307b5eSBram Moolenaar" - In a terminal window; used if possible, does not work on MS-Windows 14b3307b5eSBram Moolenaar" Not used when g:termdebug_use_prompt is set to 1. 15b3307b5eSBram Moolenaar" - Using a "prompt" buffer; may use a terminal window for the program 16b3307b5eSBram Moolenaar" 17b3307b5eSBram Moolenaar" For both the current window is used to view source code and shows the 18b3307b5eSBram Moolenaar" current statement from gdb. 19b3307b5eSBram Moolenaar" 20b3307b5eSBram Moolenaar" USING A TERMINAL WINDOW 21b3307b5eSBram Moolenaar" 22b3307b5eSBram Moolenaar" Opens two visible terminal windows: 23b3307b5eSBram Moolenaar" 1. runs a pty for the debugged program, as with ":term NONE" 24b3307b5eSBram Moolenaar" 2. runs gdb, passing the pty of the debugged program 25fe386641SBram Moolenaar" A third terminal window is hidden, it is used for communication with gdb. 26fe386641SBram Moolenaar" 27b3307b5eSBram Moolenaar" USING A PROMPT BUFFER 28b3307b5eSBram Moolenaar" 29b3307b5eSBram Moolenaar" Opens a window with a prompt buffer to communicate with gdb. 30b3307b5eSBram Moolenaar" Gdb is run as a job with callbacks for I/O. 31b3307b5eSBram Moolenaar" On Unix another terminal window is opened to run the debugged program 32b3307b5eSBram Moolenaar" On MS-Windows a separate console is opened to run the debugged program 33b3307b5eSBram Moolenaar" 34fe386641SBram Moolenaar" The communication with gdb uses GDB/MI. See: 35fe386641SBram Moolenaar" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html 36c572da5fSBram Moolenaar 37b3307b5eSBram Moolenaar" In case this gets sourced twice. 3837c64c78SBram Moolenaarif exists(':Termdebug') 3937c64c78SBram Moolenaar finish 4037c64c78SBram Moolenaarendif 4137c64c78SBram Moolenaar 42b3307b5eSBram Moolenaar" Need either the +terminal feature or +channel and the prompt buffer. 43b3307b5eSBram Moolenaar" The terminal feature does not work with gdb on win32. 44b3307b5eSBram Moolenaarif has('terminal') && !has('win32') 45b3307b5eSBram Moolenaar let s:way = 'terminal' 46b3307b5eSBram Moolenaarelseif has('channel') && exists('*prompt_setprompt') 47b3307b5eSBram Moolenaar let s:way = 'prompt' 48b3307b5eSBram Moolenaarelse 49b3307b5eSBram Moolenaar if has('terminal') 50b3307b5eSBram Moolenaar let s:err = 'Cannot debug, missing prompt buffer support' 51b3307b5eSBram Moolenaar else 52b3307b5eSBram Moolenaar let s:err = 'Cannot debug, +channel feature is not supported' 53b3307b5eSBram Moolenaar endif 54b3307b5eSBram Moolenaar command -nargs=* -complete=file -bang Termdebug echoerr s:err 55b3307b5eSBram Moolenaar command -nargs=+ -complete=file -bang TermdebugCommand echoerr s:err 56b3307b5eSBram Moolenaar finish 57b3307b5eSBram Moolenaarendif 5860e73f2aSBram Moolenaar 59ca4cc018SBram Moolenaarlet s:keepcpo = &cpo 60ca4cc018SBram Moolenaarset cpo&vim 61ca4cc018SBram Moolenaar 62fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim". 63fe386641SBram Moolenaar" To end type "quit" in the gdb window. 6432c67ba7SBram Moolenaarcommand -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>) 6532c67ba7SBram Moolenaarcommand -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>) 66c572da5fSBram Moolenaar 67fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb". 6818223a59SBram Moolenaarif !exists('g:termdebugger') 6918223a59SBram Moolenaar let g:termdebugger = 'gdb' 70c572da5fSBram Moolenaarendif 71c572da5fSBram Moolenaar 72fe386641SBram Moolenaarlet s:pc_id = 12 73de1a8314SBram Moolenaarlet s:break_id = 13 " breakpoint number is added to this 7460e73f2aSBram Moolenaarlet s:stopped = 1 75e09ba7baSBram Moolenaar 765378e1cfSBram Moolenaar" Take a breakpoint number as used by GDB and turn it into an integer. 7737402ed5SBram Moolenaar" The breakpoint may contain a dot: 123.4 -> 123004 7837402ed5SBram Moolenaar" The main breakpoint has a zero subid. 7937402ed5SBram Moolenaarfunc s:Breakpoint2SignNumber(id, subid) 8037402ed5SBram Moolenaar return s:break_id + a:id * 1000 + a:subid 815378e1cfSBram Moolenaarendfunction 825378e1cfSBram Moolenaar 83f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new) 84f07f9e73SBram Moolenaar let default = a:init ? 'default ' : '' 85f07f9e73SBram Moolenaar if a:new ==# 'light' && a:old !=# 'light' 86f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue" 87f07f9e73SBram Moolenaar elseif a:new ==# 'dark' && a:old !=# 'dark' 88f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue" 89e09ba7baSBram Moolenaar endif 90f07f9e73SBram Moolenaarendfunc 91f07f9e73SBram Moolenaar 92f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background) 93e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 94fe386641SBram Moolenaar 9532c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...) 9632c67ba7SBram Moolenaar " First argument is the command to debug, second core file or process ID. 9732c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) 9832c67ba7SBram Moolenaarendfunc 9932c67ba7SBram Moolenaar 10032c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...) 10132c67ba7SBram Moolenaar " First argument is the command to debug, rest are run arguments. 10232c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang}) 10332c67ba7SBram Moolenaarendfunc 10432c67ba7SBram Moolenaar 10532c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict) 106b3623a38SBram Moolenaar if exists('s:gdbwin') 10718223a59SBram Moolenaar echoerr 'Terminal debugger already running, cannot run two' 108b3623a38SBram Moolenaar return 109b3623a38SBram Moolenaar endif 11018223a59SBram Moolenaar if !executable(g:termdebugger) 11118223a59SBram Moolenaar echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"' 11218223a59SBram Moolenaar return 11318223a59SBram Moolenaar endif 11418223a59SBram Moolenaar 115b3307b5eSBram Moolenaar let s:ptywin = 0 1164551c0a9SBram Moolenaar let s:pid = 0 117b3623a38SBram Moolenaar 118b3307b5eSBram Moolenaar " Uncomment this line to write logging in "debuglog". 119b3307b5eSBram Moolenaar " call ch_logfile('debuglog', 'w') 120b3307b5eSBram Moolenaar 121b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 122*cb80aa2dSBram Moolenaar 123*cb80aa2dSBram Moolenaar " Remember the old value of 'signcolumn' for each buffer that it's set in, so 124*cb80aa2dSBram Moolenaar " that we can restore the value for all buffers. 125*cb80aa2dSBram Moolenaar let b:save_signcolumn = &signcolumn 126*cb80aa2dSBram Moolenaar let s:signcolumn_buflist = [bufnr()] 127fe386641SBram Moolenaar 12824a98a0eSBram Moolenaar let s:save_columns = 0 12968e6560bSBram Moolenaar let s:allleft = 0 13024a98a0eSBram Moolenaar if exists('g:termdebug_wide') 13124a98a0eSBram Moolenaar if &columns < g:termdebug_wide 13238baa3e6SBram Moolenaar let s:save_columns = &columns 13338baa3e6SBram Moolenaar let &columns = g:termdebug_wide 13468e6560bSBram Moolenaar " If we make the Vim window wider, use the whole left halve for the debug 13568e6560bSBram Moolenaar " windows. 13668e6560bSBram Moolenaar let s:allleft = 1 13724a98a0eSBram Moolenaar endif 138b3307b5eSBram Moolenaar let s:vertical = 1 13938baa3e6SBram Moolenaar else 140b3307b5eSBram Moolenaar let s:vertical = 0 14138baa3e6SBram Moolenaar endif 14238baa3e6SBram Moolenaar 143b3307b5eSBram Moolenaar " Override using a terminal window by setting g:termdebug_use_prompt to 1. 144b3307b5eSBram Moolenaar let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt 145b3307b5eSBram Moolenaar if has('terminal') && !has('win32') && !use_prompt 146b3307b5eSBram Moolenaar let s:way = 'terminal' 147b3307b5eSBram Moolenaar else 148b3307b5eSBram Moolenaar let s:way = 'prompt' 149b3307b5eSBram Moolenaar endif 150b3307b5eSBram Moolenaar 151b3307b5eSBram Moolenaar if s:way == 'prompt' 152b3307b5eSBram Moolenaar call s:StartDebug_prompt(a:dict) 153b3307b5eSBram Moolenaar else 154b3307b5eSBram Moolenaar call s:StartDebug_term(a:dict) 155b3307b5eSBram Moolenaar endif 156b3307b5eSBram Moolenaarendfunc 157b3307b5eSBram Moolenaar 158ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended. 159ef3c6a5bSBram Moolenaarfunc s:CloseBuffers() 160ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:ptybuf 161ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:commbuf 162ef3c6a5bSBram Moolenaar unlet! s:gdbwin 163ef3c6a5bSBram Moolenaarendfunc 164ef3c6a5bSBram Moolenaar 165b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 166b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 167fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 168b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 169b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 170fe386641SBram Moolenaar \ }) 171fe386641SBram Moolenaar if s:ptybuf == 0 172fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 173fe386641SBram Moolenaar return 174fe386641SBram Moolenaar endif 175fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 17645d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 177b3307b5eSBram Moolenaar if s:vertical 17851b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 17951b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 18051b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 18168e6560bSBram Moolenaar if s:allleft 18268e6560bSBram Moolenaar " use the whole left column 18368e6560bSBram Moolenaar wincmd H 18468e6560bSBram Moolenaar endif 18551b0f370SBram Moolenaar endif 186fe386641SBram Moolenaar 187fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 188fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 189fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 190fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 191fe386641SBram Moolenaar \ 'hidden': 1, 192fe386641SBram Moolenaar \ }) 193fe386641SBram Moolenaar if s:commbuf == 0 194fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 195fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 196fe386641SBram Moolenaar return 197fe386641SBram Moolenaar endif 198fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 199c572da5fSBram Moolenaar 200c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 201c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 20232c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 20332c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 20432c67ba7SBram Moolenaar 20532c67ba7SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 206b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 20760e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 208fe386641SBram Moolenaar \ 'term_finish': 'close', 209c572da5fSBram Moolenaar \ }) 21060e73f2aSBram Moolenaar if s:gdbbuf == 0 211fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 212ef3c6a5bSBram Moolenaar call s:CloseBuffers() 213fe386641SBram Moolenaar return 214fe386641SBram Moolenaar endif 21545d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 216fe386641SBram Moolenaar 21732c67ba7SBram Moolenaar " Set arguments to be run 21832c67ba7SBram Moolenaar if len(proc_args) 21932c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 22032c67ba7SBram Moolenaar endif 22132c67ba7SBram Moolenaar 222fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 22360e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 22460e73f2aSBram Moolenaar 2253e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 2263e4b84d0SBram Moolenaar " why the debugger doesn't work. 2273e4b84d0SBram Moolenaar let try_count = 0 2283e4b84d0SBram Moolenaar while 1 229ef3c6a5bSBram Moolenaar let gdbproc = term_getjob(s:gdbbuf) 230ef3c6a5bSBram Moolenaar if gdbproc == v:null || job_status(gdbproc) !=# 'run' 231ef3c6a5bSBram Moolenaar echoerr string(g:termdebugger) . ' exited unexpectedly' 232ef3c6a5bSBram Moolenaar call s:CloseBuffers() 233ef3c6a5bSBram Moolenaar return 234ef3c6a5bSBram Moolenaar endif 235ef3c6a5bSBram Moolenaar 2363e4b84d0SBram Moolenaar let response = '' 237b3623a38SBram Moolenaar for lnum in range(1, 200) 23819c8fe19SBram Moolenaar let line1 = term_getline(s:gdbbuf, lnum) 23919c8fe19SBram Moolenaar let line2 = term_getline(s:gdbbuf, lnum + 1) 24019c8fe19SBram Moolenaar if line1 =~ 'new-ui mi ' 241f63db65bSBram Moolenaar " response can be in the same line or the next line 24219c8fe19SBram Moolenaar let response = line1 . line2 2433e4b84d0SBram Moolenaar if response =~ 'Undefined command' 244f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 245ef3c6a5bSBram Moolenaar call s:CloseBuffers() 2463e4b84d0SBram Moolenaar return 2473e4b84d0SBram Moolenaar endif 2483e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2493e4b84d0SBram Moolenaar " Success! 2503e4b84d0SBram Moolenaar break 2513e4b84d0SBram Moolenaar endif 25219c8fe19SBram Moolenaar elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi ' 25319c8fe19SBram Moolenaar " Reading symbols might take a while, try more times 25406fe74aeSBram Moolenaar let try_count -= 1 25506fe74aeSBram Moolenaar endif 2563e4b84d0SBram Moolenaar endfor 2573e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2583e4b84d0SBram Moolenaar break 2593e4b84d0SBram Moolenaar endif 2603e4b84d0SBram Moolenaar let try_count += 1 2613e4b84d0SBram Moolenaar if try_count > 100 2623e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2633e4b84d0SBram Moolenaar break 2643e4b84d0SBram Moolenaar endif 2653e4b84d0SBram Moolenaar sleep 10m 2663e4b84d0SBram Moolenaar endwhile 2673e4b84d0SBram Moolenaar 26891359014SBram Moolenaar " Interpret commands while the target is running. This should usually only be 26960e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 27060e73f2aSBram Moolenaar " running. 27160e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 272b3307b5eSBram Moolenaar " Older gdb uses a different command. 273b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 274e09ba7baSBram Moolenaar 275f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 276f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 277b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 278f3ba14ffSBram Moolenaar 279ef3c6a5bSBram Moolenaar call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')}) 280b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 281b3307b5eSBram Moolenaarendfunc 282b3307b5eSBram Moolenaar 283b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 284b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 285b3307b5eSBram Moolenaar if s:vertical 286b3307b5eSBram Moolenaar vertical new 287b3307b5eSBram Moolenaar else 288b3307b5eSBram Moolenaar new 289b3307b5eSBram Moolenaar endif 290b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 291b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 292b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 293b3307b5eSBram Moolenaar set buftype=prompt 294b3307b5eSBram Moolenaar file gdb 295b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 296b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 297b3307b5eSBram Moolenaar 298b3307b5eSBram Moolenaar if s:vertical 299b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 300b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 301b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 302b3307b5eSBram Moolenaar endif 303b3307b5eSBram Moolenaar 304b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 305b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 306b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 307b3307b5eSBram Moolenaar 308b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 309b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 310b3307b5eSBram Moolenaar 311b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 312b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 313b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 314b3307b5eSBram Moolenaar \ }) 315b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 316b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 317b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 318b3307b5eSBram Moolenaar return 319b3307b5eSBram Moolenaar endif 3204551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 3214551c0a9SBram Moolenaar set modified 322b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 323b3307b5eSBram Moolenaar 32491359014SBram Moolenaar " Interpret commands while the target is running. This should usually only 325b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 326b3307b5eSBram Moolenaar " target is running. 327b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 328b3307b5eSBram Moolenaar " Older gdb uses a different command. 329b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 330b3307b5eSBram Moolenaar 331b3307b5eSBram Moolenaar let s:ptybuf = 0 332b3307b5eSBram Moolenaar if has('win32') 333b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 334b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 335b3307b5eSBram Moolenaar elseif has('terminal') 336b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 337b3307b5eSBram Moolenaar " gdb window. 338b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 339b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 340b3307b5eSBram Moolenaar \ }) 341b3307b5eSBram Moolenaar if s:ptybuf == 0 342b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 343b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 344b3307b5eSBram Moolenaar return 345b3307b5eSBram Moolenaar endif 346b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 347b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 348b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 349b3307b5eSBram Moolenaar 350b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 351b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 352b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 353b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 354b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 355b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 356b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 357b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 358b3307b5eSBram Moolenaar else 359b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 360b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 361b3307b5eSBram Moolenaar endif 362b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 363b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 364b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 365b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 366b3307b5eSBram Moolenaar 367b3307b5eSBram Moolenaar " Set arguments to be run 368b3307b5eSBram Moolenaar if len(proc_args) 369b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 370b3307b5eSBram Moolenaar endif 371b3307b5eSBram Moolenaar 372b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 373b3307b5eSBram Moolenaar startinsert 374b3307b5eSBram Moolenaarendfunc 375b3307b5eSBram Moolenaar 376b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 37738baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 37838baa3e6SBram Moolenaar " There can be only one. 37938baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 38038baa3e6SBram Moolenaar 38145d5f26dSBram Moolenaar " Install debugger commands in the text window. 382b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 383e09ba7baSBram Moolenaar call s:InstallCommands() 38445d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 385e09ba7baSBram Moolenaar 38651b0f370SBram Moolenaar " Enable showing a balloon with eval info 387246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 388246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 38951b0f370SBram Moolenaar if has("balloon_eval") 39051b0f370SBram Moolenaar set ballooneval 391246fe03dSBram Moolenaar endif 39251b0f370SBram Moolenaar if has("balloon_eval_term") 39351b0f370SBram Moolenaar set balloonevalterm 39451b0f370SBram Moolenaar endif 39551b0f370SBram Moolenaar endif 39651b0f370SBram Moolenaar 3975378e1cfSBram Moolenaar " Contains breakpoints that have been placed, key is a string with the GDB 3985378e1cfSBram Moolenaar " breakpoint number. 39937402ed5SBram Moolenaar " Each entry is a dict, containing the sub-breakpoints. Key is the subid. 40037402ed5SBram Moolenaar " For a breakpoint that is just a number the subid is zero. 40137402ed5SBram Moolenaar " For a breakpoint "123.4" the id is "123" and subid is "4". 40237402ed5SBram Moolenaar " Example, when breakpoint "44", "123", "123.1" and "123.2" exist: 40337402ed5SBram Moolenaar " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} 404e09ba7baSBram Moolenaar let s:breakpoints = {} 4051b9645deSBram Moolenaar 40637402ed5SBram Moolenaar " Contains breakpoints by file/lnum. The key is "fname:lnum". 40737402ed5SBram Moolenaar " Each entry is a list of breakpoint IDs at that position. 40837402ed5SBram Moolenaar let s:breakpoint_locations = {} 40937402ed5SBram Moolenaar 4101b9645deSBram Moolenaar augroup TermDebug 4111b9645deSBram Moolenaar au BufRead * call s:BufRead() 4121b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 413f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 4141b9645deSBram Moolenaar augroup END 41532c67ba7SBram Moolenaar 416b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 417b3307b5eSBram Moolenaar " window. 41832c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 41932c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 42032c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 42132c67ba7SBram Moolenaar endif 422c572da5fSBram Moolenaarendfunc 423c572da5fSBram Moolenaar 424b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 425b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 426b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 427b3307b5eSBram Moolenaar if s:way == 'prompt' 428b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 429b3307b5eSBram Moolenaar else 430b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 431b3307b5eSBram Moolenaar endif 432b3307b5eSBram Moolenaarendfunc 433b3307b5eSBram Moolenaar 434b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 435b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 436b3307b5eSBram Moolenaar if s:way == 'prompt' 437b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 438b3307b5eSBram Moolenaar else 439b3307b5eSBram Moolenaar let do_continue = 0 440b3307b5eSBram Moolenaar if !s:stopped 441b3307b5eSBram Moolenaar let do_continue = 1 442b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 443b3307b5eSBram Moolenaar sleep 10m 444b3307b5eSBram Moolenaar endif 445b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 446b3307b5eSBram Moolenaar if do_continue 447b3307b5eSBram Moolenaar Continue 448b3307b5eSBram Moolenaar endif 449b3307b5eSBram Moolenaar endif 450b3307b5eSBram Moolenaarendfunc 451b3307b5eSBram Moolenaar 452b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 453b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 454b3307b5eSBram Moolenaar call s:SendCommand(a:text) 455b3307b5eSBram Moolenaarendfunc 456b3307b5eSBram Moolenaar 4574551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4584551c0a9SBram Moolenaar" breakpoint. 459b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4602ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4612ed890f1SBram Moolenaar if has('win32') 4622ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4632ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4644551c0a9SBram Moolenaar if s:pid == 0 4654551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4664551c0a9SBram Moolenaar else 4674551c0a9SBram Moolenaar call debugbreak(s:pid) 4684551c0a9SBram Moolenaar endif 4692ed890f1SBram Moolenaar else 4702ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4712ed890f1SBram Moolenaar endif 472b3307b5eSBram Moolenaarendfunc 473b3307b5eSBram Moolenaar 474b3307b5eSBram Moolenaar" Function called when gdb outputs text. 475b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 476b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 477b3307b5eSBram Moolenaar 478b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 479b3307b5eSBram Moolenaar " Drop status and echo'd commands. 480a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 481b3307b5eSBram Moolenaar return 482b3307b5eSBram Moolenaar endif 483b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 484b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 485b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 486b3307b5eSBram Moolenaar " Silently drop evaluation errors. 487b3307b5eSBram Moolenaar unlet s:evalexpr 488b3307b5eSBram Moolenaar return 489b3307b5eSBram Moolenaar endif 490b3307b5eSBram Moolenaar elseif a:text[0] == '~' 491b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 492b3307b5eSBram Moolenaar else 493b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 494b3307b5eSBram Moolenaar return 495b3307b5eSBram Moolenaar endif 496b3307b5eSBram Moolenaar 497b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 498b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 499b3307b5eSBram Moolenaar 500b3307b5eSBram Moolenaar " Add the output above the current prompt. 501b3307b5eSBram Moolenaar call append(line('$') - 1, text) 5024551c0a9SBram Moolenaar set modified 503b3307b5eSBram Moolenaar 504b3307b5eSBram Moolenaar call win_gotoid(curwinid) 505b3307b5eSBram Moolenaarendfunc 506b3307b5eSBram Moolenaar 507b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 508b3307b5eSBram Moolenaar" to the next ", unescaping characters. 509b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 510b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 511a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 512b3307b5eSBram Moolenaar return 513b3307b5eSBram Moolenaar endif 514b3307b5eSBram Moolenaar let result = '' 515b3307b5eSBram Moolenaar let i = 1 516b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 517b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 518b3307b5eSBram Moolenaar let i += 1 519b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 520b3307b5eSBram Moolenaar " drop \n 521b3307b5eSBram Moolenaar let i += 1 522b3307b5eSBram Moolenaar continue 523589edb34SBram Moolenaar elseif a:quotedText[i] == 't' 524589edb34SBram Moolenaar " append \t 525589edb34SBram Moolenaar let i += 1 526589edb34SBram Moolenaar let result .= "\t" 527589edb34SBram Moolenaar continue 528b3307b5eSBram Moolenaar endif 529b3307b5eSBram Moolenaar endif 530b3307b5eSBram Moolenaar let result .= a:quotedText[i] 531b3307b5eSBram Moolenaar let i += 1 532b3307b5eSBram Moolenaar endwhile 533b3307b5eSBram Moolenaar return result 534b3307b5eSBram Moolenaarendfunc 535b3307b5eSBram Moolenaar 536a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 537a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 5385378e1cfSBram Moolenaar if a:msg !~ 'fullname' 5395378e1cfSBram Moolenaar return '' 5405378e1cfSBram Moolenaar endif 541a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 542a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 543a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 544a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 545a15b0a93SBram Moolenaar endif 546a15b0a93SBram Moolenaar return name 547a15b0a93SBram Moolenaarendfunc 548a15b0a93SBram Moolenaar 549b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 550fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 551b3623a38SBram Moolenaar unlet s:gdbwin 552e09ba7baSBram Moolenaar 553b3307b5eSBram Moolenaar call s:EndDebugCommon() 554b3307b5eSBram Moolenaarendfunc 555b3307b5eSBram Moolenaar 556b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 557e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 558e09ba7baSBram Moolenaar 559b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 560b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 561b3307b5eSBram Moolenaar endif 562b3307b5eSBram Moolenaar 563*cb80aa2dSBram Moolenaar " Restore 'signcolumn' in all buffers for which it was set. 564b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 565*cb80aa2dSBram Moolenaar let was_buf = bufnr() 566*cb80aa2dSBram Moolenaar for bufnr in s:signcolumn_buflist 567*cb80aa2dSBram Moolenaar if bufexists(bufnr) 568*cb80aa2dSBram Moolenaar exe bufnr .. "buf" 569*cb80aa2dSBram Moolenaar if exists('b:save_signcolumn') 570*cb80aa2dSBram Moolenaar let &signcolumn = b:save_signcolumn 571*cb80aa2dSBram Moolenaar unlet b:save_signcolumn 572*cb80aa2dSBram Moolenaar endif 573*cb80aa2dSBram Moolenaar endif 574*cb80aa2dSBram Moolenaar endfor 575*cb80aa2dSBram Moolenaar exe was_buf .. "buf" 576*cb80aa2dSBram Moolenaar 577e09ba7baSBram Moolenaar call s:DeleteCommands() 578e09ba7baSBram Moolenaar 579e09ba7baSBram Moolenaar call win_gotoid(curwinid) 580b3307b5eSBram Moolenaar 58138baa3e6SBram Moolenaar if s:save_columns > 0 58238baa3e6SBram Moolenaar let &columns = s:save_columns 58338baa3e6SBram Moolenaar endif 5841b9645deSBram Moolenaar 585246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 586246fe03dSBram Moolenaar set balloonexpr= 58751b0f370SBram Moolenaar if has("balloon_eval") 58851b0f370SBram Moolenaar set noballooneval 589246fe03dSBram Moolenaar endif 59051b0f370SBram Moolenaar if has("balloon_eval_term") 59151b0f370SBram Moolenaar set noballoonevalterm 59251b0f370SBram Moolenaar endif 59351b0f370SBram Moolenaar endif 59451b0f370SBram Moolenaar 5951b9645deSBram Moolenaar au! TermDebug 596fe386641SBram Moolenaarendfunc 597fe386641SBram Moolenaar 598b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 599b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 600b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 6014551c0a9SBram Moolenaar set nomodified 602b3307b5eSBram Moolenaar close 603b3307b5eSBram Moolenaar if curwinid != s:gdbwin 604b3307b5eSBram Moolenaar call win_gotoid(curwinid) 605b3307b5eSBram Moolenaar endif 606b3307b5eSBram Moolenaar 607b3307b5eSBram Moolenaar call s:EndDebugCommon() 608b3307b5eSBram Moolenaar unlet s:gdbwin 609b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 610b3307b5eSBram Moolenaarendfunc 611b3307b5eSBram Moolenaar 612fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 613fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 614fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 615fe386641SBram Moolenaar 616fe386641SBram Moolenaar for msg in msgs 617fe386641SBram Moolenaar " remove prefixed NL 618fe386641SBram Moolenaar if msg[0] == "\n" 619fe386641SBram Moolenaar let msg = msg[1:] 620fe386641SBram Moolenaar endif 621fe386641SBram Moolenaar if msg != '' 6221b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 623e09ba7baSBram Moolenaar call s:HandleCursor(msg) 62445d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 625e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 626e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 627e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 6284551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 6294551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 63045d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 63145d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 63245d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 63345d5f26dSBram Moolenaar call s:HandleError(msg) 634e09ba7baSBram Moolenaar endif 635e09ba7baSBram Moolenaar endif 636e09ba7baSBram Moolenaar endfor 637e09ba7baSBram Moolenaarendfunc 638e09ba7baSBram Moolenaar 639589edb34SBram Moolenaarfunc s:GotoProgram() 640589edb34SBram Moolenaar if has('win32') 641589edb34SBram Moolenaar if executable('powershell') 642589edb34SBram Moolenaar call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid)) 643589edb34SBram Moolenaar endif 644589edb34SBram Moolenaar else 645469bdbdeSBram Moolenaar call win_gotoid(s:ptywin) 646589edb34SBram Moolenaar endif 647589edb34SBram Moolenaarendfunc 648589edb34SBram Moolenaar 649e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 650e09ba7baSBram Moolenaarfunc s:InstallCommands() 651963c1ad5SBram Moolenaar let save_cpo = &cpo 652963c1ad5SBram Moolenaar set cpo&vim 653963c1ad5SBram Moolenaar 654589edb34SBram Moolenaar command -nargs=? Break call s:SetBreakpoint(<q-args>) 65571137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 656e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 65745d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 658e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 65960e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 66060e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 66160e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 662b3307b5eSBram Moolenaar 663b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 664b3307b5eSBram Moolenaar if s:way == 'prompt' 665b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 666b3307b5eSBram Moolenaar else 667b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 668b3307b5eSBram Moolenaar endif 669b3307b5eSBram Moolenaar 67045d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 67145d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 672589edb34SBram Moolenaar command Program call s:GotoProgram() 673b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 67471137fedSBram Moolenaar command Winbar call s:InstallWinbar() 67545d5f26dSBram Moolenaar 676388a5d4fSBram Moolenaar if !exists('g:termdebug_map_K') || g:termdebug_map_K 677388a5d4fSBram Moolenaar let s:k_map_saved = maparg('K', 'n', 0, 1) 67845d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 679388a5d4fSBram Moolenaar endif 6801b9645deSBram Moolenaar 681f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 68271137fedSBram Moolenaar call s:InstallWinbar() 68371137fedSBram Moolenaar 68471137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 68571137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 68671137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 68771137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 68871137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 68971137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 69071137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 69171137fedSBram Moolenaar endif 69271137fedSBram Moolenaar endif 693963c1ad5SBram Moolenaar 694963c1ad5SBram Moolenaar let &cpo = save_cpo 69571137fedSBram Moolenaarendfunc 69671137fedSBram Moolenaar 69771137fedSBram Moolenaarlet s:winbar_winids = [] 69871137fedSBram Moolenaar 69971137fedSBram Moolenaar" Install the window toolbar in the current window. 70071137fedSBram Moolenaarfunc s:InstallWinbar() 701c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 70224a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 70324a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 70424a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 70524a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 70660e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 70724a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 70871137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 709c4b533e1SBram Moolenaar endif 710e09ba7baSBram Moolenaarendfunc 711e09ba7baSBram Moolenaar 712e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 713e09ba7baSBram Moolenaarfunc s:DeleteCommands() 714e09ba7baSBram Moolenaar delcommand Break 71571137fedSBram Moolenaar delcommand Clear 716e09ba7baSBram Moolenaar delcommand Step 71745d5f26dSBram Moolenaar delcommand Over 718e09ba7baSBram Moolenaar delcommand Finish 71960e73f2aSBram Moolenaar delcommand Run 72060e73f2aSBram Moolenaar delcommand Arguments 72160e73f2aSBram Moolenaar delcommand Stop 722e09ba7baSBram Moolenaar delcommand Continue 72345d5f26dSBram Moolenaar delcommand Evaluate 72445d5f26dSBram Moolenaar delcommand Gdb 72545d5f26dSBram Moolenaar delcommand Program 726b3623a38SBram Moolenaar delcommand Source 72771137fedSBram Moolenaar delcommand Winbar 72845d5f26dSBram Moolenaar 72965e0d77aSBram Moolenaar if exists('s:k_map_saved') && !empty(s:k_map_saved) 730388a5d4fSBram Moolenaar call mapset('n', 0, s:k_map_saved) 731388a5d4fSBram Moolenaar unlet s:k_map_saved 732388a5d4fSBram Moolenaar endif 7331b9645deSBram Moolenaar 7341b9645deSBram Moolenaar if has('menu') 73571137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 73671137fedSBram Moolenaar let curwinid = win_getid(winnr()) 73771137fedSBram Moolenaar for winid in s:winbar_winids 73871137fedSBram Moolenaar if win_gotoid(winid) 7391b9645deSBram Moolenaar aunmenu WinBar.Step 7401b9645deSBram Moolenaar aunmenu WinBar.Next 7411b9645deSBram Moolenaar aunmenu WinBar.Finish 7421b9645deSBram Moolenaar aunmenu WinBar.Cont 74360e73f2aSBram Moolenaar aunmenu WinBar.Stop 7441b9645deSBram Moolenaar aunmenu WinBar.Eval 7451b9645deSBram Moolenaar endif 74671137fedSBram Moolenaar endfor 74771137fedSBram Moolenaar call win_gotoid(curwinid) 74871137fedSBram Moolenaar let s:winbar_winids = [] 74971137fedSBram Moolenaar 75071137fedSBram Moolenaar if exists('s:saved_mousemodel') 75171137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 75271137fedSBram Moolenaar unlet s:saved_mousemodel 75371137fedSBram Moolenaar aunmenu PopUp.-SEP3- 75471137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 75571137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 75671137fedSBram Moolenaar aunmenu PopUp.Evaluate 75771137fedSBram Moolenaar endif 75871137fedSBram Moolenaar endif 7591b9645deSBram Moolenaar 76045d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 76137402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 76237402ed5SBram Moolenaar for subid in keys(entries) 76337402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 76437402ed5SBram Moolenaar endfor 76545d5f26dSBram Moolenaar endfor 76645d5f26dSBram Moolenaar unlet s:breakpoints 76737402ed5SBram Moolenaar unlet s:breakpoint_locations 768a15b0a93SBram Moolenaar 769a15b0a93SBram Moolenaar sign undefine debugPC 770a15b0a93SBram Moolenaar for val in s:BreakpointSigns 771a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 772a15b0a93SBram Moolenaar endfor 7734551c0a9SBram Moolenaar let s:BreakpointSigns = [] 774e09ba7baSBram Moolenaarendfunc 775e09ba7baSBram Moolenaar 776e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 777589edb34SBram Moolenaarfunc s:SetBreakpoint(at) 77860e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 77960e73f2aSBram Moolenaar " Interrupt to make it work. 78060e73f2aSBram Moolenaar let do_continue = 0 78160e73f2aSBram Moolenaar if !s:stopped 78260e73f2aSBram Moolenaar let do_continue = 1 783b3307b5eSBram Moolenaar if s:way == 'prompt' 7844551c0a9SBram Moolenaar call s:PromptInterrupt() 785b3307b5eSBram Moolenaar else 78660e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 787b3307b5eSBram Moolenaar endif 78860e73f2aSBram Moolenaar sleep 10m 78960e73f2aSBram Moolenaar endif 790589edb34SBram Moolenaar 791a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 792589edb34SBram Moolenaar let at = empty(a:at) ? 793589edb34SBram Moolenaar \ fnameescape(expand('%:p')) . ':' . line('.') : a:at 794589edb34SBram Moolenaar call s:SendCommand('-break-insert ' . at) 79560e73f2aSBram Moolenaar if do_continue 79660e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 79760e73f2aSBram Moolenaar endif 798e09ba7baSBram Moolenaarendfunc 799e09ba7baSBram Moolenaar 80071137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 80171137fedSBram Moolenaarfunc s:ClearBreakpoint() 802e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 803e09ba7baSBram Moolenaar let lnum = line('.') 80437402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 80537402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 80637402ed5SBram Moolenaar let idx = 0 80737402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 80837402ed5SBram Moolenaar if has_key(s:breakpoints, id) 80937402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 81037402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 81137402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 81237402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 81337402ed5SBram Moolenaar endfor 81437402ed5SBram Moolenaar unlet s:breakpoints[id] 81537402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 816e09ba7baSBram Moolenaar break 81737402ed5SBram Moolenaar else 81837402ed5SBram Moolenaar let idx += 1 819e09ba7baSBram Moolenaar endif 820e09ba7baSBram Moolenaar endfor 82137402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 82237402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 82337402ed5SBram Moolenaar endif 82437402ed5SBram Moolenaar endif 825e09ba7baSBram Moolenaarendfunc 826e09ba7baSBram Moolenaar 82760e73f2aSBram Moolenaarfunc s:Run(args) 82860e73f2aSBram Moolenaar if a:args != '' 82960e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 83060e73f2aSBram Moolenaar endif 83160e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 83260e73f2aSBram Moolenaarendfunc 83360e73f2aSBram Moolenaar 83451b0f370SBram Moolenaarfunc s:SendEval(expr) 83551b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 83651b0f370SBram Moolenaar let s:evalexpr = a:expr 83751b0f370SBram Moolenaarendfunc 83851b0f370SBram Moolenaar 83945d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 84045d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 84145d5f26dSBram Moolenaar if a:arg != '' 84245d5f26dSBram Moolenaar let expr = a:arg 84345d5f26dSBram Moolenaar elseif a:range == 2 84445d5f26dSBram Moolenaar let pos = getcurpos() 84545d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 84645d5f26dSBram Moolenaar let regt = getregtype('v') 84745d5f26dSBram Moolenaar normal! gv"vy 84845d5f26dSBram Moolenaar let expr = @v 84945d5f26dSBram Moolenaar call setpos('.', pos) 85045d5f26dSBram Moolenaar call setreg('v', reg, regt) 85145d5f26dSBram Moolenaar else 85245d5f26dSBram Moolenaar let expr = expand('<cexpr>') 85345d5f26dSBram Moolenaar endif 85422f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 85551b0f370SBram Moolenaar call s:SendEval(expr) 85645d5f26dSBram Moolenaarendfunc 85745d5f26dSBram Moolenaar 85822f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 85951b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 86051b0f370SBram Moolenaar 86145d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 86245d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 8631b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 8641b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 86551b0f370SBram Moolenaar if s:evalFromBalloonExpr 86651b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 86751b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 86851b0f370SBram Moolenaar else 86951b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 87051b0f370SBram Moolenaar endif 87151b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 87251b0f370SBram Moolenaar else 8731b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 87451b0f370SBram Moolenaar endif 8751b9645deSBram Moolenaar 8767f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 8771b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 87822f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 87951b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 88051b0f370SBram Moolenaar else 88151b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 8821b9645deSBram Moolenaar endif 88345d5f26dSBram Moolenaarendfunc 88445d5f26dSBram Moolenaar 88551b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 88651b0f370SBram Moolenaar" if there is any. 88751b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 888b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 889396e829fSBram Moolenaar return '' 890b3307b5eSBram Moolenaar endif 891b3307b5eSBram Moolenaar if !s:stopped 892b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 893b3307b5eSBram Moolenaar " mouse triggers a balloon. 894396e829fSBram Moolenaar return '' 89551b0f370SBram Moolenaar endif 89651b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 89751b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 89822f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 89922f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 90051b0f370SBram Moolenaar return '' 90151b0f370SBram Moolenaarendfunc 90251b0f370SBram Moolenaar 90345d5f26dSBram Moolenaar" Handle an error. 90445d5f26dSBram Moolenaarfunc s:HandleError(msg) 90522f1d0e3SBram Moolenaar if s:ignoreEvalError 90651b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 90722f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 90822f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 90951b0f370SBram Moolenaar return 91051b0f370SBram Moolenaar endif 91145d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 91245d5f26dSBram Moolenaarendfunc 91345d5f26dSBram Moolenaar 914b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 915b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 916c4b533e1SBram Moolenaar new 917b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 918c4b533e1SBram Moolenaar call s:InstallWinbar() 919c4b533e1SBram Moolenaar endif 920c4b533e1SBram Moolenaarendfunc 921c4b533e1SBram Moolenaar 922e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 923e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 924e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 925fe386641SBram Moolenaar let wid = win_getid(winnr()) 926fe386641SBram Moolenaar 92760e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 9284551c0a9SBram Moolenaar call ch_log('program stopped') 92960e73f2aSBram Moolenaar let s:stopped = 1 93060e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 9314551c0a9SBram Moolenaar call ch_log('program running') 93260e73f2aSBram Moolenaar let s:stopped = 0 93360e73f2aSBram Moolenaar endif 93460e73f2aSBram Moolenaar 935a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 936a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 937a15b0a93SBram Moolenaar else 938a15b0a93SBram Moolenaar let fname = '' 939a15b0a93SBram Moolenaar endif 9401b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 941e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 942fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 9434551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 9441b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 945fe386641SBram Moolenaar if &modified 946fe386641SBram Moolenaar " TODO: find existing window 947fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 948b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 949c4b533e1SBram Moolenaar call s:InstallWinbar() 950fe386641SBram Moolenaar else 951fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 952fe386641SBram Moolenaar endif 953fe386641SBram Moolenaar endif 954fe386641SBram Moolenaar exe lnum 95501164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 95639f7aa3cSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC priority=110 file=' . fname 957*cb80aa2dSBram Moolenaar if !exists('b:save_signcolumn') 958*cb80aa2dSBram Moolenaar let b:save_signcolumn = &signcolumn 959*cb80aa2dSBram Moolenaar call add(s:signcolumn_buflist, bufnr()) 960*cb80aa2dSBram Moolenaar endif 961fe386641SBram Moolenaar setlocal signcolumn=yes 962fe386641SBram Moolenaar endif 9634551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 964fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 965fe386641SBram Moolenaar endif 966fe386641SBram Moolenaar 967fe386641SBram Moolenaar call win_gotoid(wid) 968e09ba7baSBram Moolenaarendfunc 969e09ba7baSBram Moolenaar 970de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 971a15b0a93SBram Moolenaar 97237402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 97337402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 97437402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 97537402ed5SBram Moolenaar call add(s:BreakpointSigns, nr) 97637402ed5SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 977de1a8314SBram Moolenaar endif 978de1a8314SBram Moolenaarendfunc 979de1a8314SBram Moolenaar 98037402ed5SBram Moolenaarfunc! s:SplitMsg(s) 98137402ed5SBram Moolenaar return split(a:s, '{.\{-}}\zs') 9825378e1cfSBram Moolenaarendfunction 9835378e1cfSBram Moolenaar 984e09ba7baSBram Moolenaar" Handle setting a breakpoint 985e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 986e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 9876dccc962SBram Moolenaar if a:msg !~ 'fullname=' 9886dccc962SBram Moolenaar " a watch does not have a file name 9896dccc962SBram Moolenaar return 9906dccc962SBram Moolenaar endif 9915378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 9925378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 9935378e1cfSBram Moolenaar if empty(fname) 9945378e1cfSBram Moolenaar continue 9955378e1cfSBram Moolenaar endif 9965378e1cfSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 9975378e1cfSBram Moolenaar if empty(nr) 998e09ba7baSBram Moolenaar return 999fe386641SBram Moolenaar endif 1000e09ba7baSBram Moolenaar 100137402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 100237402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 100337402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 100437402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 100537402ed5SBram Moolenaar 100637402ed5SBram Moolenaar if has_key(s:breakpoints, id) 100737402ed5SBram Moolenaar let entries = s:breakpoints[id] 100837402ed5SBram Moolenaar else 100937402ed5SBram Moolenaar let entries = {} 101037402ed5SBram Moolenaar let s:breakpoints[id] = entries 101137402ed5SBram Moolenaar endif 101237402ed5SBram Moolenaar if has_key(entries, subid) 101337402ed5SBram Moolenaar let entry = entries[subid] 1014e09ba7baSBram Moolenaar else 1015e09ba7baSBram Moolenaar let entry = {} 101637402ed5SBram Moolenaar let entries[subid] = entry 1017fe386641SBram Moolenaar endif 1018e09ba7baSBram Moolenaar 10195378e1cfSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 1020e09ba7baSBram Moolenaar let entry['fname'] = fname 1021e09ba7baSBram Moolenaar let entry['lnum'] = lnum 10221b9645deSBram Moolenaar 102337402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 102437402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 102537402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 102637402ed5SBram Moolenaar endif 102737402ed5SBram Moolenaar let s:breakpoint_locations[bploc] += [id] 102837402ed5SBram Moolenaar 10291b9645deSBram Moolenaar if bufloaded(fname) 103037402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 10311b9645deSBram Moolenaar endif 10325378e1cfSBram Moolenaar endfor 10331b9645deSBram Moolenaarendfunc 10341b9645deSBram Moolenaar 103537402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry) 103637402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 103737402ed5SBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname'] 10381b9645deSBram Moolenaar let a:entry['placed'] = 1 1039e09ba7baSBram Moolenaarendfunc 1040e09ba7baSBram Moolenaar 1041e09ba7baSBram Moolenaar" Handle deleting a breakpoint 1042e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 1043e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 104437402ed5SBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 104537402ed5SBram Moolenaar if empty(id) 1046e09ba7baSBram Moolenaar return 1047e09ba7baSBram Moolenaar endif 104837402ed5SBram Moolenaar if has_key(s:breakpoints, id) 104937402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 10501b9645deSBram Moolenaar if has_key(entry, 'placed') 105137402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 10521b9645deSBram Moolenaar unlet entry['placed'] 10531b9645deSBram Moolenaar endif 10545378e1cfSBram Moolenaar endfor 105537402ed5SBram Moolenaar unlet s:breakpoints[id] 105637402ed5SBram Moolenaar endif 1057c572da5fSBram Moolenaarendfunc 10581b9645deSBram Moolenaar 10594551c0a9SBram Moolenaar" Handle the debugged program starting to run. 10604551c0a9SBram Moolenaar" Will store the process ID in s:pid 10614551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 10624551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 10634551c0a9SBram Moolenaar if nr == 0 10644551c0a9SBram Moolenaar return 10654551c0a9SBram Moolenaar endif 10664551c0a9SBram Moolenaar let s:pid = nr 10674551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 10684551c0a9SBram Moolenaarendfunc 10694551c0a9SBram Moolenaar 10701b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 10711b9645deSBram Moolenaarfunc s:BufRead() 10721b9645deSBram Moolenaar let fname = expand('<afile>:p') 107337402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 107437402ed5SBram Moolenaar for [subid, entry] in items(entries) 10751b9645deSBram Moolenaar if entry['fname'] == fname 107637402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 10771b9645deSBram Moolenaar endif 10781b9645deSBram Moolenaar endfor 107937402ed5SBram Moolenaar endfor 10801b9645deSBram Moolenaarendfunc 10811b9645deSBram Moolenaar 10821b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 10831b9645deSBram Moolenaarfunc s:BufUnloaded() 10841b9645deSBram Moolenaar let fname = expand('<afile>:p') 108537402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 108637402ed5SBram Moolenaar for [subid, entry] in items(entries) 10871b9645deSBram Moolenaar if entry['fname'] == fname 10881b9645deSBram Moolenaar let entry['placed'] = 0 10891b9645deSBram Moolenaar endif 10901b9645deSBram Moolenaar endfor 109137402ed5SBram Moolenaar endfor 10921b9645deSBram Moolenaarendfunc 1093ca4cc018SBram Moolenaar 1094ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 1095ca4cc018SBram Moolenaarunlet s:keepcpo 1096