1fe386641SBram Moolenaar" Debugger plugin using gdb. 2c572da5fSBram Moolenaar" 3c572da5fSBram Moolenaar" WORK IN PROGRESS - much doesn't work yet 4c572da5fSBram Moolenaar" 5fe386641SBram Moolenaar" Open two visible terminal windows: 6c572da5fSBram Moolenaar" 1. run a pty, as with ":term NONE" 7c572da5fSBram Moolenaar" 2. run gdb, passing the pty 8fe386641SBram Moolenaar" The current window is used to view source code and follows gdb. 9fe386641SBram Moolenaar" 10fe386641SBram Moolenaar" A third terminal window is hidden, it is used for communication with gdb. 11fe386641SBram Moolenaar" 12fe386641SBram Moolenaar" The communication with gdb uses GDB/MI. See: 13fe386641SBram Moolenaar" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html 14c572da5fSBram Moolenaar" 15c572da5fSBram Moolenaar" Author: Bram Moolenaar 16fe386641SBram Moolenaar" Copyright: Vim license applies, see ":help license" 17c572da5fSBram Moolenaar 18*37c64c78SBram Moolenaar" In case this gets loaded twice. 19*37c64c78SBram Moolenaarif exists(':Termdebug') 20*37c64c78SBram Moolenaar finish 21*37c64c78SBram Moolenaarendif 22*37c64c78SBram Moolenaar 23fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim". 24fe386641SBram Moolenaar" To end type "quit" in the gdb window. 25c572da5fSBram Moolenaarcommand -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>) 26c572da5fSBram Moolenaar 27fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb". 28e09ba7baSBram Moolenaarif !exists('termdebugger') 29e09ba7baSBram Moolenaar let termdebugger = 'gdb' 30c572da5fSBram Moolenaarendif 31c572da5fSBram Moolenaar 32fe386641SBram Moolenaarlet s:pc_id = 12 33e09ba7baSBram Moolenaarlet s:break_id = 13 34e09ba7baSBram Moolenaar 35e09ba7baSBram Moolenaarif &background == 'light' 36e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue 37e09ba7baSBram Moolenaarelse 38e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue 39e09ba7baSBram Moolenaarendif 40e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 41fe386641SBram Moolenaar 42c572da5fSBram Moolenaarfunc s:StartDebug(cmd) 43fe386641SBram Moolenaar let s:startwin = win_getid(winnr()) 44fe386641SBram Moolenaar let s:startsigncolumn = &signcolumn 45fe386641SBram Moolenaar 4638baa3e6SBram Moolenaar if exists('g:termdebug_wide') && &columns < g:termdebug_wide 4738baa3e6SBram Moolenaar let s:save_columns = &columns 4838baa3e6SBram Moolenaar let &columns = g:termdebug_wide 4938baa3e6SBram Moolenaar let vertical = 1 5038baa3e6SBram Moolenaar else 5138baa3e6SBram Moolenaar let s:save_columns = 0 5238baa3e6SBram Moolenaar let vertical = 0 5338baa3e6SBram Moolenaar endif 5438baa3e6SBram Moolenaar 55c572da5fSBram Moolenaar " Open a terminal window without a job, to run the debugged program 56fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 57fe386641SBram Moolenaar \ 'term_name': 'gdb program', 5838baa3e6SBram Moolenaar \ 'vertical': vertical, 59fe386641SBram Moolenaar \ }) 60fe386641SBram Moolenaar if s:ptybuf == 0 61fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 62fe386641SBram Moolenaar return 63fe386641SBram Moolenaar endif 64fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 6545d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 66fe386641SBram Moolenaar 67fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 68fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 69fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 70fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 71fe386641SBram Moolenaar \ 'hidden': 1, 72fe386641SBram Moolenaar \ }) 73fe386641SBram Moolenaar if s:commbuf == 0 74fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 75fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 76fe386641SBram Moolenaar return 77fe386641SBram Moolenaar endif 78fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 79c572da5fSBram Moolenaar 80c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 81e09ba7baSBram Moolenaar let cmd = [g:termdebugger, '-tty', pty, a:cmd] 82c572da5fSBram Moolenaar echomsg 'executing "' . join(cmd) . '"' 83c572da5fSBram Moolenaar let gdbbuf = term_start(cmd, { 84c572da5fSBram Moolenaar \ 'exit_cb': function('s:EndDebug'), 85fe386641SBram Moolenaar \ 'term_finish': 'close', 86c572da5fSBram Moolenaar \ }) 87fe386641SBram Moolenaar if gdbbuf == 0 88fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 89fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 90fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 91fe386641SBram Moolenaar return 92fe386641SBram Moolenaar endif 9345d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 94fe386641SBram Moolenaar 95fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 96fe386641SBram Moolenaar call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r") 97e09ba7baSBram Moolenaar 9838baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 9938baa3e6SBram Moolenaar " There can be only one. 10038baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 10138baa3e6SBram Moolenaar 10238baa3e6SBram Moolenaar " Sign used to indicate a breakpoint. 10338baa3e6SBram Moolenaar " Can be used multiple times. 10438baa3e6SBram Moolenaar sign define debugBreakpoint text=>> texthl=debugBreakpoint 10538baa3e6SBram Moolenaar 10645d5f26dSBram Moolenaar " Install debugger commands in the text window. 10745d5f26dSBram Moolenaar call win_gotoid(s:startwin) 108e09ba7baSBram Moolenaar call s:InstallCommands() 10945d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 110e09ba7baSBram Moolenaar 111e09ba7baSBram Moolenaar let s:breakpoints = {} 1121b9645deSBram Moolenaar 1131b9645deSBram Moolenaar augroup TermDebug 1141b9645deSBram Moolenaar au BufRead * call s:BufRead() 1151b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 1161b9645deSBram Moolenaar augroup END 117c572da5fSBram Moolenaarendfunc 118c572da5fSBram Moolenaar 119c572da5fSBram Moolenaarfunc s:EndDebug(job, status) 120c572da5fSBram Moolenaar exe 'bwipe! ' . s:ptybuf 121fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 122e09ba7baSBram Moolenaar 123e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 124e09ba7baSBram Moolenaar 125e09ba7baSBram Moolenaar call win_gotoid(s:startwin) 126e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 127e09ba7baSBram Moolenaar call s:DeleteCommands() 128e09ba7baSBram Moolenaar 129e09ba7baSBram Moolenaar call win_gotoid(curwinid) 13038baa3e6SBram Moolenaar if s:save_columns > 0 13138baa3e6SBram Moolenaar let &columns = s:save_columns 13238baa3e6SBram Moolenaar endif 1331b9645deSBram Moolenaar 1341b9645deSBram Moolenaar au! TermDebug 135fe386641SBram Moolenaarendfunc 136fe386641SBram Moolenaar 137fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 138fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 139fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 140fe386641SBram Moolenaar 141fe386641SBram Moolenaar for msg in msgs 142fe386641SBram Moolenaar " remove prefixed NL 143fe386641SBram Moolenaar if msg[0] == "\n" 144fe386641SBram Moolenaar let msg = msg[1:] 145fe386641SBram Moolenaar endif 146fe386641SBram Moolenaar if msg != '' 1471b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 148e09ba7baSBram Moolenaar call s:HandleCursor(msg) 14945d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 150e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 151e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 152e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 15345d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 15445d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 15545d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 15645d5f26dSBram Moolenaar call s:HandleError(msg) 157e09ba7baSBram Moolenaar endif 158e09ba7baSBram Moolenaar endif 159e09ba7baSBram Moolenaar endfor 160e09ba7baSBram Moolenaarendfunc 161e09ba7baSBram Moolenaar 162e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 163e09ba7baSBram Moolenaarfunc s:InstallCommands() 164e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 165e09ba7baSBram Moolenaar command Delete call s:DeleteBreakpoint() 166e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 16745d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 168e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 169e09ba7baSBram Moolenaar command Continue call s:SendCommand('-exec-continue') 17045d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 17145d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 17245d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 17345d5f26dSBram Moolenaar 17445d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 17545d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 1761b9645deSBram Moolenaar 1771b9645deSBram Moolenaar if has('menu') 1781b9645deSBram Moolenaar amenu WinBar.Step :Step<CR> 1791b9645deSBram Moolenaar amenu WinBar.Next :Over<CR> 1801b9645deSBram Moolenaar amenu WinBar.Finish :Finish<CR> 1811b9645deSBram Moolenaar amenu WinBar.Cont :Continue<CR> 1821b9645deSBram Moolenaar amenu WinBar.Eval :Evaluate<CR> 1831b9645deSBram Moolenaar endif 184e09ba7baSBram Moolenaarendfunc 185e09ba7baSBram Moolenaar 186e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 187e09ba7baSBram Moolenaarfunc s:DeleteCommands() 188e09ba7baSBram Moolenaar delcommand Break 189e09ba7baSBram Moolenaar delcommand Delete 190e09ba7baSBram Moolenaar delcommand Step 19145d5f26dSBram Moolenaar delcommand Over 192e09ba7baSBram Moolenaar delcommand Finish 193e09ba7baSBram Moolenaar delcommand Continue 19445d5f26dSBram Moolenaar delcommand Evaluate 19545d5f26dSBram Moolenaar delcommand Gdb 19645d5f26dSBram Moolenaar delcommand Program 19745d5f26dSBram Moolenaar 19845d5f26dSBram Moolenaar nunmap K 1991b9645deSBram Moolenaar 2001b9645deSBram Moolenaar if has('menu') 2011b9645deSBram Moolenaar aunmenu WinBar.Step 2021b9645deSBram Moolenaar aunmenu WinBar.Next 2031b9645deSBram Moolenaar aunmenu WinBar.Finish 2041b9645deSBram Moolenaar aunmenu WinBar.Cont 2051b9645deSBram Moolenaar aunmenu WinBar.Eval 2061b9645deSBram Moolenaar endif 2071b9645deSBram Moolenaar 20845d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 20945d5f26dSBram Moolenaar for key in keys(s:breakpoints) 21045d5f26dSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 21145d5f26dSBram Moolenaar endfor 21238baa3e6SBram Moolenaar sign undefine debugPC 21338baa3e6SBram Moolenaar sign undefine debugBreakpoint 21445d5f26dSBram Moolenaar unlet s:breakpoints 215e09ba7baSBram Moolenaarendfunc 216e09ba7baSBram Moolenaar 217e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 218e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 219e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, '-break-insert --source ' 220e09ba7baSBram Moolenaar \ . fnameescape(expand('%:p')) . ' --line ' . line('.') . "\r") 221e09ba7baSBram Moolenaarendfunc 222e09ba7baSBram Moolenaar 223e09ba7baSBram Moolenaar" :Delete - Delete a breakpoint at the cursor position. 224e09ba7baSBram Moolenaarfunc s:DeleteBreakpoint() 225e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 226e09ba7baSBram Moolenaar let lnum = line('.') 227e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 228e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 229e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r") 230e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 231e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 232e09ba7baSBram Moolenaar unlet s:breakpoints[key] 233e09ba7baSBram Moolenaar break 234e09ba7baSBram Moolenaar endif 235e09ba7baSBram Moolenaar endfor 236e09ba7baSBram Moolenaarendfunc 237e09ba7baSBram Moolenaar 238e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb 239e09ba7baSBram Moolenaarfunc s:SendCommand(cmd) 240e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 241e09ba7baSBram Moolenaarendfunc 242e09ba7baSBram Moolenaar 24345d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 24445d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 24545d5f26dSBram Moolenaar if a:arg != '' 24645d5f26dSBram Moolenaar let expr = a:arg 24745d5f26dSBram Moolenaar elseif a:range == 2 24845d5f26dSBram Moolenaar let pos = getcurpos() 24945d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 25045d5f26dSBram Moolenaar let regt = getregtype('v') 25145d5f26dSBram Moolenaar normal! gv"vy 25245d5f26dSBram Moolenaar let expr = @v 25345d5f26dSBram Moolenaar call setpos('.', pos) 25445d5f26dSBram Moolenaar call setreg('v', reg, regt) 25545d5f26dSBram Moolenaar else 25645d5f26dSBram Moolenaar let expr = expand('<cexpr>') 25745d5f26dSBram Moolenaar endif 25845d5f26dSBram Moolenaar call term_sendkeys(s:commbuf, '-data-evaluate-expression "' . expr . "\"\r") 25945d5f26dSBram Moolenaar let s:evalexpr = expr 26045d5f26dSBram Moolenaarendfunc 26145d5f26dSBram Moolenaar 26245d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 26345d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 2641b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 2651b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 2661b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 2671b9645deSBram Moolenaar 2681b9645deSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value !~ '"$' 2691b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 2701b9645deSBram Moolenaar let s:evalexpr = '*' . s:evalexpr 2711b9645deSBram Moolenaar call term_sendkeys(s:commbuf, '-data-evaluate-expression "' . s:evalexpr . "\"\r") 2721b9645deSBram Moolenaar endif 27345d5f26dSBram Moolenaarendfunc 27445d5f26dSBram Moolenaar 27545d5f26dSBram Moolenaar" Handle an error. 27645d5f26dSBram Moolenaarfunc s:HandleError(msg) 27745d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 27845d5f26dSBram Moolenaarendfunc 27945d5f26dSBram Moolenaar 280e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 281e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 282e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 283fe386641SBram Moolenaar let wid = win_getid(winnr()) 284fe386641SBram Moolenaar 285fe386641SBram Moolenaar if win_gotoid(s:startwin) 286e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 2871b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 288e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 289fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 2901b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 291fe386641SBram Moolenaar if &modified 292fe386641SBram Moolenaar " TODO: find existing window 293fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 294fe386641SBram Moolenaar let s:startwin = win_getid(winnr()) 295fe386641SBram Moolenaar else 296fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 297fe386641SBram Moolenaar endif 298fe386641SBram Moolenaar endif 299fe386641SBram Moolenaar exe lnum 3001b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 301fe386641SBram Moolenaar setlocal signcolumn=yes 302fe386641SBram Moolenaar endif 303fe386641SBram Moolenaar else 304fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 305fe386641SBram Moolenaar endif 306fe386641SBram Moolenaar 307fe386641SBram Moolenaar call win_gotoid(wid) 308fe386641SBram Moolenaar endif 309e09ba7baSBram Moolenaarendfunc 310e09ba7baSBram Moolenaar 311e09ba7baSBram Moolenaar" Handle setting a breakpoint 312e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 313e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 314e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 315e09ba7baSBram Moolenaar if nr == 0 316e09ba7baSBram Moolenaar return 317fe386641SBram Moolenaar endif 318e09ba7baSBram Moolenaar 319e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 320e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 321e09ba7baSBram Moolenaar else 322e09ba7baSBram Moolenaar let entry = {} 323e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 324fe386641SBram Moolenaar endif 325e09ba7baSBram Moolenaar 326e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 327e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 328e09ba7baSBram Moolenaar let entry['fname'] = fname 329e09ba7baSBram Moolenaar let entry['lnum'] = lnum 3301b9645deSBram Moolenaar 3311b9645deSBram Moolenaar if bufloaded(fname) 3321b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 3331b9645deSBram Moolenaar endif 3341b9645deSBram Moolenaarendfunc 3351b9645deSBram Moolenaar 3361b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry) 3371b9645deSBram Moolenaar exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname'] 3381b9645deSBram Moolenaar let a:entry['placed'] = 1 339e09ba7baSBram Moolenaarendfunc 340e09ba7baSBram Moolenaar 341e09ba7baSBram Moolenaar" Handle deleting a breakpoint 342e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 343e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 344e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 345e09ba7baSBram Moolenaar if nr == 0 346e09ba7baSBram Moolenaar return 347e09ba7baSBram Moolenaar endif 3481b9645deSBram Moolenaar if has_key(s:breakpoints, nr) 3491b9645deSBram Moolenaar let entry = s:breakpoints[nr] 3501b9645deSBram Moolenaar if has_key(entry, 'placed') 351e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 3521b9645deSBram Moolenaar unlet entry['placed'] 3531b9645deSBram Moolenaar endif 354e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 3551b9645deSBram Moolenaar endif 356c572da5fSBram Moolenaarendfunc 3571b9645deSBram Moolenaar 3581b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 3591b9645deSBram Moolenaarfunc s:BufRead() 3601b9645deSBram Moolenaar let fname = expand('<afile>:p') 3611b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 3621b9645deSBram Moolenaar if entry['fname'] == fname 3631b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 3641b9645deSBram Moolenaar endif 3651b9645deSBram Moolenaar endfor 3661b9645deSBram Moolenaarendfunc 3671b9645deSBram Moolenaar 3681b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 3691b9645deSBram Moolenaarfunc s:BufUnloaded() 3701b9645deSBram Moolenaar let fname = expand('<afile>:p') 3711b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 3721b9645deSBram Moolenaar if entry['fname'] == fname 3731b9645deSBram Moolenaar let entry['placed'] = 0 3741b9645deSBram Moolenaar endif 3751b9645deSBram Moolenaar endfor 3761b9645deSBram Moolenaarendfunc 3771b9645deSBram Moolenaar 378