1fe386641SBram Moolenaar" Debugger plugin using gdb.
2c572da5fSBram Moolenaar"
3b3307b5eSBram Moolenaar" Author: Bram Moolenaar
4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license"
5*cb80aa2dSBram Moolenaar" Last Change: 2020 Oct 25
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())
122*cb80aa2dSBram Moolenaar
123*cb80aa2dSBram Moolenaar  " Remember the old value of 'signcolumn' for each buffer that it's set in, so
124*cb80aa2dSBram Moolenaar  " that we can restore the value for all buffers.
125*cb80aa2dSBram Moolenaar  let b:save_signcolumn = &signcolumn
126*cb80aa2dSBram Moolenaar  let s:signcolumn_buflist = [bufnr()]
127fe386641SBram Moolenaar
12824a98a0eSBram Moolenaar  let s:save_columns = 0
12968e6560bSBram Moolenaar  let s:allleft = 0
13024a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
13124a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
13238baa3e6SBram Moolenaar      let s:save_columns = &columns
13338baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
13468e6560bSBram Moolenaar      " If we make the Vim window wider, use the whole left halve for the debug
13568e6560bSBram Moolenaar      " windows.
13668e6560bSBram Moolenaar      let s:allleft = 1
13724a98a0eSBram Moolenaar    endif
138b3307b5eSBram Moolenaar    let s:vertical = 1
13938baa3e6SBram Moolenaar  else
140b3307b5eSBram Moolenaar    let s:vertical = 0
14138baa3e6SBram Moolenaar  endif
14238baa3e6SBram Moolenaar
143b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
144b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
145b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
146b3307b5eSBram Moolenaar    let s:way = 'terminal'
147b3307b5eSBram Moolenaar  else
148b3307b5eSBram Moolenaar    let s:way = 'prompt'
149b3307b5eSBram Moolenaar  endif
150b3307b5eSBram Moolenaar
151b3307b5eSBram Moolenaar  if s:way == 'prompt'
152b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
153b3307b5eSBram Moolenaar  else
154b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
155b3307b5eSBram Moolenaar  endif
156b3307b5eSBram Moolenaarendfunc
157b3307b5eSBram Moolenaar
158ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended.
159ef3c6a5bSBram Moolenaarfunc s:CloseBuffers()
160ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
161ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:commbuf
162ef3c6a5bSBram Moolenaar  unlet! s:gdbwin
163ef3c6a5bSBram Moolenaarendfunc
164ef3c6a5bSBram Moolenaar
165b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
166b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
167fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
168b3307b5eSBram Moolenaar	\ 'term_name': 'debugged program',
169b3307b5eSBram Moolenaar	\ 'vertical': s:vertical,
170fe386641SBram Moolenaar	\ })
171fe386641SBram Moolenaar  if s:ptybuf == 0
172fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
173fe386641SBram Moolenaar    return
174fe386641SBram Moolenaar  endif
175fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
17645d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
177b3307b5eSBram Moolenaar  if s:vertical
17851b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
17951b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
18051b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
18168e6560bSBram Moolenaar    if s:allleft
18268e6560bSBram Moolenaar      " use the whole left column
18368e6560bSBram Moolenaar      wincmd H
18468e6560bSBram Moolenaar    endif
18551b0f370SBram Moolenaar  endif
186fe386641SBram Moolenaar
187fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
188fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
189fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
190fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
191fe386641SBram Moolenaar	\ 'hidden': 1,
192fe386641SBram Moolenaar	\ })
193fe386641SBram Moolenaar  if s:commbuf == 0
194fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
195fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
196fe386641SBram Moolenaar    return
197fe386641SBram Moolenaar  endif
198fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
199c572da5fSBram Moolenaar
200c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
201c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
20232c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
20332c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
20432c67ba7SBram Moolenaar
20532c67ba7SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
206b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
20760e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
208fe386641SBram Moolenaar	\ 'term_finish': 'close',
209c572da5fSBram Moolenaar	\ })
21060e73f2aSBram Moolenaar  if s:gdbbuf == 0
211fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
212ef3c6a5bSBram Moolenaar    call s:CloseBuffers()
213fe386641SBram Moolenaar    return
214fe386641SBram Moolenaar  endif
21545d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
216fe386641SBram Moolenaar
21732c67ba7SBram Moolenaar  " Set arguments to be run
21832c67ba7SBram Moolenaar  if len(proc_args)
21932c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
22032c67ba7SBram Moolenaar  endif
22132c67ba7SBram Moolenaar
222fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
22360e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
22460e73f2aSBram Moolenaar
2253e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
2263e4b84d0SBram Moolenaar  " why the debugger doesn't work.
2273e4b84d0SBram Moolenaar  let try_count = 0
2283e4b84d0SBram Moolenaar  while 1
229ef3c6a5bSBram Moolenaar    let gdbproc = term_getjob(s:gdbbuf)
230ef3c6a5bSBram Moolenaar    if gdbproc == v:null || job_status(gdbproc) !=# 'run'
231ef3c6a5bSBram Moolenaar      echoerr string(g:termdebugger) . ' exited unexpectedly'
232ef3c6a5bSBram Moolenaar      call s:CloseBuffers()
233ef3c6a5bSBram Moolenaar      return
234ef3c6a5bSBram Moolenaar    endif
235ef3c6a5bSBram Moolenaar
2363e4b84d0SBram Moolenaar    let response = ''
237b3623a38SBram Moolenaar    for lnum in range(1, 200)
23819c8fe19SBram Moolenaar      let line1 = term_getline(s:gdbbuf, lnum)
23919c8fe19SBram Moolenaar      let line2 = term_getline(s:gdbbuf, lnum + 1)
24019c8fe19SBram Moolenaar      if line1 =~ 'new-ui mi '
241f63db65bSBram Moolenaar	" response can be in the same line or the next line
24219c8fe19SBram Moolenaar	let response = line1 . line2
2433e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
244f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
245ef3c6a5bSBram Moolenaar	  call s:CloseBuffers()
2463e4b84d0SBram Moolenaar	  return
2473e4b84d0SBram Moolenaar	endif
2483e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
2493e4b84d0SBram Moolenaar	  " Success!
2503e4b84d0SBram Moolenaar	  break
2513e4b84d0SBram Moolenaar	endif
25219c8fe19SBram Moolenaar      elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi '
25319c8fe19SBram Moolenaar	" Reading symbols might take a while, try more times
25406fe74aeSBram Moolenaar	let try_count -= 1
25506fe74aeSBram Moolenaar      endif
2563e4b84d0SBram Moolenaar    endfor
2573e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2583e4b84d0SBram Moolenaar      break
2593e4b84d0SBram Moolenaar    endif
2603e4b84d0SBram Moolenaar    let try_count += 1
2613e4b84d0SBram Moolenaar    if try_count > 100
2623e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2633e4b84d0SBram Moolenaar      break
2643e4b84d0SBram Moolenaar    endif
2653e4b84d0SBram Moolenaar    sleep 10m
2663e4b84d0SBram Moolenaar  endwhile
2673e4b84d0SBram Moolenaar
26891359014SBram Moolenaar  " Interpret commands while the target is running.  This should usually only be
26960e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
27060e73f2aSBram Moolenaar  " running.
27160e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
272b3307b5eSBram Moolenaar  " Older gdb uses a different command.
273b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
274e09ba7baSBram Moolenaar
275f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
276f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
277b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
278f3ba14ffSBram Moolenaar
279ef3c6a5bSBram Moolenaar  call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')})
280b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
281b3307b5eSBram Moolenaarendfunc
282b3307b5eSBram Moolenaar
283b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
284b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
285b3307b5eSBram Moolenaar  if s:vertical
286b3307b5eSBram Moolenaar    vertical new
287b3307b5eSBram Moolenaar  else
288b3307b5eSBram Moolenaar    new
289b3307b5eSBram Moolenaar  endif
290b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
291b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
292b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
293b3307b5eSBram Moolenaar  set buftype=prompt
294b3307b5eSBram Moolenaar  file gdb
295b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
296b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
297b3307b5eSBram Moolenaar
298b3307b5eSBram Moolenaar  if s:vertical
299b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
300b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
301b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
302b3307b5eSBram Moolenaar  endif
303b3307b5eSBram Moolenaar
304b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
305b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
306b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
307b3307b5eSBram Moolenaar
308b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
309b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
310b3307b5eSBram Moolenaar
311b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
312b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
313b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
314b3307b5eSBram Moolenaar	\ })
315b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
316b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
317b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
318b3307b5eSBram Moolenaar    return
319b3307b5eSBram Moolenaar  endif
3204551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
3214551c0a9SBram Moolenaar  set modified
322b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
323b3307b5eSBram Moolenaar
32491359014SBram Moolenaar  " Interpret commands while the target is running.  This should usually only
325b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
326b3307b5eSBram Moolenaar  " target is running.
327b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
328b3307b5eSBram Moolenaar  " Older gdb uses a different command.
329b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
330b3307b5eSBram Moolenaar
331b3307b5eSBram Moolenaar  let s:ptybuf = 0
332b3307b5eSBram Moolenaar  if has('win32')
333b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
334b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
335b3307b5eSBram Moolenaar  elseif has('terminal')
336b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
337b3307b5eSBram Moolenaar    " gdb window.
338b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
339b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
340b3307b5eSBram Moolenaar	  \ })
341b3307b5eSBram Moolenaar    if s:ptybuf == 0
342b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
343b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
344b3307b5eSBram Moolenaar      return
345b3307b5eSBram Moolenaar    endif
346b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
347b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
348b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
349b3307b5eSBram Moolenaar
350b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
351b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
352b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
353b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
354b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
355b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
356b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
357b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
358b3307b5eSBram Moolenaar  else
359b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
360b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
361b3307b5eSBram Moolenaar  endif
362b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
363b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
364b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
365b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
366b3307b5eSBram Moolenaar
367b3307b5eSBram Moolenaar  " Set arguments to be run
368b3307b5eSBram Moolenaar  if len(proc_args)
369b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
370b3307b5eSBram Moolenaar  endif
371b3307b5eSBram Moolenaar
372b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
373b3307b5eSBram Moolenaar  startinsert
374b3307b5eSBram Moolenaarendfunc
375b3307b5eSBram Moolenaar
376b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
37738baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
37838baa3e6SBram Moolenaar  " There can be only one.
37938baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
38038baa3e6SBram Moolenaar
38145d5f26dSBram Moolenaar  " Install debugger commands in the text window.
382b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
383e09ba7baSBram Moolenaar  call s:InstallCommands()
38445d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
385e09ba7baSBram Moolenaar
38651b0f370SBram Moolenaar  " Enable showing a balloon with eval info
387246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
388246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
38951b0f370SBram Moolenaar    if has("balloon_eval")
39051b0f370SBram Moolenaar      set ballooneval
391246fe03dSBram Moolenaar    endif
39251b0f370SBram Moolenaar    if has("balloon_eval_term")
39351b0f370SBram Moolenaar      set balloonevalterm
39451b0f370SBram Moolenaar    endif
39551b0f370SBram Moolenaar  endif
39651b0f370SBram Moolenaar
3975378e1cfSBram Moolenaar  " Contains breakpoints that have been placed, key is a string with the GDB
3985378e1cfSBram Moolenaar  " breakpoint number.
39937402ed5SBram Moolenaar  " Each entry is a dict, containing the sub-breakpoints.  Key is the subid.
40037402ed5SBram Moolenaar  " For a breakpoint that is just a number the subid is zero.
40137402ed5SBram Moolenaar  " For a breakpoint "123.4" the id is "123" and subid is "4".
40237402ed5SBram Moolenaar  " Example, when breakpoint "44", "123", "123.1" and "123.2" exist:
40337402ed5SBram Moolenaar  " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}}
404e09ba7baSBram Moolenaar  let s:breakpoints = {}
4051b9645deSBram Moolenaar
40637402ed5SBram Moolenaar  " Contains breakpoints by file/lnum.  The key is "fname:lnum".
40737402ed5SBram Moolenaar  " Each entry is a list of breakpoint IDs at that position.
40837402ed5SBram Moolenaar  let s:breakpoint_locations = {}
40937402ed5SBram Moolenaar
4101b9645deSBram Moolenaar  augroup TermDebug
4111b9645deSBram Moolenaar    au BufRead * call s:BufRead()
4121b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
413f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
4141b9645deSBram Moolenaar  augroup END
41532c67ba7SBram Moolenaar
416b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
417b3307b5eSBram Moolenaar  " window.
41832c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
41932c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
42032c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
42132c67ba7SBram Moolenaar  endif
422c572da5fSBram Moolenaarendfunc
423c572da5fSBram Moolenaar
424b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
425b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
426b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
427b3307b5eSBram Moolenaar  if s:way == 'prompt'
428b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
429b3307b5eSBram Moolenaar  else
430b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
431b3307b5eSBram Moolenaar  endif
432b3307b5eSBram Moolenaarendfunc
433b3307b5eSBram Moolenaar
434b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
435b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
436b3307b5eSBram Moolenaar  if s:way == 'prompt'
437b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
438b3307b5eSBram Moolenaar  else
439b3307b5eSBram Moolenaar    let do_continue = 0
440b3307b5eSBram Moolenaar    if !s:stopped
441b3307b5eSBram Moolenaar      let do_continue = 1
442b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
443b3307b5eSBram Moolenaar      sleep 10m
444b3307b5eSBram Moolenaar    endif
445b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
446b3307b5eSBram Moolenaar    if do_continue
447b3307b5eSBram Moolenaar      Continue
448b3307b5eSBram Moolenaar    endif
449b3307b5eSBram Moolenaar  endif
450b3307b5eSBram Moolenaarendfunc
451b3307b5eSBram Moolenaar
452b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
453b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
454b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
455b3307b5eSBram Moolenaarendfunc
456b3307b5eSBram Moolenaar
4574551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
4584551c0a9SBram Moolenaar" breakpoint.
459b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
4602ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
4612ed890f1SBram Moolenaar  if has('win32')
4622ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
4632ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
4644551c0a9SBram Moolenaar    if s:pid == 0
4654551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
4664551c0a9SBram Moolenaar    else
4674551c0a9SBram Moolenaar      call debugbreak(s:pid)
4684551c0a9SBram Moolenaar    endif
4692ed890f1SBram Moolenaar  else
4702ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
4712ed890f1SBram Moolenaar  endif
472b3307b5eSBram Moolenaarendfunc
473b3307b5eSBram Moolenaar
474b3307b5eSBram Moolenaar" Function called when gdb outputs text.
475b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
476b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
477b3307b5eSBram Moolenaar
478b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
479b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
480a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
481b3307b5eSBram Moolenaar    return
482b3307b5eSBram Moolenaar  endif
483b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
484b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
485b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
486b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
487b3307b5eSBram Moolenaar      unlet s:evalexpr
488b3307b5eSBram Moolenaar      return
489b3307b5eSBram Moolenaar    endif
490b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
491b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
492b3307b5eSBram Moolenaar  else
493b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
494b3307b5eSBram Moolenaar    return
495b3307b5eSBram Moolenaar  endif
496b3307b5eSBram Moolenaar
497b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
498b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
499b3307b5eSBram Moolenaar
500b3307b5eSBram Moolenaar  " Add the output above the current prompt.
501b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
5024551c0a9SBram Moolenaar  set modified
503b3307b5eSBram Moolenaar
504b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
505b3307b5eSBram Moolenaarendfunc
506b3307b5eSBram Moolenaar
507b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
508b3307b5eSBram Moolenaar" to the next ", unescaping characters.
509b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
510b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
511a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
512b3307b5eSBram Moolenaar    return
513b3307b5eSBram Moolenaar  endif
514b3307b5eSBram Moolenaar  let result = ''
515b3307b5eSBram Moolenaar  let i = 1
516b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
517b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
518b3307b5eSBram Moolenaar      let i += 1
519b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
520b3307b5eSBram Moolenaar	" drop \n
521b3307b5eSBram Moolenaar	let i += 1
522b3307b5eSBram Moolenaar	continue
523589edb34SBram Moolenaar      elseif a:quotedText[i] == 't'
524589edb34SBram Moolenaar	" append \t
525589edb34SBram Moolenaar	let i += 1
526589edb34SBram Moolenaar	let result .= "\t"
527589edb34SBram Moolenaar	continue
528b3307b5eSBram Moolenaar      endif
529b3307b5eSBram Moolenaar    endif
530b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
531b3307b5eSBram Moolenaar    let i += 1
532b3307b5eSBram Moolenaar  endwhile
533b3307b5eSBram Moolenaar  return result
534b3307b5eSBram Moolenaarendfunc
535b3307b5eSBram Moolenaar
536a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
537a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
5385378e1cfSBram Moolenaar  if a:msg !~ 'fullname'
5395378e1cfSBram Moolenaar    return ''
5405378e1cfSBram Moolenaar  endif
541a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
542a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
543a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
544a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
545a15b0a93SBram Moolenaar  endif
546a15b0a93SBram Moolenaar  return name
547a15b0a93SBram Moolenaarendfunc
548a15b0a93SBram Moolenaar
549b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
550fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
551b3623a38SBram Moolenaar  unlet s:gdbwin
552e09ba7baSBram Moolenaar
553b3307b5eSBram Moolenaar  call s:EndDebugCommon()
554b3307b5eSBram Moolenaarendfunc
555b3307b5eSBram Moolenaar
556b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
557e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
558e09ba7baSBram Moolenaar
559b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
560b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
561b3307b5eSBram Moolenaar  endif
562b3307b5eSBram Moolenaar
563*cb80aa2dSBram Moolenaar  " Restore 'signcolumn' in all buffers for which it was set.
564b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
565*cb80aa2dSBram Moolenaar  let was_buf = bufnr()
566*cb80aa2dSBram Moolenaar  for bufnr in s:signcolumn_buflist
567*cb80aa2dSBram Moolenaar    if bufexists(bufnr)
568*cb80aa2dSBram Moolenaar      exe bufnr .. "buf"
569*cb80aa2dSBram Moolenaar      if exists('b:save_signcolumn')
570*cb80aa2dSBram Moolenaar	let &signcolumn = b:save_signcolumn
571*cb80aa2dSBram Moolenaar	unlet b:save_signcolumn
572*cb80aa2dSBram Moolenaar      endif
573*cb80aa2dSBram Moolenaar    endif
574*cb80aa2dSBram Moolenaar  endfor
575*cb80aa2dSBram Moolenaar  exe was_buf .. "buf"
576*cb80aa2dSBram Moolenaar
577e09ba7baSBram Moolenaar  call s:DeleteCommands()
578e09ba7baSBram Moolenaar
579e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
580b3307b5eSBram Moolenaar
58138baa3e6SBram Moolenaar  if s:save_columns > 0
58238baa3e6SBram Moolenaar    let &columns = s:save_columns
58338baa3e6SBram Moolenaar  endif
5841b9645deSBram Moolenaar
585246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
586246fe03dSBram Moolenaar    set balloonexpr=
58751b0f370SBram Moolenaar    if has("balloon_eval")
58851b0f370SBram Moolenaar      set noballooneval
589246fe03dSBram Moolenaar    endif
59051b0f370SBram Moolenaar    if has("balloon_eval_term")
59151b0f370SBram Moolenaar      set noballoonevalterm
59251b0f370SBram Moolenaar    endif
59351b0f370SBram Moolenaar  endif
59451b0f370SBram Moolenaar
5951b9645deSBram Moolenaar  au! TermDebug
596fe386641SBram Moolenaarendfunc
597fe386641SBram Moolenaar
598b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
599b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
600b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
6014551c0a9SBram Moolenaar  set nomodified
602b3307b5eSBram Moolenaar  close
603b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
604b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
605b3307b5eSBram Moolenaar  endif
606b3307b5eSBram Moolenaar
607b3307b5eSBram Moolenaar  call s:EndDebugCommon()
608b3307b5eSBram Moolenaar  unlet s:gdbwin
609b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
610b3307b5eSBram Moolenaarendfunc
611b3307b5eSBram Moolenaar
612fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
613fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
614fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
615fe386641SBram Moolenaar
616fe386641SBram Moolenaar  for msg in msgs
617fe386641SBram Moolenaar    " remove prefixed NL
618fe386641SBram Moolenaar    if msg[0] == "\n"
619fe386641SBram Moolenaar      let msg = msg[1:]
620fe386641SBram Moolenaar    endif
621fe386641SBram Moolenaar    if msg != ''
6221b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
623e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
62445d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
625e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
626e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
627e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
6284551c0a9SBram Moolenaar      elseif msg =~ '^=thread-group-started'
6294551c0a9SBram Moolenaar	call s:HandleProgramRun(msg)
63045d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
63145d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
63245d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
63345d5f26dSBram Moolenaar	call s:HandleError(msg)
634e09ba7baSBram Moolenaar      endif
635e09ba7baSBram Moolenaar    endif
636e09ba7baSBram Moolenaar  endfor
637e09ba7baSBram Moolenaarendfunc
638e09ba7baSBram Moolenaar
639589edb34SBram Moolenaarfunc s:GotoProgram()
640589edb34SBram Moolenaar  if has('win32')
641589edb34SBram Moolenaar    if executable('powershell')
642589edb34SBram Moolenaar      call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid))
643589edb34SBram Moolenaar    endif
644589edb34SBram Moolenaar  else
645469bdbdeSBram Moolenaar    call win_gotoid(s:ptywin)
646589edb34SBram Moolenaar  endif
647589edb34SBram Moolenaarendfunc
648589edb34SBram Moolenaar
649e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
650e09ba7baSBram Moolenaarfunc s:InstallCommands()
651963c1ad5SBram Moolenaar  let save_cpo = &cpo
652963c1ad5SBram Moolenaar  set cpo&vim
653963c1ad5SBram Moolenaar
654589edb34SBram Moolenaar  command -nargs=? Break call s:SetBreakpoint(<q-args>)
65571137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
656e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
65745d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
658e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
65960e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
66060e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
66160e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
662b3307b5eSBram Moolenaar
663b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
664b3307b5eSBram Moolenaar  if s:way == 'prompt'
665b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
666b3307b5eSBram Moolenaar  else
667b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
668b3307b5eSBram Moolenaar  endif
669b3307b5eSBram Moolenaar
67045d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
67145d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
672589edb34SBram Moolenaar  command Program call s:GotoProgram()
673b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
67471137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
67545d5f26dSBram Moolenaar
676388a5d4fSBram Moolenaar  if !exists('g:termdebug_map_K') || g:termdebug_map_K
677388a5d4fSBram Moolenaar    let s:k_map_saved = maparg('K', 'n', 0, 1)
67845d5f26dSBram Moolenaar    nnoremap K :Evaluate<CR>
679388a5d4fSBram Moolenaar  endif
6801b9645deSBram Moolenaar
681f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
68271137fedSBram Moolenaar    call s:InstallWinbar()
68371137fedSBram Moolenaar
68471137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
68571137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
68671137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
68771137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
68871137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
68971137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
69071137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
69171137fedSBram Moolenaar    endif
69271137fedSBram Moolenaar  endif
693963c1ad5SBram Moolenaar
694963c1ad5SBram Moolenaar  let &cpo = save_cpo
69571137fedSBram Moolenaarendfunc
69671137fedSBram Moolenaar
69771137fedSBram Moolenaarlet s:winbar_winids = []
69871137fedSBram Moolenaar
69971137fedSBram Moolenaar" Install the window toolbar in the current window.
70071137fedSBram Moolenaarfunc s:InstallWinbar()
701c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
70224a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
70324a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
70424a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
70524a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
70660e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
70724a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
70871137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
709c4b533e1SBram Moolenaar  endif
710e09ba7baSBram Moolenaarendfunc
711e09ba7baSBram Moolenaar
712e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
713e09ba7baSBram Moolenaarfunc s:DeleteCommands()
714e09ba7baSBram Moolenaar  delcommand Break
71571137fedSBram Moolenaar  delcommand Clear
716e09ba7baSBram Moolenaar  delcommand Step
71745d5f26dSBram Moolenaar  delcommand Over
718e09ba7baSBram Moolenaar  delcommand Finish
71960e73f2aSBram Moolenaar  delcommand Run
72060e73f2aSBram Moolenaar  delcommand Arguments
72160e73f2aSBram Moolenaar  delcommand Stop
722e09ba7baSBram Moolenaar  delcommand Continue
72345d5f26dSBram Moolenaar  delcommand Evaluate
72445d5f26dSBram Moolenaar  delcommand Gdb
72545d5f26dSBram Moolenaar  delcommand Program
726b3623a38SBram Moolenaar  delcommand Source
72771137fedSBram Moolenaar  delcommand Winbar
72845d5f26dSBram Moolenaar
72965e0d77aSBram Moolenaar  if exists('s:k_map_saved') && !empty(s:k_map_saved)
730388a5d4fSBram Moolenaar    call mapset('n', 0, s:k_map_saved)
731388a5d4fSBram Moolenaar    unlet s:k_map_saved
732388a5d4fSBram Moolenaar  endif
7331b9645deSBram Moolenaar
7341b9645deSBram Moolenaar  if has('menu')
73571137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
73671137fedSBram Moolenaar    let curwinid = win_getid(winnr())
73771137fedSBram Moolenaar    for winid in s:winbar_winids
73871137fedSBram Moolenaar      if win_gotoid(winid)
7391b9645deSBram Moolenaar	aunmenu WinBar.Step
7401b9645deSBram Moolenaar	aunmenu WinBar.Next
7411b9645deSBram Moolenaar	aunmenu WinBar.Finish
7421b9645deSBram Moolenaar	aunmenu WinBar.Cont
74360e73f2aSBram Moolenaar	aunmenu WinBar.Stop
7441b9645deSBram Moolenaar	aunmenu WinBar.Eval
7451b9645deSBram Moolenaar      endif
74671137fedSBram Moolenaar    endfor
74771137fedSBram Moolenaar    call win_gotoid(curwinid)
74871137fedSBram Moolenaar    let s:winbar_winids = []
74971137fedSBram Moolenaar
75071137fedSBram Moolenaar    if exists('s:saved_mousemodel')
75171137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
75271137fedSBram Moolenaar      unlet s:saved_mousemodel
75371137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
75471137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
75571137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
75671137fedSBram Moolenaar      aunmenu PopUp.Evaluate
75771137fedSBram Moolenaar    endif
75871137fedSBram Moolenaar  endif
7591b9645deSBram Moolenaar
76045d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
76137402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
76237402ed5SBram Moolenaar    for subid in keys(entries)
76337402ed5SBram Moolenaar      exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
76437402ed5SBram Moolenaar    endfor
76545d5f26dSBram Moolenaar  endfor
76645d5f26dSBram Moolenaar  unlet s:breakpoints
76737402ed5SBram Moolenaar  unlet s:breakpoint_locations
768a15b0a93SBram Moolenaar
769a15b0a93SBram Moolenaar  sign undefine debugPC
770a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
771a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
772a15b0a93SBram Moolenaar  endfor
7734551c0a9SBram Moolenaar  let s:BreakpointSigns = []
774e09ba7baSBram Moolenaarendfunc
775e09ba7baSBram Moolenaar
776e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
777589edb34SBram Moolenaarfunc s:SetBreakpoint(at)
77860e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
77960e73f2aSBram Moolenaar  " Interrupt to make it work.
78060e73f2aSBram Moolenaar  let do_continue = 0
78160e73f2aSBram Moolenaar  if !s:stopped
78260e73f2aSBram Moolenaar    let do_continue = 1
783b3307b5eSBram Moolenaar    if s:way == 'prompt'
7844551c0a9SBram Moolenaar      call s:PromptInterrupt()
785b3307b5eSBram Moolenaar    else
78660e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
787b3307b5eSBram Moolenaar    endif
78860e73f2aSBram Moolenaar    sleep 10m
78960e73f2aSBram Moolenaar  endif
790589edb34SBram Moolenaar
791a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
792589edb34SBram Moolenaar  let at = empty(a:at) ?
793589edb34SBram Moolenaar        \ fnameescape(expand('%:p')) . ':' . line('.') : a:at
794589edb34SBram Moolenaar  call s:SendCommand('-break-insert ' . at)
79560e73f2aSBram Moolenaar  if do_continue
79660e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
79760e73f2aSBram Moolenaar  endif
798e09ba7baSBram Moolenaarendfunc
799e09ba7baSBram Moolenaar
80071137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
80171137fedSBram Moolenaarfunc s:ClearBreakpoint()
802e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
803e09ba7baSBram Moolenaar  let lnum = line('.')
80437402ed5SBram Moolenaar  let bploc = printf('%s:%d', fname, lnum)
80537402ed5SBram Moolenaar  if has_key(s:breakpoint_locations, bploc)
80637402ed5SBram Moolenaar    let idx = 0
80737402ed5SBram Moolenaar    for id in s:breakpoint_locations[bploc]
80837402ed5SBram Moolenaar      if has_key(s:breakpoints, id)
80937402ed5SBram Moolenaar	" Assume this always works, the reply is simply "^done".
81037402ed5SBram Moolenaar	call s:SendCommand('-break-delete ' . id)
81137402ed5SBram Moolenaar	for subid in keys(s:breakpoints[id])
81237402ed5SBram Moolenaar	  exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
81337402ed5SBram Moolenaar	endfor
81437402ed5SBram Moolenaar	unlet s:breakpoints[id]
81537402ed5SBram Moolenaar	unlet s:breakpoint_locations[bploc][idx]
816e09ba7baSBram Moolenaar	break
81737402ed5SBram Moolenaar      else
81837402ed5SBram Moolenaar	let idx += 1
819e09ba7baSBram Moolenaar      endif
820e09ba7baSBram Moolenaar    endfor
82137402ed5SBram Moolenaar    if empty(s:breakpoint_locations[bploc])
82237402ed5SBram Moolenaar      unlet s:breakpoint_locations[bploc]
82337402ed5SBram Moolenaar    endif
82437402ed5SBram Moolenaar  endif
825e09ba7baSBram Moolenaarendfunc
826e09ba7baSBram Moolenaar
82760e73f2aSBram Moolenaarfunc s:Run(args)
82860e73f2aSBram Moolenaar  if a:args != ''
82960e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
83060e73f2aSBram Moolenaar  endif
83160e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
83260e73f2aSBram Moolenaarendfunc
83360e73f2aSBram Moolenaar
83451b0f370SBram Moolenaarfunc s:SendEval(expr)
83551b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
83651b0f370SBram Moolenaar  let s:evalexpr = a:expr
83751b0f370SBram Moolenaarendfunc
83851b0f370SBram Moolenaar
83945d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
84045d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
84145d5f26dSBram Moolenaar  if a:arg != ''
84245d5f26dSBram Moolenaar    let expr = a:arg
84345d5f26dSBram Moolenaar  elseif a:range == 2
84445d5f26dSBram Moolenaar    let pos = getcurpos()
84545d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
84645d5f26dSBram Moolenaar    let regt = getregtype('v')
84745d5f26dSBram Moolenaar    normal! gv"vy
84845d5f26dSBram Moolenaar    let expr = @v
84945d5f26dSBram Moolenaar    call setpos('.', pos)
85045d5f26dSBram Moolenaar    call setreg('v', reg, regt)
85145d5f26dSBram Moolenaar  else
85245d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
85345d5f26dSBram Moolenaar  endif
85422f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
85551b0f370SBram Moolenaar  call s:SendEval(expr)
85645d5f26dSBram Moolenaarendfunc
85745d5f26dSBram Moolenaar
85822f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
85951b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
86051b0f370SBram Moolenaar
86145d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
86245d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
8631b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
8641b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
86551b0f370SBram Moolenaar  if s:evalFromBalloonExpr
86651b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
86751b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
86851b0f370SBram Moolenaar    else
86951b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
87051b0f370SBram Moolenaar    endif
87151b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
87251b0f370SBram Moolenaar  else
8731b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
87451b0f370SBram Moolenaar  endif
8751b9645deSBram Moolenaar
8767f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
8771b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
87822f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
87951b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
88051b0f370SBram Moolenaar  else
88151b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
8821b9645deSBram Moolenaar  endif
88345d5f26dSBram Moolenaarendfunc
88445d5f26dSBram Moolenaar
88551b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
88651b0f370SBram Moolenaar" if there is any.
88751b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
888b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
889396e829fSBram Moolenaar    return ''
890b3307b5eSBram Moolenaar  endif
891b3307b5eSBram Moolenaar  if !s:stopped
892b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
893b3307b5eSBram Moolenaar    " mouse triggers a balloon.
894396e829fSBram Moolenaar    return ''
89551b0f370SBram Moolenaar  endif
89651b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
89751b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
89822f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
89922f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
90051b0f370SBram Moolenaar  return ''
90151b0f370SBram Moolenaarendfunc
90251b0f370SBram Moolenaar
90345d5f26dSBram Moolenaar" Handle an error.
90445d5f26dSBram Moolenaarfunc s:HandleError(msg)
90522f1d0e3SBram Moolenaar  if s:ignoreEvalError
90651b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
90722f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
90822f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
90951b0f370SBram Moolenaar    return
91051b0f370SBram Moolenaar  endif
91145d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
91245d5f26dSBram Moolenaarendfunc
91345d5f26dSBram Moolenaar
914b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
915b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
916c4b533e1SBram Moolenaar    new
917b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
918c4b533e1SBram Moolenaar    call s:InstallWinbar()
919c4b533e1SBram Moolenaar  endif
920c4b533e1SBram Moolenaarendfunc
921c4b533e1SBram Moolenaar
922e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
923e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
924e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
925fe386641SBram Moolenaar  let wid = win_getid(winnr())
926fe386641SBram Moolenaar
92760e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
9284551c0a9SBram Moolenaar    call ch_log('program stopped')
92960e73f2aSBram Moolenaar    let s:stopped = 1
93060e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
9314551c0a9SBram Moolenaar    call ch_log('program running')
93260e73f2aSBram Moolenaar    let s:stopped = 0
93360e73f2aSBram Moolenaar  endif
93460e73f2aSBram Moolenaar
935a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
936a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
937a15b0a93SBram Moolenaar  else
938a15b0a93SBram Moolenaar    let fname = ''
939a15b0a93SBram Moolenaar  endif
9401b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
941e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
942fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
9434551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
9441b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
945fe386641SBram Moolenaar	if &modified
946fe386641SBram Moolenaar	  " TODO: find existing window
947fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
948b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
949c4b533e1SBram Moolenaar	  call s:InstallWinbar()
950fe386641SBram Moolenaar	else
951fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
952fe386641SBram Moolenaar	endif
953fe386641SBram Moolenaar      endif
954fe386641SBram Moolenaar      exe lnum
95501164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
95639f7aa3cSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC priority=110 file=' . fname
957*cb80aa2dSBram Moolenaar      if !exists('b:save_signcolumn')
958*cb80aa2dSBram Moolenaar	let b:save_signcolumn = &signcolumn
959*cb80aa2dSBram Moolenaar	call add(s:signcolumn_buflist, bufnr())
960*cb80aa2dSBram Moolenaar      endif
961fe386641SBram Moolenaar      setlocal signcolumn=yes
962fe386641SBram Moolenaar    endif
9634551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
964fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
965fe386641SBram Moolenaar  endif
966fe386641SBram Moolenaar
967fe386641SBram Moolenaar  call win_gotoid(wid)
968e09ba7baSBram Moolenaarendfunc
969e09ba7baSBram Moolenaar
970de1a8314SBram Moolenaarlet s:BreakpointSigns = []
971a15b0a93SBram Moolenaar
97237402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid)
97337402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
97437402ed5SBram Moolenaar  if index(s:BreakpointSigns, nr) == -1
97537402ed5SBram Moolenaar    call add(s:BreakpointSigns, nr)
97637402ed5SBram Moolenaar    exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint"
977de1a8314SBram Moolenaar  endif
978de1a8314SBram Moolenaarendfunc
979de1a8314SBram Moolenaar
98037402ed5SBram Moolenaarfunc! s:SplitMsg(s)
98137402ed5SBram Moolenaar  return split(a:s, '{.\{-}}\zs')
9825378e1cfSBram Moolenaarendfunction
9835378e1cfSBram Moolenaar
984e09ba7baSBram Moolenaar" Handle setting a breakpoint
985e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
986e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
9876dccc962SBram Moolenaar  if a:msg !~ 'fullname='
9886dccc962SBram Moolenaar    " a watch does not have a file name
9896dccc962SBram Moolenaar    return
9906dccc962SBram Moolenaar  endif
9915378e1cfSBram Moolenaar  for msg in s:SplitMsg(a:msg)
9925378e1cfSBram Moolenaar    let fname = s:GetFullname(msg)
9935378e1cfSBram Moolenaar    if empty(fname)
9945378e1cfSBram Moolenaar      continue
9955378e1cfSBram Moolenaar    endif
9965378e1cfSBram Moolenaar    let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '')
9975378e1cfSBram Moolenaar    if empty(nr)
998e09ba7baSBram Moolenaar      return
999fe386641SBram Moolenaar    endif
1000e09ba7baSBram Moolenaar
100137402ed5SBram Moolenaar    " If "nr" is 123 it becomes "123.0" and subid is "0".
100237402ed5SBram Moolenaar    " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded.
100337402ed5SBram Moolenaar    let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0')
100437402ed5SBram Moolenaar    call s:CreateBreakpoint(id, subid)
100537402ed5SBram Moolenaar
100637402ed5SBram Moolenaar    if has_key(s:breakpoints, id)
100737402ed5SBram Moolenaar      let entries = s:breakpoints[id]
100837402ed5SBram Moolenaar    else
100937402ed5SBram Moolenaar      let entries = {}
101037402ed5SBram Moolenaar      let s:breakpoints[id] = entries
101137402ed5SBram Moolenaar    endif
101237402ed5SBram Moolenaar    if has_key(entries, subid)
101337402ed5SBram Moolenaar      let entry = entries[subid]
1014e09ba7baSBram Moolenaar    else
1015e09ba7baSBram Moolenaar      let entry = {}
101637402ed5SBram Moolenaar      let entries[subid] = entry
1017fe386641SBram Moolenaar    endif
1018e09ba7baSBram Moolenaar
10195378e1cfSBram Moolenaar    let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
1020e09ba7baSBram Moolenaar    let entry['fname'] = fname
1021e09ba7baSBram Moolenaar    let entry['lnum'] = lnum
10221b9645deSBram Moolenaar
102337402ed5SBram Moolenaar    let bploc = printf('%s:%d', fname, lnum)
102437402ed5SBram Moolenaar    if !has_key(s:breakpoint_locations, bploc)
102537402ed5SBram Moolenaar      let s:breakpoint_locations[bploc] = []
102637402ed5SBram Moolenaar    endif
102737402ed5SBram Moolenaar    let s:breakpoint_locations[bploc] += [id]
102837402ed5SBram Moolenaar
10291b9645deSBram Moolenaar    if bufloaded(fname)
103037402ed5SBram Moolenaar      call s:PlaceSign(id, subid, entry)
10311b9645deSBram Moolenaar    endif
10325378e1cfSBram Moolenaar  endfor
10331b9645deSBram Moolenaarendfunc
10341b9645deSBram Moolenaar
103537402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry)
103637402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
103737402ed5SBram Moolenaar  exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname']
10381b9645deSBram Moolenaar  let a:entry['placed'] = 1
1039e09ba7baSBram Moolenaarendfunc
1040e09ba7baSBram Moolenaar
1041e09ba7baSBram Moolenaar" Handle deleting a breakpoint
1042e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
1043e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
104437402ed5SBram Moolenaar  let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
104537402ed5SBram Moolenaar  if empty(id)
1046e09ba7baSBram Moolenaar    return
1047e09ba7baSBram Moolenaar  endif
104837402ed5SBram Moolenaar  if has_key(s:breakpoints, id)
104937402ed5SBram Moolenaar    for [subid, entry] in items(s:breakpoints[id])
10501b9645deSBram Moolenaar      if has_key(entry, 'placed')
105137402ed5SBram Moolenaar	exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
10521b9645deSBram Moolenaar	unlet entry['placed']
10531b9645deSBram Moolenaar      endif
10545378e1cfSBram Moolenaar    endfor
105537402ed5SBram Moolenaar    unlet s:breakpoints[id]
105637402ed5SBram Moolenaar  endif
1057c572da5fSBram Moolenaarendfunc
10581b9645deSBram Moolenaar
10594551c0a9SBram Moolenaar" Handle the debugged program starting to run.
10604551c0a9SBram Moolenaar" Will store the process ID in s:pid
10614551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
10624551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
10634551c0a9SBram Moolenaar  if nr == 0
10644551c0a9SBram Moolenaar    return
10654551c0a9SBram Moolenaar  endif
10664551c0a9SBram Moolenaar  let s:pid = nr
10674551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
10684551c0a9SBram Moolenaarendfunc
10694551c0a9SBram Moolenaar
10701b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
10711b9645deSBram Moolenaarfunc s:BufRead()
10721b9645deSBram Moolenaar  let fname = expand('<afile>:p')
107337402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
107437402ed5SBram Moolenaar    for [subid, entry] in items(entries)
10751b9645deSBram Moolenaar      if entry['fname'] == fname
107637402ed5SBram Moolenaar	call s:PlaceSign(id, subid, entry)
10771b9645deSBram Moolenaar      endif
10781b9645deSBram Moolenaar    endfor
107937402ed5SBram Moolenaar  endfor
10801b9645deSBram Moolenaarendfunc
10811b9645deSBram Moolenaar
10821b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
10831b9645deSBram Moolenaarfunc s:BufUnloaded()
10841b9645deSBram Moolenaar  let fname = expand('<afile>:p')
108537402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
108637402ed5SBram Moolenaar    for [subid, entry] in items(entries)
10871b9645deSBram Moolenaar      if entry['fname'] == fname
10881b9645deSBram Moolenaar	let entry['placed'] = 0
10891b9645deSBram Moolenaar      endif
10901b9645deSBram Moolenaar    endfor
109137402ed5SBram Moolenaar  endfor
10921b9645deSBram Moolenaarendfunc
1093ca4cc018SBram Moolenaar
1094ca4cc018SBram Moolenaarlet &cpo = s:keepcpo
1095ca4cc018SBram Moolenaarunlet s:keepcpo
1096