1fe386641SBram Moolenaar" Debugger plugin using gdb.
2c572da5fSBram Moolenaar"
3b3307b5eSBram Moolenaar" Author: Bram Moolenaar
4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license"
5*469bdbdeSBram Moolenaar" Last Change: 2019 Dec 11
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".
6818223a59SBram Moolenaarif !exists('g:termdebugger')
6918223a59SBram Moolenaar  let g: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')
10718223a59SBram Moolenaar    echoerr 'Terminal debugger already running, cannot run two'
108b3623a38SBram Moolenaar    return
109b3623a38SBram Moolenaar  endif
11018223a59SBram Moolenaar  if !executable(g:termdebugger)
11118223a59SBram Moolenaar    echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"'
11218223a59SBram Moolenaar    return
11318223a59SBram Moolenaar  endif
11418223a59SBram Moolenaar
115b3307b5eSBram Moolenaar  let s:ptywin = 0
1164551c0a9SBram Moolenaar  let s:pid = 0
117b3623a38SBram Moolenaar
118b3307b5eSBram Moolenaar  " Uncomment this line to write logging in "debuglog".
119b3307b5eSBram Moolenaar  " call ch_logfile('debuglog', 'w')
120b3307b5eSBram Moolenaar
121b3307b5eSBram Moolenaar  let s:sourcewin = win_getid(winnr())
122fe386641SBram Moolenaar  let s:startsigncolumn = &signcolumn
123fe386641SBram Moolenaar
12424a98a0eSBram Moolenaar  let s:save_columns = 0
12568e6560bSBram Moolenaar  let s:allleft = 0
12624a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
12724a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
12838baa3e6SBram Moolenaar      let s:save_columns = &columns
12938baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
13068e6560bSBram Moolenaar      " If we make the Vim window wider, use the whole left halve for the debug
13168e6560bSBram Moolenaar      " windows.
13268e6560bSBram Moolenaar      let s:allleft = 1
13324a98a0eSBram Moolenaar    endif
134b3307b5eSBram Moolenaar    let s:vertical = 1
13538baa3e6SBram Moolenaar  else
136b3307b5eSBram Moolenaar    let s:vertical = 0
13738baa3e6SBram Moolenaar  endif
13838baa3e6SBram Moolenaar
139b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
140b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
141b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
142b3307b5eSBram Moolenaar    let s:way = 'terminal'
143b3307b5eSBram Moolenaar  else
144b3307b5eSBram Moolenaar    let s:way = 'prompt'
145b3307b5eSBram Moolenaar  endif
146b3307b5eSBram Moolenaar
147b3307b5eSBram Moolenaar  if s:way == 'prompt'
148b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
149b3307b5eSBram Moolenaar  else
150b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
151b3307b5eSBram Moolenaar  endif
152b3307b5eSBram Moolenaarendfunc
153b3307b5eSBram Moolenaar
154ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended.
155ef3c6a5bSBram Moolenaarfunc s:CloseBuffers()
156ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
157ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:commbuf
158ef3c6a5bSBram Moolenaar  unlet! s:gdbwin
159ef3c6a5bSBram Moolenaarendfunc
160ef3c6a5bSBram Moolenaar
161b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
162b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
163fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
164b3307b5eSBram Moolenaar	\ 'term_name': 'debugged program',
165b3307b5eSBram Moolenaar	\ 'vertical': s:vertical,
166fe386641SBram Moolenaar	\ })
167fe386641SBram Moolenaar  if s:ptybuf == 0
168fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
169fe386641SBram Moolenaar    return
170fe386641SBram Moolenaar  endif
171fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
17245d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
173b3307b5eSBram Moolenaar  if s:vertical
17451b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
17551b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
17651b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
17768e6560bSBram Moolenaar    if s:allleft
17868e6560bSBram Moolenaar      " use the whole left column
17968e6560bSBram Moolenaar      wincmd H
18068e6560bSBram Moolenaar    endif
18151b0f370SBram Moolenaar  endif
182fe386641SBram Moolenaar
183fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
184fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
185fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
186fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
187fe386641SBram Moolenaar	\ 'hidden': 1,
188fe386641SBram Moolenaar	\ })
189fe386641SBram Moolenaar  if s:commbuf == 0
190fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
191fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
192fe386641SBram Moolenaar    return
193fe386641SBram Moolenaar  endif
194fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
195c572da5fSBram Moolenaar
196c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
197c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
19832c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
19932c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
20032c67ba7SBram Moolenaar
20132c67ba7SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
202b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
20360e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
204fe386641SBram Moolenaar	\ 'term_finish': 'close',
205c572da5fSBram Moolenaar	\ })
20660e73f2aSBram Moolenaar  if s:gdbbuf == 0
207fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
208ef3c6a5bSBram Moolenaar    call s:CloseBuffers()
209fe386641SBram Moolenaar    return
210fe386641SBram Moolenaar  endif
21145d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
212fe386641SBram Moolenaar
21332c67ba7SBram Moolenaar  " Set arguments to be run
21432c67ba7SBram Moolenaar  if len(proc_args)
21532c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
21632c67ba7SBram Moolenaar  endif
21732c67ba7SBram Moolenaar
218fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
21960e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
22060e73f2aSBram Moolenaar
2213e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
2223e4b84d0SBram Moolenaar  " why the debugger doesn't work.
2233e4b84d0SBram Moolenaar  let try_count = 0
2243e4b84d0SBram Moolenaar  while 1
225ef3c6a5bSBram Moolenaar    let gdbproc = term_getjob(s:gdbbuf)
226ef3c6a5bSBram Moolenaar    if gdbproc == v:null || job_status(gdbproc) !=# 'run'
227ef3c6a5bSBram Moolenaar      echoerr string(g:termdebugger) . ' exited unexpectedly'
228ef3c6a5bSBram Moolenaar      call s:CloseBuffers()
229ef3c6a5bSBram Moolenaar      return
230ef3c6a5bSBram Moolenaar    endif
231ef3c6a5bSBram Moolenaar
2323e4b84d0SBram Moolenaar    let response = ''
233b3623a38SBram Moolenaar    for lnum in range(1, 200)
23419c8fe19SBram Moolenaar      let line1 = term_getline(s:gdbbuf, lnum)
23519c8fe19SBram Moolenaar      let line2 = term_getline(s:gdbbuf, lnum + 1)
23619c8fe19SBram Moolenaar      if line1 =~ 'new-ui mi '
237f63db65bSBram Moolenaar	" response can be in the same line or the next line
23819c8fe19SBram Moolenaar	let response = line1 . line2
2393e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
240f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
241ef3c6a5bSBram Moolenaar	  call s:CloseBuffers()
2423e4b84d0SBram Moolenaar	  return
2433e4b84d0SBram Moolenaar	endif
2443e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
2453e4b84d0SBram Moolenaar	  " Success!
2463e4b84d0SBram Moolenaar	  break
2473e4b84d0SBram Moolenaar	endif
24819c8fe19SBram Moolenaar      elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi '
24919c8fe19SBram Moolenaar	" Reading symbols might take a while, try more times
25006fe74aeSBram Moolenaar	let try_count -= 1
25106fe74aeSBram Moolenaar      endif
2523e4b84d0SBram Moolenaar    endfor
2533e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2543e4b84d0SBram Moolenaar      break
2553e4b84d0SBram Moolenaar    endif
2563e4b84d0SBram Moolenaar    let try_count += 1
2573e4b84d0SBram Moolenaar    if try_count > 100
2583e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2593e4b84d0SBram Moolenaar      break
2603e4b84d0SBram Moolenaar    endif
2613e4b84d0SBram Moolenaar    sleep 10m
2623e4b84d0SBram Moolenaar  endwhile
2633e4b84d0SBram Moolenaar
26491359014SBram Moolenaar  " Interpret commands while the target is running.  This should usually only be
26560e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
26660e73f2aSBram Moolenaar  " running.
26760e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
268b3307b5eSBram Moolenaar  " Older gdb uses a different command.
269b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
270e09ba7baSBram Moolenaar
271f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
272f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
273b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
274f3ba14ffSBram Moolenaar
275ef3c6a5bSBram Moolenaar  call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')})
276b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
277b3307b5eSBram Moolenaarendfunc
278b3307b5eSBram Moolenaar
279b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
280b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
281b3307b5eSBram Moolenaar  if s:vertical
282b3307b5eSBram Moolenaar    vertical new
283b3307b5eSBram Moolenaar  else
284b3307b5eSBram Moolenaar    new
285b3307b5eSBram Moolenaar  endif
286b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
287b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
288b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
289b3307b5eSBram Moolenaar  set buftype=prompt
290b3307b5eSBram Moolenaar  file gdb
291b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
292b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
293b3307b5eSBram Moolenaar
294b3307b5eSBram Moolenaar  if s:vertical
295b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
296b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
297b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
298b3307b5eSBram Moolenaar  endif
299b3307b5eSBram Moolenaar
300b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
301b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
302b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
303b3307b5eSBram Moolenaar
304b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
305b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
306b3307b5eSBram Moolenaar
307b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
308b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
309b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
310b3307b5eSBram Moolenaar	\ })
311b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
312b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
313b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
314b3307b5eSBram Moolenaar    return
315b3307b5eSBram Moolenaar  endif
3164551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
3174551c0a9SBram Moolenaar  set modified
318b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
319b3307b5eSBram Moolenaar
32091359014SBram Moolenaar  " Interpret commands while the target is running.  This should usually only
321b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
322b3307b5eSBram Moolenaar  " target is running.
323b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
324b3307b5eSBram Moolenaar  " Older gdb uses a different command.
325b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
326b3307b5eSBram Moolenaar
327b3307b5eSBram Moolenaar  let s:ptybuf = 0
328b3307b5eSBram Moolenaar  if has('win32')
329b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
330b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
331b3307b5eSBram Moolenaar  elseif has('terminal')
332b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
333b3307b5eSBram Moolenaar    " gdb window.
334b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
335b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
336b3307b5eSBram Moolenaar	  \ })
337b3307b5eSBram Moolenaar    if s:ptybuf == 0
338b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
339b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
340b3307b5eSBram Moolenaar      return
341b3307b5eSBram Moolenaar    endif
342b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
343b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
344b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
345b3307b5eSBram Moolenaar
346b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
347b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
348b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
349b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
350b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
351b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
352b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
353b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
354b3307b5eSBram Moolenaar  else
355b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
356b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
357b3307b5eSBram Moolenaar  endif
358b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
359b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
360b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
361b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
362b3307b5eSBram Moolenaar
363b3307b5eSBram Moolenaar  " Set arguments to be run
364b3307b5eSBram Moolenaar  if len(proc_args)
365b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
366b3307b5eSBram Moolenaar  endif
367b3307b5eSBram Moolenaar
368b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
369b3307b5eSBram Moolenaar  startinsert
370b3307b5eSBram Moolenaarendfunc
371b3307b5eSBram Moolenaar
372b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
37338baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
37438baa3e6SBram Moolenaar  " There can be only one.
37538baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
37638baa3e6SBram Moolenaar
37745d5f26dSBram Moolenaar  " Install debugger commands in the text window.
378b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
379e09ba7baSBram Moolenaar  call s:InstallCommands()
38045d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
381e09ba7baSBram Moolenaar
38251b0f370SBram Moolenaar  " Enable showing a balloon with eval info
383246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
384246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
38551b0f370SBram Moolenaar    if has("balloon_eval")
38651b0f370SBram Moolenaar      set ballooneval
387246fe03dSBram Moolenaar    endif
38851b0f370SBram Moolenaar    if has("balloon_eval_term")
38951b0f370SBram Moolenaar      set balloonevalterm
39051b0f370SBram Moolenaar    endif
39151b0f370SBram Moolenaar  endif
39251b0f370SBram Moolenaar
3935378e1cfSBram Moolenaar  " Contains breakpoints that have been placed, key is a string with the GDB
3945378e1cfSBram Moolenaar  " breakpoint number.
39537402ed5SBram Moolenaar  " Each entry is a dict, containing the sub-breakpoints.  Key is the subid.
39637402ed5SBram Moolenaar  " For a breakpoint that is just a number the subid is zero.
39737402ed5SBram Moolenaar  " For a breakpoint "123.4" the id is "123" and subid is "4".
39837402ed5SBram Moolenaar  " Example, when breakpoint "44", "123", "123.1" and "123.2" exist:
39937402ed5SBram Moolenaar  " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}}
400e09ba7baSBram Moolenaar  let s:breakpoints = {}
4011b9645deSBram Moolenaar
40237402ed5SBram Moolenaar  " Contains breakpoints by file/lnum.  The key is "fname:lnum".
40337402ed5SBram Moolenaar  " Each entry is a list of breakpoint IDs at that position.
40437402ed5SBram Moolenaar  let s:breakpoint_locations = {}
40537402ed5SBram Moolenaar
4061b9645deSBram Moolenaar  augroup TermDebug
4071b9645deSBram Moolenaar    au BufRead * call s:BufRead()
4081b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
409f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
4101b9645deSBram Moolenaar  augroup END
41132c67ba7SBram Moolenaar
412b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
413b3307b5eSBram Moolenaar  " window.
41432c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
41532c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
41632c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
41732c67ba7SBram Moolenaar  endif
418c572da5fSBram Moolenaarendfunc
419c572da5fSBram Moolenaar
420b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
421b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
422b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
423b3307b5eSBram Moolenaar  if s:way == 'prompt'
424b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
425b3307b5eSBram Moolenaar  else
426b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
427b3307b5eSBram Moolenaar  endif
428b3307b5eSBram Moolenaarendfunc
429b3307b5eSBram Moolenaar
430b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
431b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
432b3307b5eSBram Moolenaar  if s:way == 'prompt'
433b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
434b3307b5eSBram Moolenaar  else
435b3307b5eSBram Moolenaar    let do_continue = 0
436b3307b5eSBram Moolenaar    if !s:stopped
437b3307b5eSBram Moolenaar      let do_continue = 1
438b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
439b3307b5eSBram Moolenaar      sleep 10m
440b3307b5eSBram Moolenaar    endif
441b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
442b3307b5eSBram Moolenaar    if do_continue
443b3307b5eSBram Moolenaar      Continue
444b3307b5eSBram Moolenaar    endif
445b3307b5eSBram Moolenaar  endif
446b3307b5eSBram Moolenaarendfunc
447b3307b5eSBram Moolenaar
448b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
449b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
450b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
451b3307b5eSBram Moolenaarendfunc
452b3307b5eSBram Moolenaar
4534551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
4544551c0a9SBram Moolenaar" breakpoint.
455b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
4562ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
4572ed890f1SBram Moolenaar  if has('win32')
4582ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
4592ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
4604551c0a9SBram Moolenaar    if s:pid == 0
4614551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
4624551c0a9SBram Moolenaar    else
4634551c0a9SBram Moolenaar      call debugbreak(s:pid)
4644551c0a9SBram Moolenaar    endif
4652ed890f1SBram Moolenaar  else
4662ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
4672ed890f1SBram Moolenaar  endif
468b3307b5eSBram Moolenaarendfunc
469b3307b5eSBram Moolenaar
470b3307b5eSBram Moolenaar" Function called when gdb outputs text.
471b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
472b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
473b3307b5eSBram Moolenaar
474b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
475b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
476a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
477b3307b5eSBram Moolenaar    return
478b3307b5eSBram Moolenaar  endif
479b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
480b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
481b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
482b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
483b3307b5eSBram Moolenaar      unlet s:evalexpr
484b3307b5eSBram Moolenaar      return
485b3307b5eSBram Moolenaar    endif
486b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
487b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
488b3307b5eSBram Moolenaar  else
489b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
490b3307b5eSBram Moolenaar    return
491b3307b5eSBram Moolenaar  endif
492b3307b5eSBram Moolenaar
493b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
494b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
495b3307b5eSBram Moolenaar
496b3307b5eSBram Moolenaar  " Add the output above the current prompt.
497b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
4984551c0a9SBram Moolenaar  set modified
499b3307b5eSBram Moolenaar
500b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
501b3307b5eSBram Moolenaarendfunc
502b3307b5eSBram Moolenaar
503b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
504b3307b5eSBram Moolenaar" to the next ", unescaping characters.
505b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
506b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
507a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
508b3307b5eSBram Moolenaar    return
509b3307b5eSBram Moolenaar  endif
510b3307b5eSBram Moolenaar  let result = ''
511b3307b5eSBram Moolenaar  let i = 1
512b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
513b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
514b3307b5eSBram Moolenaar      let i += 1
515b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
516b3307b5eSBram Moolenaar	" drop \n
517b3307b5eSBram Moolenaar	let i += 1
518b3307b5eSBram Moolenaar	continue
519589edb34SBram Moolenaar      elseif a:quotedText[i] == 't'
520589edb34SBram Moolenaar	" append \t
521589edb34SBram Moolenaar	let i += 1
522589edb34SBram Moolenaar	let result .= "\t"
523589edb34SBram Moolenaar	continue
524b3307b5eSBram Moolenaar      endif
525b3307b5eSBram Moolenaar    endif
526b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
527b3307b5eSBram Moolenaar    let i += 1
528b3307b5eSBram Moolenaar  endwhile
529b3307b5eSBram Moolenaar  return result
530b3307b5eSBram Moolenaarendfunc
531b3307b5eSBram Moolenaar
532a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
533a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
5345378e1cfSBram Moolenaar  if a:msg !~ 'fullname'
5355378e1cfSBram Moolenaar    return ''
5365378e1cfSBram Moolenaar  endif
537a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
538a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
539a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
540a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
541a15b0a93SBram Moolenaar  endif
542a15b0a93SBram Moolenaar  return name
543a15b0a93SBram Moolenaarendfunc
544a15b0a93SBram Moolenaar
545b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
546fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
547b3623a38SBram Moolenaar  unlet s:gdbwin
548e09ba7baSBram Moolenaar
549b3307b5eSBram Moolenaar  call s:EndDebugCommon()
550b3307b5eSBram Moolenaarendfunc
551b3307b5eSBram Moolenaar
552b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
553e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
554e09ba7baSBram Moolenaar
555b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
556b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
557b3307b5eSBram Moolenaar  endif
558b3307b5eSBram Moolenaar
559b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
560e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
561e09ba7baSBram Moolenaar  call s:DeleteCommands()
562e09ba7baSBram Moolenaar
563e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
564b3307b5eSBram Moolenaar
56538baa3e6SBram Moolenaar  if s:save_columns > 0
56638baa3e6SBram Moolenaar    let &columns = s:save_columns
56738baa3e6SBram Moolenaar  endif
5681b9645deSBram Moolenaar
569246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
570246fe03dSBram Moolenaar    set balloonexpr=
57151b0f370SBram Moolenaar    if has("balloon_eval")
57251b0f370SBram Moolenaar      set noballooneval
573246fe03dSBram Moolenaar    endif
57451b0f370SBram Moolenaar    if has("balloon_eval_term")
57551b0f370SBram Moolenaar      set noballoonevalterm
57651b0f370SBram Moolenaar    endif
57751b0f370SBram Moolenaar  endif
57851b0f370SBram Moolenaar
5791b9645deSBram Moolenaar  au! TermDebug
580fe386641SBram Moolenaarendfunc
581fe386641SBram Moolenaar
582b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
583b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
584b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
5854551c0a9SBram Moolenaar  set nomodified
586b3307b5eSBram Moolenaar  close
587b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
588b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
589b3307b5eSBram Moolenaar  endif
590b3307b5eSBram Moolenaar
591b3307b5eSBram Moolenaar  call s:EndDebugCommon()
592b3307b5eSBram Moolenaar  unlet s:gdbwin
593b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
594b3307b5eSBram Moolenaarendfunc
595b3307b5eSBram Moolenaar
596fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
597fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
598fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
599fe386641SBram Moolenaar
600fe386641SBram Moolenaar  for msg in msgs
601fe386641SBram Moolenaar    " remove prefixed NL
602fe386641SBram Moolenaar    if msg[0] == "\n"
603fe386641SBram Moolenaar      let msg = msg[1:]
604fe386641SBram Moolenaar    endif
605fe386641SBram Moolenaar    if msg != ''
6061b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
607e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
60845d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
609e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
610e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
611e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
6124551c0a9SBram Moolenaar      elseif msg =~ '^=thread-group-started'
6134551c0a9SBram Moolenaar	call s:HandleProgramRun(msg)
61445d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
61545d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
61645d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
61745d5f26dSBram Moolenaar	call s:HandleError(msg)
618e09ba7baSBram Moolenaar      endif
619e09ba7baSBram Moolenaar    endif
620e09ba7baSBram Moolenaar  endfor
621e09ba7baSBram Moolenaarendfunc
622e09ba7baSBram Moolenaar
623589edb34SBram Moolenaarfunc s:GotoProgram()
624589edb34SBram Moolenaar  if has('win32')
625589edb34SBram Moolenaar    if executable('powershell')
626589edb34SBram Moolenaar      call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid))
627589edb34SBram Moolenaar    endif
628589edb34SBram Moolenaar  else
629*469bdbdeSBram Moolenaar    call win_gotoid(s:ptywin)
630589edb34SBram Moolenaar  endif
631589edb34SBram Moolenaarendfunc
632589edb34SBram Moolenaar
633e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
634e09ba7baSBram Moolenaarfunc s:InstallCommands()
635963c1ad5SBram Moolenaar  let save_cpo = &cpo
636963c1ad5SBram Moolenaar  set cpo&vim
637963c1ad5SBram Moolenaar
638589edb34SBram Moolenaar  command -nargs=? Break call s:SetBreakpoint(<q-args>)
63971137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
640e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
64145d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
642e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
64360e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
64460e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
64560e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
646b3307b5eSBram Moolenaar
647b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
648b3307b5eSBram Moolenaar  if s:way == 'prompt'
649b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
650b3307b5eSBram Moolenaar  else
651b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
652b3307b5eSBram Moolenaar  endif
653b3307b5eSBram Moolenaar
65445d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
65545d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
656589edb34SBram Moolenaar  command Program call s:GotoProgram()
657b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
65871137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
65945d5f26dSBram Moolenaar
66045d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
66145d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
6621b9645deSBram Moolenaar
663f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
66471137fedSBram Moolenaar    call s:InstallWinbar()
66571137fedSBram Moolenaar
66671137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
66771137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
66871137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
66971137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
67071137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
67171137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
67271137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
67371137fedSBram Moolenaar    endif
67471137fedSBram Moolenaar  endif
675963c1ad5SBram Moolenaar
676963c1ad5SBram Moolenaar  let &cpo = save_cpo
67771137fedSBram Moolenaarendfunc
67871137fedSBram Moolenaar
67971137fedSBram Moolenaarlet s:winbar_winids = []
68071137fedSBram Moolenaar
68171137fedSBram Moolenaar" Install the window toolbar in the current window.
68271137fedSBram Moolenaarfunc s:InstallWinbar()
683c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
68424a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
68524a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
68624a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
68724a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
68860e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
68924a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
69071137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
691c4b533e1SBram Moolenaar  endif
692e09ba7baSBram Moolenaarendfunc
693e09ba7baSBram Moolenaar
694e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
695e09ba7baSBram Moolenaarfunc s:DeleteCommands()
696e09ba7baSBram Moolenaar  delcommand Break
69771137fedSBram Moolenaar  delcommand Clear
698e09ba7baSBram Moolenaar  delcommand Step
69945d5f26dSBram Moolenaar  delcommand Over
700e09ba7baSBram Moolenaar  delcommand Finish
70160e73f2aSBram Moolenaar  delcommand Run
70260e73f2aSBram Moolenaar  delcommand Arguments
70360e73f2aSBram Moolenaar  delcommand Stop
704e09ba7baSBram Moolenaar  delcommand Continue
70545d5f26dSBram Moolenaar  delcommand Evaluate
70645d5f26dSBram Moolenaar  delcommand Gdb
70745d5f26dSBram Moolenaar  delcommand Program
708b3623a38SBram Moolenaar  delcommand Source
70971137fedSBram Moolenaar  delcommand Winbar
71045d5f26dSBram Moolenaar
71145d5f26dSBram Moolenaar  nunmap K
7121b9645deSBram Moolenaar
7131b9645deSBram Moolenaar  if has('menu')
71471137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
71571137fedSBram Moolenaar    let curwinid = win_getid(winnr())
71671137fedSBram Moolenaar    for winid in s:winbar_winids
71771137fedSBram Moolenaar      if win_gotoid(winid)
7181b9645deSBram Moolenaar	aunmenu WinBar.Step
7191b9645deSBram Moolenaar	aunmenu WinBar.Next
7201b9645deSBram Moolenaar	aunmenu WinBar.Finish
7211b9645deSBram Moolenaar	aunmenu WinBar.Cont
72260e73f2aSBram Moolenaar	aunmenu WinBar.Stop
7231b9645deSBram Moolenaar	aunmenu WinBar.Eval
7241b9645deSBram Moolenaar      endif
72571137fedSBram Moolenaar    endfor
72671137fedSBram Moolenaar    call win_gotoid(curwinid)
72771137fedSBram Moolenaar    let s:winbar_winids = []
72871137fedSBram Moolenaar
72971137fedSBram Moolenaar    if exists('s:saved_mousemodel')
73071137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
73171137fedSBram Moolenaar      unlet s:saved_mousemodel
73271137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
73371137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
73471137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
73571137fedSBram Moolenaar      aunmenu PopUp.Evaluate
73671137fedSBram Moolenaar    endif
73771137fedSBram Moolenaar  endif
7381b9645deSBram Moolenaar
73945d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
74037402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
74137402ed5SBram Moolenaar    for subid in keys(entries)
74237402ed5SBram Moolenaar      exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
74337402ed5SBram Moolenaar    endfor
74445d5f26dSBram Moolenaar  endfor
74545d5f26dSBram Moolenaar  unlet s:breakpoints
74637402ed5SBram Moolenaar  unlet s:breakpoint_locations
747a15b0a93SBram Moolenaar
748a15b0a93SBram Moolenaar  sign undefine debugPC
749a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
750a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
751a15b0a93SBram Moolenaar  endfor
7524551c0a9SBram Moolenaar  let s:BreakpointSigns = []
753e09ba7baSBram Moolenaarendfunc
754e09ba7baSBram Moolenaar
755e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
756589edb34SBram Moolenaarfunc s:SetBreakpoint(at)
75760e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
75860e73f2aSBram Moolenaar  " Interrupt to make it work.
75960e73f2aSBram Moolenaar  let do_continue = 0
76060e73f2aSBram Moolenaar  if !s:stopped
76160e73f2aSBram Moolenaar    let do_continue = 1
762b3307b5eSBram Moolenaar    if s:way == 'prompt'
7634551c0a9SBram Moolenaar      call s:PromptInterrupt()
764b3307b5eSBram Moolenaar    else
76560e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
766b3307b5eSBram Moolenaar    endif
76760e73f2aSBram Moolenaar    sleep 10m
76860e73f2aSBram Moolenaar  endif
769589edb34SBram Moolenaar
770a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
771589edb34SBram Moolenaar  let at = empty(a:at) ?
772589edb34SBram Moolenaar        \ fnameescape(expand('%:p')) . ':' . line('.') : a:at
773589edb34SBram Moolenaar  call s:SendCommand('-break-insert ' . at)
77460e73f2aSBram Moolenaar  if do_continue
77560e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
77660e73f2aSBram Moolenaar  endif
777e09ba7baSBram Moolenaarendfunc
778e09ba7baSBram Moolenaar
77971137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
78071137fedSBram Moolenaarfunc s:ClearBreakpoint()
781e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
782e09ba7baSBram Moolenaar  let lnum = line('.')
78337402ed5SBram Moolenaar  let bploc = printf('%s:%d', fname, lnum)
78437402ed5SBram Moolenaar  if has_key(s:breakpoint_locations, bploc)
78537402ed5SBram Moolenaar    let idx = 0
78637402ed5SBram Moolenaar    for id in s:breakpoint_locations[bploc]
78737402ed5SBram Moolenaar      if has_key(s:breakpoints, id)
78837402ed5SBram Moolenaar	" Assume this always works, the reply is simply "^done".
78937402ed5SBram Moolenaar	call s:SendCommand('-break-delete ' . id)
79037402ed5SBram Moolenaar	for subid in keys(s:breakpoints[id])
79137402ed5SBram Moolenaar	  exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
79237402ed5SBram Moolenaar	endfor
79337402ed5SBram Moolenaar	unlet s:breakpoints[id]
79437402ed5SBram Moolenaar	unlet s:breakpoint_locations[bploc][idx]
795e09ba7baSBram Moolenaar	break
79637402ed5SBram Moolenaar      else
79737402ed5SBram Moolenaar	let idx += 1
798e09ba7baSBram Moolenaar      endif
799e09ba7baSBram Moolenaar    endfor
80037402ed5SBram Moolenaar    if empty(s:breakpoint_locations[bploc])
80137402ed5SBram Moolenaar      unlet s:breakpoint_locations[bploc]
80237402ed5SBram Moolenaar    endif
80337402ed5SBram Moolenaar  endif
804e09ba7baSBram Moolenaarendfunc
805e09ba7baSBram Moolenaar
80660e73f2aSBram Moolenaarfunc s:Run(args)
80760e73f2aSBram Moolenaar  if a:args != ''
80860e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
80960e73f2aSBram Moolenaar  endif
81060e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
81160e73f2aSBram Moolenaarendfunc
81260e73f2aSBram Moolenaar
81351b0f370SBram Moolenaarfunc s:SendEval(expr)
81451b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
81551b0f370SBram Moolenaar  let s:evalexpr = a:expr
81651b0f370SBram Moolenaarendfunc
81751b0f370SBram Moolenaar
81845d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
81945d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
82045d5f26dSBram Moolenaar  if a:arg != ''
82145d5f26dSBram Moolenaar    let expr = a:arg
82245d5f26dSBram Moolenaar  elseif a:range == 2
82345d5f26dSBram Moolenaar    let pos = getcurpos()
82445d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
82545d5f26dSBram Moolenaar    let regt = getregtype('v')
82645d5f26dSBram Moolenaar    normal! gv"vy
82745d5f26dSBram Moolenaar    let expr = @v
82845d5f26dSBram Moolenaar    call setpos('.', pos)
82945d5f26dSBram Moolenaar    call setreg('v', reg, regt)
83045d5f26dSBram Moolenaar  else
83145d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
83245d5f26dSBram Moolenaar  endif
83322f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
83451b0f370SBram Moolenaar  call s:SendEval(expr)
83545d5f26dSBram Moolenaarendfunc
83645d5f26dSBram Moolenaar
83722f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
83851b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
83951b0f370SBram Moolenaar
84045d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
84145d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
8421b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
8431b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
84451b0f370SBram Moolenaar  if s:evalFromBalloonExpr
84551b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
84651b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
84751b0f370SBram Moolenaar    else
84851b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
84951b0f370SBram Moolenaar    endif
85051b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
85151b0f370SBram Moolenaar  else
8521b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
85351b0f370SBram Moolenaar  endif
8541b9645deSBram Moolenaar
8557f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
8561b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
85722f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
85851b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
85951b0f370SBram Moolenaar  else
86051b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
8611b9645deSBram Moolenaar  endif
86245d5f26dSBram Moolenaarendfunc
86345d5f26dSBram Moolenaar
86451b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
86551b0f370SBram Moolenaar" if there is any.
86651b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
867b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
868396e829fSBram Moolenaar    return ''
869b3307b5eSBram Moolenaar  endif
870b3307b5eSBram Moolenaar  if !s:stopped
871b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
872b3307b5eSBram Moolenaar    " mouse triggers a balloon.
873396e829fSBram Moolenaar    return ''
87451b0f370SBram Moolenaar  endif
87551b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
87651b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
87722f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
87822f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
87951b0f370SBram Moolenaar  return ''
88051b0f370SBram Moolenaarendfunc
88151b0f370SBram Moolenaar
88245d5f26dSBram Moolenaar" Handle an error.
88345d5f26dSBram Moolenaarfunc s:HandleError(msg)
88422f1d0e3SBram Moolenaar  if s:ignoreEvalError
88551b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
88622f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
88722f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
88851b0f370SBram Moolenaar    return
88951b0f370SBram Moolenaar  endif
89045d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
89145d5f26dSBram Moolenaarendfunc
89245d5f26dSBram Moolenaar
893b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
894b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
895c4b533e1SBram Moolenaar    new
896b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
897c4b533e1SBram Moolenaar    call s:InstallWinbar()
898c4b533e1SBram Moolenaar  endif
899c4b533e1SBram Moolenaarendfunc
900c4b533e1SBram Moolenaar
901e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
902e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
903e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
904fe386641SBram Moolenaar  let wid = win_getid(winnr())
905fe386641SBram Moolenaar
90660e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
9074551c0a9SBram Moolenaar    call ch_log('program stopped')
90860e73f2aSBram Moolenaar    let s:stopped = 1
90960e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
9104551c0a9SBram Moolenaar    call ch_log('program running')
91160e73f2aSBram Moolenaar    let s:stopped = 0
91260e73f2aSBram Moolenaar  endif
91360e73f2aSBram Moolenaar
914a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
915a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
916a15b0a93SBram Moolenaar  else
917a15b0a93SBram Moolenaar    let fname = ''
918a15b0a93SBram Moolenaar  endif
9191b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
920e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
921fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
9224551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
9231b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
924fe386641SBram Moolenaar	if &modified
925fe386641SBram Moolenaar	  " TODO: find existing window
926fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
927b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
928c4b533e1SBram Moolenaar	  call s:InstallWinbar()
929fe386641SBram Moolenaar	else
930fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
931fe386641SBram Moolenaar	endif
932fe386641SBram Moolenaar      endif
933fe386641SBram Moolenaar      exe lnum
93401164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
9351b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
936fe386641SBram Moolenaar      setlocal signcolumn=yes
937fe386641SBram Moolenaar    endif
9384551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
939fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
940fe386641SBram Moolenaar  endif
941fe386641SBram Moolenaar
942fe386641SBram Moolenaar  call win_gotoid(wid)
943e09ba7baSBram Moolenaarendfunc
944e09ba7baSBram Moolenaar
945de1a8314SBram Moolenaarlet s:BreakpointSigns = []
946a15b0a93SBram Moolenaar
94737402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid)
94837402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
94937402ed5SBram Moolenaar  if index(s:BreakpointSigns, nr) == -1
95037402ed5SBram Moolenaar    call add(s:BreakpointSigns, nr)
95137402ed5SBram Moolenaar    exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint"
952de1a8314SBram Moolenaar  endif
953de1a8314SBram Moolenaarendfunc
954de1a8314SBram Moolenaar
95537402ed5SBram Moolenaarfunc! s:SplitMsg(s)
95637402ed5SBram Moolenaar  return split(a:s, '{.\{-}}\zs')
9575378e1cfSBram Moolenaarendfunction
9585378e1cfSBram Moolenaar
959e09ba7baSBram Moolenaar" Handle setting a breakpoint
960e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
961e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
9626dccc962SBram Moolenaar  if a:msg !~ 'fullname='
9636dccc962SBram Moolenaar    " a watch does not have a file name
9646dccc962SBram Moolenaar    return
9656dccc962SBram Moolenaar  endif
9665378e1cfSBram Moolenaar  for msg in s:SplitMsg(a:msg)
9675378e1cfSBram Moolenaar    let fname = s:GetFullname(msg)
9685378e1cfSBram Moolenaar    if empty(fname)
9695378e1cfSBram Moolenaar      continue
9705378e1cfSBram Moolenaar    endif
9715378e1cfSBram Moolenaar    let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '')
9725378e1cfSBram Moolenaar    if empty(nr)
973e09ba7baSBram Moolenaar      return
974fe386641SBram Moolenaar    endif
975e09ba7baSBram Moolenaar
97637402ed5SBram Moolenaar    " If "nr" is 123 it becomes "123.0" and subid is "0".
97737402ed5SBram Moolenaar    " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded.
97837402ed5SBram Moolenaar    let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0')
97937402ed5SBram Moolenaar    call s:CreateBreakpoint(id, subid)
98037402ed5SBram Moolenaar
98137402ed5SBram Moolenaar    if has_key(s:breakpoints, id)
98237402ed5SBram Moolenaar      let entries = s:breakpoints[id]
98337402ed5SBram Moolenaar    else
98437402ed5SBram Moolenaar      let entries = {}
98537402ed5SBram Moolenaar      let s:breakpoints[id] = entries
98637402ed5SBram Moolenaar    endif
98737402ed5SBram Moolenaar    if has_key(entries, subid)
98837402ed5SBram Moolenaar      let entry = entries[subid]
989e09ba7baSBram Moolenaar    else
990e09ba7baSBram Moolenaar      let entry = {}
99137402ed5SBram Moolenaar      let entries[subid] = entry
992fe386641SBram Moolenaar    endif
993e09ba7baSBram Moolenaar
9945378e1cfSBram Moolenaar    let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
995e09ba7baSBram Moolenaar    let entry['fname'] = fname
996e09ba7baSBram Moolenaar    let entry['lnum'] = lnum
9971b9645deSBram Moolenaar
99837402ed5SBram Moolenaar    let bploc = printf('%s:%d', fname, lnum)
99937402ed5SBram Moolenaar    if !has_key(s:breakpoint_locations, bploc)
100037402ed5SBram Moolenaar      let s:breakpoint_locations[bploc] = []
100137402ed5SBram Moolenaar    endif
100237402ed5SBram Moolenaar    let s:breakpoint_locations[bploc] += [id]
100337402ed5SBram Moolenaar
10041b9645deSBram Moolenaar    if bufloaded(fname)
100537402ed5SBram Moolenaar      call s:PlaceSign(id, subid, entry)
10061b9645deSBram Moolenaar    endif
10075378e1cfSBram Moolenaar  endfor
10081b9645deSBram Moolenaarendfunc
10091b9645deSBram Moolenaar
101037402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry)
101137402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
101237402ed5SBram Moolenaar  exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname']
10131b9645deSBram Moolenaar  let a:entry['placed'] = 1
1014e09ba7baSBram Moolenaarendfunc
1015e09ba7baSBram Moolenaar
1016e09ba7baSBram Moolenaar" Handle deleting a breakpoint
1017e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
1018e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
101937402ed5SBram Moolenaar  let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
102037402ed5SBram Moolenaar  if empty(id)
1021e09ba7baSBram Moolenaar    return
1022e09ba7baSBram Moolenaar  endif
102337402ed5SBram Moolenaar  if has_key(s:breakpoints, id)
102437402ed5SBram Moolenaar    for [subid, entry] in items(s:breakpoints[id])
10251b9645deSBram Moolenaar      if has_key(entry, 'placed')
102637402ed5SBram Moolenaar	exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
10271b9645deSBram Moolenaar	unlet entry['placed']
10281b9645deSBram Moolenaar      endif
10295378e1cfSBram Moolenaar    endfor
103037402ed5SBram Moolenaar    unlet s:breakpoints[id]
103137402ed5SBram Moolenaar  endif
1032c572da5fSBram Moolenaarendfunc
10331b9645deSBram Moolenaar
10344551c0a9SBram Moolenaar" Handle the debugged program starting to run.
10354551c0a9SBram Moolenaar" Will store the process ID in s:pid
10364551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
10374551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
10384551c0a9SBram Moolenaar  if nr == 0
10394551c0a9SBram Moolenaar    return
10404551c0a9SBram Moolenaar  endif
10414551c0a9SBram Moolenaar  let s:pid = nr
10424551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
10434551c0a9SBram Moolenaarendfunc
10444551c0a9SBram Moolenaar
10451b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
10461b9645deSBram Moolenaarfunc s:BufRead()
10471b9645deSBram Moolenaar  let fname = expand('<afile>:p')
104837402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
104937402ed5SBram Moolenaar    for [subid, entry] in items(entries)
10501b9645deSBram Moolenaar      if entry['fname'] == fname
105137402ed5SBram Moolenaar	call s:PlaceSign(id, subid, entry)
10521b9645deSBram Moolenaar      endif
10531b9645deSBram Moolenaar    endfor
105437402ed5SBram Moolenaar  endfor
10551b9645deSBram Moolenaarendfunc
10561b9645deSBram Moolenaar
10571b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
10581b9645deSBram Moolenaarfunc s:BufUnloaded()
10591b9645deSBram Moolenaar  let fname = expand('<afile>:p')
106037402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
106137402ed5SBram Moolenaar    for [subid, entry] in items(entries)
10621b9645deSBram Moolenaar      if entry['fname'] == fname
10631b9645deSBram Moolenaar	let entry['placed'] = 0
10641b9645deSBram Moolenaar      endif
10651b9645deSBram Moolenaar    endfor
106637402ed5SBram Moolenaar  endfor
10671b9645deSBram Moolenaarendfunc
1068ca4cc018SBram Moolenaar
1069ca4cc018SBram Moolenaarlet &cpo = s:keepcpo
1070ca4cc018SBram Moolenaarunlet s:keepcpo
1071