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
76f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new)
77f07f9e73SBram Moolenaar  let default = a:init ? 'default ' : ''
78f07f9e73SBram Moolenaar  if a:new ==# 'light' && a:old !=# 'light'
79f07f9e73SBram Moolenaar    exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue"
80f07f9e73SBram Moolenaar  elseif a:new ==# 'dark' && a:old !=# 'dark'
81f07f9e73SBram Moolenaar    exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue"
82e09ba7baSBram Moolenaar  endif
83f07f9e73SBram Moolenaarendfunc
84f07f9e73SBram Moolenaar
85f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background)
86e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red
87fe386641SBram Moolenaar
8832c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...)
8932c67ba7SBram Moolenaar  " First argument is the command to debug, second core file or process ID.
9032c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang})
9132c67ba7SBram Moolenaarendfunc
9232c67ba7SBram Moolenaar
9332c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...)
9432c67ba7SBram Moolenaar  " First argument is the command to debug, rest are run arguments.
9532c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang})
9632c67ba7SBram Moolenaarendfunc
9732c67ba7SBram Moolenaar
9832c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict)
99b3623a38SBram Moolenaar  if exists('s:gdbwin')
100b3623a38SBram Moolenaar    echoerr 'Terminal debugger already running'
101b3623a38SBram Moolenaar    return
102b3623a38SBram Moolenaar  endif
103b3307b5eSBram Moolenaar  let s:ptywin = 0
1044551c0a9SBram Moolenaar  let s:pid = 0
105b3623a38SBram Moolenaar
106b3307b5eSBram Moolenaar  " Uncomment this line to write logging in "debuglog".
107b3307b5eSBram Moolenaar  " call ch_logfile('debuglog', 'w')
108b3307b5eSBram Moolenaar
109b3307b5eSBram Moolenaar  let s:sourcewin = win_getid(winnr())
110fe386641SBram Moolenaar  let s:startsigncolumn = &signcolumn
111fe386641SBram Moolenaar
11224a98a0eSBram Moolenaar  let s:save_columns = 0
11324a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
11424a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
11538baa3e6SBram Moolenaar      let s:save_columns = &columns
11638baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
11724a98a0eSBram Moolenaar    endif
118b3307b5eSBram Moolenaar    let s:vertical = 1
11938baa3e6SBram Moolenaar  else
120b3307b5eSBram Moolenaar    let s:vertical = 0
12138baa3e6SBram Moolenaar  endif
12238baa3e6SBram Moolenaar
123b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
124b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
125b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
126b3307b5eSBram Moolenaar    let s:way = 'terminal'
127b3307b5eSBram Moolenaar  else
128b3307b5eSBram Moolenaar    let s:way = 'prompt'
129b3307b5eSBram Moolenaar  endif
130b3307b5eSBram Moolenaar
131b3307b5eSBram Moolenaar  if s:way == 'prompt'
132b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
133b3307b5eSBram Moolenaar  else
134b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
135b3307b5eSBram Moolenaar  endif
136b3307b5eSBram Moolenaarendfunc
137b3307b5eSBram Moolenaar
138b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
139b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
140fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
141b3307b5eSBram Moolenaar	\ 'term_name': 'debugged program',
142b3307b5eSBram Moolenaar	\ 'vertical': s:vertical,
143fe386641SBram Moolenaar	\ })
144fe386641SBram Moolenaar  if s:ptybuf == 0
145fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
146fe386641SBram Moolenaar    return
147fe386641SBram Moolenaar  endif
148fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
14945d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
150b3307b5eSBram Moolenaar  if s:vertical
15151b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
15251b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
15351b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
15451b0f370SBram Moolenaar  endif
155fe386641SBram Moolenaar
156fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
157fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
158fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
159fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
160fe386641SBram Moolenaar	\ 'hidden': 1,
161fe386641SBram Moolenaar	\ })
162fe386641SBram Moolenaar  if s:commbuf == 0
163fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
164fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
165fe386641SBram Moolenaar    return
166fe386641SBram Moolenaar  endif
167fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
168c572da5fSBram Moolenaar
169c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
170c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
17132c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
17232c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
17332c67ba7SBram Moolenaar
17432c67ba7SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
175b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
17660e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
177b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndTermDebug'),
178fe386641SBram Moolenaar	\ 'term_finish': 'close',
179c572da5fSBram Moolenaar	\ })
18060e73f2aSBram Moolenaar  if s:gdbbuf == 0
181fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
182fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
183fe386641SBram Moolenaar    exe 'bwipe! ' . s:commbuf
184fe386641SBram Moolenaar    return
185fe386641SBram Moolenaar  endif
18645d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
187fe386641SBram Moolenaar
18832c67ba7SBram Moolenaar  " Set arguments to be run
18932c67ba7SBram Moolenaar  if len(proc_args)
19032c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
19132c67ba7SBram Moolenaar  endif
19232c67ba7SBram Moolenaar
193fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
19460e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
19560e73f2aSBram Moolenaar
1963e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
1973e4b84d0SBram Moolenaar  " why the debugger doesn't work.
1983e4b84d0SBram Moolenaar  let try_count = 0
1993e4b84d0SBram Moolenaar  while 1
2003e4b84d0SBram Moolenaar    let response = ''
201b3623a38SBram Moolenaar    for lnum in range(1,200)
2023e4b84d0SBram Moolenaar      if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi '
2033e4b84d0SBram Moolenaar	let response = term_getline(s:gdbbuf, lnum + 1)
2043e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
205f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
2063e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:ptybuf
2073e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:commbuf
2083e4b84d0SBram Moolenaar	  return
2093e4b84d0SBram Moolenaar	endif
2103e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
2113e4b84d0SBram Moolenaar	  " Success!
2123e4b84d0SBram Moolenaar	  break
2133e4b84d0SBram Moolenaar	endif
2143e4b84d0SBram Moolenaar      endif
2153e4b84d0SBram Moolenaar    endfor
2163e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2173e4b84d0SBram Moolenaar      break
2183e4b84d0SBram Moolenaar    endif
2193e4b84d0SBram Moolenaar    let try_count += 1
2203e4b84d0SBram Moolenaar    if try_count > 100
2213e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2223e4b84d0SBram Moolenaar      break
2233e4b84d0SBram Moolenaar    endif
2243e4b84d0SBram Moolenaar    sleep 10m
2253e4b84d0SBram Moolenaar  endwhile
2263e4b84d0SBram Moolenaar
22760e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
22860e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
22960e73f2aSBram Moolenaar  " running.
23060e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
231b3307b5eSBram Moolenaar  " Older gdb uses a different command.
232b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
233e09ba7baSBram Moolenaar
234f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
235f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
236b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
237f3ba14ffSBram Moolenaar
238b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
239b3307b5eSBram Moolenaarendfunc
240b3307b5eSBram Moolenaar
241b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
242b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
243b3307b5eSBram Moolenaar  if s:vertical
244b3307b5eSBram Moolenaar    vertical new
245b3307b5eSBram Moolenaar  else
246b3307b5eSBram Moolenaar    new
247b3307b5eSBram Moolenaar  endif
248b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
249b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
250b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
251b3307b5eSBram Moolenaar  set buftype=prompt
252b3307b5eSBram Moolenaar  file gdb
253b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
254b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
255b3307b5eSBram Moolenaar
256b3307b5eSBram Moolenaar  if s:vertical
257b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
258b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
259b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
260b3307b5eSBram Moolenaar  endif
261b3307b5eSBram Moolenaar
262b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
263b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
264b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
265b3307b5eSBram Moolenaar
266b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
267b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
268b3307b5eSBram Moolenaar
269b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
270b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
271b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
272b3307b5eSBram Moolenaar	\ })
273b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
274b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
275b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
276b3307b5eSBram Moolenaar    return
277b3307b5eSBram Moolenaar  endif
2784551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
2794551c0a9SBram Moolenaar  set modified
280b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
281b3307b5eSBram Moolenaar
282b3307b5eSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only
283b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
284b3307b5eSBram Moolenaar  " target is running.
285b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
286b3307b5eSBram Moolenaar  " Older gdb uses a different command.
287b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
288b3307b5eSBram Moolenaar
289b3307b5eSBram Moolenaar  let s:ptybuf = 0
290b3307b5eSBram Moolenaar  if has('win32')
291b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
292b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
293b3307b5eSBram Moolenaar  elseif has('terminal')
294b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
295b3307b5eSBram Moolenaar    " gdb window.
296b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
297b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
298b3307b5eSBram Moolenaar	  \ })
299b3307b5eSBram Moolenaar    if s:ptybuf == 0
300b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
301b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
302b3307b5eSBram Moolenaar      return
303b3307b5eSBram Moolenaar    endif
304b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
305b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
306b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
307b3307b5eSBram Moolenaar
308b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
309b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
310b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
311b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
312b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
313b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
314b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
315b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
316b3307b5eSBram Moolenaar  else
317b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
318b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
319b3307b5eSBram Moolenaar  endif
320b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
321b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
322b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
323b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
324b3307b5eSBram Moolenaar
325b3307b5eSBram Moolenaar  " Set arguments to be run
326b3307b5eSBram Moolenaar  if len(proc_args)
327b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
328b3307b5eSBram Moolenaar  endif
329b3307b5eSBram Moolenaar
330b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
331b3307b5eSBram Moolenaar  startinsert
332b3307b5eSBram Moolenaarendfunc
333b3307b5eSBram Moolenaar
334b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
33538baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
33638baa3e6SBram Moolenaar  " There can be only one.
33738baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
33838baa3e6SBram Moolenaar
33945d5f26dSBram Moolenaar  " Install debugger commands in the text window.
340b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
341e09ba7baSBram Moolenaar  call s:InstallCommands()
34245d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
343e09ba7baSBram Moolenaar
34451b0f370SBram Moolenaar  " Enable showing a balloon with eval info
345246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
346246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
34751b0f370SBram Moolenaar    if has("balloon_eval")
34851b0f370SBram Moolenaar      set ballooneval
349246fe03dSBram Moolenaar    endif
35051b0f370SBram Moolenaar    if has("balloon_eval_term")
35151b0f370SBram Moolenaar      set balloonevalterm
35251b0f370SBram Moolenaar    endif
35351b0f370SBram Moolenaar  endif
35451b0f370SBram Moolenaar
355de1a8314SBram Moolenaar  " Contains breakpoints that have been placed, key is the number.
356e09ba7baSBram Moolenaar  let s:breakpoints = {}
3571b9645deSBram Moolenaar
3581b9645deSBram Moolenaar  augroup TermDebug
3591b9645deSBram Moolenaar    au BufRead * call s:BufRead()
3601b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
361f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
3621b9645deSBram Moolenaar  augroup END
36332c67ba7SBram Moolenaar
364b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
365b3307b5eSBram Moolenaar  " window.
36632c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
36732c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
36832c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
36932c67ba7SBram Moolenaar  endif
370c572da5fSBram Moolenaarendfunc
371c572da5fSBram Moolenaar
372b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
373b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
374b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
375b3307b5eSBram Moolenaar  if s:way == 'prompt'
376b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
377b3307b5eSBram Moolenaar  else
378b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
379b3307b5eSBram Moolenaar  endif
380b3307b5eSBram Moolenaarendfunc
381b3307b5eSBram Moolenaar
382b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
383b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
384b3307b5eSBram Moolenaar  if s:way == 'prompt'
385b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
386b3307b5eSBram Moolenaar  else
387b3307b5eSBram Moolenaar    let do_continue = 0
388b3307b5eSBram Moolenaar    if !s:stopped
389b3307b5eSBram Moolenaar      let do_continue = 1
390b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
391b3307b5eSBram Moolenaar      sleep 10m
392b3307b5eSBram Moolenaar    endif
393b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
394b3307b5eSBram Moolenaar    if do_continue
395b3307b5eSBram Moolenaar      Continue
396b3307b5eSBram Moolenaar    endif
397b3307b5eSBram Moolenaar  endif
398b3307b5eSBram Moolenaarendfunc
399b3307b5eSBram Moolenaar
400b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
401b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
402b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
403b3307b5eSBram Moolenaarendfunc
404b3307b5eSBram Moolenaar
4054551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
4064551c0a9SBram Moolenaar" breakpoint.
407b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
4082ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
4092ed890f1SBram Moolenaar  if has('win32')
4102ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
4112ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
4124551c0a9SBram Moolenaar    if s:pid == 0
4134551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
4144551c0a9SBram Moolenaar    else
4154551c0a9SBram Moolenaar      call debugbreak(s:pid)
4164551c0a9SBram Moolenaar    endif
4172ed890f1SBram Moolenaar  else
4182ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
4192ed890f1SBram Moolenaar  endif
420b3307b5eSBram Moolenaarendfunc
421b3307b5eSBram Moolenaar
422b3307b5eSBram Moolenaar" Function called when gdb outputs text.
423b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
424b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
425b3307b5eSBram Moolenaar
426b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
427b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
428a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
429b3307b5eSBram Moolenaar    return
430b3307b5eSBram Moolenaar  endif
431b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
432b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
433b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
434b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
435b3307b5eSBram Moolenaar      unlet s:evalexpr
436b3307b5eSBram Moolenaar      return
437b3307b5eSBram Moolenaar    endif
438b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
439b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
440b3307b5eSBram Moolenaar  else
441b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
442b3307b5eSBram Moolenaar    return
443b3307b5eSBram Moolenaar  endif
444b3307b5eSBram Moolenaar
445b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
446b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
447b3307b5eSBram Moolenaar
448b3307b5eSBram Moolenaar  " Add the output above the current prompt.
449b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
4504551c0a9SBram Moolenaar  set modified
451b3307b5eSBram Moolenaar
452b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
453b3307b5eSBram Moolenaarendfunc
454b3307b5eSBram Moolenaar
455b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
456b3307b5eSBram Moolenaar" to the next ", unescaping characters.
457b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
458b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
459a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
460b3307b5eSBram Moolenaar    return
461b3307b5eSBram Moolenaar  endif
462b3307b5eSBram Moolenaar  let result = ''
463b3307b5eSBram Moolenaar  let i = 1
464b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
465b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
466b3307b5eSBram Moolenaar      let i += 1
467b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
468b3307b5eSBram Moolenaar	" drop \n
469b3307b5eSBram Moolenaar	let i += 1
470b3307b5eSBram Moolenaar	continue
471b3307b5eSBram Moolenaar      endif
472b3307b5eSBram Moolenaar    endif
473b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
474b3307b5eSBram Moolenaar    let i += 1
475b3307b5eSBram Moolenaar  endwhile
476b3307b5eSBram Moolenaar  return result
477b3307b5eSBram Moolenaarendfunc
478b3307b5eSBram Moolenaar
479a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
480a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
481a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
482a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
483a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
484a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
485a15b0a93SBram Moolenaar  endif
486a15b0a93SBram Moolenaar  return name
487a15b0a93SBram Moolenaarendfunc
488a15b0a93SBram Moolenaar
489b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
490fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
491b3623a38SBram Moolenaar  unlet s:gdbwin
492e09ba7baSBram Moolenaar
493b3307b5eSBram Moolenaar  call s:EndDebugCommon()
494b3307b5eSBram Moolenaarendfunc
495b3307b5eSBram Moolenaar
496b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
497e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
498e09ba7baSBram Moolenaar
499b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
500b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
501b3307b5eSBram Moolenaar  endif
502b3307b5eSBram Moolenaar
503b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
504e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
505e09ba7baSBram Moolenaar  call s:DeleteCommands()
506e09ba7baSBram Moolenaar
507e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
508b3307b5eSBram Moolenaar
50938baa3e6SBram Moolenaar  if s:save_columns > 0
51038baa3e6SBram Moolenaar    let &columns = s:save_columns
51138baa3e6SBram Moolenaar  endif
5121b9645deSBram Moolenaar
513246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
514246fe03dSBram Moolenaar    set balloonexpr=
51551b0f370SBram Moolenaar    if has("balloon_eval")
51651b0f370SBram Moolenaar      set noballooneval
517246fe03dSBram Moolenaar    endif
51851b0f370SBram Moolenaar    if has("balloon_eval_term")
51951b0f370SBram Moolenaar      set noballoonevalterm
52051b0f370SBram Moolenaar    endif
52151b0f370SBram Moolenaar  endif
52251b0f370SBram Moolenaar
5231b9645deSBram Moolenaar  au! TermDebug
524fe386641SBram Moolenaarendfunc
525fe386641SBram Moolenaar
526b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
527b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
528b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
5294551c0a9SBram Moolenaar  set nomodified
530b3307b5eSBram Moolenaar  close
531b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
532b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
533b3307b5eSBram Moolenaar  endif
534b3307b5eSBram Moolenaar
535b3307b5eSBram Moolenaar  call s:EndDebugCommon()
536b3307b5eSBram Moolenaar  unlet s:gdbwin
537b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
538b3307b5eSBram Moolenaarendfunc
539b3307b5eSBram Moolenaar
540fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
541fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
542fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
543fe386641SBram Moolenaar
544fe386641SBram Moolenaar  for msg in msgs
545fe386641SBram Moolenaar    " remove prefixed NL
546fe386641SBram Moolenaar    if msg[0] == "\n"
547fe386641SBram Moolenaar      let msg = msg[1:]
548fe386641SBram Moolenaar    endif
549fe386641SBram Moolenaar    if msg != ''
5501b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
551e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
55245d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
553e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
554e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
555e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
5564551c0a9SBram Moolenaar      elseif msg =~ '^=thread-group-started'
5574551c0a9SBram Moolenaar	call s:HandleProgramRun(msg)
55845d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
55945d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
56045d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
56145d5f26dSBram Moolenaar	call s:HandleError(msg)
562e09ba7baSBram Moolenaar      endif
563e09ba7baSBram Moolenaar    endif
564e09ba7baSBram Moolenaar  endfor
565e09ba7baSBram Moolenaarendfunc
566e09ba7baSBram Moolenaar
567e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
568e09ba7baSBram Moolenaarfunc s:InstallCommands()
569*963c1ad5SBram Moolenaar  let save_cpo = &cpo
570*963c1ad5SBram Moolenaar  set cpo&vim
571*963c1ad5SBram Moolenaar
572e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
57371137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
574e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
57545d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
576e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
57760e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
57860e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
57960e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
580b3307b5eSBram Moolenaar
581b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
582b3307b5eSBram Moolenaar  if s:way == 'prompt'
583b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
584b3307b5eSBram Moolenaar  else
585b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
586b3307b5eSBram Moolenaar  endif
587b3307b5eSBram Moolenaar
58845d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
58945d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
59045d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
591b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
59271137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
59345d5f26dSBram Moolenaar
59445d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
59545d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
5961b9645deSBram Moolenaar
597f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
59871137fedSBram Moolenaar    call s:InstallWinbar()
59971137fedSBram Moolenaar
60071137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
60171137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
60271137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
60371137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
60471137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
60571137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
60671137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
60771137fedSBram Moolenaar    endif
60871137fedSBram Moolenaar  endif
609*963c1ad5SBram Moolenaar
610*963c1ad5SBram Moolenaar  let &cpo = save_cpo
61171137fedSBram Moolenaarendfunc
61271137fedSBram Moolenaar
61371137fedSBram Moolenaarlet s:winbar_winids = []
61471137fedSBram Moolenaar
61571137fedSBram Moolenaar" Install the window toolbar in the current window.
61671137fedSBram Moolenaarfunc s:InstallWinbar()
617c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
61824a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
61924a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
62024a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
62124a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
62260e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
62324a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
62471137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
625c4b533e1SBram Moolenaar  endif
626e09ba7baSBram Moolenaarendfunc
627e09ba7baSBram Moolenaar
628e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
629e09ba7baSBram Moolenaarfunc s:DeleteCommands()
630e09ba7baSBram Moolenaar  delcommand Break
63171137fedSBram Moolenaar  delcommand Clear
632e09ba7baSBram Moolenaar  delcommand Step
63345d5f26dSBram Moolenaar  delcommand Over
634e09ba7baSBram Moolenaar  delcommand Finish
63560e73f2aSBram Moolenaar  delcommand Run
63660e73f2aSBram Moolenaar  delcommand Arguments
63760e73f2aSBram Moolenaar  delcommand Stop
638e09ba7baSBram Moolenaar  delcommand Continue
63945d5f26dSBram Moolenaar  delcommand Evaluate
64045d5f26dSBram Moolenaar  delcommand Gdb
64145d5f26dSBram Moolenaar  delcommand Program
642b3623a38SBram Moolenaar  delcommand Source
64371137fedSBram Moolenaar  delcommand Winbar
64445d5f26dSBram Moolenaar
64545d5f26dSBram Moolenaar  nunmap K
6461b9645deSBram Moolenaar
6471b9645deSBram Moolenaar  if has('menu')
64871137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
64971137fedSBram Moolenaar    let curwinid = win_getid(winnr())
65071137fedSBram Moolenaar    for winid in s:winbar_winids
65171137fedSBram Moolenaar      if win_gotoid(winid)
6521b9645deSBram Moolenaar	aunmenu WinBar.Step
6531b9645deSBram Moolenaar	aunmenu WinBar.Next
6541b9645deSBram Moolenaar	aunmenu WinBar.Finish
6551b9645deSBram Moolenaar	aunmenu WinBar.Cont
65660e73f2aSBram Moolenaar	aunmenu WinBar.Stop
6571b9645deSBram Moolenaar	aunmenu WinBar.Eval
6581b9645deSBram Moolenaar      endif
65971137fedSBram Moolenaar    endfor
66071137fedSBram Moolenaar    call win_gotoid(curwinid)
66171137fedSBram Moolenaar    let s:winbar_winids = []
66271137fedSBram Moolenaar
66371137fedSBram Moolenaar    if exists('s:saved_mousemodel')
66471137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
66571137fedSBram Moolenaar      unlet s:saved_mousemodel
66671137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
66771137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
66871137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
66971137fedSBram Moolenaar      aunmenu PopUp.Evaluate
67071137fedSBram Moolenaar    endif
67171137fedSBram Moolenaar  endif
6721b9645deSBram Moolenaar
67345d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
67445d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
67545d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
67645d5f26dSBram Moolenaar  endfor
67745d5f26dSBram Moolenaar  unlet s:breakpoints
678a15b0a93SBram Moolenaar
679a15b0a93SBram Moolenaar  sign undefine debugPC
680a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
681a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
682a15b0a93SBram Moolenaar  endfor
6834551c0a9SBram Moolenaar  let s:BreakpointSigns = []
684e09ba7baSBram Moolenaarendfunc
685e09ba7baSBram Moolenaar
686e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
687e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
68860e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
68960e73f2aSBram Moolenaar  " Interrupt to make it work.
69060e73f2aSBram Moolenaar  let do_continue = 0
69160e73f2aSBram Moolenaar  if !s:stopped
69260e73f2aSBram Moolenaar    let do_continue = 1
693b3307b5eSBram Moolenaar    if s:way == 'prompt'
6944551c0a9SBram Moolenaar      call s:PromptInterrupt()
695b3307b5eSBram Moolenaar    else
69660e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
697b3307b5eSBram Moolenaar    endif
69860e73f2aSBram Moolenaar    sleep 10m
69960e73f2aSBram Moolenaar  endif
700a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
701a15b0a93SBram Moolenaar  call s:SendCommand('-break-insert '
702a15b0a93SBram Moolenaar	\ . fnameescape(expand('%:p')) . ':' . line('.'))
70360e73f2aSBram Moolenaar  if do_continue
70460e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
70560e73f2aSBram Moolenaar  endif
706e09ba7baSBram Moolenaarendfunc
707e09ba7baSBram Moolenaar
70871137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
70971137fedSBram Moolenaarfunc s:ClearBreakpoint()
710e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
711e09ba7baSBram Moolenaar  let lnum = line('.')
712e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
713e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
714b3307b5eSBram Moolenaar      call s:SendCommand('-break-delete ' . key)
715e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
716e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
717e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
718e09ba7baSBram Moolenaar      break
719e09ba7baSBram Moolenaar    endif
720e09ba7baSBram Moolenaar  endfor
721e09ba7baSBram Moolenaarendfunc
722e09ba7baSBram Moolenaar
72360e73f2aSBram Moolenaarfunc s:Run(args)
72460e73f2aSBram Moolenaar  if a:args != ''
72560e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
72660e73f2aSBram Moolenaar  endif
72760e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
72860e73f2aSBram Moolenaarendfunc
72960e73f2aSBram Moolenaar
73051b0f370SBram Moolenaarfunc s:SendEval(expr)
73151b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
73251b0f370SBram Moolenaar  let s:evalexpr = a:expr
73351b0f370SBram Moolenaarendfunc
73451b0f370SBram Moolenaar
73545d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
73645d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
73745d5f26dSBram Moolenaar  if a:arg != ''
73845d5f26dSBram Moolenaar    let expr = a:arg
73945d5f26dSBram Moolenaar  elseif a:range == 2
74045d5f26dSBram Moolenaar    let pos = getcurpos()
74145d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
74245d5f26dSBram Moolenaar    let regt = getregtype('v')
74345d5f26dSBram Moolenaar    normal! gv"vy
74445d5f26dSBram Moolenaar    let expr = @v
74545d5f26dSBram Moolenaar    call setpos('.', pos)
74645d5f26dSBram Moolenaar    call setreg('v', reg, regt)
74745d5f26dSBram Moolenaar  else
74845d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
74945d5f26dSBram Moolenaar  endif
75022f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
75151b0f370SBram Moolenaar  call s:SendEval(expr)
75245d5f26dSBram Moolenaarendfunc
75345d5f26dSBram Moolenaar
75422f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
75551b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
75651b0f370SBram Moolenaar
75745d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
75845d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
7591b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
7601b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
76151b0f370SBram Moolenaar  if s:evalFromBalloonExpr
76251b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
76351b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
76451b0f370SBram Moolenaar    else
76551b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
76651b0f370SBram Moolenaar    endif
76751b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
76851b0f370SBram Moolenaar  else
7691b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
77051b0f370SBram Moolenaar  endif
7711b9645deSBram Moolenaar
7727f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
7731b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
77422f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
77551b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
77651b0f370SBram Moolenaar  else
77751b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
7781b9645deSBram Moolenaar  endif
77945d5f26dSBram Moolenaarendfunc
78045d5f26dSBram Moolenaar
78151b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
78251b0f370SBram Moolenaar" if there is any.
78351b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
784b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
785b3307b5eSBram Moolenaar    return
786b3307b5eSBram Moolenaar  endif
787b3307b5eSBram Moolenaar  if !s:stopped
788b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
789b3307b5eSBram Moolenaar    " mouse triggers a balloon.
79051b0f370SBram Moolenaar    return
79151b0f370SBram Moolenaar  endif
79251b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
79351b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
79422f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
79522f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
79651b0f370SBram Moolenaar  return ''
79751b0f370SBram Moolenaarendfunc
79851b0f370SBram Moolenaar
79945d5f26dSBram Moolenaar" Handle an error.
80045d5f26dSBram Moolenaarfunc s:HandleError(msg)
80122f1d0e3SBram Moolenaar  if s:ignoreEvalError
80251b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
80322f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
80422f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
80551b0f370SBram Moolenaar    return
80651b0f370SBram Moolenaar  endif
80745d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
80845d5f26dSBram Moolenaarendfunc
80945d5f26dSBram Moolenaar
810b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
811b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
812c4b533e1SBram Moolenaar    new
813b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
814c4b533e1SBram Moolenaar    call s:InstallWinbar()
815c4b533e1SBram Moolenaar  endif
816c4b533e1SBram Moolenaarendfunc
817c4b533e1SBram Moolenaar
818e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
819e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
820e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
821fe386641SBram Moolenaar  let wid = win_getid(winnr())
822fe386641SBram Moolenaar
82360e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
8244551c0a9SBram Moolenaar    call ch_log('program stopped')
82560e73f2aSBram Moolenaar    let s:stopped = 1
82660e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
8274551c0a9SBram Moolenaar    call ch_log('program running')
82860e73f2aSBram Moolenaar    let s:stopped = 0
82960e73f2aSBram Moolenaar  endif
83060e73f2aSBram Moolenaar
831a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
832a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
833a15b0a93SBram Moolenaar  else
834a15b0a93SBram Moolenaar    let fname = ''
835a15b0a93SBram Moolenaar  endif
8361b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
837e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
838fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
8394551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
8401b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
841fe386641SBram Moolenaar	if &modified
842fe386641SBram Moolenaar	  " TODO: find existing window
843fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
844b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
845c4b533e1SBram Moolenaar	  call s:InstallWinbar()
846fe386641SBram Moolenaar	else
847fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
848fe386641SBram Moolenaar	endif
849fe386641SBram Moolenaar      endif
850fe386641SBram Moolenaar      exe lnum
85101164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
8521b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
853fe386641SBram Moolenaar      setlocal signcolumn=yes
854fe386641SBram Moolenaar    endif
8554551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
856fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
857fe386641SBram Moolenaar  endif
858fe386641SBram Moolenaar
859fe386641SBram Moolenaar  call win_gotoid(wid)
860e09ba7baSBram Moolenaarendfunc
861e09ba7baSBram Moolenaar
862de1a8314SBram Moolenaarlet s:BreakpointSigns = []
863a15b0a93SBram Moolenaar
864a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr)
865de1a8314SBram Moolenaar  if index(s:BreakpointSigns, a:nr) == -1
866de1a8314SBram Moolenaar    call add(s:BreakpointSigns, a:nr)
867de1a8314SBram Moolenaar    exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint"
868de1a8314SBram Moolenaar  endif
869de1a8314SBram Moolenaarendfunc
870de1a8314SBram Moolenaar
871e09ba7baSBram Moolenaar" Handle setting a breakpoint
872e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
873e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
8746dccc962SBram Moolenaar  if a:msg !~ 'fullname='
8756dccc962SBram Moolenaar    " a watch does not have a file name
8766dccc962SBram Moolenaar    return
8776dccc962SBram Moolenaar  endif
8786dccc962SBram Moolenaar
879e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
880e09ba7baSBram Moolenaar  if nr == 0
881e09ba7baSBram Moolenaar    return
882fe386641SBram Moolenaar  endif
883de1a8314SBram Moolenaar  call s:CreateBreakpoint(nr)
884e09ba7baSBram Moolenaar
885e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
886e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
887e09ba7baSBram Moolenaar  else
888e09ba7baSBram Moolenaar    let entry = {}
889e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
890fe386641SBram Moolenaar  endif
891e09ba7baSBram Moolenaar
892a15b0a93SBram Moolenaar  let fname = s:GetFullname(a:msg)
893e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
894e09ba7baSBram Moolenaar  let entry['fname'] = fname
895e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
8961b9645deSBram Moolenaar
8971b9645deSBram Moolenaar  if bufloaded(fname)
8981b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
8991b9645deSBram Moolenaar  endif
9001b9645deSBram Moolenaarendfunc
9011b9645deSBram Moolenaar
9021b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
903de1a8314SBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname']
9041b9645deSBram Moolenaar  let a:entry['placed'] = 1
905e09ba7baSBram Moolenaarendfunc
906e09ba7baSBram Moolenaar
907e09ba7baSBram Moolenaar" Handle deleting a breakpoint
908e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
909e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
910e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
911e09ba7baSBram Moolenaar  if nr == 0
912e09ba7baSBram Moolenaar    return
913e09ba7baSBram Moolenaar  endif
9141b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
9151b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
9161b9645deSBram Moolenaar    if has_key(entry, 'placed')
917e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
9181b9645deSBram Moolenaar      unlet entry['placed']
9191b9645deSBram Moolenaar    endif
920e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
9211b9645deSBram Moolenaar  endif
922c572da5fSBram Moolenaarendfunc
9231b9645deSBram Moolenaar
9244551c0a9SBram Moolenaar" Handle the debugged program starting to run.
9254551c0a9SBram Moolenaar" Will store the process ID in s:pid
9264551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
9274551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
9284551c0a9SBram Moolenaar  if nr == 0
9294551c0a9SBram Moolenaar    return
9304551c0a9SBram Moolenaar  endif
9314551c0a9SBram Moolenaar  let s:pid = nr
9324551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
9334551c0a9SBram Moolenaarendfunc
9344551c0a9SBram Moolenaar
9351b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
9361b9645deSBram Moolenaarfunc s:BufRead()
9371b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9381b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9391b9645deSBram Moolenaar    if entry['fname'] == fname
9401b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
9411b9645deSBram Moolenaar    endif
9421b9645deSBram Moolenaar  endfor
9431b9645deSBram Moolenaarendfunc
9441b9645deSBram Moolenaar
9451b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
9461b9645deSBram Moolenaarfunc s:BufUnloaded()
9471b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9481b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9491b9645deSBram Moolenaar    if entry['fname'] == fname
9501b9645deSBram Moolenaar      let entry['placed'] = 0
9511b9645deSBram Moolenaar    endif
9521b9645deSBram Moolenaar  endfor
9531b9645deSBram Moolenaarendfunc
954ca4cc018SBram Moolenaar
955ca4cc018SBram Moolenaarlet &cpo = s:keepcpo
956ca4cc018SBram Moolenaarunlet s:keepcpo
957