1fe386641SBram Moolenaar" Debugger plugin using gdb.
2c572da5fSBram Moolenaar"
3b3307b5eSBram Moolenaar" Author: Bram Moolenaar
4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license"
5b3307b5eSBram Moolenaar" Last Update: 2018 Jun 3
6c572da5fSBram Moolenaar"
7b3307b5eSBram Moolenaar" WORK IN PROGRESS - Only the basics work
8b3307b5eSBram Moolenaar" Note: On MS-Windows you need a recent version of gdb.  The one included with
9b3307b5eSBram Moolenaar" MingW is too old (7.6.1).
10b3307b5eSBram Moolenaar" I used version 7.12 from http://www.equation.com/servlet/equation.cmd?fa=gdb
11fe386641SBram Moolenaar"
12b3307b5eSBram Moolenaar" There are two ways to run gdb:
13b3307b5eSBram Moolenaar" - In a terminal window; used if possible, does not work on MS-Windows
14b3307b5eSBram Moolenaar"   Not used when g:termdebug_use_prompt is set to 1.
15b3307b5eSBram Moolenaar" - Using a "prompt" buffer; may use a terminal window for the program
16b3307b5eSBram Moolenaar"
17b3307b5eSBram Moolenaar" For both the current window is used to view source code and shows the
18b3307b5eSBram Moolenaar" current statement from gdb.
19b3307b5eSBram Moolenaar"
20b3307b5eSBram Moolenaar" USING A TERMINAL WINDOW
21b3307b5eSBram Moolenaar"
22b3307b5eSBram Moolenaar" Opens two visible terminal windows:
23b3307b5eSBram Moolenaar" 1. runs a pty for the debugged program, as with ":term NONE"
24b3307b5eSBram Moolenaar" 2. runs gdb, passing the pty of the debugged program
25fe386641SBram Moolenaar" A third terminal window is hidden, it is used for communication with gdb.
26fe386641SBram Moolenaar"
27b3307b5eSBram Moolenaar" USING A PROMPT BUFFER
28b3307b5eSBram Moolenaar"
29b3307b5eSBram Moolenaar" Opens a window with a prompt buffer to communicate with gdb.
30b3307b5eSBram Moolenaar" Gdb is run as a job with callbacks for I/O.
31b3307b5eSBram Moolenaar" On Unix another terminal window is opened to run the debugged program
32b3307b5eSBram Moolenaar" On MS-Windows a separate console is opened to run the debugged program
33b3307b5eSBram Moolenaar"
34fe386641SBram Moolenaar" The communication with gdb uses GDB/MI.  See:
35fe386641SBram Moolenaar" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html
36c572da5fSBram Moolenaar
37b3307b5eSBram Moolenaar" In case this gets sourced twice.
3837c64c78SBram Moolenaarif exists(':Termdebug')
3937c64c78SBram Moolenaar  finish
4037c64c78SBram Moolenaarendif
4137c64c78SBram Moolenaar
42b3307b5eSBram Moolenaar" Need either the +terminal feature or +channel and the prompt buffer.
43b3307b5eSBram Moolenaar" The terminal feature does not work with gdb on win32.
44b3307b5eSBram Moolenaarif has('terminal') && !has('win32')
45b3307b5eSBram Moolenaar  let s:way = 'terminal'
46b3307b5eSBram Moolenaarelseif has('channel') && exists('*prompt_setprompt')
47b3307b5eSBram Moolenaar  let s:way = 'prompt'
48b3307b5eSBram Moolenaarelse
49b3307b5eSBram Moolenaar  if has('terminal')
50b3307b5eSBram Moolenaar    let s:err = 'Cannot debug, missing prompt buffer support'
51b3307b5eSBram Moolenaar  else
52b3307b5eSBram Moolenaar    let s:err = 'Cannot debug, +channel feature is not supported'
53b3307b5eSBram Moolenaar  endif
54b3307b5eSBram Moolenaar  command -nargs=* -complete=file -bang Termdebug echoerr s:err
55b3307b5eSBram Moolenaar  command -nargs=+ -complete=file -bang TermdebugCommand echoerr s:err
56b3307b5eSBram Moolenaar  finish
57b3307b5eSBram Moolenaarendif
5860e73f2aSBram Moolenaar
59ca4cc018SBram Moolenaarlet s:keepcpo = &cpo
60ca4cc018SBram Moolenaarset cpo&vim
61ca4cc018SBram Moolenaar
62fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim".
63fe386641SBram Moolenaar" To end type "quit" in the gdb window.
6432c67ba7SBram Moolenaarcommand -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>)
6532c67ba7SBram Moolenaarcommand -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>)
66c572da5fSBram Moolenaar
67fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb".
68e09ba7baSBram Moolenaarif !exists('termdebugger')
69e09ba7baSBram Moolenaar  let termdebugger = 'gdb'
70c572da5fSBram Moolenaarendif
71c572da5fSBram Moolenaar
72fe386641SBram Moolenaarlet s:pc_id = 12
73de1a8314SBram Moolenaarlet s:break_id = 13  " breakpoint number is added to this
7460e73f2aSBram Moolenaarlet s:stopped = 1
75e09ba7baSBram Moolenaar
765378e1cfSBram Moolenaar" Take a breakpoint number as used by GDB and turn it into an integer.
7737402ed5SBram Moolenaar" The breakpoint may contain a dot: 123.4 -> 123004
7837402ed5SBram Moolenaar" The main breakpoint has a zero subid.
7937402ed5SBram Moolenaarfunc s:Breakpoint2SignNumber(id, subid)
8037402ed5SBram Moolenaar  return s:break_id + a:id * 1000 + a:subid
815378e1cfSBram Moolenaarendfunction
825378e1cfSBram Moolenaar
83f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new)
84f07f9e73SBram Moolenaar  let default = a:init ? 'default ' : ''
85f07f9e73SBram Moolenaar  if a:new ==# 'light' && a:old !=# 'light'
86f07f9e73SBram Moolenaar    exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue"
87f07f9e73SBram Moolenaar  elseif a:new ==# 'dark' && a:old !=# 'dark'
88f07f9e73SBram Moolenaar    exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue"
89e09ba7baSBram Moolenaar  endif
90f07f9e73SBram Moolenaarendfunc
91f07f9e73SBram Moolenaar
92f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background)
93e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red
94fe386641SBram Moolenaar
9532c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...)
9632c67ba7SBram Moolenaar  " First argument is the command to debug, second core file or process ID.
9732c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang})
9832c67ba7SBram Moolenaarendfunc
9932c67ba7SBram Moolenaar
10032c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...)
10132c67ba7SBram Moolenaar  " First argument is the command to debug, rest are run arguments.
10232c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang})
10332c67ba7SBram Moolenaarendfunc
10432c67ba7SBram Moolenaar
10532c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict)
106b3623a38SBram Moolenaar  if exists('s:gdbwin')
107b3623a38SBram Moolenaar    echoerr 'Terminal debugger already running'
108b3623a38SBram Moolenaar    return
109b3623a38SBram Moolenaar  endif
110b3307b5eSBram Moolenaar  let s:ptywin = 0
1114551c0a9SBram Moolenaar  let s:pid = 0
112b3623a38SBram Moolenaar
113b3307b5eSBram Moolenaar  " Uncomment this line to write logging in "debuglog".
114b3307b5eSBram Moolenaar  " call ch_logfile('debuglog', 'w')
115b3307b5eSBram Moolenaar
116b3307b5eSBram Moolenaar  let s:sourcewin = win_getid(winnr())
117fe386641SBram Moolenaar  let s:startsigncolumn = &signcolumn
118fe386641SBram Moolenaar
11924a98a0eSBram Moolenaar  let s:save_columns = 0
12068e6560bSBram Moolenaar  let s:allleft = 0
12124a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
12224a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
12338baa3e6SBram Moolenaar      let s:save_columns = &columns
12438baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
12568e6560bSBram Moolenaar      " If we make the Vim window wider, use the whole left halve for the debug
12668e6560bSBram Moolenaar      " windows.
12768e6560bSBram Moolenaar      let s:allleft = 1
12824a98a0eSBram Moolenaar    endif
129b3307b5eSBram Moolenaar    let s:vertical = 1
13038baa3e6SBram Moolenaar  else
131b3307b5eSBram Moolenaar    let s:vertical = 0
13238baa3e6SBram Moolenaar  endif
13338baa3e6SBram Moolenaar
134b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
135b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
136b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
137b3307b5eSBram Moolenaar    let s:way = 'terminal'
138b3307b5eSBram Moolenaar  else
139b3307b5eSBram Moolenaar    let s:way = 'prompt'
140b3307b5eSBram Moolenaar  endif
141b3307b5eSBram Moolenaar
142b3307b5eSBram Moolenaar  if s:way == 'prompt'
143b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
144b3307b5eSBram Moolenaar  else
145b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
146b3307b5eSBram Moolenaar  endif
147b3307b5eSBram Moolenaarendfunc
148b3307b5eSBram Moolenaar
149ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended.
150ef3c6a5bSBram Moolenaarfunc s:CloseBuffers()
151ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
152ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:commbuf
153ef3c6a5bSBram Moolenaar  unlet! s:gdbwin
154ef3c6a5bSBram Moolenaarendfunc
155ef3c6a5bSBram Moolenaar
156b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
157b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
158fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
159b3307b5eSBram Moolenaar        \ 'term_name': 'debugged program',
160b3307b5eSBram Moolenaar        \ 'vertical': s:vertical,
161fe386641SBram Moolenaar        \ })
162fe386641SBram Moolenaar  if s:ptybuf == 0
163fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
164fe386641SBram Moolenaar    return
165fe386641SBram Moolenaar  endif
166fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
16745d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
168b3307b5eSBram Moolenaar  if s:vertical
16951b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
17051b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
17151b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
17268e6560bSBram Moolenaar    if s:allleft
17368e6560bSBram Moolenaar      " use the whole left column
17468e6560bSBram Moolenaar      wincmd H
17568e6560bSBram Moolenaar    endif
17651b0f370SBram Moolenaar  endif
177fe386641SBram Moolenaar
178fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
179fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
180fe386641SBram Moolenaar        \ 'term_name': 'gdb communication',
181fe386641SBram Moolenaar        \ 'out_cb': function('s:CommOutput'),
182fe386641SBram Moolenaar        \ 'hidden': 1,
183fe386641SBram Moolenaar        \ })
184fe386641SBram Moolenaar  if s:commbuf == 0
185fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
186fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
187fe386641SBram Moolenaar    return
188fe386641SBram Moolenaar  endif
189fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
190c572da5fSBram Moolenaar
191c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
192c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
19332c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
19432c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
19532c67ba7SBram Moolenaar
19632c67ba7SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
197b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
19860e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
199fe386641SBram Moolenaar        \ 'term_finish': 'close',
200c572da5fSBram Moolenaar        \ })
20160e73f2aSBram Moolenaar  if s:gdbbuf == 0
202fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
203ef3c6a5bSBram Moolenaar    call s:CloseBuffers()
204fe386641SBram Moolenaar    return
205fe386641SBram Moolenaar  endif
20645d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
207fe386641SBram Moolenaar
20832c67ba7SBram Moolenaar  " Set arguments to be run
20932c67ba7SBram Moolenaar  if len(proc_args)
21032c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
21132c67ba7SBram Moolenaar  endif
21232c67ba7SBram Moolenaar
213fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
21460e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
21560e73f2aSBram Moolenaar
2163e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
2173e4b84d0SBram Moolenaar  " why the debugger doesn't work.
2183e4b84d0SBram Moolenaar  let try_count = 0
2193e4b84d0SBram Moolenaar  while 1
220ef3c6a5bSBram Moolenaar    let gdbproc = term_getjob(s:gdbbuf)
221ef3c6a5bSBram Moolenaar    if gdbproc == v:null || job_status(gdbproc) !=# 'run'
222ef3c6a5bSBram Moolenaar      echoerr string(g:termdebugger) . ' exited unexpectedly'
223ef3c6a5bSBram Moolenaar      call s:CloseBuffers()
224ef3c6a5bSBram Moolenaar      return
225ef3c6a5bSBram Moolenaar    endif
226ef3c6a5bSBram Moolenaar
2273e4b84d0SBram Moolenaar    let response = ''
228b3623a38SBram Moolenaar    for lnum in range(1, 200)
229*19c8fe19SBram Moolenaar      let line1 = term_getline(s:gdbbuf, lnum)
230*19c8fe19SBram Moolenaar      let line2 = term_getline(s:gdbbuf, lnum + 1)
231*19c8fe19SBram Moolenaar      if line1 =~ 'new-ui mi '
232f63db65bSBram Moolenaar        " response can be in the same line or the next line
233*19c8fe19SBram Moolenaar        let response = line1 . line2
2343e4b84d0SBram Moolenaar        if response =~ 'Undefined command'
235f3ba14ffSBram Moolenaar          echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
236ef3c6a5bSBram Moolenaar	  call s:CloseBuffers()
2373e4b84d0SBram Moolenaar          return
2383e4b84d0SBram Moolenaar        endif
2393e4b84d0SBram Moolenaar        if response =~ 'New UI allocated'
2403e4b84d0SBram Moolenaar          " Success!
2413e4b84d0SBram Moolenaar          break
2423e4b84d0SBram Moolenaar        endif
243*19c8fe19SBram Moolenaar      elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi '
244*19c8fe19SBram Moolenaar	" Reading symbols might take a while, try more times
24506fe74aeSBram Moolenaar	let try_count -= 1
24606fe74aeSBram Moolenaar      endif
2473e4b84d0SBram Moolenaar    endfor
2483e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2493e4b84d0SBram Moolenaar      break
2503e4b84d0SBram Moolenaar    endif
2513e4b84d0SBram Moolenaar    let try_count += 1
2523e4b84d0SBram Moolenaar    if try_count > 100
2533e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2543e4b84d0SBram Moolenaar      break
2553e4b84d0SBram Moolenaar    endif
2563e4b84d0SBram Moolenaar    sleep 10m
2573e4b84d0SBram Moolenaar  endwhile
2583e4b84d0SBram Moolenaar
25960e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
26060e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
26160e73f2aSBram Moolenaar  " running.
26260e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
263b3307b5eSBram Moolenaar  " Older gdb uses a different command.
264b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
265e09ba7baSBram Moolenaar
266f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
267f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
268b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
269f3ba14ffSBram Moolenaar
270ef3c6a5bSBram Moolenaar  call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')})
271b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
272b3307b5eSBram Moolenaarendfunc
273b3307b5eSBram Moolenaar
274b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
275b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
276b3307b5eSBram Moolenaar  if s:vertical
277b3307b5eSBram Moolenaar    vertical new
278b3307b5eSBram Moolenaar  else
279b3307b5eSBram Moolenaar    new
280b3307b5eSBram Moolenaar  endif
281b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
282b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
283b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
284b3307b5eSBram Moolenaar  set buftype=prompt
285b3307b5eSBram Moolenaar  file gdb
286b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
287b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
288b3307b5eSBram Moolenaar
289b3307b5eSBram Moolenaar  if s:vertical
290b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
291b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
292b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
293b3307b5eSBram Moolenaar  endif
294b3307b5eSBram Moolenaar
295b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
296b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
297b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
298b3307b5eSBram Moolenaar
299b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
300b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
301b3307b5eSBram Moolenaar
302b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
303b3307b5eSBram Moolenaar        \ 'exit_cb': function('s:EndPromptDebug'),
304b3307b5eSBram Moolenaar        \ 'out_cb': function('s:GdbOutCallback'),
305b3307b5eSBram Moolenaar        \ })
306b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
307b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
308b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
309b3307b5eSBram Moolenaar    return
310b3307b5eSBram Moolenaar  endif
3114551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
3124551c0a9SBram Moolenaar  set modified
313b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
314b3307b5eSBram Moolenaar
315b3307b5eSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only
316b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
317b3307b5eSBram Moolenaar  " target is running.
318b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
319b3307b5eSBram Moolenaar  " Older gdb uses a different command.
320b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
321b3307b5eSBram Moolenaar
322b3307b5eSBram Moolenaar  let s:ptybuf = 0
323b3307b5eSBram Moolenaar  if has('win32')
324b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
325b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
326b3307b5eSBram Moolenaar  elseif has('terminal')
327b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
328b3307b5eSBram Moolenaar    " gdb window.
329b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
330b3307b5eSBram Moolenaar          \ 'term_name': 'debugged program',
331b3307b5eSBram Moolenaar          \ })
332b3307b5eSBram Moolenaar    if s:ptybuf == 0
333b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
334b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
335b3307b5eSBram Moolenaar      return
336b3307b5eSBram Moolenaar    endif
337b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
338b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
339b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
340b3307b5eSBram Moolenaar
341b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
342b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
343b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
344b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
345b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
346b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
347b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
348b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
349b3307b5eSBram Moolenaar  else
350b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
351b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
352b3307b5eSBram Moolenaar  endif
353b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
354b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
355b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
356b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
357b3307b5eSBram Moolenaar
358b3307b5eSBram Moolenaar  " Set arguments to be run
359b3307b5eSBram Moolenaar  if len(proc_args)
360b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
361b3307b5eSBram Moolenaar  endif
362b3307b5eSBram Moolenaar
363b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
364b3307b5eSBram Moolenaar  startinsert
365b3307b5eSBram Moolenaarendfunc
366b3307b5eSBram Moolenaar
367b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
36838baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
36938baa3e6SBram Moolenaar  " There can be only one.
37038baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
37138baa3e6SBram Moolenaar
37245d5f26dSBram Moolenaar  " Install debugger commands in the text window.
373b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
374e09ba7baSBram Moolenaar  call s:InstallCommands()
37545d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
376e09ba7baSBram Moolenaar
37751b0f370SBram Moolenaar  " Enable showing a balloon with eval info
378246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
379246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
38051b0f370SBram Moolenaar    if has("balloon_eval")
38151b0f370SBram Moolenaar      set ballooneval
382246fe03dSBram Moolenaar    endif
38351b0f370SBram Moolenaar    if has("balloon_eval_term")
38451b0f370SBram Moolenaar      set balloonevalterm
38551b0f370SBram Moolenaar    endif
38651b0f370SBram Moolenaar  endif
38751b0f370SBram Moolenaar
3885378e1cfSBram Moolenaar  " Contains breakpoints that have been placed, key is a string with the GDB
3895378e1cfSBram Moolenaar  " breakpoint number.
39037402ed5SBram Moolenaar  " Each entry is a dict, containing the sub-breakpoints.  Key is the subid.
39137402ed5SBram Moolenaar  " For a breakpoint that is just a number the subid is zero.
39237402ed5SBram Moolenaar  " For a breakpoint "123.4" the id is "123" and subid is "4".
39337402ed5SBram Moolenaar  " Example, when breakpoint "44", "123", "123.1" and "123.2" exist:
39437402ed5SBram Moolenaar  " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}}
395e09ba7baSBram Moolenaar  let s:breakpoints = {}
3961b9645deSBram Moolenaar
39737402ed5SBram Moolenaar  " Contains breakpoints by file/lnum.  The key is "fname:lnum".
39837402ed5SBram Moolenaar  " Each entry is a list of breakpoint IDs at that position.
39937402ed5SBram Moolenaar  let s:breakpoint_locations = {}
40037402ed5SBram Moolenaar
4011b9645deSBram Moolenaar  augroup TermDebug
4021b9645deSBram Moolenaar    au BufRead * call s:BufRead()
4031b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
404f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
4051b9645deSBram Moolenaar  augroup END
40632c67ba7SBram Moolenaar
407b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
408b3307b5eSBram Moolenaar  " window.
40932c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
41032c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
41132c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
41232c67ba7SBram Moolenaar  endif
413c572da5fSBram Moolenaarendfunc
414c572da5fSBram Moolenaar
415b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
416b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
417b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
418b3307b5eSBram Moolenaar  if s:way == 'prompt'
419b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
420b3307b5eSBram Moolenaar  else
421b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
422b3307b5eSBram Moolenaar  endif
423b3307b5eSBram Moolenaarendfunc
424b3307b5eSBram Moolenaar
425b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
426b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
427b3307b5eSBram Moolenaar  if s:way == 'prompt'
428b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
429b3307b5eSBram Moolenaar  else
430b3307b5eSBram Moolenaar    let do_continue = 0
431b3307b5eSBram Moolenaar    if !s:stopped
432b3307b5eSBram Moolenaar      let do_continue = 1
433b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
434b3307b5eSBram Moolenaar      sleep 10m
435b3307b5eSBram Moolenaar    endif
436b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
437b3307b5eSBram Moolenaar    if do_continue
438b3307b5eSBram Moolenaar      Continue
439b3307b5eSBram Moolenaar    endif
440b3307b5eSBram Moolenaar  endif
441b3307b5eSBram Moolenaarendfunc
442b3307b5eSBram Moolenaar
443b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
444b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
445b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
446b3307b5eSBram Moolenaarendfunc
447b3307b5eSBram Moolenaar
4484551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
4494551c0a9SBram Moolenaar" breakpoint.
450b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
4512ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
4522ed890f1SBram Moolenaar  if has('win32')
4532ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
4542ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
4554551c0a9SBram Moolenaar    if s:pid == 0
4564551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
4574551c0a9SBram Moolenaar    else
4584551c0a9SBram Moolenaar      call debugbreak(s:pid)
4594551c0a9SBram Moolenaar    endif
4602ed890f1SBram Moolenaar  else
4612ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
4622ed890f1SBram Moolenaar  endif
463b3307b5eSBram Moolenaarendfunc
464b3307b5eSBram Moolenaar
465b3307b5eSBram Moolenaar" Function called when gdb outputs text.
466b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
467b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
468b3307b5eSBram Moolenaar
469b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
470b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
471a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
472b3307b5eSBram Moolenaar    return
473b3307b5eSBram Moolenaar  endif
474b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
475b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
476b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
477b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
478b3307b5eSBram Moolenaar      unlet s:evalexpr
479b3307b5eSBram Moolenaar      return
480b3307b5eSBram Moolenaar    endif
481b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
482b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
483b3307b5eSBram Moolenaar  else
484b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
485b3307b5eSBram Moolenaar    return
486b3307b5eSBram Moolenaar  endif
487b3307b5eSBram Moolenaar
488b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
489b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
490b3307b5eSBram Moolenaar
491b3307b5eSBram Moolenaar  " Add the output above the current prompt.
492b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
4934551c0a9SBram Moolenaar  set modified
494b3307b5eSBram Moolenaar
495b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
496b3307b5eSBram Moolenaarendfunc
497b3307b5eSBram Moolenaar
498b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
499b3307b5eSBram Moolenaar" to the next ", unescaping characters.
500b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
501b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
502a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
503b3307b5eSBram Moolenaar    return
504b3307b5eSBram Moolenaar  endif
505b3307b5eSBram Moolenaar  let result = ''
506b3307b5eSBram Moolenaar  let i = 1
507b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
508b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
509b3307b5eSBram Moolenaar      let i += 1
510b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
511b3307b5eSBram Moolenaar        " drop \n
512b3307b5eSBram Moolenaar        let i += 1
513b3307b5eSBram Moolenaar        continue
514b3307b5eSBram Moolenaar      endif
515b3307b5eSBram Moolenaar    endif
516b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
517b3307b5eSBram Moolenaar    let i += 1
518b3307b5eSBram Moolenaar  endwhile
519b3307b5eSBram Moolenaar  return result
520b3307b5eSBram Moolenaarendfunc
521b3307b5eSBram Moolenaar
522a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
523a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
5245378e1cfSBram Moolenaar  if a:msg !~ 'fullname'
5255378e1cfSBram Moolenaar    return ''
5265378e1cfSBram Moolenaar  endif
527a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
528a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
529a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
530a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
531a15b0a93SBram Moolenaar  endif
532a15b0a93SBram Moolenaar  return name
533a15b0a93SBram Moolenaarendfunc
534a15b0a93SBram Moolenaar
535b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
536fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
537b3623a38SBram Moolenaar  unlet s:gdbwin
538e09ba7baSBram Moolenaar
539b3307b5eSBram Moolenaar  call s:EndDebugCommon()
540b3307b5eSBram Moolenaarendfunc
541b3307b5eSBram Moolenaar
542b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
543e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
544e09ba7baSBram Moolenaar
545b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
546b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
547b3307b5eSBram Moolenaar  endif
548b3307b5eSBram Moolenaar
549b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
550e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
551e09ba7baSBram Moolenaar  call s:DeleteCommands()
552e09ba7baSBram Moolenaar
553e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
554b3307b5eSBram Moolenaar
55538baa3e6SBram Moolenaar  if s:save_columns > 0
55638baa3e6SBram Moolenaar    let &columns = s:save_columns
55738baa3e6SBram Moolenaar  endif
5581b9645deSBram Moolenaar
559246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
560246fe03dSBram Moolenaar    set balloonexpr=
56151b0f370SBram Moolenaar    if has("balloon_eval")
56251b0f370SBram Moolenaar      set noballooneval
563246fe03dSBram Moolenaar    endif
56451b0f370SBram Moolenaar    if has("balloon_eval_term")
56551b0f370SBram Moolenaar      set noballoonevalterm
56651b0f370SBram Moolenaar    endif
56751b0f370SBram Moolenaar  endif
56851b0f370SBram Moolenaar
5691b9645deSBram Moolenaar  au! TermDebug
570fe386641SBram Moolenaarendfunc
571fe386641SBram Moolenaar
572b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
573b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
574b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
5754551c0a9SBram Moolenaar  set nomodified
576b3307b5eSBram Moolenaar  close
577b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
578b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
579b3307b5eSBram Moolenaar  endif
580b3307b5eSBram Moolenaar
581b3307b5eSBram Moolenaar  call s:EndDebugCommon()
582b3307b5eSBram Moolenaar  unlet s:gdbwin
583b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
584b3307b5eSBram Moolenaarendfunc
585b3307b5eSBram Moolenaar
586fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
587fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
588fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
589fe386641SBram Moolenaar
590fe386641SBram Moolenaar  for msg in msgs
591fe386641SBram Moolenaar    " remove prefixed NL
592fe386641SBram Moolenaar    if msg[0] == "\n"
593fe386641SBram Moolenaar      let msg = msg[1:]
594fe386641SBram Moolenaar    endif
595fe386641SBram Moolenaar    if msg != ''
5961b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
597e09ba7baSBram Moolenaar        call s:HandleCursor(msg)
59845d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
599e09ba7baSBram Moolenaar        call s:HandleNewBreakpoint(msg)
600e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
601e09ba7baSBram Moolenaar        call s:HandleBreakpointDelete(msg)
6024551c0a9SBram Moolenaar      elseif msg =~ '^=thread-group-started'
6034551c0a9SBram Moolenaar        call s:HandleProgramRun(msg)
60445d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
60545d5f26dSBram Moolenaar        call s:HandleEvaluate(msg)
60645d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
60745d5f26dSBram Moolenaar        call s:HandleError(msg)
608e09ba7baSBram Moolenaar      endif
609e09ba7baSBram Moolenaar    endif
610e09ba7baSBram Moolenaar  endfor
611e09ba7baSBram Moolenaarendfunc
612e09ba7baSBram Moolenaar
613e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
614e09ba7baSBram Moolenaarfunc s:InstallCommands()
615963c1ad5SBram Moolenaar  let save_cpo = &cpo
616963c1ad5SBram Moolenaar  set cpo&vim
617963c1ad5SBram Moolenaar
618e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
61971137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
620e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
62145d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
622e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
62360e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
62460e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
62560e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
626b3307b5eSBram Moolenaar
627b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
628b3307b5eSBram Moolenaar  if s:way == 'prompt'
629b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
630b3307b5eSBram Moolenaar  else
631b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
632b3307b5eSBram Moolenaar  endif
633b3307b5eSBram Moolenaar
63445d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
63545d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
63645d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
637b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
63871137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
63945d5f26dSBram Moolenaar
64045d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
64145d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
6421b9645deSBram Moolenaar
643f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
64471137fedSBram Moolenaar    call s:InstallWinbar()
64571137fedSBram Moolenaar
64671137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
64771137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
64871137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
64971137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
65071137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
65171137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
65271137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
65371137fedSBram Moolenaar    endif
65471137fedSBram Moolenaar  endif
655963c1ad5SBram Moolenaar
656963c1ad5SBram Moolenaar  let &cpo = save_cpo
65771137fedSBram Moolenaarendfunc
65871137fedSBram Moolenaar
65971137fedSBram Moolenaarlet s:winbar_winids = []
66071137fedSBram Moolenaar
66171137fedSBram Moolenaar" Install the window toolbar in the current window.
66271137fedSBram Moolenaarfunc s:InstallWinbar()
663c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
66424a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
66524a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
66624a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
66724a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
66860e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
66924a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
67071137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
671c4b533e1SBram Moolenaar  endif
672e09ba7baSBram Moolenaarendfunc
673e09ba7baSBram Moolenaar
674e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
675e09ba7baSBram Moolenaarfunc s:DeleteCommands()
676e09ba7baSBram Moolenaar  delcommand Break
67771137fedSBram Moolenaar  delcommand Clear
678e09ba7baSBram Moolenaar  delcommand Step
67945d5f26dSBram Moolenaar  delcommand Over
680e09ba7baSBram Moolenaar  delcommand Finish
68160e73f2aSBram Moolenaar  delcommand Run
68260e73f2aSBram Moolenaar  delcommand Arguments
68360e73f2aSBram Moolenaar  delcommand Stop
684e09ba7baSBram Moolenaar  delcommand Continue
68545d5f26dSBram Moolenaar  delcommand Evaluate
68645d5f26dSBram Moolenaar  delcommand Gdb
68745d5f26dSBram Moolenaar  delcommand Program
688b3623a38SBram Moolenaar  delcommand Source
68971137fedSBram Moolenaar  delcommand Winbar
69045d5f26dSBram Moolenaar
69145d5f26dSBram Moolenaar  nunmap K
6921b9645deSBram Moolenaar
6931b9645deSBram Moolenaar  if has('menu')
69471137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
69571137fedSBram Moolenaar    let curwinid = win_getid(winnr())
69671137fedSBram Moolenaar    for winid in s:winbar_winids
69771137fedSBram Moolenaar      if win_gotoid(winid)
6981b9645deSBram Moolenaar        aunmenu WinBar.Step
6991b9645deSBram Moolenaar        aunmenu WinBar.Next
7001b9645deSBram Moolenaar        aunmenu WinBar.Finish
7011b9645deSBram Moolenaar        aunmenu WinBar.Cont
70260e73f2aSBram Moolenaar        aunmenu WinBar.Stop
7031b9645deSBram Moolenaar        aunmenu WinBar.Eval
7041b9645deSBram Moolenaar      endif
70571137fedSBram Moolenaar    endfor
70671137fedSBram Moolenaar    call win_gotoid(curwinid)
70771137fedSBram Moolenaar    let s:winbar_winids = []
70871137fedSBram Moolenaar
70971137fedSBram Moolenaar    if exists('s:saved_mousemodel')
71071137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
71171137fedSBram Moolenaar      unlet s:saved_mousemodel
71271137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
71371137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
71471137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
71571137fedSBram Moolenaar      aunmenu PopUp.Evaluate
71671137fedSBram Moolenaar    endif
71771137fedSBram Moolenaar  endif
7181b9645deSBram Moolenaar
71945d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
72037402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
72137402ed5SBram Moolenaar    for subid in keys(entries)
72237402ed5SBram Moolenaar      exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
72337402ed5SBram Moolenaar    endfor
72445d5f26dSBram Moolenaar  endfor
72545d5f26dSBram Moolenaar  unlet s:breakpoints
72637402ed5SBram Moolenaar  unlet s:breakpoint_locations
727a15b0a93SBram Moolenaar
728a15b0a93SBram Moolenaar  sign undefine debugPC
729a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
730a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
731a15b0a93SBram Moolenaar  endfor
7324551c0a9SBram Moolenaar  let s:BreakpointSigns = []
733e09ba7baSBram Moolenaarendfunc
734e09ba7baSBram Moolenaar
735e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
736e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
73760e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
73860e73f2aSBram Moolenaar  " Interrupt to make it work.
73960e73f2aSBram Moolenaar  let do_continue = 0
74060e73f2aSBram Moolenaar  if !s:stopped
74160e73f2aSBram Moolenaar    let do_continue = 1
742b3307b5eSBram Moolenaar    if s:way == 'prompt'
7434551c0a9SBram Moolenaar      call s:PromptInterrupt()
744b3307b5eSBram Moolenaar    else
74560e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
746b3307b5eSBram Moolenaar    endif
74760e73f2aSBram Moolenaar    sleep 10m
74860e73f2aSBram Moolenaar  endif
749a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
750a15b0a93SBram Moolenaar  call s:SendCommand('-break-insert '
751a15b0a93SBram Moolenaar        \ . fnameescape(expand('%:p')) . ':' . line('.'))
75260e73f2aSBram Moolenaar  if do_continue
75360e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
75460e73f2aSBram Moolenaar  endif
755e09ba7baSBram Moolenaarendfunc
756e09ba7baSBram Moolenaar
75771137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
75871137fedSBram Moolenaarfunc s:ClearBreakpoint()
759e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
760e09ba7baSBram Moolenaar  let lnum = line('.')
76137402ed5SBram Moolenaar  let bploc = printf('%s:%d', fname, lnum)
76237402ed5SBram Moolenaar  if has_key(s:breakpoint_locations, bploc)
76337402ed5SBram Moolenaar    let idx = 0
76437402ed5SBram Moolenaar    for id in s:breakpoint_locations[bploc]
76537402ed5SBram Moolenaar      if has_key(s:breakpoints, id)
76637402ed5SBram Moolenaar        " Assume this always works, the reply is simply "^done".
76737402ed5SBram Moolenaar        call s:SendCommand('-break-delete ' . id)
76837402ed5SBram Moolenaar        for subid in keys(s:breakpoints[id])
76937402ed5SBram Moolenaar          exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
77037402ed5SBram Moolenaar        endfor
77137402ed5SBram Moolenaar        unlet s:breakpoints[id]
77237402ed5SBram Moolenaar        unlet s:breakpoint_locations[bploc][idx]
773e09ba7baSBram Moolenaar        break
77437402ed5SBram Moolenaar      else
77537402ed5SBram Moolenaar	let idx += 1
776e09ba7baSBram Moolenaar      endif
777e09ba7baSBram Moolenaar    endfor
77837402ed5SBram Moolenaar    if empty(s:breakpoint_locations[bploc])
77937402ed5SBram Moolenaar      unlet s:breakpoint_locations[bploc]
78037402ed5SBram Moolenaar    endif
78137402ed5SBram Moolenaar  endif
782e09ba7baSBram Moolenaarendfunc
783e09ba7baSBram Moolenaar
78460e73f2aSBram Moolenaarfunc s:Run(args)
78560e73f2aSBram Moolenaar  if a:args != ''
78660e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
78760e73f2aSBram Moolenaar  endif
78860e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
78960e73f2aSBram Moolenaarendfunc
79060e73f2aSBram Moolenaar
79151b0f370SBram Moolenaarfunc s:SendEval(expr)
79251b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
79351b0f370SBram Moolenaar  let s:evalexpr = a:expr
79451b0f370SBram Moolenaarendfunc
79551b0f370SBram Moolenaar
79645d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
79745d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
79845d5f26dSBram Moolenaar  if a:arg != ''
79945d5f26dSBram Moolenaar    let expr = a:arg
80045d5f26dSBram Moolenaar  elseif a:range == 2
80145d5f26dSBram Moolenaar    let pos = getcurpos()
80245d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
80345d5f26dSBram Moolenaar    let regt = getregtype('v')
80445d5f26dSBram Moolenaar    normal! gv"vy
80545d5f26dSBram Moolenaar    let expr = @v
80645d5f26dSBram Moolenaar    call setpos('.', pos)
80745d5f26dSBram Moolenaar    call setreg('v', reg, regt)
80845d5f26dSBram Moolenaar  else
80945d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
81045d5f26dSBram Moolenaar  endif
81122f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
81251b0f370SBram Moolenaar  call s:SendEval(expr)
81345d5f26dSBram Moolenaarendfunc
81445d5f26dSBram Moolenaar
81522f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
81651b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
81751b0f370SBram Moolenaar
81845d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
81945d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
8201b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
8211b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
82251b0f370SBram Moolenaar  if s:evalFromBalloonExpr
82351b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
82451b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
82551b0f370SBram Moolenaar    else
82651b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
82751b0f370SBram Moolenaar    endif
82851b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
82951b0f370SBram Moolenaar  else
8301b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
83151b0f370SBram Moolenaar  endif
8321b9645deSBram Moolenaar
8337f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
8341b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
83522f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
83651b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
83751b0f370SBram Moolenaar  else
83851b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
8391b9645deSBram Moolenaar  endif
84045d5f26dSBram Moolenaarendfunc
84145d5f26dSBram Moolenaar
84251b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
84351b0f370SBram Moolenaar" if there is any.
84451b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
845b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
846396e829fSBram Moolenaar    return ''
847b3307b5eSBram Moolenaar  endif
848b3307b5eSBram Moolenaar  if !s:stopped
849b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
850b3307b5eSBram Moolenaar    " mouse triggers a balloon.
851396e829fSBram Moolenaar    return ''
85251b0f370SBram Moolenaar  endif
85351b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
85451b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
85522f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
85622f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
85751b0f370SBram Moolenaar  return ''
85851b0f370SBram Moolenaarendfunc
85951b0f370SBram Moolenaar
86045d5f26dSBram Moolenaar" Handle an error.
86145d5f26dSBram Moolenaarfunc s:HandleError(msg)
86222f1d0e3SBram Moolenaar  if s:ignoreEvalError
86351b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
86422f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
86522f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
86651b0f370SBram Moolenaar    return
86751b0f370SBram Moolenaar  endif
86845d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
86945d5f26dSBram Moolenaarendfunc
87045d5f26dSBram Moolenaar
871b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
872b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
873c4b533e1SBram Moolenaar    new
874b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
875c4b533e1SBram Moolenaar    call s:InstallWinbar()
876c4b533e1SBram Moolenaar  endif
877c4b533e1SBram Moolenaarendfunc
878c4b533e1SBram Moolenaar
879e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
880e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
881e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
882fe386641SBram Moolenaar  let wid = win_getid(winnr())
883fe386641SBram Moolenaar
88460e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
8854551c0a9SBram Moolenaar    call ch_log('program stopped')
88660e73f2aSBram Moolenaar    let s:stopped = 1
88760e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
8884551c0a9SBram Moolenaar    call ch_log('program running')
88960e73f2aSBram Moolenaar    let s:stopped = 0
89060e73f2aSBram Moolenaar  endif
89160e73f2aSBram Moolenaar
892a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
893a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
894a15b0a93SBram Moolenaar  else
895a15b0a93SBram Moolenaar    let fname = ''
896a15b0a93SBram Moolenaar  endif
8971b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
898e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
899fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
9004551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
9011b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
902fe386641SBram Moolenaar        if &modified
903fe386641SBram Moolenaar          " TODO: find existing window
904fe386641SBram Moolenaar          exe 'split ' . fnameescape(fname)
905b3307b5eSBram Moolenaar          let s:sourcewin = win_getid(winnr())
906c4b533e1SBram Moolenaar          call s:InstallWinbar()
907fe386641SBram Moolenaar        else
908fe386641SBram Moolenaar          exe 'edit ' . fnameescape(fname)
909fe386641SBram Moolenaar        endif
910fe386641SBram Moolenaar      endif
911fe386641SBram Moolenaar      exe lnum
91201164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
9131b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
914fe386641SBram Moolenaar      setlocal signcolumn=yes
915fe386641SBram Moolenaar    endif
9164551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
917fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
918fe386641SBram Moolenaar  endif
919fe386641SBram Moolenaar
920fe386641SBram Moolenaar  call win_gotoid(wid)
921e09ba7baSBram Moolenaarendfunc
922e09ba7baSBram Moolenaar
923de1a8314SBram Moolenaarlet s:BreakpointSigns = []
924a15b0a93SBram Moolenaar
92537402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid)
92637402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
92737402ed5SBram Moolenaar  if index(s:BreakpointSigns, nr) == -1
92837402ed5SBram Moolenaar    call add(s:BreakpointSigns, nr)
92937402ed5SBram Moolenaar    exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint"
930de1a8314SBram Moolenaar  endif
931de1a8314SBram Moolenaarendfunc
932de1a8314SBram Moolenaar
93337402ed5SBram Moolenaarfunc! s:SplitMsg(s)
93437402ed5SBram Moolenaar  return split(a:s, '{.\{-}}\zs')
9355378e1cfSBram Moolenaarendfunction
9365378e1cfSBram Moolenaar
937e09ba7baSBram Moolenaar" Handle setting a breakpoint
938e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
939e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
9406dccc962SBram Moolenaar  if a:msg !~ 'fullname='
9416dccc962SBram Moolenaar    " a watch does not have a file name
9426dccc962SBram Moolenaar    return
9436dccc962SBram Moolenaar  endif
9445378e1cfSBram Moolenaar  for msg in s:SplitMsg(a:msg)
9455378e1cfSBram Moolenaar    let fname = s:GetFullname(msg)
9465378e1cfSBram Moolenaar    if empty(fname)
9475378e1cfSBram Moolenaar      continue
9485378e1cfSBram Moolenaar    endif
9495378e1cfSBram Moolenaar    let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '')
9505378e1cfSBram Moolenaar    if empty(nr)
951e09ba7baSBram Moolenaar      return
952fe386641SBram Moolenaar    endif
953e09ba7baSBram Moolenaar
95437402ed5SBram Moolenaar    " If "nr" is 123 it becomes "123.0" and subid is "0".
95537402ed5SBram Moolenaar    " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded.
95637402ed5SBram Moolenaar    let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0')
95737402ed5SBram Moolenaar    call s:CreateBreakpoint(id, subid)
95837402ed5SBram Moolenaar
95937402ed5SBram Moolenaar    if has_key(s:breakpoints, id)
96037402ed5SBram Moolenaar      let entries = s:breakpoints[id]
96137402ed5SBram Moolenaar    else
96237402ed5SBram Moolenaar      let entries = {}
96337402ed5SBram Moolenaar      let s:breakpoints[id] = entries
96437402ed5SBram Moolenaar    endif
96537402ed5SBram Moolenaar    if has_key(entries, subid)
96637402ed5SBram Moolenaar      let entry = entries[subid]
967e09ba7baSBram Moolenaar    else
968e09ba7baSBram Moolenaar      let entry = {}
96937402ed5SBram Moolenaar      let entries[subid] = entry
970fe386641SBram Moolenaar    endif
971e09ba7baSBram Moolenaar
9725378e1cfSBram Moolenaar    let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
973e09ba7baSBram Moolenaar    let entry['fname'] = fname
974e09ba7baSBram Moolenaar    let entry['lnum'] = lnum
9751b9645deSBram Moolenaar
97637402ed5SBram Moolenaar    let bploc = printf('%s:%d', fname, lnum)
97737402ed5SBram Moolenaar    if !has_key(s:breakpoint_locations, bploc)
97837402ed5SBram Moolenaar      let s:breakpoint_locations[bploc] = []
97937402ed5SBram Moolenaar    endif
98037402ed5SBram Moolenaar    let s:breakpoint_locations[bploc] += [id]
98137402ed5SBram Moolenaar
9821b9645deSBram Moolenaar    if bufloaded(fname)
98337402ed5SBram Moolenaar      call s:PlaceSign(id, subid, entry)
9841b9645deSBram Moolenaar    endif
9855378e1cfSBram Moolenaar  endfor
9861b9645deSBram Moolenaarendfunc
9871b9645deSBram Moolenaar
98837402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry)
98937402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
99037402ed5SBram Moolenaar  exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname']
9911b9645deSBram Moolenaar  let a:entry['placed'] = 1
992e09ba7baSBram Moolenaarendfunc
993e09ba7baSBram Moolenaar
994e09ba7baSBram Moolenaar" Handle deleting a breakpoint
995e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
996e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
99737402ed5SBram Moolenaar  let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
99837402ed5SBram Moolenaar  if empty(id)
999e09ba7baSBram Moolenaar    return
1000e09ba7baSBram Moolenaar  endif
100137402ed5SBram Moolenaar  if has_key(s:breakpoints, id)
100237402ed5SBram Moolenaar    for [subid, entry] in items(s:breakpoints[id])
10031b9645deSBram Moolenaar      if has_key(entry, 'placed')
100437402ed5SBram Moolenaar        exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
10051b9645deSBram Moolenaar        unlet entry['placed']
10061b9645deSBram Moolenaar      endif
10075378e1cfSBram Moolenaar    endfor
100837402ed5SBram Moolenaar    unlet s:breakpoints[id]
100937402ed5SBram Moolenaar  endif
1010c572da5fSBram Moolenaarendfunc
10111b9645deSBram Moolenaar
10124551c0a9SBram Moolenaar" Handle the debugged program starting to run.
10134551c0a9SBram Moolenaar" Will store the process ID in s:pid
10144551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
10154551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
10164551c0a9SBram Moolenaar  if nr == 0
10174551c0a9SBram Moolenaar    return
10184551c0a9SBram Moolenaar  endif
10194551c0a9SBram Moolenaar  let s:pid = nr
10204551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
10214551c0a9SBram Moolenaarendfunc
10224551c0a9SBram Moolenaar
10231b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
10241b9645deSBram Moolenaarfunc s:BufRead()
10251b9645deSBram Moolenaar  let fname = expand('<afile>:p')
102637402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
102737402ed5SBram Moolenaar    for [subid, entry] in items(entries)
10281b9645deSBram Moolenaar      if entry['fname'] == fname
102937402ed5SBram Moolenaar        call s:PlaceSign(id, subid, entry)
10301b9645deSBram Moolenaar      endif
10311b9645deSBram Moolenaar    endfor
103237402ed5SBram Moolenaar  endfor
10331b9645deSBram Moolenaarendfunc
10341b9645deSBram Moolenaar
10351b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
10361b9645deSBram Moolenaarfunc s:BufUnloaded()
10371b9645deSBram Moolenaar  let fname = expand('<afile>:p')
103837402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
103937402ed5SBram Moolenaar    for [subid, entry] in items(entries)
10401b9645deSBram Moolenaar      if entry['fname'] == fname
10411b9645deSBram Moolenaar        let entry['placed'] = 0
10421b9645deSBram Moolenaar      endif
10431b9645deSBram Moolenaar    endfor
104437402ed5SBram Moolenaar  endfor
10451b9645deSBram Moolenaarendfunc
1046ca4cc018SBram Moolenaar
1047ca4cc018SBram Moolenaarlet &cpo = s:keepcpo
1048ca4cc018SBram Moolenaarunlet s:keepcpo
1049