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
76*5378e1cfSBram Moolenaar" Take a breakpoint number as used by GDB and turn it into an integer.
77*5378e1cfSBram Moolenaar" The breakpoint may contain a dot: 123.4
78*5378e1cfSBram Moolenaarfunc s:Breakpoint2SignNumber(nr)
79*5378e1cfSBram Moolenaar  let t = split(a:nr, '\.')
80*5378e1cfSBram Moolenaar  return t[0] * 1000 + (len(t) == 2 ? t[1] : 0)
81*5378e1cfSBram Moolenaarendfunction
82*5378e1cfSBram 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
12024a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
12124a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
12238baa3e6SBram Moolenaar      let s:save_columns = &columns
12338baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
12424a98a0eSBram Moolenaar    endif
125b3307b5eSBram Moolenaar    let s:vertical = 1
12638baa3e6SBram Moolenaar  else
127b3307b5eSBram Moolenaar    let s:vertical = 0
12838baa3e6SBram Moolenaar  endif
12938baa3e6SBram Moolenaar
130b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
131b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
132b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
133b3307b5eSBram Moolenaar    let s:way = 'terminal'
134b3307b5eSBram Moolenaar  else
135b3307b5eSBram Moolenaar    let s:way = 'prompt'
136b3307b5eSBram Moolenaar  endif
137b3307b5eSBram Moolenaar
138b3307b5eSBram Moolenaar  if s:way == 'prompt'
139b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
140b3307b5eSBram Moolenaar  else
141b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
142b3307b5eSBram Moolenaar  endif
143b3307b5eSBram Moolenaarendfunc
144b3307b5eSBram Moolenaar
145b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
146b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
147fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
148b3307b5eSBram Moolenaar        \ 'term_name': 'debugged program',
149b3307b5eSBram Moolenaar        \ 'vertical': s:vertical,
150fe386641SBram Moolenaar        \ })
151fe386641SBram Moolenaar  if s:ptybuf == 0
152fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
153fe386641SBram Moolenaar    return
154fe386641SBram Moolenaar  endif
155fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
15645d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
157b3307b5eSBram Moolenaar  if s:vertical
15851b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
15951b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
16051b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
16151b0f370SBram Moolenaar  endif
162fe386641SBram Moolenaar
163fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
164fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
165fe386641SBram Moolenaar        \ 'term_name': 'gdb communication',
166fe386641SBram Moolenaar        \ 'out_cb': function('s:CommOutput'),
167fe386641SBram Moolenaar        \ 'hidden': 1,
168fe386641SBram Moolenaar        \ })
169fe386641SBram Moolenaar  if s:commbuf == 0
170fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
171fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
172fe386641SBram Moolenaar    return
173fe386641SBram Moolenaar  endif
174fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
175c572da5fSBram Moolenaar
176c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
177c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
17832c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
17932c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
18032c67ba7SBram Moolenaar
18132c67ba7SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
182b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
18360e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
184b3307b5eSBram Moolenaar        \ 'exit_cb': function('s:EndTermDebug'),
185fe386641SBram Moolenaar        \ 'term_finish': 'close',
186c572da5fSBram Moolenaar        \ })
18760e73f2aSBram Moolenaar  if s:gdbbuf == 0
188fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
189fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
190fe386641SBram Moolenaar    exe 'bwipe! ' . s:commbuf
191fe386641SBram Moolenaar    return
192fe386641SBram Moolenaar  endif
19345d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
194fe386641SBram Moolenaar
19532c67ba7SBram Moolenaar  " Set arguments to be run
19632c67ba7SBram Moolenaar  if len(proc_args)
19732c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
19832c67ba7SBram Moolenaar  endif
19932c67ba7SBram Moolenaar
200fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
20160e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
20260e73f2aSBram Moolenaar
2033e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
2043e4b84d0SBram Moolenaar  " why the debugger doesn't work.
2053e4b84d0SBram Moolenaar  let try_count = 0
2063e4b84d0SBram Moolenaar  while 1
2073e4b84d0SBram Moolenaar    let response = ''
208b3623a38SBram Moolenaar    for lnum in range(1,200)
2093e4b84d0SBram Moolenaar      if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi '
210f63db65bSBram Moolenaar        " response can be in the same line or the next line
211f63db65bSBram Moolenaar        let response = term_getline(s:gdbbuf, lnum) . term_getline(s:gdbbuf, lnum + 1)
2123e4b84d0SBram Moolenaar        if response =~ 'Undefined command'
213f3ba14ffSBram Moolenaar          echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
2143e4b84d0SBram Moolenaar          exe 'bwipe! ' . s:ptybuf
2153e4b84d0SBram Moolenaar          exe 'bwipe! ' . s:commbuf
2163e4b84d0SBram Moolenaar          return
2173e4b84d0SBram Moolenaar        endif
2183e4b84d0SBram Moolenaar        if response =~ 'New UI allocated'
2193e4b84d0SBram Moolenaar          " Success!
2203e4b84d0SBram Moolenaar          break
2213e4b84d0SBram Moolenaar        endif
2223e4b84d0SBram Moolenaar      endif
2233e4b84d0SBram Moolenaar    endfor
2243e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2253e4b84d0SBram Moolenaar      break
2263e4b84d0SBram Moolenaar    endif
2273e4b84d0SBram Moolenaar    let try_count += 1
2283e4b84d0SBram Moolenaar    if try_count > 100
2293e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2303e4b84d0SBram Moolenaar      break
2313e4b84d0SBram Moolenaar    endif
2323e4b84d0SBram Moolenaar    sleep 10m
2333e4b84d0SBram Moolenaar  endwhile
2343e4b84d0SBram Moolenaar
23560e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
23660e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
23760e73f2aSBram Moolenaar  " running.
23860e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
239b3307b5eSBram Moolenaar  " Older gdb uses a different command.
240b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
241e09ba7baSBram Moolenaar
242f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
243f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
244b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
245f3ba14ffSBram Moolenaar
246b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
247b3307b5eSBram Moolenaarendfunc
248b3307b5eSBram Moolenaar
249b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
250b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
251b3307b5eSBram Moolenaar  if s:vertical
252b3307b5eSBram Moolenaar    vertical new
253b3307b5eSBram Moolenaar  else
254b3307b5eSBram Moolenaar    new
255b3307b5eSBram Moolenaar  endif
256b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
257b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
258b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
259b3307b5eSBram Moolenaar  set buftype=prompt
260b3307b5eSBram Moolenaar  file gdb
261b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
262b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
263b3307b5eSBram Moolenaar
264b3307b5eSBram Moolenaar  if s:vertical
265b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
266b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
267b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
268b3307b5eSBram Moolenaar  endif
269b3307b5eSBram Moolenaar
270b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
271b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
272b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
273b3307b5eSBram Moolenaar
274b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
275b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
276b3307b5eSBram Moolenaar
277b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
278b3307b5eSBram Moolenaar        \ 'exit_cb': function('s:EndPromptDebug'),
279b3307b5eSBram Moolenaar        \ 'out_cb': function('s:GdbOutCallback'),
280b3307b5eSBram Moolenaar        \ })
281b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
282b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
283b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
284b3307b5eSBram Moolenaar    return
285b3307b5eSBram Moolenaar  endif
2864551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
2874551c0a9SBram Moolenaar  set modified
288b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
289b3307b5eSBram Moolenaar
290b3307b5eSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only
291b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
292b3307b5eSBram Moolenaar  " target is running.
293b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
294b3307b5eSBram Moolenaar  " Older gdb uses a different command.
295b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
296b3307b5eSBram Moolenaar
297b3307b5eSBram Moolenaar  let s:ptybuf = 0
298b3307b5eSBram Moolenaar  if has('win32')
299b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
300b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
301b3307b5eSBram Moolenaar  elseif has('terminal')
302b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
303b3307b5eSBram Moolenaar    " gdb window.
304b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
305b3307b5eSBram Moolenaar          \ 'term_name': 'debugged program',
306b3307b5eSBram Moolenaar          \ })
307b3307b5eSBram Moolenaar    if s:ptybuf == 0
308b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
309b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
310b3307b5eSBram Moolenaar      return
311b3307b5eSBram Moolenaar    endif
312b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
313b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
314b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
315b3307b5eSBram Moolenaar
316b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
317b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
318b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
319b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
320b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
321b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
322b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
323b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
324b3307b5eSBram Moolenaar  else
325b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
326b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
327b3307b5eSBram Moolenaar  endif
328b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
329b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
330b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
331b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
332b3307b5eSBram Moolenaar
333b3307b5eSBram Moolenaar  " Set arguments to be run
334b3307b5eSBram Moolenaar  if len(proc_args)
335b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
336b3307b5eSBram Moolenaar  endif
337b3307b5eSBram Moolenaar
338b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
339b3307b5eSBram Moolenaar  startinsert
340b3307b5eSBram Moolenaarendfunc
341b3307b5eSBram Moolenaar
342b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
34338baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
34438baa3e6SBram Moolenaar  " There can be only one.
34538baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
34638baa3e6SBram Moolenaar
34745d5f26dSBram Moolenaar  " Install debugger commands in the text window.
348b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
349e09ba7baSBram Moolenaar  call s:InstallCommands()
35045d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
351e09ba7baSBram Moolenaar
35251b0f370SBram Moolenaar  " Enable showing a balloon with eval info
353246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
354246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
35551b0f370SBram Moolenaar    if has("balloon_eval")
35651b0f370SBram Moolenaar      set ballooneval
357246fe03dSBram Moolenaar    endif
35851b0f370SBram Moolenaar    if has("balloon_eval_term")
35951b0f370SBram Moolenaar      set balloonevalterm
36051b0f370SBram Moolenaar    endif
36151b0f370SBram Moolenaar  endif
36251b0f370SBram Moolenaar
363*5378e1cfSBram Moolenaar  " Contains breakpoints that have been placed, key is a string with the GDB
364*5378e1cfSBram Moolenaar  " breakpoint number.
365e09ba7baSBram Moolenaar  let s:breakpoints = {}
3661b9645deSBram Moolenaar
3671b9645deSBram Moolenaar  augroup TermDebug
3681b9645deSBram Moolenaar    au BufRead * call s:BufRead()
3691b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
370f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
3711b9645deSBram Moolenaar  augroup END
37232c67ba7SBram Moolenaar
373b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
374b3307b5eSBram Moolenaar  " window.
37532c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
37632c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
37732c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
37832c67ba7SBram Moolenaar  endif
379c572da5fSBram Moolenaarendfunc
380c572da5fSBram Moolenaar
381b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
382b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
383b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
384b3307b5eSBram Moolenaar  if s:way == 'prompt'
385b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
386b3307b5eSBram Moolenaar  else
387b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
388b3307b5eSBram Moolenaar  endif
389b3307b5eSBram Moolenaarendfunc
390b3307b5eSBram Moolenaar
391b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
392b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
393b3307b5eSBram Moolenaar  if s:way == 'prompt'
394b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
395b3307b5eSBram Moolenaar  else
396b3307b5eSBram Moolenaar    let do_continue = 0
397b3307b5eSBram Moolenaar    if !s:stopped
398b3307b5eSBram Moolenaar      let do_continue = 1
399b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
400b3307b5eSBram Moolenaar      sleep 10m
401b3307b5eSBram Moolenaar    endif
402b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
403b3307b5eSBram Moolenaar    if do_continue
404b3307b5eSBram Moolenaar      Continue
405b3307b5eSBram Moolenaar    endif
406b3307b5eSBram Moolenaar  endif
407b3307b5eSBram Moolenaarendfunc
408b3307b5eSBram Moolenaar
409b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
410b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
411b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
412b3307b5eSBram Moolenaarendfunc
413b3307b5eSBram Moolenaar
4144551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
4154551c0a9SBram Moolenaar" breakpoint.
416b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
4172ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
4182ed890f1SBram Moolenaar  if has('win32')
4192ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
4202ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
4214551c0a9SBram Moolenaar    if s:pid == 0
4224551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
4234551c0a9SBram Moolenaar    else
4244551c0a9SBram Moolenaar      call debugbreak(s:pid)
4254551c0a9SBram Moolenaar    endif
4262ed890f1SBram Moolenaar  else
4272ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
4282ed890f1SBram Moolenaar  endif
429b3307b5eSBram Moolenaarendfunc
430b3307b5eSBram Moolenaar
431b3307b5eSBram Moolenaar" Function called when gdb outputs text.
432b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
433b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
434b3307b5eSBram Moolenaar
435b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
436b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
437a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
438b3307b5eSBram Moolenaar    return
439b3307b5eSBram Moolenaar  endif
440b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
441b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
442b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
443b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
444b3307b5eSBram Moolenaar      unlet s:evalexpr
445b3307b5eSBram Moolenaar      return
446b3307b5eSBram Moolenaar    endif
447b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
448b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
449b3307b5eSBram Moolenaar  else
450b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
451b3307b5eSBram Moolenaar    return
452b3307b5eSBram Moolenaar  endif
453b3307b5eSBram Moolenaar
454b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
455b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
456b3307b5eSBram Moolenaar
457b3307b5eSBram Moolenaar  " Add the output above the current prompt.
458b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
4594551c0a9SBram Moolenaar  set modified
460b3307b5eSBram Moolenaar
461b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
462b3307b5eSBram Moolenaarendfunc
463b3307b5eSBram Moolenaar
464b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
465b3307b5eSBram Moolenaar" to the next ", unescaping characters.
466b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
467b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
468a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
469b3307b5eSBram Moolenaar    return
470b3307b5eSBram Moolenaar  endif
471b3307b5eSBram Moolenaar  let result = ''
472b3307b5eSBram Moolenaar  let i = 1
473b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
474b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
475b3307b5eSBram Moolenaar      let i += 1
476b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
477b3307b5eSBram Moolenaar        " drop \n
478b3307b5eSBram Moolenaar        let i += 1
479b3307b5eSBram Moolenaar        continue
480b3307b5eSBram Moolenaar      endif
481b3307b5eSBram Moolenaar    endif
482b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
483b3307b5eSBram Moolenaar    let i += 1
484b3307b5eSBram Moolenaar  endwhile
485b3307b5eSBram Moolenaar  return result
486b3307b5eSBram Moolenaarendfunc
487b3307b5eSBram Moolenaar
488a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
489a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
490*5378e1cfSBram Moolenaar  if a:msg !~ 'fullname'
491*5378e1cfSBram Moolenaar    return ''
492*5378e1cfSBram Moolenaar  endif
493a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
494a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
495a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
496a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
497a15b0a93SBram Moolenaar  endif
498a15b0a93SBram Moolenaar  return name
499a15b0a93SBram Moolenaarendfunc
500a15b0a93SBram Moolenaar
501b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
502fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
503b3623a38SBram Moolenaar  unlet s:gdbwin
504e09ba7baSBram Moolenaar
505b3307b5eSBram Moolenaar  call s:EndDebugCommon()
506b3307b5eSBram Moolenaarendfunc
507b3307b5eSBram Moolenaar
508b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
509e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
510e09ba7baSBram Moolenaar
511b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
512b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
513b3307b5eSBram Moolenaar  endif
514b3307b5eSBram Moolenaar
515b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
516e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
517e09ba7baSBram Moolenaar  call s:DeleteCommands()
518e09ba7baSBram Moolenaar
519e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
520b3307b5eSBram Moolenaar
52138baa3e6SBram Moolenaar  if s:save_columns > 0
52238baa3e6SBram Moolenaar    let &columns = s:save_columns
52338baa3e6SBram Moolenaar  endif
5241b9645deSBram Moolenaar
525246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
526246fe03dSBram Moolenaar    set balloonexpr=
52751b0f370SBram Moolenaar    if has("balloon_eval")
52851b0f370SBram Moolenaar      set noballooneval
529246fe03dSBram Moolenaar    endif
53051b0f370SBram Moolenaar    if has("balloon_eval_term")
53151b0f370SBram Moolenaar      set noballoonevalterm
53251b0f370SBram Moolenaar    endif
53351b0f370SBram Moolenaar  endif
53451b0f370SBram Moolenaar
5351b9645deSBram Moolenaar  au! TermDebug
536fe386641SBram Moolenaarendfunc
537fe386641SBram Moolenaar
538b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
539b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
540b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
5414551c0a9SBram Moolenaar  set nomodified
542b3307b5eSBram Moolenaar  close
543b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
544b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
545b3307b5eSBram Moolenaar  endif
546b3307b5eSBram Moolenaar
547b3307b5eSBram Moolenaar  call s:EndDebugCommon()
548b3307b5eSBram Moolenaar  unlet s:gdbwin
549b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
550b3307b5eSBram Moolenaarendfunc
551b3307b5eSBram Moolenaar
552fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
553fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
554fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
555fe386641SBram Moolenaar
556fe386641SBram Moolenaar  for msg in msgs
557fe386641SBram Moolenaar    " remove prefixed NL
558fe386641SBram Moolenaar    if msg[0] == "\n"
559fe386641SBram Moolenaar      let msg = msg[1:]
560fe386641SBram Moolenaar    endif
561fe386641SBram Moolenaar    if msg != ''
5621b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
563e09ba7baSBram Moolenaar        call s:HandleCursor(msg)
56445d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
565e09ba7baSBram Moolenaar        call s:HandleNewBreakpoint(msg)
566e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
567e09ba7baSBram Moolenaar        call s:HandleBreakpointDelete(msg)
5684551c0a9SBram Moolenaar      elseif msg =~ '^=thread-group-started'
5694551c0a9SBram Moolenaar        call s:HandleProgramRun(msg)
57045d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
57145d5f26dSBram Moolenaar        call s:HandleEvaluate(msg)
57245d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
57345d5f26dSBram Moolenaar        call s:HandleError(msg)
574e09ba7baSBram Moolenaar      endif
575e09ba7baSBram Moolenaar    endif
576e09ba7baSBram Moolenaar  endfor
577e09ba7baSBram Moolenaarendfunc
578e09ba7baSBram Moolenaar
579e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
580e09ba7baSBram Moolenaarfunc s:InstallCommands()
581963c1ad5SBram Moolenaar  let save_cpo = &cpo
582963c1ad5SBram Moolenaar  set cpo&vim
583963c1ad5SBram Moolenaar
584e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
58571137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
586e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
58745d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
588e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
58960e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
59060e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
59160e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
592b3307b5eSBram Moolenaar
593b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
594b3307b5eSBram Moolenaar  if s:way == 'prompt'
595b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
596b3307b5eSBram Moolenaar  else
597b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
598b3307b5eSBram Moolenaar  endif
599b3307b5eSBram Moolenaar
60045d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
60145d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
60245d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
603b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
60471137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
60545d5f26dSBram Moolenaar
60645d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
60745d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
6081b9645deSBram Moolenaar
609f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
61071137fedSBram Moolenaar    call s:InstallWinbar()
61171137fedSBram Moolenaar
61271137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
61371137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
61471137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
61571137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
61671137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
61771137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
61871137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
61971137fedSBram Moolenaar    endif
62071137fedSBram Moolenaar  endif
621963c1ad5SBram Moolenaar
622963c1ad5SBram Moolenaar  let &cpo = save_cpo
62371137fedSBram Moolenaarendfunc
62471137fedSBram Moolenaar
62571137fedSBram Moolenaarlet s:winbar_winids = []
62671137fedSBram Moolenaar
62771137fedSBram Moolenaar" Install the window toolbar in the current window.
62871137fedSBram Moolenaarfunc s:InstallWinbar()
629c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
63024a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
63124a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
63224a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
63324a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
63460e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
63524a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
63671137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
637c4b533e1SBram Moolenaar  endif
638e09ba7baSBram Moolenaarendfunc
639e09ba7baSBram Moolenaar
640e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
641e09ba7baSBram Moolenaarfunc s:DeleteCommands()
642e09ba7baSBram Moolenaar  delcommand Break
64371137fedSBram Moolenaar  delcommand Clear
644e09ba7baSBram Moolenaar  delcommand Step
64545d5f26dSBram Moolenaar  delcommand Over
646e09ba7baSBram Moolenaar  delcommand Finish
64760e73f2aSBram Moolenaar  delcommand Run
64860e73f2aSBram Moolenaar  delcommand Arguments
64960e73f2aSBram Moolenaar  delcommand Stop
650e09ba7baSBram Moolenaar  delcommand Continue
65145d5f26dSBram Moolenaar  delcommand Evaluate
65245d5f26dSBram Moolenaar  delcommand Gdb
65345d5f26dSBram Moolenaar  delcommand Program
654b3623a38SBram Moolenaar  delcommand Source
65571137fedSBram Moolenaar  delcommand Winbar
65645d5f26dSBram Moolenaar
65745d5f26dSBram Moolenaar  nunmap K
6581b9645deSBram Moolenaar
6591b9645deSBram Moolenaar  if has('menu')
66071137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
66171137fedSBram Moolenaar    let curwinid = win_getid(winnr())
66271137fedSBram Moolenaar    for winid in s:winbar_winids
66371137fedSBram Moolenaar      if win_gotoid(winid)
6641b9645deSBram Moolenaar        aunmenu WinBar.Step
6651b9645deSBram Moolenaar        aunmenu WinBar.Next
6661b9645deSBram Moolenaar        aunmenu WinBar.Finish
6671b9645deSBram Moolenaar        aunmenu WinBar.Cont
66860e73f2aSBram Moolenaar        aunmenu WinBar.Stop
6691b9645deSBram Moolenaar        aunmenu WinBar.Eval
6701b9645deSBram Moolenaar      endif
67171137fedSBram Moolenaar    endfor
67271137fedSBram Moolenaar    call win_gotoid(curwinid)
67371137fedSBram Moolenaar    let s:winbar_winids = []
67471137fedSBram Moolenaar
67571137fedSBram Moolenaar    if exists('s:saved_mousemodel')
67671137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
67771137fedSBram Moolenaar      unlet s:saved_mousemodel
67871137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
67971137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
68071137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
68171137fedSBram Moolenaar      aunmenu PopUp.Evaluate
68271137fedSBram Moolenaar    endif
68371137fedSBram Moolenaar  endif
6841b9645deSBram Moolenaar
68545d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
68645d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
687*5378e1cfSBram Moolenaar    exe 'sign unplace ' . (s:break_id + s:Breakpoint2SignNumber(key))
68845d5f26dSBram Moolenaar  endfor
68945d5f26dSBram Moolenaar  unlet s:breakpoints
690a15b0a93SBram Moolenaar
691a15b0a93SBram Moolenaar  sign undefine debugPC
692a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
693a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
694a15b0a93SBram Moolenaar  endfor
6954551c0a9SBram Moolenaar  let s:BreakpointSigns = []
696e09ba7baSBram Moolenaarendfunc
697e09ba7baSBram Moolenaar
698e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
699e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
70060e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
70160e73f2aSBram Moolenaar  " Interrupt to make it work.
70260e73f2aSBram Moolenaar  let do_continue = 0
70360e73f2aSBram Moolenaar  if !s:stopped
70460e73f2aSBram Moolenaar    let do_continue = 1
705b3307b5eSBram Moolenaar    if s:way == 'prompt'
7064551c0a9SBram Moolenaar      call s:PromptInterrupt()
707b3307b5eSBram Moolenaar    else
70860e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
709b3307b5eSBram Moolenaar    endif
71060e73f2aSBram Moolenaar    sleep 10m
71160e73f2aSBram Moolenaar  endif
712a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
713a15b0a93SBram Moolenaar  call s:SendCommand('-break-insert '
714a15b0a93SBram Moolenaar        \ . fnameescape(expand('%:p')) . ':' . line('.'))
71560e73f2aSBram Moolenaar  if do_continue
71660e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
71760e73f2aSBram Moolenaar  endif
718e09ba7baSBram Moolenaarendfunc
719e09ba7baSBram Moolenaar
72071137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
72171137fedSBram Moolenaarfunc s:ClearBreakpoint()
722e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
723e09ba7baSBram Moolenaar  let lnum = line('.')
724e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
725e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
726b3307b5eSBram Moolenaar      call s:SendCommand('-break-delete ' . key)
727e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
728*5378e1cfSBram Moolenaar      exe 'sign unplace ' . (s:break_id + s:Breakpoint2SignNumber(key))
729e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
730e09ba7baSBram Moolenaar      break
731e09ba7baSBram Moolenaar    endif
732e09ba7baSBram Moolenaar  endfor
733e09ba7baSBram Moolenaarendfunc
734e09ba7baSBram Moolenaar
73560e73f2aSBram Moolenaarfunc s:Run(args)
73660e73f2aSBram Moolenaar  if a:args != ''
73760e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
73860e73f2aSBram Moolenaar  endif
73960e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
74060e73f2aSBram Moolenaarendfunc
74160e73f2aSBram Moolenaar
74251b0f370SBram Moolenaarfunc s:SendEval(expr)
74351b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
74451b0f370SBram Moolenaar  let s:evalexpr = a:expr
74551b0f370SBram Moolenaarendfunc
74651b0f370SBram Moolenaar
74745d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
74845d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
74945d5f26dSBram Moolenaar  if a:arg != ''
75045d5f26dSBram Moolenaar    let expr = a:arg
75145d5f26dSBram Moolenaar  elseif a:range == 2
75245d5f26dSBram Moolenaar    let pos = getcurpos()
75345d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
75445d5f26dSBram Moolenaar    let regt = getregtype('v')
75545d5f26dSBram Moolenaar    normal! gv"vy
75645d5f26dSBram Moolenaar    let expr = @v
75745d5f26dSBram Moolenaar    call setpos('.', pos)
75845d5f26dSBram Moolenaar    call setreg('v', reg, regt)
75945d5f26dSBram Moolenaar  else
76045d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
76145d5f26dSBram Moolenaar  endif
76222f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
76351b0f370SBram Moolenaar  call s:SendEval(expr)
76445d5f26dSBram Moolenaarendfunc
76545d5f26dSBram Moolenaar
76622f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
76751b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
76851b0f370SBram Moolenaar
76945d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
77045d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
7711b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
7721b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
77351b0f370SBram Moolenaar  if s:evalFromBalloonExpr
77451b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
77551b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
77651b0f370SBram Moolenaar    else
77751b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
77851b0f370SBram Moolenaar    endif
77951b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
78051b0f370SBram Moolenaar  else
7811b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
78251b0f370SBram Moolenaar  endif
7831b9645deSBram Moolenaar
7847f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
7851b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
78622f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
78751b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
78851b0f370SBram Moolenaar  else
78951b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
7901b9645deSBram Moolenaar  endif
79145d5f26dSBram Moolenaarendfunc
79245d5f26dSBram Moolenaar
79351b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
79451b0f370SBram Moolenaar" if there is any.
79551b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
796b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
797b3307b5eSBram Moolenaar    return
798b3307b5eSBram Moolenaar  endif
799b3307b5eSBram Moolenaar  if !s:stopped
800b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
801b3307b5eSBram Moolenaar    " mouse triggers a balloon.
80251b0f370SBram Moolenaar    return
80351b0f370SBram Moolenaar  endif
80451b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
80551b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
80622f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
80722f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
80851b0f370SBram Moolenaar  return ''
80951b0f370SBram Moolenaarendfunc
81051b0f370SBram Moolenaar
81145d5f26dSBram Moolenaar" Handle an error.
81245d5f26dSBram Moolenaarfunc s:HandleError(msg)
81322f1d0e3SBram Moolenaar  if s:ignoreEvalError
81451b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
81522f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
81622f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
81751b0f370SBram Moolenaar    return
81851b0f370SBram Moolenaar  endif
81945d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
82045d5f26dSBram Moolenaarendfunc
82145d5f26dSBram Moolenaar
822b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
823b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
824c4b533e1SBram Moolenaar    new
825b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
826c4b533e1SBram Moolenaar    call s:InstallWinbar()
827c4b533e1SBram Moolenaar  endif
828c4b533e1SBram Moolenaarendfunc
829c4b533e1SBram Moolenaar
830e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
831e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
832e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
833fe386641SBram Moolenaar  let wid = win_getid(winnr())
834fe386641SBram Moolenaar
83560e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
8364551c0a9SBram Moolenaar    call ch_log('program stopped')
83760e73f2aSBram Moolenaar    let s:stopped = 1
83860e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
8394551c0a9SBram Moolenaar    call ch_log('program running')
84060e73f2aSBram Moolenaar    let s:stopped = 0
84160e73f2aSBram Moolenaar  endif
84260e73f2aSBram Moolenaar
843a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
844a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
845a15b0a93SBram Moolenaar  else
846a15b0a93SBram Moolenaar    let fname = ''
847a15b0a93SBram Moolenaar  endif
8481b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
849e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
850fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
8514551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
8521b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
853fe386641SBram Moolenaar        if &modified
854fe386641SBram Moolenaar          " TODO: find existing window
855fe386641SBram Moolenaar          exe 'split ' . fnameescape(fname)
856b3307b5eSBram Moolenaar          let s:sourcewin = win_getid(winnr())
857c4b533e1SBram Moolenaar          call s:InstallWinbar()
858fe386641SBram Moolenaar        else
859fe386641SBram Moolenaar          exe 'edit ' . fnameescape(fname)
860fe386641SBram Moolenaar        endif
861fe386641SBram Moolenaar      endif
862fe386641SBram Moolenaar      exe lnum
86301164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
8641b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
865fe386641SBram Moolenaar      setlocal signcolumn=yes
866fe386641SBram Moolenaar    endif
8674551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
868fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
869fe386641SBram Moolenaar  endif
870fe386641SBram Moolenaar
871fe386641SBram Moolenaar  call win_gotoid(wid)
872e09ba7baSBram Moolenaarendfunc
873e09ba7baSBram Moolenaar
874de1a8314SBram Moolenaarlet s:BreakpointSigns = []
875a15b0a93SBram Moolenaar
876a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr)
877de1a8314SBram Moolenaar  if index(s:BreakpointSigns, a:nr) == -1
878de1a8314SBram Moolenaar    call add(s:BreakpointSigns, a:nr)
879*5378e1cfSBram Moolenaar    exe "sign define debugBreakpoint" . a:nr . " text=" . substitute(a:nr, '\..*', '', '') . " texthl=debugBreakpoint"
880de1a8314SBram Moolenaar  endif
881de1a8314SBram Moolenaarendfunc
882de1a8314SBram Moolenaar
883*5378e1cfSBram Moolenaarfunc s:SplitMsg(s)
884*5378e1cfSBram Moolenaar  return split(a:s, '{\%([a-z-]\+=[^,]\+,*\)\+}\zs')
885*5378e1cfSBram Moolenaarendfunction
886*5378e1cfSBram Moolenaar
887e09ba7baSBram Moolenaar" Handle setting a breakpoint
888e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
889e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
8906dccc962SBram Moolenaar  if a:msg !~ 'fullname='
8916dccc962SBram Moolenaar    " a watch does not have a file name
8926dccc962SBram Moolenaar    return
8936dccc962SBram Moolenaar  endif
894*5378e1cfSBram Moolenaar  for msg in s:SplitMsg(a:msg)
895*5378e1cfSBram Moolenaar    let fname = s:GetFullname(msg)
896*5378e1cfSBram Moolenaar    if empty(fname)
897*5378e1cfSBram Moolenaar      continue
898*5378e1cfSBram Moolenaar    endif
899*5378e1cfSBram Moolenaar    let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '')
900*5378e1cfSBram Moolenaar    if empty(nr)
901e09ba7baSBram Moolenaar      return
902fe386641SBram Moolenaar    endif
903de1a8314SBram Moolenaar    call s:CreateBreakpoint(nr)
904e09ba7baSBram Moolenaar
905e09ba7baSBram Moolenaar    if has_key(s:breakpoints, nr)
906e09ba7baSBram Moolenaar      let entry = s:breakpoints[nr]
907e09ba7baSBram Moolenaar    else
908e09ba7baSBram Moolenaar      let entry = {}
909e09ba7baSBram Moolenaar      let s:breakpoints[nr] = entry
910fe386641SBram Moolenaar    endif
911e09ba7baSBram Moolenaar
912*5378e1cfSBram Moolenaar    let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
913e09ba7baSBram Moolenaar    let entry['fname'] = fname
914e09ba7baSBram Moolenaar    let entry['lnum'] = lnum
9151b9645deSBram Moolenaar
9161b9645deSBram Moolenaar    if bufloaded(fname)
9171b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
9181b9645deSBram Moolenaar    endif
919*5378e1cfSBram Moolenaar  endfor
9201b9645deSBram Moolenaarendfunc
9211b9645deSBram Moolenaar
9221b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
923*5378e1cfSBram Moolenaar  exe 'sign place ' . (s:break_id +  s:Breakpoint2SignNumber(a:nr)) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname']
9241b9645deSBram Moolenaar  let a:entry['placed'] = 1
925e09ba7baSBram Moolenaarendfunc
926e09ba7baSBram Moolenaar
927e09ba7baSBram Moolenaar" Handle deleting a breakpoint
928e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
929e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
930*5378e1cfSBram Moolenaar  let key = substitute(a:msg, '.*id="\([0-9.]*\)\".*', '\1', '')
931*5378e1cfSBram Moolenaar  if empty(key)
932e09ba7baSBram Moolenaar    return
933e09ba7baSBram Moolenaar  endif
934*5378e1cfSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
935*5378e1cfSBram Moolenaar    if stridx(nr, key) != 0
936*5378e1cfSBram Moolenaar      continue
937*5378e1cfSBram Moolenaar    endif
9381b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
9391b9645deSBram Moolenaar    if has_key(entry, 'placed')
940*5378e1cfSBram Moolenaar      exe 'sign unplace ' . (s:break_id + s:Breakpoint2SignNumber(nr))
9411b9645deSBram Moolenaar      unlet entry['placed']
9421b9645deSBram Moolenaar    endif
943e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
944*5378e1cfSBram Moolenaar  endfor
945c572da5fSBram Moolenaarendfunc
9461b9645deSBram Moolenaar
9474551c0a9SBram Moolenaar" Handle the debugged program starting to run.
9484551c0a9SBram Moolenaar" Will store the process ID in s:pid
9494551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
9504551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
9514551c0a9SBram Moolenaar  if nr == 0
9524551c0a9SBram Moolenaar    return
9534551c0a9SBram Moolenaar  endif
9544551c0a9SBram Moolenaar  let s:pid = nr
9554551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
9564551c0a9SBram Moolenaarendfunc
9574551c0a9SBram Moolenaar
9581b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
9591b9645deSBram Moolenaarfunc s:BufRead()
9601b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9611b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9621b9645deSBram Moolenaar    if entry['fname'] == fname
9631b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
9641b9645deSBram Moolenaar    endif
9651b9645deSBram Moolenaar  endfor
9661b9645deSBram Moolenaarendfunc
9671b9645deSBram Moolenaar
9681b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
9691b9645deSBram Moolenaarfunc s:BufUnloaded()
9701b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9711b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9721b9645deSBram Moolenaar    if entry['fname'] == fname
9731b9645deSBram Moolenaar      let entry['placed'] = 0
9741b9645deSBram Moolenaar    endif
9751b9645deSBram Moolenaar  endfor
9761b9645deSBram Moolenaarendfunc
977ca4cc018SBram Moolenaar
978ca4cc018SBram Moolenaarlet &cpo = s:keepcpo
979ca4cc018SBram Moolenaarunlet s:keepcpo
980