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