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
59*ca4cc018SBram Moolenaarlet s:keepcpo = &cpo
60*ca4cc018SBram Moolenaarset cpo&vim
61*ca4cc018SBram 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()
569e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
57071137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
571e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
57245d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
573e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
57460e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
57560e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
57660e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
577b3307b5eSBram Moolenaar
578b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
579b3307b5eSBram Moolenaar  if s:way == 'prompt'
580b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
581b3307b5eSBram Moolenaar  else
582b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
583b3307b5eSBram Moolenaar  endif
584b3307b5eSBram Moolenaar
58545d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
58645d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
58745d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
588b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
58971137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
59045d5f26dSBram Moolenaar
59145d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
59245d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
5931b9645deSBram Moolenaar
594f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
59571137fedSBram Moolenaar    call s:InstallWinbar()
59671137fedSBram Moolenaar
59771137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
59871137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
59971137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
60071137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
60171137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
60271137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
60371137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
60471137fedSBram Moolenaar    endif
60571137fedSBram Moolenaar  endif
60671137fedSBram Moolenaarendfunc
60771137fedSBram Moolenaar
60871137fedSBram Moolenaarlet s:winbar_winids = []
60971137fedSBram Moolenaar
61071137fedSBram Moolenaar" Install the window toolbar in the current window.
61171137fedSBram Moolenaarfunc s:InstallWinbar()
612c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
61324a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
61424a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
61524a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
61624a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
61760e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
61824a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
61971137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
620c4b533e1SBram Moolenaar  endif
621e09ba7baSBram Moolenaarendfunc
622e09ba7baSBram Moolenaar
623e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
624e09ba7baSBram Moolenaarfunc s:DeleteCommands()
625e09ba7baSBram Moolenaar  delcommand Break
62671137fedSBram Moolenaar  delcommand Clear
627e09ba7baSBram Moolenaar  delcommand Step
62845d5f26dSBram Moolenaar  delcommand Over
629e09ba7baSBram Moolenaar  delcommand Finish
63060e73f2aSBram Moolenaar  delcommand Run
63160e73f2aSBram Moolenaar  delcommand Arguments
63260e73f2aSBram Moolenaar  delcommand Stop
633e09ba7baSBram Moolenaar  delcommand Continue
63445d5f26dSBram Moolenaar  delcommand Evaluate
63545d5f26dSBram Moolenaar  delcommand Gdb
63645d5f26dSBram Moolenaar  delcommand Program
637b3623a38SBram Moolenaar  delcommand Source
63871137fedSBram Moolenaar  delcommand Winbar
63945d5f26dSBram Moolenaar
64045d5f26dSBram Moolenaar  nunmap K
6411b9645deSBram Moolenaar
6421b9645deSBram Moolenaar  if has('menu')
64371137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
64471137fedSBram Moolenaar    let curwinid = win_getid(winnr())
64571137fedSBram Moolenaar    for winid in s:winbar_winids
64671137fedSBram Moolenaar      if win_gotoid(winid)
6471b9645deSBram Moolenaar	aunmenu WinBar.Step
6481b9645deSBram Moolenaar	aunmenu WinBar.Next
6491b9645deSBram Moolenaar	aunmenu WinBar.Finish
6501b9645deSBram Moolenaar	aunmenu WinBar.Cont
65160e73f2aSBram Moolenaar	aunmenu WinBar.Stop
6521b9645deSBram Moolenaar	aunmenu WinBar.Eval
6531b9645deSBram Moolenaar      endif
65471137fedSBram Moolenaar    endfor
65571137fedSBram Moolenaar    call win_gotoid(curwinid)
65671137fedSBram Moolenaar    let s:winbar_winids = []
65771137fedSBram Moolenaar
65871137fedSBram Moolenaar    if exists('s:saved_mousemodel')
65971137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
66071137fedSBram Moolenaar      unlet s:saved_mousemodel
66171137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
66271137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
66371137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
66471137fedSBram Moolenaar      aunmenu PopUp.Evaluate
66571137fedSBram Moolenaar    endif
66671137fedSBram Moolenaar  endif
6671b9645deSBram Moolenaar
66845d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
66945d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
67045d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
67145d5f26dSBram Moolenaar  endfor
67245d5f26dSBram Moolenaar  unlet s:breakpoints
673a15b0a93SBram Moolenaar
674a15b0a93SBram Moolenaar  sign undefine debugPC
675a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
676a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
677a15b0a93SBram Moolenaar  endfor
6784551c0a9SBram Moolenaar  let s:BreakpointSigns = []
679e09ba7baSBram Moolenaarendfunc
680e09ba7baSBram Moolenaar
681e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
682e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
68360e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
68460e73f2aSBram Moolenaar  " Interrupt to make it work.
68560e73f2aSBram Moolenaar  let do_continue = 0
68660e73f2aSBram Moolenaar  if !s:stopped
68760e73f2aSBram Moolenaar    let do_continue = 1
688b3307b5eSBram Moolenaar    if s:way == 'prompt'
6894551c0a9SBram Moolenaar      call s:PromptInterrupt()
690b3307b5eSBram Moolenaar    else
69160e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
692b3307b5eSBram Moolenaar    endif
69360e73f2aSBram Moolenaar    sleep 10m
69460e73f2aSBram Moolenaar  endif
695a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
696a15b0a93SBram Moolenaar  call s:SendCommand('-break-insert '
697a15b0a93SBram Moolenaar	\ . fnameescape(expand('%:p')) . ':' . line('.'))
69860e73f2aSBram Moolenaar  if do_continue
69960e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
70060e73f2aSBram Moolenaar  endif
701e09ba7baSBram Moolenaarendfunc
702e09ba7baSBram Moolenaar
70371137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
70471137fedSBram Moolenaarfunc s:ClearBreakpoint()
705e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
706e09ba7baSBram Moolenaar  let lnum = line('.')
707e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
708e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
709b3307b5eSBram Moolenaar      call s:SendCommand('-break-delete ' . key)
710e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
711e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
712e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
713e09ba7baSBram Moolenaar      break
714e09ba7baSBram Moolenaar    endif
715e09ba7baSBram Moolenaar  endfor
716e09ba7baSBram Moolenaarendfunc
717e09ba7baSBram Moolenaar
71860e73f2aSBram Moolenaarfunc s:Run(args)
71960e73f2aSBram Moolenaar  if a:args != ''
72060e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
72160e73f2aSBram Moolenaar  endif
72260e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
72360e73f2aSBram Moolenaarendfunc
72460e73f2aSBram Moolenaar
72551b0f370SBram Moolenaarfunc s:SendEval(expr)
72651b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
72751b0f370SBram Moolenaar  let s:evalexpr = a:expr
72851b0f370SBram Moolenaarendfunc
72951b0f370SBram Moolenaar
73045d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
73145d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
73245d5f26dSBram Moolenaar  if a:arg != ''
73345d5f26dSBram Moolenaar    let expr = a:arg
73445d5f26dSBram Moolenaar  elseif a:range == 2
73545d5f26dSBram Moolenaar    let pos = getcurpos()
73645d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
73745d5f26dSBram Moolenaar    let regt = getregtype('v')
73845d5f26dSBram Moolenaar    normal! gv"vy
73945d5f26dSBram Moolenaar    let expr = @v
74045d5f26dSBram Moolenaar    call setpos('.', pos)
74145d5f26dSBram Moolenaar    call setreg('v', reg, regt)
74245d5f26dSBram Moolenaar  else
74345d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
74445d5f26dSBram Moolenaar  endif
74522f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
74651b0f370SBram Moolenaar  call s:SendEval(expr)
74745d5f26dSBram Moolenaarendfunc
74845d5f26dSBram Moolenaar
74922f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
75051b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
75151b0f370SBram Moolenaar
75245d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
75345d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
7541b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
7551b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
75651b0f370SBram Moolenaar  if s:evalFromBalloonExpr
75751b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
75851b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
75951b0f370SBram Moolenaar    else
76051b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
76151b0f370SBram Moolenaar    endif
76251b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
76351b0f370SBram Moolenaar  else
7641b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
76551b0f370SBram Moolenaar  endif
7661b9645deSBram Moolenaar
7677f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
7681b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
76922f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
77051b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
77151b0f370SBram Moolenaar  else
77251b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
7731b9645deSBram Moolenaar  endif
77445d5f26dSBram Moolenaarendfunc
77545d5f26dSBram Moolenaar
77651b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
77751b0f370SBram Moolenaar" if there is any.
77851b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
779b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
780b3307b5eSBram Moolenaar    return
781b3307b5eSBram Moolenaar  endif
782b3307b5eSBram Moolenaar  if !s:stopped
783b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
784b3307b5eSBram Moolenaar    " mouse triggers a balloon.
78551b0f370SBram Moolenaar    return
78651b0f370SBram Moolenaar  endif
78751b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
78851b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
78922f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
79022f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
79151b0f370SBram Moolenaar  return ''
79251b0f370SBram Moolenaarendfunc
79351b0f370SBram Moolenaar
79445d5f26dSBram Moolenaar" Handle an error.
79545d5f26dSBram Moolenaarfunc s:HandleError(msg)
79622f1d0e3SBram Moolenaar  if s:ignoreEvalError
79751b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
79822f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
79922f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
80051b0f370SBram Moolenaar    return
80151b0f370SBram Moolenaar  endif
80245d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
80345d5f26dSBram Moolenaarendfunc
80445d5f26dSBram Moolenaar
805b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
806b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
807c4b533e1SBram Moolenaar    new
808b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
809c4b533e1SBram Moolenaar    call s:InstallWinbar()
810c4b533e1SBram Moolenaar  endif
811c4b533e1SBram Moolenaarendfunc
812c4b533e1SBram Moolenaar
813e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
814e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
815e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
816fe386641SBram Moolenaar  let wid = win_getid(winnr())
817fe386641SBram Moolenaar
81860e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
8194551c0a9SBram Moolenaar    call ch_log('program stopped')
82060e73f2aSBram Moolenaar    let s:stopped = 1
82160e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
8224551c0a9SBram Moolenaar    call ch_log('program running')
82360e73f2aSBram Moolenaar    let s:stopped = 0
82460e73f2aSBram Moolenaar  endif
82560e73f2aSBram Moolenaar
826a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
827a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
828a15b0a93SBram Moolenaar  else
829a15b0a93SBram Moolenaar    let fname = ''
830a15b0a93SBram Moolenaar  endif
8311b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
832e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
833fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
8344551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
8351b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
836fe386641SBram Moolenaar	if &modified
837fe386641SBram Moolenaar	  " TODO: find existing window
838fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
839b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
840c4b533e1SBram Moolenaar	  call s:InstallWinbar()
841fe386641SBram Moolenaar	else
842fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
843fe386641SBram Moolenaar	endif
844fe386641SBram Moolenaar      endif
845fe386641SBram Moolenaar      exe lnum
84601164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
8471b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
848fe386641SBram Moolenaar      setlocal signcolumn=yes
849fe386641SBram Moolenaar    endif
8504551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
851fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
852fe386641SBram Moolenaar  endif
853fe386641SBram Moolenaar
854fe386641SBram Moolenaar  call win_gotoid(wid)
855e09ba7baSBram Moolenaarendfunc
856e09ba7baSBram Moolenaar
857de1a8314SBram Moolenaarlet s:BreakpointSigns = []
858a15b0a93SBram Moolenaar
859a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr)
860de1a8314SBram Moolenaar  if index(s:BreakpointSigns, a:nr) == -1
861de1a8314SBram Moolenaar    call add(s:BreakpointSigns, a:nr)
862de1a8314SBram Moolenaar    exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint"
863de1a8314SBram Moolenaar  endif
864de1a8314SBram Moolenaarendfunc
865de1a8314SBram Moolenaar
866e09ba7baSBram Moolenaar" Handle setting a breakpoint
867e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
868e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
8696dccc962SBram Moolenaar  if a:msg !~ 'fullname='
8706dccc962SBram Moolenaar    " a watch does not have a file name
8716dccc962SBram Moolenaar    return
8726dccc962SBram Moolenaar  endif
8736dccc962SBram Moolenaar
874e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
875e09ba7baSBram Moolenaar  if nr == 0
876e09ba7baSBram Moolenaar    return
877fe386641SBram Moolenaar  endif
878de1a8314SBram Moolenaar  call s:CreateBreakpoint(nr)
879e09ba7baSBram Moolenaar
880e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
881e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
882e09ba7baSBram Moolenaar  else
883e09ba7baSBram Moolenaar    let entry = {}
884e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
885fe386641SBram Moolenaar  endif
886e09ba7baSBram Moolenaar
887a15b0a93SBram Moolenaar  let fname = s:GetFullname(a:msg)
888e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
889e09ba7baSBram Moolenaar  let entry['fname'] = fname
890e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
8911b9645deSBram Moolenaar
8921b9645deSBram Moolenaar  if bufloaded(fname)
8931b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
8941b9645deSBram Moolenaar  endif
8951b9645deSBram Moolenaarendfunc
8961b9645deSBram Moolenaar
8971b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
898de1a8314SBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname']
8991b9645deSBram Moolenaar  let a:entry['placed'] = 1
900e09ba7baSBram Moolenaarendfunc
901e09ba7baSBram Moolenaar
902e09ba7baSBram Moolenaar" Handle deleting a breakpoint
903e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
904e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
905e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
906e09ba7baSBram Moolenaar  if nr == 0
907e09ba7baSBram Moolenaar    return
908e09ba7baSBram Moolenaar  endif
9091b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
9101b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
9111b9645deSBram Moolenaar    if has_key(entry, 'placed')
912e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
9131b9645deSBram Moolenaar      unlet entry['placed']
9141b9645deSBram Moolenaar    endif
915e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
9161b9645deSBram Moolenaar  endif
917c572da5fSBram Moolenaarendfunc
9181b9645deSBram Moolenaar
9194551c0a9SBram Moolenaar" Handle the debugged program starting to run.
9204551c0a9SBram Moolenaar" Will store the process ID in s:pid
9214551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
9224551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
9234551c0a9SBram Moolenaar  if nr == 0
9244551c0a9SBram Moolenaar    return
9254551c0a9SBram Moolenaar  endif
9264551c0a9SBram Moolenaar  let s:pid = nr
9274551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
9284551c0a9SBram Moolenaarendfunc
9294551c0a9SBram Moolenaar
9301b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
9311b9645deSBram Moolenaarfunc s:BufRead()
9321b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9331b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9341b9645deSBram Moolenaar    if entry['fname'] == fname
9351b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
9361b9645deSBram Moolenaar    endif
9371b9645deSBram Moolenaar  endfor
9381b9645deSBram Moolenaarendfunc
9391b9645deSBram Moolenaar
9401b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
9411b9645deSBram Moolenaarfunc s:BufUnloaded()
9421b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9431b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9441b9645deSBram Moolenaar    if entry['fname'] == fname
9451b9645deSBram Moolenaar      let entry['placed'] = 0
9461b9645deSBram Moolenaar    endif
9471b9645deSBram Moolenaar  endfor
9481b9645deSBram Moolenaarendfunc
949*ca4cc018SBram Moolenaar
950*ca4cc018SBram Moolenaarlet &cpo = s:keepcpo
951*ca4cc018SBram Moolenaarunlet s:keepcpo
952