1fe386641SBram Moolenaar" Debugger plugin using gdb.
2c572da5fSBram Moolenaar"
3b3307b5eSBram Moolenaar" Author: Bram Moolenaar
4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license"
5b3307b5eSBram Moolenaar" Last Update: 2018 Jun 3
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".
68e09ba7baSBram Moolenaarif !exists('termdebugger')
69e09ba7baSBram Moolenaar  let 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')
107b3623a38SBram Moolenaar    echoerr 'Terminal debugger already running'
108b3623a38SBram Moolenaar    return
109b3623a38SBram Moolenaar  endif
110b3307b5eSBram Moolenaar  let s:ptywin = 0
1114551c0a9SBram Moolenaar  let s:pid = 0
112b3623a38SBram Moolenaar
113b3307b5eSBram Moolenaar  " Uncomment this line to write logging in "debuglog".
114b3307b5eSBram Moolenaar  " call ch_logfile('debuglog', 'w')
115b3307b5eSBram Moolenaar
116b3307b5eSBram Moolenaar  let s:sourcewin = win_getid(winnr())
117fe386641SBram Moolenaar  let s:startsigncolumn = &signcolumn
118fe386641SBram Moolenaar
11924a98a0eSBram Moolenaar  let s:save_columns = 0
12068e6560bSBram Moolenaar  let s:allleft = 0
12124a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
12224a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
12338baa3e6SBram Moolenaar      let s:save_columns = &columns
12438baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
12568e6560bSBram Moolenaar      " If we make the Vim window wider, use the whole left halve for the debug
12668e6560bSBram Moolenaar      " windows.
12768e6560bSBram Moolenaar      let s:allleft = 1
12824a98a0eSBram Moolenaar    endif
129b3307b5eSBram Moolenaar    let s:vertical = 1
13038baa3e6SBram Moolenaar  else
131b3307b5eSBram Moolenaar    let s:vertical = 0
13238baa3e6SBram Moolenaar  endif
13338baa3e6SBram Moolenaar
134b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
135b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
136b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
137b3307b5eSBram Moolenaar    let s:way = 'terminal'
138b3307b5eSBram Moolenaar  else
139b3307b5eSBram Moolenaar    let s:way = 'prompt'
140b3307b5eSBram Moolenaar  endif
141b3307b5eSBram Moolenaar
142b3307b5eSBram Moolenaar  if s:way == 'prompt'
143b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
144b3307b5eSBram Moolenaar  else
145b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
146b3307b5eSBram Moolenaar  endif
147b3307b5eSBram Moolenaarendfunc
148b3307b5eSBram Moolenaar
149ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended.
150ef3c6a5bSBram Moolenaarfunc s:CloseBuffers()
151ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
152ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:commbuf
153ef3c6a5bSBram Moolenaar  unlet! s:gdbwin
154ef3c6a5bSBram Moolenaarendfunc
155ef3c6a5bSBram Moolenaar
156b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
157b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
158fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
159b3307b5eSBram Moolenaar	\ 'term_name': 'debugged program',
160b3307b5eSBram Moolenaar	\ 'vertical': s:vertical,
161fe386641SBram Moolenaar	\ })
162fe386641SBram Moolenaar  if s:ptybuf == 0
163fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
164fe386641SBram Moolenaar    return
165fe386641SBram Moolenaar  endif
166fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
16745d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
168b3307b5eSBram Moolenaar  if s:vertical
16951b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
17051b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
17151b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
17268e6560bSBram Moolenaar    if s:allleft
17368e6560bSBram Moolenaar      " use the whole left column
17468e6560bSBram Moolenaar      wincmd H
17568e6560bSBram Moolenaar    endif
17651b0f370SBram Moolenaar  endif
177fe386641SBram Moolenaar
178fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
179fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
180fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
181fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
182fe386641SBram Moolenaar	\ 'hidden': 1,
183fe386641SBram Moolenaar	\ })
184fe386641SBram Moolenaar  if s:commbuf == 0
185fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
186fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
187fe386641SBram Moolenaar    return
188fe386641SBram Moolenaar  endif
189fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
190c572da5fSBram Moolenaar
191c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
192c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
19332c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
19432c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
19532c67ba7SBram Moolenaar
19632c67ba7SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
197b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
19860e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
199fe386641SBram Moolenaar	\ 'term_finish': 'close',
200c572da5fSBram Moolenaar	\ })
20160e73f2aSBram Moolenaar  if s:gdbbuf == 0
202fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
203ef3c6a5bSBram Moolenaar    call s:CloseBuffers()
204fe386641SBram Moolenaar    return
205fe386641SBram Moolenaar  endif
20645d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
207fe386641SBram Moolenaar
20832c67ba7SBram Moolenaar  " Set arguments to be run
20932c67ba7SBram Moolenaar  if len(proc_args)
21032c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
21132c67ba7SBram Moolenaar  endif
21232c67ba7SBram Moolenaar
213fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
21460e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
21560e73f2aSBram Moolenaar
2163e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
2173e4b84d0SBram Moolenaar  " why the debugger doesn't work.
2183e4b84d0SBram Moolenaar  let try_count = 0
2193e4b84d0SBram Moolenaar  while 1
220ef3c6a5bSBram Moolenaar    let gdbproc = term_getjob(s:gdbbuf)
221ef3c6a5bSBram Moolenaar    if gdbproc == v:null || job_status(gdbproc) !=# 'run'
222ef3c6a5bSBram Moolenaar      echoerr string(g:termdebugger) . ' exited unexpectedly'
223ef3c6a5bSBram Moolenaar      call s:CloseBuffers()
224ef3c6a5bSBram Moolenaar      return
225ef3c6a5bSBram Moolenaar    endif
226ef3c6a5bSBram Moolenaar
2273e4b84d0SBram Moolenaar    let response = ''
228b3623a38SBram Moolenaar    for lnum in range(1, 200)
22919c8fe19SBram Moolenaar      let line1 = term_getline(s:gdbbuf, lnum)
23019c8fe19SBram Moolenaar      let line2 = term_getline(s:gdbbuf, lnum + 1)
23119c8fe19SBram Moolenaar      if line1 =~ 'new-ui mi '
232f63db65bSBram Moolenaar	" response can be in the same line or the next line
23319c8fe19SBram Moolenaar	let response = line1 . line2
2343e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
235f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
236ef3c6a5bSBram Moolenaar	  call s:CloseBuffers()
2373e4b84d0SBram Moolenaar	  return
2383e4b84d0SBram Moolenaar	endif
2393e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
2403e4b84d0SBram Moolenaar	  " Success!
2413e4b84d0SBram Moolenaar	  break
2423e4b84d0SBram Moolenaar	endif
24319c8fe19SBram Moolenaar      elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi '
24419c8fe19SBram Moolenaar	" Reading symbols might take a while, try more times
24506fe74aeSBram Moolenaar	let try_count -= 1
24606fe74aeSBram Moolenaar      endif
2473e4b84d0SBram Moolenaar    endfor
2483e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2493e4b84d0SBram Moolenaar      break
2503e4b84d0SBram Moolenaar    endif
2513e4b84d0SBram Moolenaar    let try_count += 1
2523e4b84d0SBram Moolenaar    if try_count > 100
2533e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2543e4b84d0SBram Moolenaar      break
2553e4b84d0SBram Moolenaar    endif
2563e4b84d0SBram Moolenaar    sleep 10m
2573e4b84d0SBram Moolenaar  endwhile
2583e4b84d0SBram Moolenaar
25960e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
26060e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
26160e73f2aSBram Moolenaar  " running.
26260e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
263b3307b5eSBram Moolenaar  " Older gdb uses a different command.
264b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
265e09ba7baSBram Moolenaar
266f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
267f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
268b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
269f3ba14ffSBram Moolenaar
270ef3c6a5bSBram Moolenaar  call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')})
271b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
272b3307b5eSBram Moolenaarendfunc
273b3307b5eSBram Moolenaar
274b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
275b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
276b3307b5eSBram Moolenaar  if s:vertical
277b3307b5eSBram Moolenaar    vertical new
278b3307b5eSBram Moolenaar  else
279b3307b5eSBram Moolenaar    new
280b3307b5eSBram Moolenaar  endif
281b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
282b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
283b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
284b3307b5eSBram Moolenaar  set buftype=prompt
285b3307b5eSBram Moolenaar  file gdb
286b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
287b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
288b3307b5eSBram Moolenaar
289b3307b5eSBram Moolenaar  if s:vertical
290b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
291b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
292b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
293b3307b5eSBram Moolenaar  endif
294b3307b5eSBram Moolenaar
295b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
296b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
297b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
298b3307b5eSBram Moolenaar
299b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
300b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
301b3307b5eSBram Moolenaar
302b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
303b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
304b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
305b3307b5eSBram Moolenaar	\ })
306b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
307b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
308b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
309b3307b5eSBram Moolenaar    return
310b3307b5eSBram Moolenaar  endif
3114551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
3124551c0a9SBram Moolenaar  set modified
313b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
314b3307b5eSBram Moolenaar
315b3307b5eSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only
316b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
317b3307b5eSBram Moolenaar  " target is running.
318b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
319b3307b5eSBram Moolenaar  " Older gdb uses a different command.
320b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
321b3307b5eSBram Moolenaar
322b3307b5eSBram Moolenaar  let s:ptybuf = 0
323b3307b5eSBram Moolenaar  if has('win32')
324b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
325b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
326b3307b5eSBram Moolenaar  elseif has('terminal')
327b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
328b3307b5eSBram Moolenaar    " gdb window.
329b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
330b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
331b3307b5eSBram Moolenaar	  \ })
332b3307b5eSBram Moolenaar    if s:ptybuf == 0
333b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
334b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
335b3307b5eSBram Moolenaar      return
336b3307b5eSBram Moolenaar    endif
337b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
338b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
339b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
340b3307b5eSBram Moolenaar
341b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
342b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
343b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
344b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
345b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
346b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
347b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
348b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
349b3307b5eSBram Moolenaar  else
350b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
351b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
352b3307b5eSBram Moolenaar  endif
353b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
354b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
355b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
356b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
357b3307b5eSBram Moolenaar
358b3307b5eSBram Moolenaar  " Set arguments to be run
359b3307b5eSBram Moolenaar  if len(proc_args)
360b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
361b3307b5eSBram Moolenaar  endif
362b3307b5eSBram Moolenaar
363b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
364b3307b5eSBram Moolenaar  startinsert
365b3307b5eSBram Moolenaarendfunc
366b3307b5eSBram Moolenaar
367b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
36838baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
36938baa3e6SBram Moolenaar  " There can be only one.
37038baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
37138baa3e6SBram Moolenaar
37245d5f26dSBram Moolenaar  " Install debugger commands in the text window.
373b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
374e09ba7baSBram Moolenaar  call s:InstallCommands()
37545d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
376e09ba7baSBram Moolenaar
37751b0f370SBram Moolenaar  " Enable showing a balloon with eval info
378246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
379246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
38051b0f370SBram Moolenaar    if has("balloon_eval")
38151b0f370SBram Moolenaar      set ballooneval
382246fe03dSBram Moolenaar    endif
38351b0f370SBram Moolenaar    if has("balloon_eval_term")
38451b0f370SBram Moolenaar      set balloonevalterm
38551b0f370SBram Moolenaar    endif
38651b0f370SBram Moolenaar  endif
38751b0f370SBram Moolenaar
3885378e1cfSBram Moolenaar  " Contains breakpoints that have been placed, key is a string with the GDB
3895378e1cfSBram Moolenaar  " breakpoint number.
39037402ed5SBram Moolenaar  " Each entry is a dict, containing the sub-breakpoints.  Key is the subid.
39137402ed5SBram Moolenaar  " For a breakpoint that is just a number the subid is zero.
39237402ed5SBram Moolenaar  " For a breakpoint "123.4" the id is "123" and subid is "4".
39337402ed5SBram Moolenaar  " Example, when breakpoint "44", "123", "123.1" and "123.2" exist:
39437402ed5SBram Moolenaar  " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}}
395e09ba7baSBram Moolenaar  let s:breakpoints = {}
3961b9645deSBram Moolenaar
39737402ed5SBram Moolenaar  " Contains breakpoints by file/lnum.  The key is "fname:lnum".
39837402ed5SBram Moolenaar  " Each entry is a list of breakpoint IDs at that position.
39937402ed5SBram Moolenaar  let s:breakpoint_locations = {}
40037402ed5SBram Moolenaar
4011b9645deSBram Moolenaar  augroup TermDebug
4021b9645deSBram Moolenaar    au BufRead * call s:BufRead()
4031b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
404f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
4051b9645deSBram Moolenaar  augroup END
40632c67ba7SBram Moolenaar
407b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
408b3307b5eSBram Moolenaar  " window.
40932c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
41032c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
41132c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
41232c67ba7SBram Moolenaar  endif
413c572da5fSBram Moolenaarendfunc
414c572da5fSBram Moolenaar
415b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
416b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
417b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
418b3307b5eSBram Moolenaar  if s:way == 'prompt'
419b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
420b3307b5eSBram Moolenaar  else
421b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
422b3307b5eSBram Moolenaar  endif
423b3307b5eSBram Moolenaarendfunc
424b3307b5eSBram Moolenaar
425b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
426b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
427b3307b5eSBram Moolenaar  if s:way == 'prompt'
428b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
429b3307b5eSBram Moolenaar  else
430b3307b5eSBram Moolenaar    let do_continue = 0
431b3307b5eSBram Moolenaar    if !s:stopped
432b3307b5eSBram Moolenaar      let do_continue = 1
433b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
434b3307b5eSBram Moolenaar      sleep 10m
435b3307b5eSBram Moolenaar    endif
436b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
437b3307b5eSBram Moolenaar    if do_continue
438b3307b5eSBram Moolenaar      Continue
439b3307b5eSBram Moolenaar    endif
440b3307b5eSBram Moolenaar  endif
441b3307b5eSBram Moolenaarendfunc
442b3307b5eSBram Moolenaar
443b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
444b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
445b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
446b3307b5eSBram Moolenaarendfunc
447b3307b5eSBram Moolenaar
4484551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
4494551c0a9SBram Moolenaar" breakpoint.
450b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
4512ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
4522ed890f1SBram Moolenaar  if has('win32')
4532ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
4542ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
4554551c0a9SBram Moolenaar    if s:pid == 0
4564551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
4574551c0a9SBram Moolenaar    else
4584551c0a9SBram Moolenaar      call debugbreak(s:pid)
4594551c0a9SBram Moolenaar    endif
4602ed890f1SBram Moolenaar  else
4612ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
4622ed890f1SBram Moolenaar  endif
463b3307b5eSBram Moolenaarendfunc
464b3307b5eSBram Moolenaar
465b3307b5eSBram Moolenaar" Function called when gdb outputs text.
466b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
467b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
468b3307b5eSBram Moolenaar
469b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
470b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
471a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
472b3307b5eSBram Moolenaar    return
473b3307b5eSBram Moolenaar  endif
474b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
475b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
476b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
477b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
478b3307b5eSBram Moolenaar      unlet s:evalexpr
479b3307b5eSBram Moolenaar      return
480b3307b5eSBram Moolenaar    endif
481b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
482b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
483b3307b5eSBram Moolenaar  else
484b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
485b3307b5eSBram Moolenaar    return
486b3307b5eSBram Moolenaar  endif
487b3307b5eSBram Moolenaar
488b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
489b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
490b3307b5eSBram Moolenaar
491b3307b5eSBram Moolenaar  " Add the output above the current prompt.
492b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
4934551c0a9SBram Moolenaar  set modified
494b3307b5eSBram Moolenaar
495b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
496b3307b5eSBram Moolenaarendfunc
497b3307b5eSBram Moolenaar
498b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
499b3307b5eSBram Moolenaar" to the next ", unescaping characters.
500b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
501b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
502a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
503b3307b5eSBram Moolenaar    return
504b3307b5eSBram Moolenaar  endif
505b3307b5eSBram Moolenaar  let result = ''
506b3307b5eSBram Moolenaar  let i = 1
507b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
508b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
509b3307b5eSBram Moolenaar      let i += 1
510b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
511b3307b5eSBram Moolenaar	" drop \n
512b3307b5eSBram Moolenaar	let i += 1
513b3307b5eSBram Moolenaar	continue
514*589edb34SBram Moolenaar      elseif a:quotedText[i] == 't'
515*589edb34SBram Moolenaar	" append \t
516*589edb34SBram Moolenaar	let i += 1
517*589edb34SBram Moolenaar	let result .= "\t"
518*589edb34SBram Moolenaar	continue
519b3307b5eSBram Moolenaar      endif
520b3307b5eSBram Moolenaar    endif
521b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
522b3307b5eSBram Moolenaar    let i += 1
523b3307b5eSBram Moolenaar  endwhile
524b3307b5eSBram Moolenaar  return result
525b3307b5eSBram Moolenaarendfunc
526b3307b5eSBram Moolenaar
527a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
528a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
5295378e1cfSBram Moolenaar  if a:msg !~ 'fullname'
5305378e1cfSBram Moolenaar    return ''
5315378e1cfSBram Moolenaar  endif
532a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
533a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
534a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
535a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
536a15b0a93SBram Moolenaar  endif
537a15b0a93SBram Moolenaar  return name
538a15b0a93SBram Moolenaarendfunc
539a15b0a93SBram Moolenaar
540b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
541fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
542b3623a38SBram Moolenaar  unlet s:gdbwin
543e09ba7baSBram Moolenaar
544b3307b5eSBram Moolenaar  call s:EndDebugCommon()
545b3307b5eSBram Moolenaarendfunc
546b3307b5eSBram Moolenaar
547b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
548e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
549e09ba7baSBram Moolenaar
550b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
551b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
552b3307b5eSBram Moolenaar  endif
553b3307b5eSBram Moolenaar
554b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
555e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
556e09ba7baSBram Moolenaar  call s:DeleteCommands()
557e09ba7baSBram Moolenaar
558e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
559b3307b5eSBram Moolenaar
56038baa3e6SBram Moolenaar  if s:save_columns > 0
56138baa3e6SBram Moolenaar    let &columns = s:save_columns
56238baa3e6SBram Moolenaar  endif
5631b9645deSBram Moolenaar
564246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
565246fe03dSBram Moolenaar    set balloonexpr=
56651b0f370SBram Moolenaar    if has("balloon_eval")
56751b0f370SBram Moolenaar      set noballooneval
568246fe03dSBram Moolenaar    endif
56951b0f370SBram Moolenaar    if has("balloon_eval_term")
57051b0f370SBram Moolenaar      set noballoonevalterm
57151b0f370SBram Moolenaar    endif
57251b0f370SBram Moolenaar  endif
57351b0f370SBram Moolenaar
5741b9645deSBram Moolenaar  au! TermDebug
575fe386641SBram Moolenaarendfunc
576fe386641SBram Moolenaar
577b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
578b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
579b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
5804551c0a9SBram Moolenaar  set nomodified
581b3307b5eSBram Moolenaar  close
582b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
583b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
584b3307b5eSBram Moolenaar  endif
585b3307b5eSBram Moolenaar
586b3307b5eSBram Moolenaar  call s:EndDebugCommon()
587b3307b5eSBram Moolenaar  unlet s:gdbwin
588b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
589b3307b5eSBram Moolenaarendfunc
590b3307b5eSBram Moolenaar
591fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
592fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
593fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
594fe386641SBram Moolenaar
595fe386641SBram Moolenaar  for msg in msgs
596fe386641SBram Moolenaar    " remove prefixed NL
597fe386641SBram Moolenaar    if msg[0] == "\n"
598fe386641SBram Moolenaar      let msg = msg[1:]
599fe386641SBram Moolenaar    endif
600fe386641SBram Moolenaar    if msg != ''
6011b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
602e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
60345d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
604e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
605e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
606e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
6074551c0a9SBram Moolenaar      elseif msg =~ '^=thread-group-started'
6084551c0a9SBram Moolenaar	call s:HandleProgramRun(msg)
60945d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
61045d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
61145d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
61245d5f26dSBram Moolenaar	call s:HandleError(msg)
613e09ba7baSBram Moolenaar      endif
614e09ba7baSBram Moolenaar    endif
615e09ba7baSBram Moolenaar  endfor
616e09ba7baSBram Moolenaarendfunc
617e09ba7baSBram Moolenaar
618*589edb34SBram Moolenaarfunc s:GotoProgram()
619*589edb34SBram Moolenaar  if has('win32')
620*589edb34SBram Moolenaar    if executable('powershell')
621*589edb34SBram Moolenaar      call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid))
622*589edb34SBram Moolenaar    endif
623*589edb34SBram Moolenaar  else
624*589edb34SBram Moolenaar    win_gotoid(s:ptywin)
625*589edb34SBram Moolenaar  endif
626*589edb34SBram Moolenaarendfunc
627*589edb34SBram Moolenaar
628e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
629e09ba7baSBram Moolenaarfunc s:InstallCommands()
630963c1ad5SBram Moolenaar  let save_cpo = &cpo
631963c1ad5SBram Moolenaar  set cpo&vim
632963c1ad5SBram Moolenaar
633*589edb34SBram Moolenaar  command -nargs=? Break call s:SetBreakpoint(<q-args>)
63471137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
635e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
63645d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
637e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
63860e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
63960e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
64060e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
641b3307b5eSBram Moolenaar
642b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
643b3307b5eSBram Moolenaar  if s:way == 'prompt'
644b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
645b3307b5eSBram Moolenaar  else
646b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
647b3307b5eSBram Moolenaar  endif
648b3307b5eSBram Moolenaar
64945d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
65045d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
651*589edb34SBram Moolenaar  command Program call s:GotoProgram()
652b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
65371137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
65445d5f26dSBram Moolenaar
65545d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
65645d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
6571b9645deSBram Moolenaar
658f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
65971137fedSBram Moolenaar    call s:InstallWinbar()
66071137fedSBram Moolenaar
66171137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
66271137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
66371137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
66471137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
66571137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
66671137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
66771137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
66871137fedSBram Moolenaar    endif
66971137fedSBram Moolenaar  endif
670963c1ad5SBram Moolenaar
671963c1ad5SBram Moolenaar  let &cpo = save_cpo
67271137fedSBram Moolenaarendfunc
67371137fedSBram Moolenaar
67471137fedSBram Moolenaarlet s:winbar_winids = []
67571137fedSBram Moolenaar
67671137fedSBram Moolenaar" Install the window toolbar in the current window.
67771137fedSBram Moolenaarfunc s:InstallWinbar()
678c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
67924a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
68024a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
68124a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
68224a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
68360e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
68424a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
68571137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
686c4b533e1SBram Moolenaar  endif
687e09ba7baSBram Moolenaarendfunc
688e09ba7baSBram Moolenaar
689e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
690e09ba7baSBram Moolenaarfunc s:DeleteCommands()
691e09ba7baSBram Moolenaar  delcommand Break
69271137fedSBram Moolenaar  delcommand Clear
693e09ba7baSBram Moolenaar  delcommand Step
69445d5f26dSBram Moolenaar  delcommand Over
695e09ba7baSBram Moolenaar  delcommand Finish
69660e73f2aSBram Moolenaar  delcommand Run
69760e73f2aSBram Moolenaar  delcommand Arguments
69860e73f2aSBram Moolenaar  delcommand Stop
699e09ba7baSBram Moolenaar  delcommand Continue
70045d5f26dSBram Moolenaar  delcommand Evaluate
70145d5f26dSBram Moolenaar  delcommand Gdb
70245d5f26dSBram Moolenaar  delcommand Program
703b3623a38SBram Moolenaar  delcommand Source
70471137fedSBram Moolenaar  delcommand Winbar
70545d5f26dSBram Moolenaar
70645d5f26dSBram Moolenaar  nunmap K
7071b9645deSBram Moolenaar
7081b9645deSBram Moolenaar  if has('menu')
70971137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
71071137fedSBram Moolenaar    let curwinid = win_getid(winnr())
71171137fedSBram Moolenaar    for winid in s:winbar_winids
71271137fedSBram Moolenaar      if win_gotoid(winid)
7131b9645deSBram Moolenaar	aunmenu WinBar.Step
7141b9645deSBram Moolenaar	aunmenu WinBar.Next
7151b9645deSBram Moolenaar	aunmenu WinBar.Finish
7161b9645deSBram Moolenaar	aunmenu WinBar.Cont
71760e73f2aSBram Moolenaar	aunmenu WinBar.Stop
7181b9645deSBram Moolenaar	aunmenu WinBar.Eval
7191b9645deSBram Moolenaar      endif
72071137fedSBram Moolenaar    endfor
72171137fedSBram Moolenaar    call win_gotoid(curwinid)
72271137fedSBram Moolenaar    let s:winbar_winids = []
72371137fedSBram Moolenaar
72471137fedSBram Moolenaar    if exists('s:saved_mousemodel')
72571137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
72671137fedSBram Moolenaar      unlet s:saved_mousemodel
72771137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
72871137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
72971137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
73071137fedSBram Moolenaar      aunmenu PopUp.Evaluate
73171137fedSBram Moolenaar    endif
73271137fedSBram Moolenaar  endif
7331b9645deSBram Moolenaar
73445d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
73537402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
73637402ed5SBram Moolenaar    for subid in keys(entries)
73737402ed5SBram Moolenaar      exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
73837402ed5SBram Moolenaar    endfor
73945d5f26dSBram Moolenaar  endfor
74045d5f26dSBram Moolenaar  unlet s:breakpoints
74137402ed5SBram Moolenaar  unlet s:breakpoint_locations
742a15b0a93SBram Moolenaar
743a15b0a93SBram Moolenaar  sign undefine debugPC
744a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
745a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
746a15b0a93SBram Moolenaar  endfor
7474551c0a9SBram Moolenaar  let s:BreakpointSigns = []
748e09ba7baSBram Moolenaarendfunc
749e09ba7baSBram Moolenaar
750e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
751*589edb34SBram Moolenaarfunc s:SetBreakpoint(at)
75260e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
75360e73f2aSBram Moolenaar  " Interrupt to make it work.
75460e73f2aSBram Moolenaar  let do_continue = 0
75560e73f2aSBram Moolenaar  if !s:stopped
75660e73f2aSBram Moolenaar    let do_continue = 1
757b3307b5eSBram Moolenaar    if s:way == 'prompt'
7584551c0a9SBram Moolenaar      call s:PromptInterrupt()
759b3307b5eSBram Moolenaar    else
76060e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
761b3307b5eSBram Moolenaar    endif
76260e73f2aSBram Moolenaar    sleep 10m
76360e73f2aSBram Moolenaar  endif
764*589edb34SBram Moolenaar
765a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
766*589edb34SBram Moolenaar  let at = empty(a:at) ?
767*589edb34SBram Moolenaar        \ fnameescape(expand('%:p')) . ':' . line('.') : a:at
768*589edb34SBram Moolenaar  call s:SendCommand('-break-insert ' . at)
76960e73f2aSBram Moolenaar  if do_continue
77060e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
77160e73f2aSBram Moolenaar  endif
772e09ba7baSBram Moolenaarendfunc
773e09ba7baSBram Moolenaar
77471137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
77571137fedSBram Moolenaarfunc s:ClearBreakpoint()
776e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
777e09ba7baSBram Moolenaar  let lnum = line('.')
77837402ed5SBram Moolenaar  let bploc = printf('%s:%d', fname, lnum)
77937402ed5SBram Moolenaar  if has_key(s:breakpoint_locations, bploc)
78037402ed5SBram Moolenaar    let idx = 0
78137402ed5SBram Moolenaar    for id in s:breakpoint_locations[bploc]
78237402ed5SBram Moolenaar      if has_key(s:breakpoints, id)
78337402ed5SBram Moolenaar	" Assume this always works, the reply is simply "^done".
78437402ed5SBram Moolenaar	call s:SendCommand('-break-delete ' . id)
78537402ed5SBram Moolenaar	for subid in keys(s:breakpoints[id])
78637402ed5SBram Moolenaar	  exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
78737402ed5SBram Moolenaar	endfor
78837402ed5SBram Moolenaar	unlet s:breakpoints[id]
78937402ed5SBram Moolenaar	unlet s:breakpoint_locations[bploc][idx]
790e09ba7baSBram Moolenaar	break
79137402ed5SBram Moolenaar      else
79237402ed5SBram Moolenaar	let idx += 1
793e09ba7baSBram Moolenaar      endif
794e09ba7baSBram Moolenaar    endfor
79537402ed5SBram Moolenaar    if empty(s:breakpoint_locations[bploc])
79637402ed5SBram Moolenaar      unlet s:breakpoint_locations[bploc]
79737402ed5SBram Moolenaar    endif
79837402ed5SBram Moolenaar  endif
799e09ba7baSBram Moolenaarendfunc
800e09ba7baSBram Moolenaar
80160e73f2aSBram Moolenaarfunc s:Run(args)
80260e73f2aSBram Moolenaar  if a:args != ''
80360e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
80460e73f2aSBram Moolenaar  endif
80560e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
80660e73f2aSBram Moolenaarendfunc
80760e73f2aSBram Moolenaar
80851b0f370SBram Moolenaarfunc s:SendEval(expr)
80951b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
81051b0f370SBram Moolenaar  let s:evalexpr = a:expr
81151b0f370SBram Moolenaarendfunc
81251b0f370SBram Moolenaar
81345d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
81445d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
81545d5f26dSBram Moolenaar  if a:arg != ''
81645d5f26dSBram Moolenaar    let expr = a:arg
81745d5f26dSBram Moolenaar  elseif a:range == 2
81845d5f26dSBram Moolenaar    let pos = getcurpos()
81945d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
82045d5f26dSBram Moolenaar    let regt = getregtype('v')
82145d5f26dSBram Moolenaar    normal! gv"vy
82245d5f26dSBram Moolenaar    let expr = @v
82345d5f26dSBram Moolenaar    call setpos('.', pos)
82445d5f26dSBram Moolenaar    call setreg('v', reg, regt)
82545d5f26dSBram Moolenaar  else
82645d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
82745d5f26dSBram Moolenaar  endif
82822f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
82951b0f370SBram Moolenaar  call s:SendEval(expr)
83045d5f26dSBram Moolenaarendfunc
83145d5f26dSBram Moolenaar
83222f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
83351b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
83451b0f370SBram Moolenaar
83545d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
83645d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
8371b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
8381b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
83951b0f370SBram Moolenaar  if s:evalFromBalloonExpr
84051b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
84151b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
84251b0f370SBram Moolenaar    else
84351b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
84451b0f370SBram Moolenaar    endif
84551b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
84651b0f370SBram Moolenaar  else
8471b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
84851b0f370SBram Moolenaar  endif
8491b9645deSBram Moolenaar
8507f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
8511b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
85222f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
85351b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
85451b0f370SBram Moolenaar  else
85551b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
8561b9645deSBram Moolenaar  endif
85745d5f26dSBram Moolenaarendfunc
85845d5f26dSBram Moolenaar
85951b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
86051b0f370SBram Moolenaar" if there is any.
86151b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
862b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
863396e829fSBram Moolenaar    return ''
864b3307b5eSBram Moolenaar  endif
865b3307b5eSBram Moolenaar  if !s:stopped
866b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
867b3307b5eSBram Moolenaar    " mouse triggers a balloon.
868396e829fSBram Moolenaar    return ''
86951b0f370SBram Moolenaar  endif
87051b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
87151b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
87222f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
87322f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
87451b0f370SBram Moolenaar  return ''
87551b0f370SBram Moolenaarendfunc
87651b0f370SBram Moolenaar
87745d5f26dSBram Moolenaar" Handle an error.
87845d5f26dSBram Moolenaarfunc s:HandleError(msg)
87922f1d0e3SBram Moolenaar  if s:ignoreEvalError
88051b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
88122f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
88222f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
88351b0f370SBram Moolenaar    return
88451b0f370SBram Moolenaar  endif
88545d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
88645d5f26dSBram Moolenaarendfunc
88745d5f26dSBram Moolenaar
888b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
889b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
890c4b533e1SBram Moolenaar    new
891b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
892c4b533e1SBram Moolenaar    call s:InstallWinbar()
893c4b533e1SBram Moolenaar  endif
894c4b533e1SBram Moolenaarendfunc
895c4b533e1SBram Moolenaar
896e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
897e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
898e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
899fe386641SBram Moolenaar  let wid = win_getid(winnr())
900fe386641SBram Moolenaar
90160e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
9024551c0a9SBram Moolenaar    call ch_log('program stopped')
90360e73f2aSBram Moolenaar    let s:stopped = 1
90460e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
9054551c0a9SBram Moolenaar    call ch_log('program running')
90660e73f2aSBram Moolenaar    let s:stopped = 0
90760e73f2aSBram Moolenaar  endif
90860e73f2aSBram Moolenaar
909a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
910a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
911a15b0a93SBram Moolenaar  else
912a15b0a93SBram Moolenaar    let fname = ''
913a15b0a93SBram Moolenaar  endif
9141b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
915e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
916fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
9174551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
9181b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
919fe386641SBram Moolenaar	if &modified
920fe386641SBram Moolenaar	  " TODO: find existing window
921fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
922b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
923c4b533e1SBram Moolenaar	  call s:InstallWinbar()
924fe386641SBram Moolenaar	else
925fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
926fe386641SBram Moolenaar	endif
927fe386641SBram Moolenaar      endif
928fe386641SBram Moolenaar      exe lnum
92901164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
9301b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
931fe386641SBram Moolenaar      setlocal signcolumn=yes
932fe386641SBram Moolenaar    endif
9334551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
934fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
935fe386641SBram Moolenaar  endif
936fe386641SBram Moolenaar
937fe386641SBram Moolenaar  call win_gotoid(wid)
938e09ba7baSBram Moolenaarendfunc
939e09ba7baSBram Moolenaar
940de1a8314SBram Moolenaarlet s:BreakpointSigns = []
941a15b0a93SBram Moolenaar
94237402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid)
94337402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
94437402ed5SBram Moolenaar  if index(s:BreakpointSigns, nr) == -1
94537402ed5SBram Moolenaar    call add(s:BreakpointSigns, nr)
94637402ed5SBram Moolenaar    exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint"
947de1a8314SBram Moolenaar  endif
948de1a8314SBram Moolenaarendfunc
949de1a8314SBram Moolenaar
95037402ed5SBram Moolenaarfunc! s:SplitMsg(s)
95137402ed5SBram Moolenaar  return split(a:s, '{.\{-}}\zs')
9525378e1cfSBram Moolenaarendfunction
9535378e1cfSBram Moolenaar
954e09ba7baSBram Moolenaar" Handle setting a breakpoint
955e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
956e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
9576dccc962SBram Moolenaar  if a:msg !~ 'fullname='
9586dccc962SBram Moolenaar    " a watch does not have a file name
9596dccc962SBram Moolenaar    return
9606dccc962SBram Moolenaar  endif
9615378e1cfSBram Moolenaar  for msg in s:SplitMsg(a:msg)
9625378e1cfSBram Moolenaar    let fname = s:GetFullname(msg)
9635378e1cfSBram Moolenaar    if empty(fname)
9645378e1cfSBram Moolenaar      continue
9655378e1cfSBram Moolenaar    endif
9665378e1cfSBram Moolenaar    let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '')
9675378e1cfSBram Moolenaar    if empty(nr)
968e09ba7baSBram Moolenaar      return
969fe386641SBram Moolenaar    endif
970e09ba7baSBram Moolenaar
97137402ed5SBram Moolenaar    " If "nr" is 123 it becomes "123.0" and subid is "0".
97237402ed5SBram Moolenaar    " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded.
97337402ed5SBram Moolenaar    let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0')
97437402ed5SBram Moolenaar    call s:CreateBreakpoint(id, subid)
97537402ed5SBram Moolenaar
97637402ed5SBram Moolenaar    if has_key(s:breakpoints, id)
97737402ed5SBram Moolenaar      let entries = s:breakpoints[id]
97837402ed5SBram Moolenaar    else
97937402ed5SBram Moolenaar      let entries = {}
98037402ed5SBram Moolenaar      let s:breakpoints[id] = entries
98137402ed5SBram Moolenaar    endif
98237402ed5SBram Moolenaar    if has_key(entries, subid)
98337402ed5SBram Moolenaar      let entry = entries[subid]
984e09ba7baSBram Moolenaar    else
985e09ba7baSBram Moolenaar      let entry = {}
98637402ed5SBram Moolenaar      let entries[subid] = entry
987fe386641SBram Moolenaar    endif
988e09ba7baSBram Moolenaar
9895378e1cfSBram Moolenaar    let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
990e09ba7baSBram Moolenaar    let entry['fname'] = fname
991e09ba7baSBram Moolenaar    let entry['lnum'] = lnum
9921b9645deSBram Moolenaar
99337402ed5SBram Moolenaar    let bploc = printf('%s:%d', fname, lnum)
99437402ed5SBram Moolenaar    if !has_key(s:breakpoint_locations, bploc)
99537402ed5SBram Moolenaar      let s:breakpoint_locations[bploc] = []
99637402ed5SBram Moolenaar    endif
99737402ed5SBram Moolenaar    let s:breakpoint_locations[bploc] += [id]
99837402ed5SBram Moolenaar
9991b9645deSBram Moolenaar    if bufloaded(fname)
100037402ed5SBram Moolenaar      call s:PlaceSign(id, subid, entry)
10011b9645deSBram Moolenaar    endif
10025378e1cfSBram Moolenaar  endfor
10031b9645deSBram Moolenaarendfunc
10041b9645deSBram Moolenaar
100537402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry)
100637402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
100737402ed5SBram Moolenaar  exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname']
10081b9645deSBram Moolenaar  let a:entry['placed'] = 1
1009e09ba7baSBram Moolenaarendfunc
1010e09ba7baSBram Moolenaar
1011e09ba7baSBram Moolenaar" Handle deleting a breakpoint
1012e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
1013e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
101437402ed5SBram Moolenaar  let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
101537402ed5SBram Moolenaar  if empty(id)
1016e09ba7baSBram Moolenaar    return
1017e09ba7baSBram Moolenaar  endif
101837402ed5SBram Moolenaar  if has_key(s:breakpoints, id)
101937402ed5SBram Moolenaar    for [subid, entry] in items(s:breakpoints[id])
10201b9645deSBram Moolenaar      if has_key(entry, 'placed')
102137402ed5SBram Moolenaar	exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
10221b9645deSBram Moolenaar	unlet entry['placed']
10231b9645deSBram Moolenaar      endif
10245378e1cfSBram Moolenaar    endfor
102537402ed5SBram Moolenaar    unlet s:breakpoints[id]
102637402ed5SBram Moolenaar  endif
1027c572da5fSBram Moolenaarendfunc
10281b9645deSBram Moolenaar
10294551c0a9SBram Moolenaar" Handle the debugged program starting to run.
10304551c0a9SBram Moolenaar" Will store the process ID in s:pid
10314551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
10324551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
10334551c0a9SBram Moolenaar  if nr == 0
10344551c0a9SBram Moolenaar    return
10354551c0a9SBram Moolenaar  endif
10364551c0a9SBram Moolenaar  let s:pid = nr
10374551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
10384551c0a9SBram Moolenaarendfunc
10394551c0a9SBram Moolenaar
10401b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
10411b9645deSBram Moolenaarfunc s:BufRead()
10421b9645deSBram Moolenaar  let fname = expand('<afile>:p')
104337402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
104437402ed5SBram Moolenaar    for [subid, entry] in items(entries)
10451b9645deSBram Moolenaar      if entry['fname'] == fname
104637402ed5SBram Moolenaar	call s:PlaceSign(id, subid, entry)
10471b9645deSBram Moolenaar      endif
10481b9645deSBram Moolenaar    endfor
104937402ed5SBram Moolenaar  endfor
10501b9645deSBram Moolenaarendfunc
10511b9645deSBram Moolenaar
10521b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
10531b9645deSBram Moolenaarfunc s:BufUnloaded()
10541b9645deSBram Moolenaar  let fname = expand('<afile>:p')
105537402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
105637402ed5SBram Moolenaar    for [subid, entry] in items(entries)
10571b9645deSBram Moolenaar      if entry['fname'] == fname
10581b9645deSBram Moolenaar	let entry['placed'] = 0
10591b9645deSBram Moolenaar      endif
10601b9645deSBram Moolenaar    endfor
106137402ed5SBram Moolenaar  endfor
10621b9645deSBram Moolenaarendfunc
1063ca4cc018SBram Moolenaar
1064ca4cc018SBram Moolenaarlet &cpo = s:keepcpo
1065ca4cc018SBram Moolenaarunlet s:keepcpo
1066