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 '
203*f63db65bSBram Moolenaar	" response can be in the same line or the next line
204*f63db65bSBram Moolenaar	let response = term_getline(s:gdbbuf, lnum) . term_getline(s:gdbbuf, lnum + 1)
2053e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
206f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
2073e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:ptybuf
2083e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:commbuf
2093e4b84d0SBram Moolenaar	  return
2103e4b84d0SBram Moolenaar	endif
2113e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
2123e4b84d0SBram Moolenaar	  " Success!
2133e4b84d0SBram Moolenaar	  break
2143e4b84d0SBram Moolenaar	endif
2153e4b84d0SBram Moolenaar      endif
2163e4b84d0SBram Moolenaar    endfor
2173e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2183e4b84d0SBram Moolenaar      break
2193e4b84d0SBram Moolenaar    endif
2203e4b84d0SBram Moolenaar    let try_count += 1
2213e4b84d0SBram Moolenaar    if try_count > 100
2223e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2233e4b84d0SBram Moolenaar      break
2243e4b84d0SBram Moolenaar    endif
2253e4b84d0SBram Moolenaar    sleep 10m
2263e4b84d0SBram Moolenaar  endwhile
2273e4b84d0SBram Moolenaar
22860e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
22960e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
23060e73f2aSBram Moolenaar  " running.
23160e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
232b3307b5eSBram Moolenaar  " Older gdb uses a different command.
233b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
234e09ba7baSBram Moolenaar
235f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
236f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
237b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
238f3ba14ffSBram Moolenaar
239b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
240b3307b5eSBram Moolenaarendfunc
241b3307b5eSBram Moolenaar
242b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
243b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
244b3307b5eSBram Moolenaar  if s:vertical
245b3307b5eSBram Moolenaar    vertical new
246b3307b5eSBram Moolenaar  else
247b3307b5eSBram Moolenaar    new
248b3307b5eSBram Moolenaar  endif
249b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
250b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
251b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
252b3307b5eSBram Moolenaar  set buftype=prompt
253b3307b5eSBram Moolenaar  file gdb
254b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
255b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
256b3307b5eSBram Moolenaar
257b3307b5eSBram Moolenaar  if s:vertical
258b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
259b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
260b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
261b3307b5eSBram Moolenaar  endif
262b3307b5eSBram Moolenaar
263b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
264b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
265b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
266b3307b5eSBram Moolenaar
267b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
268b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
269b3307b5eSBram Moolenaar
270b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
271b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
272b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
273b3307b5eSBram Moolenaar	\ })
274b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
275b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
276b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
277b3307b5eSBram Moolenaar    return
278b3307b5eSBram Moolenaar  endif
2794551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
2804551c0a9SBram Moolenaar  set modified
281b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
282b3307b5eSBram Moolenaar
283b3307b5eSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only
284b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
285b3307b5eSBram Moolenaar  " target is running.
286b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
287b3307b5eSBram Moolenaar  " Older gdb uses a different command.
288b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
289b3307b5eSBram Moolenaar
290b3307b5eSBram Moolenaar  let s:ptybuf = 0
291b3307b5eSBram Moolenaar  if has('win32')
292b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
293b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
294b3307b5eSBram Moolenaar  elseif has('terminal')
295b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
296b3307b5eSBram Moolenaar    " gdb window.
297b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
298b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
299b3307b5eSBram Moolenaar	  \ })
300b3307b5eSBram Moolenaar    if s:ptybuf == 0
301b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
302b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
303b3307b5eSBram Moolenaar      return
304b3307b5eSBram Moolenaar    endif
305b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
306b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
307b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
308b3307b5eSBram Moolenaar
309b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
310b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
311b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
312b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
313b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
314b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
315b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
316b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
317b3307b5eSBram Moolenaar  else
318b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
319b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
320b3307b5eSBram Moolenaar  endif
321b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
322b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
323b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
324b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
325b3307b5eSBram Moolenaar
326b3307b5eSBram Moolenaar  " Set arguments to be run
327b3307b5eSBram Moolenaar  if len(proc_args)
328b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
329b3307b5eSBram Moolenaar  endif
330b3307b5eSBram Moolenaar
331b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
332b3307b5eSBram Moolenaar  startinsert
333b3307b5eSBram Moolenaarendfunc
334b3307b5eSBram Moolenaar
335b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
33638baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
33738baa3e6SBram Moolenaar  " There can be only one.
33838baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
33938baa3e6SBram Moolenaar
34045d5f26dSBram Moolenaar  " Install debugger commands in the text window.
341b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
342e09ba7baSBram Moolenaar  call s:InstallCommands()
34345d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
344e09ba7baSBram Moolenaar
34551b0f370SBram Moolenaar  " Enable showing a balloon with eval info
346246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
347246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
34851b0f370SBram Moolenaar    if has("balloon_eval")
34951b0f370SBram Moolenaar      set ballooneval
350246fe03dSBram Moolenaar    endif
35151b0f370SBram Moolenaar    if has("balloon_eval_term")
35251b0f370SBram Moolenaar      set balloonevalterm
35351b0f370SBram Moolenaar    endif
35451b0f370SBram Moolenaar  endif
35551b0f370SBram Moolenaar
356de1a8314SBram Moolenaar  " Contains breakpoints that have been placed, key is the number.
357e09ba7baSBram Moolenaar  let s:breakpoints = {}
3581b9645deSBram Moolenaar
3591b9645deSBram Moolenaar  augroup TermDebug
3601b9645deSBram Moolenaar    au BufRead * call s:BufRead()
3611b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
362f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
3631b9645deSBram Moolenaar  augroup END
36432c67ba7SBram Moolenaar
365b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
366b3307b5eSBram Moolenaar  " window.
36732c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
36832c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
36932c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
37032c67ba7SBram Moolenaar  endif
371c572da5fSBram Moolenaarendfunc
372c572da5fSBram Moolenaar
373b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
374b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
375b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
376b3307b5eSBram Moolenaar  if s:way == 'prompt'
377b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
378b3307b5eSBram Moolenaar  else
379b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
380b3307b5eSBram Moolenaar  endif
381b3307b5eSBram Moolenaarendfunc
382b3307b5eSBram Moolenaar
383b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
384b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
385b3307b5eSBram Moolenaar  if s:way == 'prompt'
386b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
387b3307b5eSBram Moolenaar  else
388b3307b5eSBram Moolenaar    let do_continue = 0
389b3307b5eSBram Moolenaar    if !s:stopped
390b3307b5eSBram Moolenaar      let do_continue = 1
391b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
392b3307b5eSBram Moolenaar      sleep 10m
393b3307b5eSBram Moolenaar    endif
394b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
395b3307b5eSBram Moolenaar    if do_continue
396b3307b5eSBram Moolenaar      Continue
397b3307b5eSBram Moolenaar    endif
398b3307b5eSBram Moolenaar  endif
399b3307b5eSBram Moolenaarendfunc
400b3307b5eSBram Moolenaar
401b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
402b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
403b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
404b3307b5eSBram Moolenaarendfunc
405b3307b5eSBram Moolenaar
4064551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
4074551c0a9SBram Moolenaar" breakpoint.
408b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
4092ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
4102ed890f1SBram Moolenaar  if has('win32')
4112ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
4122ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
4134551c0a9SBram Moolenaar    if s:pid == 0
4144551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
4154551c0a9SBram Moolenaar    else
4164551c0a9SBram Moolenaar      call debugbreak(s:pid)
4174551c0a9SBram Moolenaar    endif
4182ed890f1SBram Moolenaar  else
4192ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
4202ed890f1SBram Moolenaar  endif
421b3307b5eSBram Moolenaarendfunc
422b3307b5eSBram Moolenaar
423b3307b5eSBram Moolenaar" Function called when gdb outputs text.
424b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
425b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
426b3307b5eSBram Moolenaar
427b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
428b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
429a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
430b3307b5eSBram Moolenaar    return
431b3307b5eSBram Moolenaar  endif
432b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
433b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
434b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
435b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
436b3307b5eSBram Moolenaar      unlet s:evalexpr
437b3307b5eSBram Moolenaar      return
438b3307b5eSBram Moolenaar    endif
439b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
440b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
441b3307b5eSBram Moolenaar  else
442b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
443b3307b5eSBram Moolenaar    return
444b3307b5eSBram Moolenaar  endif
445b3307b5eSBram Moolenaar
446b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
447b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
448b3307b5eSBram Moolenaar
449b3307b5eSBram Moolenaar  " Add the output above the current prompt.
450b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
4514551c0a9SBram Moolenaar  set modified
452b3307b5eSBram Moolenaar
453b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
454b3307b5eSBram Moolenaarendfunc
455b3307b5eSBram Moolenaar
456b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
457b3307b5eSBram Moolenaar" to the next ", unescaping characters.
458b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
459b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
460a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
461b3307b5eSBram Moolenaar    return
462b3307b5eSBram Moolenaar  endif
463b3307b5eSBram Moolenaar  let result = ''
464b3307b5eSBram Moolenaar  let i = 1
465b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
466b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
467b3307b5eSBram Moolenaar      let i += 1
468b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
469b3307b5eSBram Moolenaar	" drop \n
470b3307b5eSBram Moolenaar	let i += 1
471b3307b5eSBram Moolenaar	continue
472b3307b5eSBram Moolenaar      endif
473b3307b5eSBram Moolenaar    endif
474b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
475b3307b5eSBram Moolenaar    let i += 1
476b3307b5eSBram Moolenaar  endwhile
477b3307b5eSBram Moolenaar  return result
478b3307b5eSBram Moolenaarendfunc
479b3307b5eSBram Moolenaar
480a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
481a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
482a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
483a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
484a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
485a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
486a15b0a93SBram Moolenaar  endif
487a15b0a93SBram Moolenaar  return name
488a15b0a93SBram Moolenaarendfunc
489a15b0a93SBram Moolenaar
490b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
491fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
492b3623a38SBram Moolenaar  unlet s:gdbwin
493e09ba7baSBram Moolenaar
494b3307b5eSBram Moolenaar  call s:EndDebugCommon()
495b3307b5eSBram Moolenaarendfunc
496b3307b5eSBram Moolenaar
497b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
498e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
499e09ba7baSBram Moolenaar
500b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
501b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
502b3307b5eSBram Moolenaar  endif
503b3307b5eSBram Moolenaar
504b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
505e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
506e09ba7baSBram Moolenaar  call s:DeleteCommands()
507e09ba7baSBram Moolenaar
508e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
509b3307b5eSBram Moolenaar
51038baa3e6SBram Moolenaar  if s:save_columns > 0
51138baa3e6SBram Moolenaar    let &columns = s:save_columns
51238baa3e6SBram Moolenaar  endif
5131b9645deSBram Moolenaar
514246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
515246fe03dSBram Moolenaar    set balloonexpr=
51651b0f370SBram Moolenaar    if has("balloon_eval")
51751b0f370SBram Moolenaar      set noballooneval
518246fe03dSBram Moolenaar    endif
51951b0f370SBram Moolenaar    if has("balloon_eval_term")
52051b0f370SBram Moolenaar      set noballoonevalterm
52151b0f370SBram Moolenaar    endif
52251b0f370SBram Moolenaar  endif
52351b0f370SBram Moolenaar
5241b9645deSBram Moolenaar  au! TermDebug
525fe386641SBram Moolenaarendfunc
526fe386641SBram Moolenaar
527b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
528b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
529b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
5304551c0a9SBram Moolenaar  set nomodified
531b3307b5eSBram Moolenaar  close
532b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
533b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
534b3307b5eSBram Moolenaar  endif
535b3307b5eSBram Moolenaar
536b3307b5eSBram Moolenaar  call s:EndDebugCommon()
537b3307b5eSBram Moolenaar  unlet s:gdbwin
538b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
539b3307b5eSBram Moolenaarendfunc
540b3307b5eSBram Moolenaar
541fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
542fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
543fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
544fe386641SBram Moolenaar
545fe386641SBram Moolenaar  for msg in msgs
546fe386641SBram Moolenaar    " remove prefixed NL
547fe386641SBram Moolenaar    if msg[0] == "\n"
548fe386641SBram Moolenaar      let msg = msg[1:]
549fe386641SBram Moolenaar    endif
550fe386641SBram Moolenaar    if msg != ''
5511b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
552e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
55345d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
554e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
555e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
556e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
5574551c0a9SBram Moolenaar      elseif msg =~ '^=thread-group-started'
5584551c0a9SBram Moolenaar	call s:HandleProgramRun(msg)
55945d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
56045d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
56145d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
56245d5f26dSBram Moolenaar	call s:HandleError(msg)
563e09ba7baSBram Moolenaar      endif
564e09ba7baSBram Moolenaar    endif
565e09ba7baSBram Moolenaar  endfor
566e09ba7baSBram Moolenaarendfunc
567e09ba7baSBram Moolenaar
568e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
569e09ba7baSBram Moolenaarfunc s:InstallCommands()
570963c1ad5SBram Moolenaar  let save_cpo = &cpo
571963c1ad5SBram Moolenaar  set cpo&vim
572963c1ad5SBram Moolenaar
573e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
57471137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
575e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
57645d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
577e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
57860e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
57960e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
58060e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
581b3307b5eSBram Moolenaar
582b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
583b3307b5eSBram Moolenaar  if s:way == 'prompt'
584b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
585b3307b5eSBram Moolenaar  else
586b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
587b3307b5eSBram Moolenaar  endif
588b3307b5eSBram Moolenaar
58945d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
59045d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
59145d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
592b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
59371137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
59445d5f26dSBram Moolenaar
59545d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
59645d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
5971b9645deSBram Moolenaar
598f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
59971137fedSBram Moolenaar    call s:InstallWinbar()
60071137fedSBram Moolenaar
60171137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
60271137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
60371137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
60471137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
60571137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
60671137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
60771137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
60871137fedSBram Moolenaar    endif
60971137fedSBram Moolenaar  endif
610963c1ad5SBram Moolenaar
611963c1ad5SBram Moolenaar  let &cpo = save_cpo
61271137fedSBram Moolenaarendfunc
61371137fedSBram Moolenaar
61471137fedSBram Moolenaarlet s:winbar_winids = []
61571137fedSBram Moolenaar
61671137fedSBram Moolenaar" Install the window toolbar in the current window.
61771137fedSBram Moolenaarfunc s:InstallWinbar()
618c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
61924a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
62024a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
62124a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
62224a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
62360e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
62424a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
62571137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
626c4b533e1SBram Moolenaar  endif
627e09ba7baSBram Moolenaarendfunc
628e09ba7baSBram Moolenaar
629e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
630e09ba7baSBram Moolenaarfunc s:DeleteCommands()
631e09ba7baSBram Moolenaar  delcommand Break
63271137fedSBram Moolenaar  delcommand Clear
633e09ba7baSBram Moolenaar  delcommand Step
63445d5f26dSBram Moolenaar  delcommand Over
635e09ba7baSBram Moolenaar  delcommand Finish
63660e73f2aSBram Moolenaar  delcommand Run
63760e73f2aSBram Moolenaar  delcommand Arguments
63860e73f2aSBram Moolenaar  delcommand Stop
639e09ba7baSBram Moolenaar  delcommand Continue
64045d5f26dSBram Moolenaar  delcommand Evaluate
64145d5f26dSBram Moolenaar  delcommand Gdb
64245d5f26dSBram Moolenaar  delcommand Program
643b3623a38SBram Moolenaar  delcommand Source
64471137fedSBram Moolenaar  delcommand Winbar
64545d5f26dSBram Moolenaar
64645d5f26dSBram Moolenaar  nunmap K
6471b9645deSBram Moolenaar
6481b9645deSBram Moolenaar  if has('menu')
64971137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
65071137fedSBram Moolenaar    let curwinid = win_getid(winnr())
65171137fedSBram Moolenaar    for winid in s:winbar_winids
65271137fedSBram Moolenaar      if win_gotoid(winid)
6531b9645deSBram Moolenaar	aunmenu WinBar.Step
6541b9645deSBram Moolenaar	aunmenu WinBar.Next
6551b9645deSBram Moolenaar	aunmenu WinBar.Finish
6561b9645deSBram Moolenaar	aunmenu WinBar.Cont
65760e73f2aSBram Moolenaar	aunmenu WinBar.Stop
6581b9645deSBram Moolenaar	aunmenu WinBar.Eval
6591b9645deSBram Moolenaar      endif
66071137fedSBram Moolenaar    endfor
66171137fedSBram Moolenaar    call win_gotoid(curwinid)
66271137fedSBram Moolenaar    let s:winbar_winids = []
66371137fedSBram Moolenaar
66471137fedSBram Moolenaar    if exists('s:saved_mousemodel')
66571137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
66671137fedSBram Moolenaar      unlet s:saved_mousemodel
66771137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
66871137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
66971137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
67071137fedSBram Moolenaar      aunmenu PopUp.Evaluate
67171137fedSBram Moolenaar    endif
67271137fedSBram Moolenaar  endif
6731b9645deSBram Moolenaar
67445d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
67545d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
67645d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
67745d5f26dSBram Moolenaar  endfor
67845d5f26dSBram Moolenaar  unlet s:breakpoints
679a15b0a93SBram Moolenaar
680a15b0a93SBram Moolenaar  sign undefine debugPC
681a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
682a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
683a15b0a93SBram Moolenaar  endfor
6844551c0a9SBram Moolenaar  let s:BreakpointSigns = []
685e09ba7baSBram Moolenaarendfunc
686e09ba7baSBram Moolenaar
687e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
688e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
68960e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
69060e73f2aSBram Moolenaar  " Interrupt to make it work.
69160e73f2aSBram Moolenaar  let do_continue = 0
69260e73f2aSBram Moolenaar  if !s:stopped
69360e73f2aSBram Moolenaar    let do_continue = 1
694b3307b5eSBram Moolenaar    if s:way == 'prompt'
6954551c0a9SBram Moolenaar      call s:PromptInterrupt()
696b3307b5eSBram Moolenaar    else
69760e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
698b3307b5eSBram Moolenaar    endif
69960e73f2aSBram Moolenaar    sleep 10m
70060e73f2aSBram Moolenaar  endif
701a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
702a15b0a93SBram Moolenaar  call s:SendCommand('-break-insert '
703a15b0a93SBram Moolenaar	\ . fnameescape(expand('%:p')) . ':' . line('.'))
70460e73f2aSBram Moolenaar  if do_continue
70560e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
70660e73f2aSBram Moolenaar  endif
707e09ba7baSBram Moolenaarendfunc
708e09ba7baSBram Moolenaar
70971137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
71071137fedSBram Moolenaarfunc s:ClearBreakpoint()
711e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
712e09ba7baSBram Moolenaar  let lnum = line('.')
713e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
714e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
715b3307b5eSBram Moolenaar      call s:SendCommand('-break-delete ' . key)
716e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
717e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
718e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
719e09ba7baSBram Moolenaar      break
720e09ba7baSBram Moolenaar    endif
721e09ba7baSBram Moolenaar  endfor
722e09ba7baSBram Moolenaarendfunc
723e09ba7baSBram Moolenaar
72460e73f2aSBram Moolenaarfunc s:Run(args)
72560e73f2aSBram Moolenaar  if a:args != ''
72660e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
72760e73f2aSBram Moolenaar  endif
72860e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
72960e73f2aSBram Moolenaarendfunc
73060e73f2aSBram Moolenaar
73151b0f370SBram Moolenaarfunc s:SendEval(expr)
73251b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
73351b0f370SBram Moolenaar  let s:evalexpr = a:expr
73451b0f370SBram Moolenaarendfunc
73551b0f370SBram Moolenaar
73645d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
73745d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
73845d5f26dSBram Moolenaar  if a:arg != ''
73945d5f26dSBram Moolenaar    let expr = a:arg
74045d5f26dSBram Moolenaar  elseif a:range == 2
74145d5f26dSBram Moolenaar    let pos = getcurpos()
74245d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
74345d5f26dSBram Moolenaar    let regt = getregtype('v')
74445d5f26dSBram Moolenaar    normal! gv"vy
74545d5f26dSBram Moolenaar    let expr = @v
74645d5f26dSBram Moolenaar    call setpos('.', pos)
74745d5f26dSBram Moolenaar    call setreg('v', reg, regt)
74845d5f26dSBram Moolenaar  else
74945d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
75045d5f26dSBram Moolenaar  endif
75122f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
75251b0f370SBram Moolenaar  call s:SendEval(expr)
75345d5f26dSBram Moolenaarendfunc
75445d5f26dSBram Moolenaar
75522f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
75651b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
75751b0f370SBram Moolenaar
75845d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
75945d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
7601b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
7611b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
76251b0f370SBram Moolenaar  if s:evalFromBalloonExpr
76351b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
76451b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
76551b0f370SBram Moolenaar    else
76651b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
76751b0f370SBram Moolenaar    endif
76851b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
76951b0f370SBram Moolenaar  else
7701b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
77151b0f370SBram Moolenaar  endif
7721b9645deSBram Moolenaar
7737f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
7741b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
77522f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
77651b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
77751b0f370SBram Moolenaar  else
77851b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
7791b9645deSBram Moolenaar  endif
78045d5f26dSBram Moolenaarendfunc
78145d5f26dSBram Moolenaar
78251b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
78351b0f370SBram Moolenaar" if there is any.
78451b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
785b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
786b3307b5eSBram Moolenaar    return
787b3307b5eSBram Moolenaar  endif
788b3307b5eSBram Moolenaar  if !s:stopped
789b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
790b3307b5eSBram Moolenaar    " mouse triggers a balloon.
79151b0f370SBram Moolenaar    return
79251b0f370SBram Moolenaar  endif
79351b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
79451b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
79522f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
79622f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
79751b0f370SBram Moolenaar  return ''
79851b0f370SBram Moolenaarendfunc
79951b0f370SBram Moolenaar
80045d5f26dSBram Moolenaar" Handle an error.
80145d5f26dSBram Moolenaarfunc s:HandleError(msg)
80222f1d0e3SBram Moolenaar  if s:ignoreEvalError
80351b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
80422f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
80522f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
80651b0f370SBram Moolenaar    return
80751b0f370SBram Moolenaar  endif
80845d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
80945d5f26dSBram Moolenaarendfunc
81045d5f26dSBram Moolenaar
811b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
812b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
813c4b533e1SBram Moolenaar    new
814b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
815c4b533e1SBram Moolenaar    call s:InstallWinbar()
816c4b533e1SBram Moolenaar  endif
817c4b533e1SBram Moolenaarendfunc
818c4b533e1SBram Moolenaar
819e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
820e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
821e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
822fe386641SBram Moolenaar  let wid = win_getid(winnr())
823fe386641SBram Moolenaar
82460e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
8254551c0a9SBram Moolenaar    call ch_log('program stopped')
82660e73f2aSBram Moolenaar    let s:stopped = 1
82760e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
8284551c0a9SBram Moolenaar    call ch_log('program running')
82960e73f2aSBram Moolenaar    let s:stopped = 0
83060e73f2aSBram Moolenaar  endif
83160e73f2aSBram Moolenaar
832a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
833a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
834a15b0a93SBram Moolenaar  else
835a15b0a93SBram Moolenaar    let fname = ''
836a15b0a93SBram Moolenaar  endif
8371b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
838e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
839fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
8404551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
8411b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
842fe386641SBram Moolenaar	if &modified
843fe386641SBram Moolenaar	  " TODO: find existing window
844fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
845b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
846c4b533e1SBram Moolenaar	  call s:InstallWinbar()
847fe386641SBram Moolenaar	else
848fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
849fe386641SBram Moolenaar	endif
850fe386641SBram Moolenaar      endif
851fe386641SBram Moolenaar      exe lnum
85201164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
8531b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
854fe386641SBram Moolenaar      setlocal signcolumn=yes
855fe386641SBram Moolenaar    endif
8564551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
857fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
858fe386641SBram Moolenaar  endif
859fe386641SBram Moolenaar
860fe386641SBram Moolenaar  call win_gotoid(wid)
861e09ba7baSBram Moolenaarendfunc
862e09ba7baSBram Moolenaar
863de1a8314SBram Moolenaarlet s:BreakpointSigns = []
864a15b0a93SBram Moolenaar
865a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr)
866de1a8314SBram Moolenaar  if index(s:BreakpointSigns, a:nr) == -1
867de1a8314SBram Moolenaar    call add(s:BreakpointSigns, a:nr)
868de1a8314SBram Moolenaar    exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint"
869de1a8314SBram Moolenaar  endif
870de1a8314SBram Moolenaarendfunc
871de1a8314SBram Moolenaar
872e09ba7baSBram Moolenaar" Handle setting a breakpoint
873e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
874e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
8756dccc962SBram Moolenaar  if a:msg !~ 'fullname='
8766dccc962SBram Moolenaar    " a watch does not have a file name
8776dccc962SBram Moolenaar    return
8786dccc962SBram Moolenaar  endif
8796dccc962SBram Moolenaar
880e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
881e09ba7baSBram Moolenaar  if nr == 0
882e09ba7baSBram Moolenaar    return
883fe386641SBram Moolenaar  endif
884de1a8314SBram Moolenaar  call s:CreateBreakpoint(nr)
885e09ba7baSBram Moolenaar
886e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
887e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
888e09ba7baSBram Moolenaar  else
889e09ba7baSBram Moolenaar    let entry = {}
890e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
891fe386641SBram Moolenaar  endif
892e09ba7baSBram Moolenaar
893a15b0a93SBram Moolenaar  let fname = s:GetFullname(a:msg)
894e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
895e09ba7baSBram Moolenaar  let entry['fname'] = fname
896e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
8971b9645deSBram Moolenaar
8981b9645deSBram Moolenaar  if bufloaded(fname)
8991b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
9001b9645deSBram Moolenaar  endif
9011b9645deSBram Moolenaarendfunc
9021b9645deSBram Moolenaar
9031b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
904de1a8314SBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname']
9051b9645deSBram Moolenaar  let a:entry['placed'] = 1
906e09ba7baSBram Moolenaarendfunc
907e09ba7baSBram Moolenaar
908e09ba7baSBram Moolenaar" Handle deleting a breakpoint
909e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
910e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
911e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
912e09ba7baSBram Moolenaar  if nr == 0
913e09ba7baSBram Moolenaar    return
914e09ba7baSBram Moolenaar  endif
9151b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
9161b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
9171b9645deSBram Moolenaar    if has_key(entry, 'placed')
918e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
9191b9645deSBram Moolenaar      unlet entry['placed']
9201b9645deSBram Moolenaar    endif
921e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
9221b9645deSBram Moolenaar  endif
923c572da5fSBram Moolenaarendfunc
9241b9645deSBram Moolenaar
9254551c0a9SBram Moolenaar" Handle the debugged program starting to run.
9264551c0a9SBram Moolenaar" Will store the process ID in s:pid
9274551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
9284551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
9294551c0a9SBram Moolenaar  if nr == 0
9304551c0a9SBram Moolenaar    return
9314551c0a9SBram Moolenaar  endif
9324551c0a9SBram Moolenaar  let s:pid = nr
9334551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
9344551c0a9SBram Moolenaarendfunc
9354551c0a9SBram Moolenaar
9361b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
9371b9645deSBram Moolenaarfunc s:BufRead()
9381b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9391b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9401b9645deSBram Moolenaar    if entry['fname'] == fname
9411b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
9421b9645deSBram Moolenaar    endif
9431b9645deSBram Moolenaar  endfor
9441b9645deSBram Moolenaarendfunc
9451b9645deSBram Moolenaar
9461b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
9471b9645deSBram Moolenaarfunc s:BufUnloaded()
9481b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9491b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9501b9645deSBram Moolenaar    if entry['fname'] == fname
9511b9645deSBram Moolenaar      let entry['placed'] = 0
9521b9645deSBram Moolenaar    endif
9531b9645deSBram Moolenaar  endfor
9541b9645deSBram Moolenaarendfunc
955ca4cc018SBram Moolenaar
956ca4cc018SBram Moolenaarlet &cpo = s:keepcpo
957ca4cc018SBram Moolenaarunlet s:keepcpo
958