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 18fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim". 19fe386641SBram Moolenaar" To end type "quit" in the gdb window. 20c572da5fSBram Moolenaarcommand -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>) 21c572da5fSBram Moolenaar 22fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb". 23e09ba7baSBram Moolenaarif !exists('termdebugger') 24e09ba7baSBram Moolenaar let termdebugger = 'gdb' 25c572da5fSBram Moolenaarendif 26c572da5fSBram Moolenaar 27fe386641SBram Moolenaarlet s:pc_id = 12 28e09ba7baSBram Moolenaarlet s:break_id = 13 29e09ba7baSBram Moolenaar 30e09ba7baSBram Moolenaarif &background == 'light' 31e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue 32e09ba7baSBram Moolenaarelse 33e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue 34e09ba7baSBram Moolenaarendif 35e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 36fe386641SBram Moolenaar 37c572da5fSBram Moolenaarfunc s:StartDebug(cmd) 38fe386641SBram Moolenaar let s:startwin = win_getid(winnr()) 39fe386641SBram Moolenaar let s:startsigncolumn = &signcolumn 40fe386641SBram Moolenaar 4138baa3e6SBram Moolenaar if exists('g:termdebug_wide') && &columns < g:termdebug_wide 4238baa3e6SBram Moolenaar let s:save_columns = &columns 4338baa3e6SBram Moolenaar let &columns = g:termdebug_wide 4438baa3e6SBram Moolenaar let vertical = 1 4538baa3e6SBram Moolenaar else 4638baa3e6SBram Moolenaar let s:save_columns = 0 4738baa3e6SBram Moolenaar let vertical = 0 4838baa3e6SBram Moolenaar endif 4938baa3e6SBram Moolenaar 50c572da5fSBram Moolenaar " Open a terminal window without a job, to run the debugged program 51fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 52fe386641SBram Moolenaar \ 'term_name': 'gdb program', 5338baa3e6SBram Moolenaar \ 'vertical': vertical, 54fe386641SBram Moolenaar \ }) 55fe386641SBram Moolenaar if s:ptybuf == 0 56fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 57fe386641SBram Moolenaar return 58fe386641SBram Moolenaar endif 59fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 6045d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 61fe386641SBram Moolenaar 62fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 63fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 64fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 65fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 66fe386641SBram Moolenaar \ 'hidden': 1, 67fe386641SBram Moolenaar \ }) 68fe386641SBram Moolenaar if s:commbuf == 0 69fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 70fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 71fe386641SBram Moolenaar return 72fe386641SBram Moolenaar endif 73fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 74c572da5fSBram Moolenaar 75c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 76e09ba7baSBram Moolenaar let cmd = [g:termdebugger, '-tty', pty, a:cmd] 77c572da5fSBram Moolenaar echomsg 'executing "' . join(cmd) . '"' 78c572da5fSBram Moolenaar let gdbbuf = term_start(cmd, { 79c572da5fSBram Moolenaar \ 'exit_cb': function('s:EndDebug'), 80fe386641SBram Moolenaar \ 'term_finish': 'close', 81c572da5fSBram Moolenaar \ }) 82fe386641SBram Moolenaar if gdbbuf == 0 83fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 84fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 85fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 86fe386641SBram Moolenaar return 87fe386641SBram Moolenaar endif 8845d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 89fe386641SBram Moolenaar 90fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 91fe386641SBram Moolenaar call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r") 92e09ba7baSBram Moolenaar 9338baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 9438baa3e6SBram Moolenaar " There can be only one. 9538baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 9638baa3e6SBram Moolenaar 9738baa3e6SBram Moolenaar " Sign used to indicate a breakpoint. 9838baa3e6SBram Moolenaar " Can be used multiple times. 9938baa3e6SBram Moolenaar sign define debugBreakpoint text=>> texthl=debugBreakpoint 10038baa3e6SBram Moolenaar 10145d5f26dSBram Moolenaar " Install debugger commands in the text window. 10245d5f26dSBram Moolenaar call win_gotoid(s:startwin) 103e09ba7baSBram Moolenaar call s:InstallCommands() 10445d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 105e09ba7baSBram Moolenaar 106e09ba7baSBram Moolenaar let s:breakpoints = {} 107*1b9645deSBram Moolenaar 108*1b9645deSBram Moolenaar augroup TermDebug 109*1b9645deSBram Moolenaar au BufRead * call s:BufRead() 110*1b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 111*1b9645deSBram Moolenaar augroup END 112c572da5fSBram Moolenaarendfunc 113c572da5fSBram Moolenaar 114c572da5fSBram Moolenaarfunc s:EndDebug(job, status) 115c572da5fSBram Moolenaar exe 'bwipe! ' . s:ptybuf 116fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 117e09ba7baSBram Moolenaar 118e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 119e09ba7baSBram Moolenaar 120e09ba7baSBram Moolenaar call win_gotoid(s:startwin) 121e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 122e09ba7baSBram Moolenaar call s:DeleteCommands() 123e09ba7baSBram Moolenaar 124e09ba7baSBram Moolenaar call win_gotoid(curwinid) 12538baa3e6SBram Moolenaar if s:save_columns > 0 12638baa3e6SBram Moolenaar let &columns = s:save_columns 12738baa3e6SBram Moolenaar endif 128*1b9645deSBram Moolenaar 129*1b9645deSBram Moolenaar au! TermDebug 130fe386641SBram Moolenaarendfunc 131fe386641SBram Moolenaar 132fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 133fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 134fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 135fe386641SBram Moolenaar 136fe386641SBram Moolenaar for msg in msgs 137fe386641SBram Moolenaar " remove prefixed NL 138fe386641SBram Moolenaar if msg[0] == "\n" 139fe386641SBram Moolenaar let msg = msg[1:] 140fe386641SBram Moolenaar endif 141fe386641SBram Moolenaar if msg != '' 142*1b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 143e09ba7baSBram Moolenaar call s:HandleCursor(msg) 14445d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 145e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 146e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 147e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 14845d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 14945d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 15045d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 15145d5f26dSBram Moolenaar call s:HandleError(msg) 152e09ba7baSBram Moolenaar endif 153e09ba7baSBram Moolenaar endif 154e09ba7baSBram Moolenaar endfor 155e09ba7baSBram Moolenaarendfunc 156e09ba7baSBram Moolenaar 157e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 158e09ba7baSBram Moolenaarfunc s:InstallCommands() 159e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 160e09ba7baSBram Moolenaar command Delete call s:DeleteBreakpoint() 161e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 16245d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 163e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 164e09ba7baSBram Moolenaar command Continue call s:SendCommand('-exec-continue') 16545d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 16645d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 16745d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 16845d5f26dSBram Moolenaar 16945d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 17045d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 171*1b9645deSBram Moolenaar 172*1b9645deSBram Moolenaar if has('menu') 173*1b9645deSBram Moolenaar amenu WinBar.Step :Step<CR> 174*1b9645deSBram Moolenaar amenu WinBar.Next :Over<CR> 175*1b9645deSBram Moolenaar amenu WinBar.Finish :Finish<CR> 176*1b9645deSBram Moolenaar amenu WinBar.Cont :Continue<CR> 177*1b9645deSBram Moolenaar amenu WinBar.Eval :Evaluate<CR> 178*1b9645deSBram Moolenaar endif 179e09ba7baSBram Moolenaarendfunc 180e09ba7baSBram Moolenaar 181e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 182e09ba7baSBram Moolenaarfunc s:DeleteCommands() 183e09ba7baSBram Moolenaar delcommand Break 184e09ba7baSBram Moolenaar delcommand Delete 185e09ba7baSBram Moolenaar delcommand Step 18645d5f26dSBram Moolenaar delcommand Over 187e09ba7baSBram Moolenaar delcommand Finish 188e09ba7baSBram Moolenaar delcommand Continue 18945d5f26dSBram Moolenaar delcommand Evaluate 19045d5f26dSBram Moolenaar delcommand Gdb 19145d5f26dSBram Moolenaar delcommand Program 19245d5f26dSBram Moolenaar 19345d5f26dSBram Moolenaar nunmap K 194*1b9645deSBram Moolenaar 195*1b9645deSBram Moolenaar if has('menu') 196*1b9645deSBram Moolenaar aunmenu WinBar.Step 197*1b9645deSBram Moolenaar aunmenu WinBar.Next 198*1b9645deSBram Moolenaar aunmenu WinBar.Finish 199*1b9645deSBram Moolenaar aunmenu WinBar.Cont 200*1b9645deSBram Moolenaar aunmenu WinBar.Eval 201*1b9645deSBram Moolenaar endif 202*1b9645deSBram Moolenaar 20345d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 20445d5f26dSBram Moolenaar for key in keys(s:breakpoints) 20545d5f26dSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 20645d5f26dSBram Moolenaar endfor 20738baa3e6SBram Moolenaar sign undefine debugPC 20838baa3e6SBram Moolenaar sign undefine debugBreakpoint 20945d5f26dSBram Moolenaar unlet s:breakpoints 210e09ba7baSBram Moolenaarendfunc 211e09ba7baSBram Moolenaar 212e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 213e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 214e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, '-break-insert --source ' 215e09ba7baSBram Moolenaar \ . fnameescape(expand('%:p')) . ' --line ' . line('.') . "\r") 216e09ba7baSBram Moolenaarendfunc 217e09ba7baSBram Moolenaar 218e09ba7baSBram Moolenaar" :Delete - Delete a breakpoint at the cursor position. 219e09ba7baSBram Moolenaarfunc s:DeleteBreakpoint() 220e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 221e09ba7baSBram Moolenaar let lnum = line('.') 222e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 223e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 224e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r") 225e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 226e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 227e09ba7baSBram Moolenaar unlet s:breakpoints[key] 228e09ba7baSBram Moolenaar break 229e09ba7baSBram Moolenaar endif 230e09ba7baSBram Moolenaar endfor 231e09ba7baSBram Moolenaarendfunc 232e09ba7baSBram Moolenaar 233e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb 234e09ba7baSBram Moolenaarfunc s:SendCommand(cmd) 235e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 236e09ba7baSBram Moolenaarendfunc 237e09ba7baSBram Moolenaar 23845d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 23945d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 24045d5f26dSBram Moolenaar if a:arg != '' 24145d5f26dSBram Moolenaar let expr = a:arg 24245d5f26dSBram Moolenaar elseif a:range == 2 24345d5f26dSBram Moolenaar let pos = getcurpos() 24445d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 24545d5f26dSBram Moolenaar let regt = getregtype('v') 24645d5f26dSBram Moolenaar normal! gv"vy 24745d5f26dSBram Moolenaar let expr = @v 24845d5f26dSBram Moolenaar call setpos('.', pos) 24945d5f26dSBram Moolenaar call setreg('v', reg, regt) 25045d5f26dSBram Moolenaar else 25145d5f26dSBram Moolenaar let expr = expand('<cexpr>') 25245d5f26dSBram Moolenaar endif 25345d5f26dSBram Moolenaar call term_sendkeys(s:commbuf, '-data-evaluate-expression "' . expr . "\"\r") 25445d5f26dSBram Moolenaar let s:evalexpr = expr 25545d5f26dSBram Moolenaarendfunc 25645d5f26dSBram Moolenaar 25745d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 25845d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 259*1b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 260*1b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 261*1b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 262*1b9645deSBram Moolenaar 263*1b9645deSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value !~ '"$' 264*1b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 265*1b9645deSBram Moolenaar let s:evalexpr = '*' . s:evalexpr 266*1b9645deSBram Moolenaar call term_sendkeys(s:commbuf, '-data-evaluate-expression "' . s:evalexpr . "\"\r") 267*1b9645deSBram Moolenaar endif 26845d5f26dSBram Moolenaarendfunc 26945d5f26dSBram Moolenaar 27045d5f26dSBram Moolenaar" Handle an error. 27145d5f26dSBram Moolenaarfunc s:HandleError(msg) 27245d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 27345d5f26dSBram Moolenaarendfunc 27445d5f26dSBram Moolenaar 275e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 276e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 277e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 278fe386641SBram Moolenaar let wid = win_getid(winnr()) 279fe386641SBram Moolenaar 280fe386641SBram Moolenaar if win_gotoid(s:startwin) 281e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 282*1b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 283e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 284fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 285*1b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 286fe386641SBram Moolenaar if &modified 287fe386641SBram Moolenaar " TODO: find existing window 288fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 289fe386641SBram Moolenaar let s:startwin = win_getid(winnr()) 290fe386641SBram Moolenaar else 291fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 292fe386641SBram Moolenaar endif 293fe386641SBram Moolenaar endif 294fe386641SBram Moolenaar exe lnum 295*1b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 296fe386641SBram Moolenaar setlocal signcolumn=yes 297fe386641SBram Moolenaar endif 298fe386641SBram Moolenaar else 299fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 300fe386641SBram Moolenaar endif 301fe386641SBram Moolenaar 302fe386641SBram Moolenaar call win_gotoid(wid) 303fe386641SBram Moolenaar endif 304e09ba7baSBram Moolenaarendfunc 305e09ba7baSBram Moolenaar 306e09ba7baSBram Moolenaar" Handle setting a breakpoint 307e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 308e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 309e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 310e09ba7baSBram Moolenaar if nr == 0 311e09ba7baSBram Moolenaar return 312fe386641SBram Moolenaar endif 313e09ba7baSBram Moolenaar 314e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 315e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 316e09ba7baSBram Moolenaar else 317e09ba7baSBram Moolenaar let entry = {} 318e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 319fe386641SBram Moolenaar endif 320e09ba7baSBram Moolenaar 321e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 322e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 323e09ba7baSBram Moolenaar let entry['fname'] = fname 324e09ba7baSBram Moolenaar let entry['lnum'] = lnum 325*1b9645deSBram Moolenaar 326*1b9645deSBram Moolenaar if bufloaded(fname) 327*1b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 328*1b9645deSBram Moolenaar endif 329*1b9645deSBram Moolenaarendfunc 330*1b9645deSBram Moolenaar 331*1b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry) 332*1b9645deSBram Moolenaar exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname'] 333*1b9645deSBram Moolenaar let a:entry['placed'] = 1 334e09ba7baSBram Moolenaarendfunc 335e09ba7baSBram Moolenaar 336e09ba7baSBram Moolenaar" Handle deleting a breakpoint 337e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 338e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 339e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 340e09ba7baSBram Moolenaar if nr == 0 341e09ba7baSBram Moolenaar return 342e09ba7baSBram Moolenaar endif 343*1b9645deSBram Moolenaar if has_key(s:breakpoints, nr) 344*1b9645deSBram Moolenaar let entry = s:breakpoints[nr] 345*1b9645deSBram Moolenaar if has_key(entry, 'placed') 346e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 347*1b9645deSBram Moolenaar unlet entry['placed'] 348*1b9645deSBram Moolenaar endif 349e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 350*1b9645deSBram Moolenaar endif 351c572da5fSBram Moolenaarendfunc 352*1b9645deSBram Moolenaar 353*1b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 354*1b9645deSBram Moolenaarfunc s:BufRead() 355*1b9645deSBram Moolenaar let fname = expand('<afile>:p') 356*1b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 357*1b9645deSBram Moolenaar if entry['fname'] == fname 358*1b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 359*1b9645deSBram Moolenaar endif 360*1b9645deSBram Moolenaar endfor 361*1b9645deSBram Moolenaarendfunc 362*1b9645deSBram Moolenaar 363*1b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 364*1b9645deSBram Moolenaarfunc s:BufUnloaded() 365*1b9645deSBram Moolenaar let fname = expand('<afile>:p') 366*1b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 367*1b9645deSBram Moolenaar if entry['fname'] == fname 368*1b9645deSBram Moolenaar let entry['placed'] = 0 369*1b9645deSBram Moolenaar endif 370*1b9645deSBram Moolenaar endfor 371*1b9645deSBram Moolenaarendfunc 372*1b9645deSBram Moolenaar 373