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
1837c64c78SBram Moolenaar" In case this gets loaded twice.
1937c64c78SBram Moolenaarif exists(':Termdebug')
2037c64c78SBram Moolenaar  finish
2137c64c78SBram Moolenaarendif
2237c64c78SBram 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
4624a98a0eSBram Moolenaar  let s:save_columns = 0
4724a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
4824a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
4938baa3e6SBram Moolenaar      let s:save_columns = &columns
5038baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
5124a98a0eSBram Moolenaar    endif
5238baa3e6SBram Moolenaar    let vertical = 1
5338baa3e6SBram Moolenaar  else
5438baa3e6SBram Moolenaar    let vertical = 0
5538baa3e6SBram Moolenaar  endif
5638baa3e6SBram Moolenaar
57c572da5fSBram Moolenaar  " Open a terminal window without a job, to run the debugged program
58fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
59fe386641SBram Moolenaar	\ 'term_name': 'gdb program',
6038baa3e6SBram Moolenaar	\ 'vertical': vertical,
61fe386641SBram Moolenaar	\ })
62fe386641SBram Moolenaar  if s:ptybuf == 0
63fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
64fe386641SBram Moolenaar    return
65fe386641SBram Moolenaar  endif
66fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
6745d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
68fe386641SBram Moolenaar
69fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
70fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
71fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
72fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
73fe386641SBram Moolenaar	\ 'hidden': 1,
74fe386641SBram Moolenaar	\ })
75fe386641SBram Moolenaar  if s:commbuf == 0
76fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
77fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
78fe386641SBram Moolenaar    return
79fe386641SBram Moolenaar  endif
80fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
81c572da5fSBram Moolenaar
82c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
83c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
84c3632516SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty, a:cmd]
85c572da5fSBram Moolenaar  echomsg 'executing "' . join(cmd) . '"'
86c572da5fSBram Moolenaar  let gdbbuf = term_start(cmd, {
87c572da5fSBram Moolenaar	\ 'exit_cb': function('s:EndDebug'),
88fe386641SBram Moolenaar	\ 'term_finish': 'close',
89c572da5fSBram Moolenaar	\ })
90fe386641SBram Moolenaar  if gdbbuf == 0
91fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
92fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
93fe386641SBram Moolenaar    exe 'bwipe! ' . s:commbuf
94fe386641SBram Moolenaar    return
95fe386641SBram Moolenaar  endif
9645d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
97fe386641SBram Moolenaar
98fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
9901164a65SBram Moolenaar  " If you get an error "undefined command" your GDB is too old.
100fe386641SBram Moolenaar  call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r")
101e09ba7baSBram Moolenaar
10238baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
10338baa3e6SBram Moolenaar  " There can be only one.
10438baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
10538baa3e6SBram Moolenaar
10638baa3e6SBram Moolenaar  " Sign used to indicate a breakpoint.
10738baa3e6SBram Moolenaar  " Can be used multiple times.
10838baa3e6SBram Moolenaar  sign define debugBreakpoint text=>> texthl=debugBreakpoint
10938baa3e6SBram Moolenaar
11045d5f26dSBram Moolenaar  " Install debugger commands in the text window.
11145d5f26dSBram Moolenaar  call win_gotoid(s:startwin)
112e09ba7baSBram Moolenaar  call s:InstallCommands()
11345d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
114e09ba7baSBram Moolenaar
115e09ba7baSBram Moolenaar  let s:breakpoints = {}
1161b9645deSBram Moolenaar
1171b9645deSBram Moolenaar  augroup TermDebug
1181b9645deSBram Moolenaar    au BufRead * call s:BufRead()
1191b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
1201b9645deSBram Moolenaar  augroup END
121c572da5fSBram Moolenaarendfunc
122c572da5fSBram Moolenaar
123c572da5fSBram Moolenaarfunc s:EndDebug(job, status)
124c572da5fSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
125fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
126e09ba7baSBram Moolenaar
127e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
128e09ba7baSBram Moolenaar
129e09ba7baSBram Moolenaar  call win_gotoid(s:startwin)
130e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
131e09ba7baSBram Moolenaar  call s:DeleteCommands()
132e09ba7baSBram Moolenaar
133e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
13438baa3e6SBram Moolenaar  if s:save_columns > 0
13538baa3e6SBram Moolenaar    let &columns = s:save_columns
13638baa3e6SBram Moolenaar  endif
1371b9645deSBram Moolenaar
1381b9645deSBram Moolenaar  au! TermDebug
139fe386641SBram Moolenaarendfunc
140fe386641SBram Moolenaar
141fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
142fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
143fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
144fe386641SBram Moolenaar
145fe386641SBram Moolenaar  for msg in msgs
146fe386641SBram Moolenaar    " remove prefixed NL
147fe386641SBram Moolenaar    if msg[0] == "\n"
148fe386641SBram Moolenaar      let msg = msg[1:]
149fe386641SBram Moolenaar    endif
150fe386641SBram Moolenaar    if msg != ''
1511b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
152e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
15345d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
154e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
155e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
156e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
15745d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
15845d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
15945d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
16045d5f26dSBram Moolenaar	call s:HandleError(msg)
161e09ba7baSBram Moolenaar      endif
162e09ba7baSBram Moolenaar    endif
163e09ba7baSBram Moolenaar  endfor
164e09ba7baSBram Moolenaarendfunc
165e09ba7baSBram Moolenaar
166e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
167e09ba7baSBram Moolenaarfunc s:InstallCommands()
168e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
169e09ba7baSBram Moolenaar  command Delete call s:DeleteBreakpoint()
170e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
17145d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
172e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
173e09ba7baSBram Moolenaar  command Continue call s:SendCommand('-exec-continue')
17445d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
17545d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
17645d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
17745d5f26dSBram Moolenaar
17845d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
17945d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
1801b9645deSBram Moolenaar
1811b9645deSBram Moolenaar  if has('menu')
18224a98a0eSBram Moolenaar    nnoremenu WinBar.Step :Step<CR>
18324a98a0eSBram Moolenaar    nnoremenu WinBar.Next :Over<CR>
18424a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
18524a98a0eSBram Moolenaar    nnoremenu WinBar.Cont :Continue<CR>
18624a98a0eSBram Moolenaar    nnoremenu WinBar.Eval :Evaluate<CR>
1871b9645deSBram Moolenaar  endif
188e09ba7baSBram Moolenaarendfunc
189e09ba7baSBram Moolenaar
190e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
191e09ba7baSBram Moolenaarfunc s:DeleteCommands()
192e09ba7baSBram Moolenaar  delcommand Break
193e09ba7baSBram Moolenaar  delcommand Delete
194e09ba7baSBram Moolenaar  delcommand Step
19545d5f26dSBram Moolenaar  delcommand Over
196e09ba7baSBram Moolenaar  delcommand Finish
197e09ba7baSBram Moolenaar  delcommand Continue
19845d5f26dSBram Moolenaar  delcommand Evaluate
19945d5f26dSBram Moolenaar  delcommand Gdb
20045d5f26dSBram Moolenaar  delcommand Program
20145d5f26dSBram Moolenaar
20245d5f26dSBram Moolenaar  nunmap K
2031b9645deSBram Moolenaar
2041b9645deSBram Moolenaar  if has('menu')
2051b9645deSBram Moolenaar    aunmenu WinBar.Step
2061b9645deSBram Moolenaar    aunmenu WinBar.Next
2071b9645deSBram Moolenaar    aunmenu WinBar.Finish
2081b9645deSBram Moolenaar    aunmenu WinBar.Cont
2091b9645deSBram Moolenaar    aunmenu WinBar.Eval
2101b9645deSBram Moolenaar  endif
2111b9645deSBram Moolenaar
21245d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
21345d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
21445d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
21545d5f26dSBram Moolenaar  endfor
21638baa3e6SBram Moolenaar  sign undefine debugPC
21738baa3e6SBram Moolenaar  sign undefine debugBreakpoint
21845d5f26dSBram Moolenaar  unlet s:breakpoints
219e09ba7baSBram Moolenaarendfunc
220e09ba7baSBram Moolenaar
221e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
222e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
223e09ba7baSBram Moolenaar  call term_sendkeys(s:commbuf, '-break-insert --source '
224e09ba7baSBram Moolenaar	\ . fnameescape(expand('%:p')) . ' --line ' . line('.') . "\r")
225e09ba7baSBram Moolenaarendfunc
226e09ba7baSBram Moolenaar
227e09ba7baSBram Moolenaar" :Delete - Delete a breakpoint at the cursor position.
228e09ba7baSBram Moolenaarfunc s:DeleteBreakpoint()
229e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
230e09ba7baSBram Moolenaar  let lnum = line('.')
231e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
232e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
233e09ba7baSBram Moolenaar      call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r")
234e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
235e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
236e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
237e09ba7baSBram Moolenaar      break
238e09ba7baSBram Moolenaar    endif
239e09ba7baSBram Moolenaar  endfor
240e09ba7baSBram Moolenaarendfunc
241e09ba7baSBram Moolenaar
242e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb
243e09ba7baSBram Moolenaarfunc s:SendCommand(cmd)
244e09ba7baSBram Moolenaar  call term_sendkeys(s:commbuf, a:cmd . "\r")
245e09ba7baSBram Moolenaarendfunc
246e09ba7baSBram Moolenaar
24745d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
24845d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
24945d5f26dSBram Moolenaar  if a:arg != ''
25045d5f26dSBram Moolenaar    let expr = a:arg
25145d5f26dSBram Moolenaar  elseif a:range == 2
25245d5f26dSBram Moolenaar    let pos = getcurpos()
25345d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
25445d5f26dSBram Moolenaar    let regt = getregtype('v')
25545d5f26dSBram Moolenaar    normal! gv"vy
25645d5f26dSBram Moolenaar    let expr = @v
25745d5f26dSBram Moolenaar    call setpos('.', pos)
25845d5f26dSBram Moolenaar    call setreg('v', reg, regt)
25945d5f26dSBram Moolenaar  else
26045d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
26145d5f26dSBram Moolenaar  endif
26245d5f26dSBram Moolenaar  call term_sendkeys(s:commbuf, '-data-evaluate-expression "' . expr . "\"\r")
26345d5f26dSBram Moolenaar  let s:evalexpr = expr
26445d5f26dSBram Moolenaarendfunc
26545d5f26dSBram Moolenaar
26645d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
26745d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
2681b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
2691b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
2701b9645deSBram Moolenaar  echomsg '"' . s:evalexpr . '": ' . value
2711b9645deSBram Moolenaar
272*7f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
2731b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
2741b9645deSBram Moolenaar    let s:evalexpr = '*' . s:evalexpr
2751b9645deSBram Moolenaar    call term_sendkeys(s:commbuf, '-data-evaluate-expression "' . s:evalexpr . "\"\r")
2761b9645deSBram Moolenaar  endif
27745d5f26dSBram Moolenaarendfunc
27845d5f26dSBram Moolenaar
27945d5f26dSBram Moolenaar" Handle an error.
28045d5f26dSBram Moolenaarfunc s:HandleError(msg)
28145d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
28245d5f26dSBram Moolenaarendfunc
28345d5f26dSBram Moolenaar
284e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
285e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
286e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
287fe386641SBram Moolenaar  let wid = win_getid(winnr())
288fe386641SBram Moolenaar
289fe386641SBram Moolenaar  if win_gotoid(s:startwin)
290e09ba7baSBram Moolenaar    let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
2911b9645deSBram Moolenaar    if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
292e09ba7baSBram Moolenaar      let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
293fe386641SBram Moolenaar      if lnum =~ '^[0-9]*$'
2941b9645deSBram Moolenaar	if expand('%:p') != fnamemodify(fname, ':p')
295fe386641SBram Moolenaar	  if &modified
296fe386641SBram Moolenaar	    " TODO: find existing window
297fe386641SBram Moolenaar	    exe 'split ' . fnameescape(fname)
298fe386641SBram Moolenaar	    let s:startwin = win_getid(winnr())
299fe386641SBram Moolenaar	  else
300fe386641SBram Moolenaar	    exe 'edit ' . fnameescape(fname)
301fe386641SBram Moolenaar	  endif
302fe386641SBram Moolenaar	endif
303fe386641SBram Moolenaar	exe lnum
30401164a65SBram Moolenaar	exe 'sign unplace ' . s:pc_id
3051b9645deSBram Moolenaar	exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
306fe386641SBram Moolenaar	setlocal signcolumn=yes
307fe386641SBram Moolenaar      endif
308fe386641SBram Moolenaar    else
309fe386641SBram Moolenaar      exe 'sign unplace ' . s:pc_id
310fe386641SBram Moolenaar    endif
311fe386641SBram Moolenaar
312fe386641SBram Moolenaar    call win_gotoid(wid)
313fe386641SBram Moolenaar  endif
314e09ba7baSBram Moolenaarendfunc
315e09ba7baSBram Moolenaar
316e09ba7baSBram Moolenaar" Handle setting a breakpoint
317e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
318e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
319e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
320e09ba7baSBram Moolenaar  if nr == 0
321e09ba7baSBram Moolenaar    return
322fe386641SBram Moolenaar  endif
323e09ba7baSBram Moolenaar
324e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
325e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
326e09ba7baSBram Moolenaar  else
327e09ba7baSBram Moolenaar    let entry = {}
328e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
329fe386641SBram Moolenaar  endif
330e09ba7baSBram Moolenaar
331e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
332e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
333e09ba7baSBram Moolenaar  let entry['fname'] = fname
334e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
3351b9645deSBram Moolenaar
3361b9645deSBram Moolenaar  if bufloaded(fname)
3371b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
3381b9645deSBram Moolenaar  endif
3391b9645deSBram Moolenaarendfunc
3401b9645deSBram Moolenaar
3411b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
3421b9645deSBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname']
3431b9645deSBram Moolenaar  let a:entry['placed'] = 1
344e09ba7baSBram Moolenaarendfunc
345e09ba7baSBram Moolenaar
346e09ba7baSBram Moolenaar" Handle deleting a breakpoint
347e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
348e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
349e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
350e09ba7baSBram Moolenaar  if nr == 0
351e09ba7baSBram Moolenaar    return
352e09ba7baSBram Moolenaar  endif
3531b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
3541b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
3551b9645deSBram Moolenaar    if has_key(entry, 'placed')
356e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
3571b9645deSBram Moolenaar      unlet entry['placed']
3581b9645deSBram Moolenaar    endif
359e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
3601b9645deSBram Moolenaar  endif
361c572da5fSBram Moolenaarendfunc
3621b9645deSBram Moolenaar
3631b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
3641b9645deSBram Moolenaarfunc s:BufRead()
3651b9645deSBram Moolenaar  let fname = expand('<afile>:p')
3661b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
3671b9645deSBram Moolenaar    if entry['fname'] == fname
3681b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
3691b9645deSBram Moolenaar    endif
3701b9645deSBram Moolenaar  endfor
3711b9645deSBram Moolenaarendfunc
3721b9645deSBram Moolenaar
3731b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
3741b9645deSBram Moolenaarfunc s:BufUnloaded()
3751b9645deSBram Moolenaar  let fname = expand('<afile>:p')
3761b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
3771b9645deSBram Moolenaar    if entry['fname'] == fname
3781b9645deSBram Moolenaar      let entry['placed'] = 0
3791b9645deSBram Moolenaar    endif
3801b9645deSBram Moolenaar  endfor
3811b9645deSBram Moolenaarendfunc
3821b9645deSBram Moolenaar
383