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
120*68e6560bSBram 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
125*68e6560bSBram Moolenaar      " If we make the Vim window wider, use the whole left halve for the debug
126*68e6560bSBram Moolenaar      " windows.
127*68e6560bSBram 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 |"
172*68e6560bSBram Moolenaar    if s:allleft
173*68e6560bSBram Moolenaar      " use the whole left column
174*68e6560bSBram Moolenaar      wincmd H
175*68e6560bSBram 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)
2293e4b84d0SBram Moolenaar      if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi '
230f63db65bSBram Moolenaar        " response can be in the same line or the next line
231f63db65bSBram Moolenaar        let response = term_getline(s:gdbbuf, lnum) . term_getline(s:gdbbuf, lnum + 1)
2323e4b84d0SBram Moolenaar        if response =~ 'Undefined command'
233f3ba14ffSBram Moolenaar          echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
234ef3c6a5bSBram Moolenaar	  call s:CloseBuffers()
2353e4b84d0SBram Moolenaar          return
2363e4b84d0SBram Moolenaar        endif
2373e4b84d0SBram Moolenaar        if response =~ 'New UI allocated'
2383e4b84d0SBram Moolenaar          " Success!
2393e4b84d0SBram Moolenaar          break
2403e4b84d0SBram Moolenaar        endif
2413e4b84d0SBram Moolenaar      endif
2423e4b84d0SBram Moolenaar    endfor
2433e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2443e4b84d0SBram Moolenaar      break
2453e4b84d0SBram Moolenaar    endif
2463e4b84d0SBram Moolenaar    let try_count += 1
2473e4b84d0SBram Moolenaar    if try_count > 100
2483e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2493e4b84d0SBram Moolenaar      break
2503e4b84d0SBram Moolenaar    endif
2513e4b84d0SBram Moolenaar    sleep 10m
2523e4b84d0SBram Moolenaar  endwhile
2533e4b84d0SBram Moolenaar
25460e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
25560e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
25660e73f2aSBram Moolenaar  " running.
25760e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
258b3307b5eSBram Moolenaar  " Older gdb uses a different command.
259b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
260e09ba7baSBram Moolenaar
261f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
262f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
263b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
264f3ba14ffSBram Moolenaar
265ef3c6a5bSBram Moolenaar  call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')})
266b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
267b3307b5eSBram Moolenaarendfunc
268b3307b5eSBram Moolenaar
269b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
270b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
271b3307b5eSBram Moolenaar  if s:vertical
272b3307b5eSBram Moolenaar    vertical new
273b3307b5eSBram Moolenaar  else
274b3307b5eSBram Moolenaar    new
275b3307b5eSBram Moolenaar  endif
276b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
277b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
278b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
279b3307b5eSBram Moolenaar  set buftype=prompt
280b3307b5eSBram Moolenaar  file gdb
281b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
282b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
283b3307b5eSBram Moolenaar
284b3307b5eSBram Moolenaar  if s:vertical
285b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
286b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
287b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
288b3307b5eSBram Moolenaar  endif
289b3307b5eSBram Moolenaar
290b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
291b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
292b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
293b3307b5eSBram Moolenaar
294b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
295b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
296b3307b5eSBram Moolenaar
297b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
298b3307b5eSBram Moolenaar        \ 'exit_cb': function('s:EndPromptDebug'),
299b3307b5eSBram Moolenaar        \ 'out_cb': function('s:GdbOutCallback'),
300b3307b5eSBram Moolenaar        \ })
301b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
302b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
303b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
304b3307b5eSBram Moolenaar    return
305b3307b5eSBram Moolenaar  endif
3064551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
3074551c0a9SBram Moolenaar  set modified
308b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
309b3307b5eSBram Moolenaar
310b3307b5eSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only
311b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
312b3307b5eSBram Moolenaar  " target is running.
313b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
314b3307b5eSBram Moolenaar  " Older gdb uses a different command.
315b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
316b3307b5eSBram Moolenaar
317b3307b5eSBram Moolenaar  let s:ptybuf = 0
318b3307b5eSBram Moolenaar  if has('win32')
319b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
320b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
321b3307b5eSBram Moolenaar  elseif has('terminal')
322b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
323b3307b5eSBram Moolenaar    " gdb window.
324b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
325b3307b5eSBram Moolenaar          \ 'term_name': 'debugged program',
326b3307b5eSBram Moolenaar          \ })
327b3307b5eSBram Moolenaar    if s:ptybuf == 0
328b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
329b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
330b3307b5eSBram Moolenaar      return
331b3307b5eSBram Moolenaar    endif
332b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
333b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
334b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
335b3307b5eSBram Moolenaar
336b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
337b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
338b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
339b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
340b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
341b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
342b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
343b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
344b3307b5eSBram Moolenaar  else
345b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
346b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
347b3307b5eSBram Moolenaar  endif
348b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
349b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
350b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
351b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
352b3307b5eSBram Moolenaar
353b3307b5eSBram Moolenaar  " Set arguments to be run
354b3307b5eSBram Moolenaar  if len(proc_args)
355b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
356b3307b5eSBram Moolenaar  endif
357b3307b5eSBram Moolenaar
358b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
359b3307b5eSBram Moolenaar  startinsert
360b3307b5eSBram Moolenaarendfunc
361b3307b5eSBram Moolenaar
362b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
36338baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
36438baa3e6SBram Moolenaar  " There can be only one.
36538baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
36638baa3e6SBram Moolenaar
36745d5f26dSBram Moolenaar  " Install debugger commands in the text window.
368b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
369e09ba7baSBram Moolenaar  call s:InstallCommands()
37045d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
371e09ba7baSBram Moolenaar
37251b0f370SBram Moolenaar  " Enable showing a balloon with eval info
373246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
374246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
37551b0f370SBram Moolenaar    if has("balloon_eval")
37651b0f370SBram Moolenaar      set ballooneval
377246fe03dSBram Moolenaar    endif
37851b0f370SBram Moolenaar    if has("balloon_eval_term")
37951b0f370SBram Moolenaar      set balloonevalterm
38051b0f370SBram Moolenaar    endif
38151b0f370SBram Moolenaar  endif
38251b0f370SBram Moolenaar
3835378e1cfSBram Moolenaar  " Contains breakpoints that have been placed, key is a string with the GDB
3845378e1cfSBram Moolenaar  " breakpoint number.
38537402ed5SBram Moolenaar  " Each entry is a dict, containing the sub-breakpoints.  Key is the subid.
38637402ed5SBram Moolenaar  " For a breakpoint that is just a number the subid is zero.
38737402ed5SBram Moolenaar  " For a breakpoint "123.4" the id is "123" and subid is "4".
38837402ed5SBram Moolenaar  " Example, when breakpoint "44", "123", "123.1" and "123.2" exist:
38937402ed5SBram Moolenaar  " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}}
390e09ba7baSBram Moolenaar  let s:breakpoints = {}
3911b9645deSBram Moolenaar
39237402ed5SBram Moolenaar  " Contains breakpoints by file/lnum.  The key is "fname:lnum".
39337402ed5SBram Moolenaar  " Each entry is a list of breakpoint IDs at that position.
39437402ed5SBram Moolenaar  let s:breakpoint_locations = {}
39537402ed5SBram Moolenaar
3961b9645deSBram Moolenaar  augroup TermDebug
3971b9645deSBram Moolenaar    au BufRead * call s:BufRead()
3981b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
399f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
4001b9645deSBram Moolenaar  augroup END
40132c67ba7SBram Moolenaar
402b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
403b3307b5eSBram Moolenaar  " window.
40432c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
40532c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
40632c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
40732c67ba7SBram Moolenaar  endif
408c572da5fSBram Moolenaarendfunc
409c572da5fSBram Moolenaar
410b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
411b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
412b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
413b3307b5eSBram Moolenaar  if s:way == 'prompt'
414b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
415b3307b5eSBram Moolenaar  else
416b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
417b3307b5eSBram Moolenaar  endif
418b3307b5eSBram Moolenaarendfunc
419b3307b5eSBram Moolenaar
420b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
421b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
422b3307b5eSBram Moolenaar  if s:way == 'prompt'
423b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
424b3307b5eSBram Moolenaar  else
425b3307b5eSBram Moolenaar    let do_continue = 0
426b3307b5eSBram Moolenaar    if !s:stopped
427b3307b5eSBram Moolenaar      let do_continue = 1
428b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
429b3307b5eSBram Moolenaar      sleep 10m
430b3307b5eSBram Moolenaar    endif
431b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
432b3307b5eSBram Moolenaar    if do_continue
433b3307b5eSBram Moolenaar      Continue
434b3307b5eSBram Moolenaar    endif
435b3307b5eSBram Moolenaar  endif
436b3307b5eSBram Moolenaarendfunc
437b3307b5eSBram Moolenaar
438b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
439b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
440b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
441b3307b5eSBram Moolenaarendfunc
442b3307b5eSBram Moolenaar
4434551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
4444551c0a9SBram Moolenaar" breakpoint.
445b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
4462ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
4472ed890f1SBram Moolenaar  if has('win32')
4482ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
4492ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
4504551c0a9SBram Moolenaar    if s:pid == 0
4514551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
4524551c0a9SBram Moolenaar    else
4534551c0a9SBram Moolenaar      call debugbreak(s:pid)
4544551c0a9SBram Moolenaar    endif
4552ed890f1SBram Moolenaar  else
4562ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
4572ed890f1SBram Moolenaar  endif
458b3307b5eSBram Moolenaarendfunc
459b3307b5eSBram Moolenaar
460b3307b5eSBram Moolenaar" Function called when gdb outputs text.
461b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
462b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
463b3307b5eSBram Moolenaar
464b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
465b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
466a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
467b3307b5eSBram Moolenaar    return
468b3307b5eSBram Moolenaar  endif
469b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
470b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
471b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
472b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
473b3307b5eSBram Moolenaar      unlet s:evalexpr
474b3307b5eSBram Moolenaar      return
475b3307b5eSBram Moolenaar    endif
476b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
477b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
478b3307b5eSBram Moolenaar  else
479b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
480b3307b5eSBram Moolenaar    return
481b3307b5eSBram Moolenaar  endif
482b3307b5eSBram Moolenaar
483b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
484b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
485b3307b5eSBram Moolenaar
486b3307b5eSBram Moolenaar  " Add the output above the current prompt.
487b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
4884551c0a9SBram Moolenaar  set modified
489b3307b5eSBram Moolenaar
490b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
491b3307b5eSBram Moolenaarendfunc
492b3307b5eSBram Moolenaar
493b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
494b3307b5eSBram Moolenaar" to the next ", unescaping characters.
495b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
496b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
497a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
498b3307b5eSBram Moolenaar    return
499b3307b5eSBram Moolenaar  endif
500b3307b5eSBram Moolenaar  let result = ''
501b3307b5eSBram Moolenaar  let i = 1
502b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
503b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
504b3307b5eSBram Moolenaar      let i += 1
505b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
506b3307b5eSBram Moolenaar        " drop \n
507b3307b5eSBram Moolenaar        let i += 1
508b3307b5eSBram Moolenaar        continue
509b3307b5eSBram Moolenaar      endif
510b3307b5eSBram Moolenaar    endif
511b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
512b3307b5eSBram Moolenaar    let i += 1
513b3307b5eSBram Moolenaar  endwhile
514b3307b5eSBram Moolenaar  return result
515b3307b5eSBram Moolenaarendfunc
516b3307b5eSBram Moolenaar
517a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
518a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
5195378e1cfSBram Moolenaar  if a:msg !~ 'fullname'
5205378e1cfSBram Moolenaar    return ''
5215378e1cfSBram Moolenaar  endif
522a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
523a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
524a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
525a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
526a15b0a93SBram Moolenaar  endif
527a15b0a93SBram Moolenaar  return name
528a15b0a93SBram Moolenaarendfunc
529a15b0a93SBram Moolenaar
530b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
531fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
532b3623a38SBram Moolenaar  unlet s:gdbwin
533e09ba7baSBram Moolenaar
534b3307b5eSBram Moolenaar  call s:EndDebugCommon()
535b3307b5eSBram Moolenaarendfunc
536b3307b5eSBram Moolenaar
537b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
538e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
539e09ba7baSBram Moolenaar
540b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
541b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
542b3307b5eSBram Moolenaar  endif
543b3307b5eSBram Moolenaar
544b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
545e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
546e09ba7baSBram Moolenaar  call s:DeleteCommands()
547e09ba7baSBram Moolenaar
548e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
549b3307b5eSBram Moolenaar
55038baa3e6SBram Moolenaar  if s:save_columns > 0
55138baa3e6SBram Moolenaar    let &columns = s:save_columns
55238baa3e6SBram Moolenaar  endif
5531b9645deSBram Moolenaar
554246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
555246fe03dSBram Moolenaar    set balloonexpr=
55651b0f370SBram Moolenaar    if has("balloon_eval")
55751b0f370SBram Moolenaar      set noballooneval
558246fe03dSBram Moolenaar    endif
55951b0f370SBram Moolenaar    if has("balloon_eval_term")
56051b0f370SBram Moolenaar      set noballoonevalterm
56151b0f370SBram Moolenaar    endif
56251b0f370SBram Moolenaar  endif
56351b0f370SBram Moolenaar
5641b9645deSBram Moolenaar  au! TermDebug
565fe386641SBram Moolenaarendfunc
566fe386641SBram Moolenaar
567b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
568b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
569b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
5704551c0a9SBram Moolenaar  set nomodified
571b3307b5eSBram Moolenaar  close
572b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
573b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
574b3307b5eSBram Moolenaar  endif
575b3307b5eSBram Moolenaar
576b3307b5eSBram Moolenaar  call s:EndDebugCommon()
577b3307b5eSBram Moolenaar  unlet s:gdbwin
578b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
579b3307b5eSBram Moolenaarendfunc
580b3307b5eSBram Moolenaar
581fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
582fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
583fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
584fe386641SBram Moolenaar
585fe386641SBram Moolenaar  for msg in msgs
586fe386641SBram Moolenaar    " remove prefixed NL
587fe386641SBram Moolenaar    if msg[0] == "\n"
588fe386641SBram Moolenaar      let msg = msg[1:]
589fe386641SBram Moolenaar    endif
590fe386641SBram Moolenaar    if msg != ''
5911b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
592e09ba7baSBram Moolenaar        call s:HandleCursor(msg)
59345d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
594e09ba7baSBram Moolenaar        call s:HandleNewBreakpoint(msg)
595e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
596e09ba7baSBram Moolenaar        call s:HandleBreakpointDelete(msg)
5974551c0a9SBram Moolenaar      elseif msg =~ '^=thread-group-started'
5984551c0a9SBram Moolenaar        call s:HandleProgramRun(msg)
59945d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
60045d5f26dSBram Moolenaar        call s:HandleEvaluate(msg)
60145d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
60245d5f26dSBram Moolenaar        call s:HandleError(msg)
603e09ba7baSBram Moolenaar      endif
604e09ba7baSBram Moolenaar    endif
605e09ba7baSBram Moolenaar  endfor
606e09ba7baSBram Moolenaarendfunc
607e09ba7baSBram Moolenaar
608e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
609e09ba7baSBram Moolenaarfunc s:InstallCommands()
610963c1ad5SBram Moolenaar  let save_cpo = &cpo
611963c1ad5SBram Moolenaar  set cpo&vim
612963c1ad5SBram Moolenaar
613e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
61471137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
615e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
61645d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
617e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
61860e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
61960e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
62060e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
621b3307b5eSBram Moolenaar
622b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
623b3307b5eSBram Moolenaar  if s:way == 'prompt'
624b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
625b3307b5eSBram Moolenaar  else
626b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
627b3307b5eSBram Moolenaar  endif
628b3307b5eSBram Moolenaar
62945d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
63045d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
63145d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
632b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
63371137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
63445d5f26dSBram Moolenaar
63545d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
63645d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
6371b9645deSBram Moolenaar
638f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
63971137fedSBram Moolenaar    call s:InstallWinbar()
64071137fedSBram Moolenaar
64171137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
64271137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
64371137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
64471137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
64571137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
64671137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
64771137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
64871137fedSBram Moolenaar    endif
64971137fedSBram Moolenaar  endif
650963c1ad5SBram Moolenaar
651963c1ad5SBram Moolenaar  let &cpo = save_cpo
65271137fedSBram Moolenaarendfunc
65371137fedSBram Moolenaar
65471137fedSBram Moolenaarlet s:winbar_winids = []
65571137fedSBram Moolenaar
65671137fedSBram Moolenaar" Install the window toolbar in the current window.
65771137fedSBram Moolenaarfunc s:InstallWinbar()
658c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
65924a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
66024a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
66124a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
66224a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
66360e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
66424a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
66571137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
666c4b533e1SBram Moolenaar  endif
667e09ba7baSBram Moolenaarendfunc
668e09ba7baSBram Moolenaar
669e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
670e09ba7baSBram Moolenaarfunc s:DeleteCommands()
671e09ba7baSBram Moolenaar  delcommand Break
67271137fedSBram Moolenaar  delcommand Clear
673e09ba7baSBram Moolenaar  delcommand Step
67445d5f26dSBram Moolenaar  delcommand Over
675e09ba7baSBram Moolenaar  delcommand Finish
67660e73f2aSBram Moolenaar  delcommand Run
67760e73f2aSBram Moolenaar  delcommand Arguments
67860e73f2aSBram Moolenaar  delcommand Stop
679e09ba7baSBram Moolenaar  delcommand Continue
68045d5f26dSBram Moolenaar  delcommand Evaluate
68145d5f26dSBram Moolenaar  delcommand Gdb
68245d5f26dSBram Moolenaar  delcommand Program
683b3623a38SBram Moolenaar  delcommand Source
68471137fedSBram Moolenaar  delcommand Winbar
68545d5f26dSBram Moolenaar
68645d5f26dSBram Moolenaar  nunmap K
6871b9645deSBram Moolenaar
6881b9645deSBram Moolenaar  if has('menu')
68971137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
69071137fedSBram Moolenaar    let curwinid = win_getid(winnr())
69171137fedSBram Moolenaar    for winid in s:winbar_winids
69271137fedSBram Moolenaar      if win_gotoid(winid)
6931b9645deSBram Moolenaar        aunmenu WinBar.Step
6941b9645deSBram Moolenaar        aunmenu WinBar.Next
6951b9645deSBram Moolenaar        aunmenu WinBar.Finish
6961b9645deSBram Moolenaar        aunmenu WinBar.Cont
69760e73f2aSBram Moolenaar        aunmenu WinBar.Stop
6981b9645deSBram Moolenaar        aunmenu WinBar.Eval
6991b9645deSBram Moolenaar      endif
70071137fedSBram Moolenaar    endfor
70171137fedSBram Moolenaar    call win_gotoid(curwinid)
70271137fedSBram Moolenaar    let s:winbar_winids = []
70371137fedSBram Moolenaar
70471137fedSBram Moolenaar    if exists('s:saved_mousemodel')
70571137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
70671137fedSBram Moolenaar      unlet s:saved_mousemodel
70771137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
70871137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
70971137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
71071137fedSBram Moolenaar      aunmenu PopUp.Evaluate
71171137fedSBram Moolenaar    endif
71271137fedSBram Moolenaar  endif
7131b9645deSBram Moolenaar
71445d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
71537402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
71637402ed5SBram Moolenaar    for subid in keys(entries)
71737402ed5SBram Moolenaar      exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
71837402ed5SBram Moolenaar    endfor
71945d5f26dSBram Moolenaar  endfor
72045d5f26dSBram Moolenaar  unlet s:breakpoints
72137402ed5SBram Moolenaar  unlet s:breakpoint_locations
722a15b0a93SBram Moolenaar
723a15b0a93SBram Moolenaar  sign undefine debugPC
724a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
725a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
726a15b0a93SBram Moolenaar  endfor
7274551c0a9SBram Moolenaar  let s:BreakpointSigns = []
728e09ba7baSBram Moolenaarendfunc
729e09ba7baSBram Moolenaar
730e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
731e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
73260e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
73360e73f2aSBram Moolenaar  " Interrupt to make it work.
73460e73f2aSBram Moolenaar  let do_continue = 0
73560e73f2aSBram Moolenaar  if !s:stopped
73660e73f2aSBram Moolenaar    let do_continue = 1
737b3307b5eSBram Moolenaar    if s:way == 'prompt'
7384551c0a9SBram Moolenaar      call s:PromptInterrupt()
739b3307b5eSBram Moolenaar    else
74060e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
741b3307b5eSBram Moolenaar    endif
74260e73f2aSBram Moolenaar    sleep 10m
74360e73f2aSBram Moolenaar  endif
744a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
745a15b0a93SBram Moolenaar  call s:SendCommand('-break-insert '
746a15b0a93SBram Moolenaar        \ . fnameescape(expand('%:p')) . ':' . line('.'))
74760e73f2aSBram Moolenaar  if do_continue
74860e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
74960e73f2aSBram Moolenaar  endif
750e09ba7baSBram Moolenaarendfunc
751e09ba7baSBram Moolenaar
75271137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
75371137fedSBram Moolenaarfunc s:ClearBreakpoint()
754e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
755e09ba7baSBram Moolenaar  let lnum = line('.')
75637402ed5SBram Moolenaar  let bploc = printf('%s:%d', fname, lnum)
75737402ed5SBram Moolenaar  if has_key(s:breakpoint_locations, bploc)
75837402ed5SBram Moolenaar    let idx = 0
75937402ed5SBram Moolenaar    for id in s:breakpoint_locations[bploc]
76037402ed5SBram Moolenaar      if has_key(s:breakpoints, id)
76137402ed5SBram Moolenaar        " Assume this always works, the reply is simply "^done".
76237402ed5SBram Moolenaar        call s:SendCommand('-break-delete ' . id)
76337402ed5SBram Moolenaar        for subid in keys(s:breakpoints[id])
76437402ed5SBram Moolenaar          exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
76537402ed5SBram Moolenaar        endfor
76637402ed5SBram Moolenaar        unlet s:breakpoints[id]
76737402ed5SBram Moolenaar        unlet s:breakpoint_locations[bploc][idx]
768e09ba7baSBram Moolenaar        break
76937402ed5SBram Moolenaar      else
77037402ed5SBram Moolenaar	let idx += 1
771e09ba7baSBram Moolenaar      endif
772e09ba7baSBram Moolenaar    endfor
77337402ed5SBram Moolenaar    if empty(s:breakpoint_locations[bploc])
77437402ed5SBram Moolenaar      unlet s:breakpoint_locations[bploc]
77537402ed5SBram Moolenaar    endif
77637402ed5SBram Moolenaar  endif
777e09ba7baSBram Moolenaarendfunc
778e09ba7baSBram Moolenaar
77960e73f2aSBram Moolenaarfunc s:Run(args)
78060e73f2aSBram Moolenaar  if a:args != ''
78160e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
78260e73f2aSBram Moolenaar  endif
78360e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
78460e73f2aSBram Moolenaarendfunc
78560e73f2aSBram Moolenaar
78651b0f370SBram Moolenaarfunc s:SendEval(expr)
78751b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
78851b0f370SBram Moolenaar  let s:evalexpr = a:expr
78951b0f370SBram Moolenaarendfunc
79051b0f370SBram Moolenaar
79145d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
79245d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
79345d5f26dSBram Moolenaar  if a:arg != ''
79445d5f26dSBram Moolenaar    let expr = a:arg
79545d5f26dSBram Moolenaar  elseif a:range == 2
79645d5f26dSBram Moolenaar    let pos = getcurpos()
79745d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
79845d5f26dSBram Moolenaar    let regt = getregtype('v')
79945d5f26dSBram Moolenaar    normal! gv"vy
80045d5f26dSBram Moolenaar    let expr = @v
80145d5f26dSBram Moolenaar    call setpos('.', pos)
80245d5f26dSBram Moolenaar    call setreg('v', reg, regt)
80345d5f26dSBram Moolenaar  else
80445d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
80545d5f26dSBram Moolenaar  endif
80622f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
80751b0f370SBram Moolenaar  call s:SendEval(expr)
80845d5f26dSBram Moolenaarendfunc
80945d5f26dSBram Moolenaar
81022f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
81151b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
81251b0f370SBram Moolenaar
81345d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
81445d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
8151b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
8161b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
81751b0f370SBram Moolenaar  if s:evalFromBalloonExpr
81851b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
81951b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
82051b0f370SBram Moolenaar    else
82151b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
82251b0f370SBram Moolenaar    endif
82351b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
82451b0f370SBram Moolenaar  else
8251b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
82651b0f370SBram Moolenaar  endif
8271b9645deSBram Moolenaar
8287f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
8291b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
83022f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
83151b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
83251b0f370SBram Moolenaar  else
83351b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
8341b9645deSBram Moolenaar  endif
83545d5f26dSBram Moolenaarendfunc
83645d5f26dSBram Moolenaar
83751b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
83851b0f370SBram Moolenaar" if there is any.
83951b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
840b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
841b3307b5eSBram Moolenaar    return
842b3307b5eSBram Moolenaar  endif
843b3307b5eSBram Moolenaar  if !s:stopped
844b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
845b3307b5eSBram Moolenaar    " mouse triggers a balloon.
84651b0f370SBram Moolenaar    return
84751b0f370SBram Moolenaar  endif
84851b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
84951b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
85022f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
85122f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
85251b0f370SBram Moolenaar  return ''
85351b0f370SBram Moolenaarendfunc
85451b0f370SBram Moolenaar
85545d5f26dSBram Moolenaar" Handle an error.
85645d5f26dSBram Moolenaarfunc s:HandleError(msg)
85722f1d0e3SBram Moolenaar  if s:ignoreEvalError
85851b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
85922f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
86022f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
86151b0f370SBram Moolenaar    return
86251b0f370SBram Moolenaar  endif
86345d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
86445d5f26dSBram Moolenaarendfunc
86545d5f26dSBram Moolenaar
866b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
867b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
868c4b533e1SBram Moolenaar    new
869b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
870c4b533e1SBram Moolenaar    call s:InstallWinbar()
871c4b533e1SBram Moolenaar  endif
872c4b533e1SBram Moolenaarendfunc
873c4b533e1SBram Moolenaar
874e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
875e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
876e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
877fe386641SBram Moolenaar  let wid = win_getid(winnr())
878fe386641SBram Moolenaar
87960e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
8804551c0a9SBram Moolenaar    call ch_log('program stopped')
88160e73f2aSBram Moolenaar    let s:stopped = 1
88260e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
8834551c0a9SBram Moolenaar    call ch_log('program running')
88460e73f2aSBram Moolenaar    let s:stopped = 0
88560e73f2aSBram Moolenaar  endif
88660e73f2aSBram Moolenaar
887a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
888a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
889a15b0a93SBram Moolenaar  else
890a15b0a93SBram Moolenaar    let fname = ''
891a15b0a93SBram Moolenaar  endif
8921b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
893e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
894fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
8954551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
8961b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
897fe386641SBram Moolenaar        if &modified
898fe386641SBram Moolenaar          " TODO: find existing window
899fe386641SBram Moolenaar          exe 'split ' . fnameescape(fname)
900b3307b5eSBram Moolenaar          let s:sourcewin = win_getid(winnr())
901c4b533e1SBram Moolenaar          call s:InstallWinbar()
902fe386641SBram Moolenaar        else
903fe386641SBram Moolenaar          exe 'edit ' . fnameescape(fname)
904fe386641SBram Moolenaar        endif
905fe386641SBram Moolenaar      endif
906fe386641SBram Moolenaar      exe lnum
90701164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
9081b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
909fe386641SBram Moolenaar      setlocal signcolumn=yes
910fe386641SBram Moolenaar    endif
9114551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
912fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
913fe386641SBram Moolenaar  endif
914fe386641SBram Moolenaar
915fe386641SBram Moolenaar  call win_gotoid(wid)
916e09ba7baSBram Moolenaarendfunc
917e09ba7baSBram Moolenaar
918de1a8314SBram Moolenaarlet s:BreakpointSigns = []
919a15b0a93SBram Moolenaar
92037402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid)
92137402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
92237402ed5SBram Moolenaar  if index(s:BreakpointSigns, nr) == -1
92337402ed5SBram Moolenaar    call add(s:BreakpointSigns, nr)
92437402ed5SBram Moolenaar    exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint"
925de1a8314SBram Moolenaar  endif
926de1a8314SBram Moolenaarendfunc
927de1a8314SBram Moolenaar
92837402ed5SBram Moolenaarfunc! s:SplitMsg(s)
92937402ed5SBram Moolenaar  return split(a:s, '{.\{-}}\zs')
9305378e1cfSBram Moolenaarendfunction
9315378e1cfSBram Moolenaar
932e09ba7baSBram Moolenaar" Handle setting a breakpoint
933e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
934e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
9356dccc962SBram Moolenaar  if a:msg !~ 'fullname='
9366dccc962SBram Moolenaar    " a watch does not have a file name
9376dccc962SBram Moolenaar    return
9386dccc962SBram Moolenaar  endif
9395378e1cfSBram Moolenaar  for msg in s:SplitMsg(a:msg)
9405378e1cfSBram Moolenaar    let fname = s:GetFullname(msg)
9415378e1cfSBram Moolenaar    if empty(fname)
9425378e1cfSBram Moolenaar      continue
9435378e1cfSBram Moolenaar    endif
9445378e1cfSBram Moolenaar    let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '')
9455378e1cfSBram Moolenaar    if empty(nr)
946e09ba7baSBram Moolenaar      return
947fe386641SBram Moolenaar    endif
948e09ba7baSBram Moolenaar
94937402ed5SBram Moolenaar    " If "nr" is 123 it becomes "123.0" and subid is "0".
95037402ed5SBram Moolenaar    " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded.
95137402ed5SBram Moolenaar    let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0')
95237402ed5SBram Moolenaar    call s:CreateBreakpoint(id, subid)
95337402ed5SBram Moolenaar
95437402ed5SBram Moolenaar    if has_key(s:breakpoints, id)
95537402ed5SBram Moolenaar      let entries = s:breakpoints[id]
95637402ed5SBram Moolenaar    else
95737402ed5SBram Moolenaar      let entries = {}
95837402ed5SBram Moolenaar      let s:breakpoints[id] = entries
95937402ed5SBram Moolenaar    endif
96037402ed5SBram Moolenaar    if has_key(entries, subid)
96137402ed5SBram Moolenaar      let entry = entries[subid]
962e09ba7baSBram Moolenaar    else
963e09ba7baSBram Moolenaar      let entry = {}
96437402ed5SBram Moolenaar      let entries[subid] = entry
965fe386641SBram Moolenaar    endif
966e09ba7baSBram Moolenaar
9675378e1cfSBram Moolenaar    let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
968e09ba7baSBram Moolenaar    let entry['fname'] = fname
969e09ba7baSBram Moolenaar    let entry['lnum'] = lnum
9701b9645deSBram Moolenaar
97137402ed5SBram Moolenaar    let bploc = printf('%s:%d', fname, lnum)
97237402ed5SBram Moolenaar    if !has_key(s:breakpoint_locations, bploc)
97337402ed5SBram Moolenaar      let s:breakpoint_locations[bploc] = []
97437402ed5SBram Moolenaar    endif
97537402ed5SBram Moolenaar    let s:breakpoint_locations[bploc] += [id]
97637402ed5SBram Moolenaar
9771b9645deSBram Moolenaar    if bufloaded(fname)
97837402ed5SBram Moolenaar      call s:PlaceSign(id, subid, entry)
9791b9645deSBram Moolenaar    endif
9805378e1cfSBram Moolenaar  endfor
9811b9645deSBram Moolenaarendfunc
9821b9645deSBram Moolenaar
98337402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry)
98437402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
98537402ed5SBram Moolenaar  exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname']
9861b9645deSBram Moolenaar  let a:entry['placed'] = 1
987e09ba7baSBram Moolenaarendfunc
988e09ba7baSBram Moolenaar
989e09ba7baSBram Moolenaar" Handle deleting a breakpoint
990e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
991e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
99237402ed5SBram Moolenaar  let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
99337402ed5SBram Moolenaar  if empty(id)
994e09ba7baSBram Moolenaar    return
995e09ba7baSBram Moolenaar  endif
99637402ed5SBram Moolenaar  if has_key(s:breakpoints, id)
99737402ed5SBram Moolenaar    for [subid, entry] in items(s:breakpoints[id])
9981b9645deSBram Moolenaar      if has_key(entry, 'placed')
99937402ed5SBram Moolenaar        exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
10001b9645deSBram Moolenaar        unlet entry['placed']
10011b9645deSBram Moolenaar      endif
10025378e1cfSBram Moolenaar    endfor
100337402ed5SBram Moolenaar    unlet s:breakpoints[id]
100437402ed5SBram Moolenaar  endif
1005c572da5fSBram Moolenaarendfunc
10061b9645deSBram Moolenaar
10074551c0a9SBram Moolenaar" Handle the debugged program starting to run.
10084551c0a9SBram Moolenaar" Will store the process ID in s:pid
10094551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
10104551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
10114551c0a9SBram Moolenaar  if nr == 0
10124551c0a9SBram Moolenaar    return
10134551c0a9SBram Moolenaar  endif
10144551c0a9SBram Moolenaar  let s:pid = nr
10154551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
10164551c0a9SBram Moolenaarendfunc
10174551c0a9SBram Moolenaar
10181b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
10191b9645deSBram Moolenaarfunc s:BufRead()
10201b9645deSBram Moolenaar  let fname = expand('<afile>:p')
102137402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
102237402ed5SBram Moolenaar    for [subid, entry] in items(entries)
10231b9645deSBram Moolenaar      if entry['fname'] == fname
102437402ed5SBram Moolenaar        call s:PlaceSign(id, subid, entry)
10251b9645deSBram Moolenaar      endif
10261b9645deSBram Moolenaar    endfor
102737402ed5SBram Moolenaar  endfor
10281b9645deSBram Moolenaarendfunc
10291b9645deSBram Moolenaar
10301b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
10311b9645deSBram Moolenaarfunc s:BufUnloaded()
10321b9645deSBram Moolenaar  let fname = expand('<afile>:p')
103337402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
103437402ed5SBram Moolenaar    for [subid, entry] in items(entries)
10351b9645deSBram Moolenaar      if entry['fname'] == fname
10361b9645deSBram Moolenaar        let entry['placed'] = 0
10371b9645deSBram Moolenaar      endif
10381b9645deSBram Moolenaar    endfor
103937402ed5SBram Moolenaar  endfor
10401b9645deSBram Moolenaarendfunc
1041ca4cc018SBram Moolenaar
1042ca4cc018SBram Moolenaarlet &cpo = s:keepcpo
1043ca4cc018SBram Moolenaarunlet s:keepcpo
1044