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
59fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim".
60fe386641SBram Moolenaar" To end type "quit" in the gdb window.
6132c67ba7SBram Moolenaarcommand -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>)
6232c67ba7SBram Moolenaarcommand -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>)
63c572da5fSBram Moolenaar
64fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb".
65e09ba7baSBram Moolenaarif !exists('termdebugger')
66e09ba7baSBram Moolenaar  let termdebugger = 'gdb'
67c572da5fSBram Moolenaarendif
68c572da5fSBram Moolenaar
69fe386641SBram Moolenaarlet s:pc_id = 12
70*de1a8314SBram Moolenaarlet s:break_id = 13  " breakpoint number is added to this
7160e73f2aSBram Moolenaarlet s:stopped = 1
72e09ba7baSBram Moolenaar
73e09ba7baSBram Moolenaarif &background == 'light'
74e09ba7baSBram Moolenaar  hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue
75e09ba7baSBram Moolenaarelse
76e09ba7baSBram Moolenaar  hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue
77e09ba7baSBram Moolenaarendif
78e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red
79fe386641SBram Moolenaar
8032c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...)
8132c67ba7SBram Moolenaar  " First argument is the command to debug, second core file or process ID.
8232c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang})
8332c67ba7SBram Moolenaarendfunc
8432c67ba7SBram Moolenaar
8532c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...)
8632c67ba7SBram Moolenaar  " First argument is the command to debug, rest are run arguments.
8732c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang})
8832c67ba7SBram Moolenaarendfunc
8932c67ba7SBram Moolenaar
9032c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict)
91b3623a38SBram Moolenaar  if exists('s:gdbwin')
92b3623a38SBram Moolenaar    echoerr 'Terminal debugger already running'
93b3623a38SBram Moolenaar    return
94b3623a38SBram Moolenaar  endif
95b3307b5eSBram Moolenaar  let s:ptywin = 0
96b3623a38SBram Moolenaar
97b3307b5eSBram Moolenaar  " Uncomment this line to write logging in "debuglog".
98b3307b5eSBram Moolenaar  " call ch_logfile('debuglog', 'w')
99b3307b5eSBram Moolenaar
100b3307b5eSBram Moolenaar  let s:sourcewin = win_getid(winnr())
101fe386641SBram Moolenaar  let s:startsigncolumn = &signcolumn
102fe386641SBram Moolenaar
10324a98a0eSBram Moolenaar  let s:save_columns = 0
10424a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
10524a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
10638baa3e6SBram Moolenaar      let s:save_columns = &columns
10738baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
10824a98a0eSBram Moolenaar    endif
109b3307b5eSBram Moolenaar    let s:vertical = 1
11038baa3e6SBram Moolenaar  else
111b3307b5eSBram Moolenaar    let s:vertical = 0
11238baa3e6SBram Moolenaar  endif
11338baa3e6SBram Moolenaar
114b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
115b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
116b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
117b3307b5eSBram Moolenaar    let s:way = 'terminal'
118b3307b5eSBram Moolenaar  else
119b3307b5eSBram Moolenaar    let s:way = 'prompt'
120b3307b5eSBram Moolenaar  endif
121b3307b5eSBram Moolenaar
122b3307b5eSBram Moolenaar  if s:way == 'prompt'
123b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
124b3307b5eSBram Moolenaar  else
125b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
126b3307b5eSBram Moolenaar  endif
127b3307b5eSBram Moolenaarendfunc
128b3307b5eSBram Moolenaar
129b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
130b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
131fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
132b3307b5eSBram Moolenaar	\ 'term_name': 'debugged program',
133b3307b5eSBram Moolenaar	\ 'vertical': s:vertical,
134fe386641SBram Moolenaar	\ })
135fe386641SBram Moolenaar  if s:ptybuf == 0
136fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
137fe386641SBram Moolenaar    return
138fe386641SBram Moolenaar  endif
139fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
14045d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
141b3307b5eSBram Moolenaar  if s:vertical
14251b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
14351b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
14451b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
14551b0f370SBram Moolenaar  endif
146fe386641SBram Moolenaar
147fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
148fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
149fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
150fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
151fe386641SBram Moolenaar	\ 'hidden': 1,
152fe386641SBram Moolenaar	\ })
153fe386641SBram Moolenaar  if s:commbuf == 0
154fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
155fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
156fe386641SBram Moolenaar    return
157fe386641SBram Moolenaar  endif
158fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
159c572da5fSBram Moolenaar
160c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
161c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
16232c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
16332c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
16432c67ba7SBram Moolenaar
16532c67ba7SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
166b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
16760e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
168b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndTermDebug'),
169fe386641SBram Moolenaar	\ 'term_finish': 'close',
170c572da5fSBram Moolenaar	\ })
17160e73f2aSBram Moolenaar  if s:gdbbuf == 0
172fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
173fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
174fe386641SBram Moolenaar    exe 'bwipe! ' . s:commbuf
175fe386641SBram Moolenaar    return
176fe386641SBram Moolenaar  endif
17745d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
178fe386641SBram Moolenaar
17932c67ba7SBram Moolenaar  " Set arguments to be run
18032c67ba7SBram Moolenaar  if len(proc_args)
18132c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
18232c67ba7SBram Moolenaar  endif
18332c67ba7SBram Moolenaar
184fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
18560e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
18660e73f2aSBram Moolenaar
1873e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
1883e4b84d0SBram Moolenaar  " why the debugger doesn't work.
1893e4b84d0SBram Moolenaar  let try_count = 0
1903e4b84d0SBram Moolenaar  while 1
1913e4b84d0SBram Moolenaar    let response = ''
192b3623a38SBram Moolenaar    for lnum in range(1,200)
1933e4b84d0SBram Moolenaar      if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi '
1943e4b84d0SBram Moolenaar	let response = term_getline(s:gdbbuf, lnum + 1)
1953e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
196f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
1973e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:ptybuf
1983e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:commbuf
1993e4b84d0SBram Moolenaar	  return
2003e4b84d0SBram Moolenaar	endif
2013e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
2023e4b84d0SBram Moolenaar	  " Success!
2033e4b84d0SBram Moolenaar	  break
2043e4b84d0SBram Moolenaar	endif
2053e4b84d0SBram Moolenaar      endif
2063e4b84d0SBram Moolenaar    endfor
2073e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2083e4b84d0SBram Moolenaar      break
2093e4b84d0SBram Moolenaar    endif
2103e4b84d0SBram Moolenaar    let try_count += 1
2113e4b84d0SBram Moolenaar    if try_count > 100
2123e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2133e4b84d0SBram Moolenaar      break
2143e4b84d0SBram Moolenaar    endif
2153e4b84d0SBram Moolenaar    sleep 10m
2163e4b84d0SBram Moolenaar  endwhile
2173e4b84d0SBram Moolenaar
21860e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
21960e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
22060e73f2aSBram Moolenaar  " running.
22160e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
222b3307b5eSBram Moolenaar  " Older gdb uses a different command.
223b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
224e09ba7baSBram Moolenaar
225f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
226f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
227b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
228f3ba14ffSBram Moolenaar
229b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
230b3307b5eSBram Moolenaarendfunc
231b3307b5eSBram Moolenaar
232b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
233b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
234b3307b5eSBram Moolenaar  if s:vertical
235b3307b5eSBram Moolenaar    vertical new
236b3307b5eSBram Moolenaar  else
237b3307b5eSBram Moolenaar    new
238b3307b5eSBram Moolenaar  endif
239b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
240b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
241b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
242b3307b5eSBram Moolenaar  set buftype=prompt
243b3307b5eSBram Moolenaar  file gdb
244b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
245b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
246b3307b5eSBram Moolenaar
247b3307b5eSBram Moolenaar  if s:vertical
248b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
249b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
250b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
251b3307b5eSBram Moolenaar  endif
252b3307b5eSBram Moolenaar
253b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
254b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
255b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
256b3307b5eSBram Moolenaar
257b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
258b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
259b3307b5eSBram Moolenaar
260b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
261b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
262b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
263b3307b5eSBram Moolenaar	\ })
264b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
265b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
266b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
267b3307b5eSBram Moolenaar    return
268b3307b5eSBram Moolenaar  endif
269b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
270b3307b5eSBram Moolenaar
271b3307b5eSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only
272b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
273b3307b5eSBram Moolenaar  " target is running.
274b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
275b3307b5eSBram Moolenaar  " Older gdb uses a different command.
276b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
277b3307b5eSBram Moolenaar
278b3307b5eSBram Moolenaar  let s:ptybuf = 0
279b3307b5eSBram Moolenaar  if has('win32')
280b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
281b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
282b3307b5eSBram Moolenaar  elseif has('terminal')
283b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
284b3307b5eSBram Moolenaar    " gdb window.
285b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
286b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
287b3307b5eSBram Moolenaar	  \ })
288b3307b5eSBram Moolenaar    if s:ptybuf == 0
289b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
290b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
291b3307b5eSBram Moolenaar      return
292b3307b5eSBram Moolenaar    endif
293b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
294b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
295b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
296b3307b5eSBram Moolenaar
297b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
298b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
299b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
300b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
301b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
302b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
303b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
304b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
305b3307b5eSBram Moolenaar  else
306b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
307b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
308b3307b5eSBram Moolenaar  endif
309b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
310b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
311b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
312b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
313b3307b5eSBram Moolenaar
314b3307b5eSBram Moolenaar  " Set arguments to be run
315b3307b5eSBram Moolenaar  if len(proc_args)
316b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
317b3307b5eSBram Moolenaar  endif
318b3307b5eSBram Moolenaar
319b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
320b3307b5eSBram Moolenaar  startinsert
321b3307b5eSBram Moolenaarendfunc
322b3307b5eSBram Moolenaar
323b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
32438baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
32538baa3e6SBram Moolenaar  " There can be only one.
32638baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
32738baa3e6SBram Moolenaar
32845d5f26dSBram Moolenaar  " Install debugger commands in the text window.
329b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
330e09ba7baSBram Moolenaar  call s:InstallCommands()
33145d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
332e09ba7baSBram Moolenaar
33351b0f370SBram Moolenaar  " Enable showing a balloon with eval info
334246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
335246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
33651b0f370SBram Moolenaar    if has("balloon_eval")
33751b0f370SBram Moolenaar      set ballooneval
338246fe03dSBram Moolenaar    endif
33951b0f370SBram Moolenaar    if has("balloon_eval_term")
34051b0f370SBram Moolenaar      set balloonevalterm
34151b0f370SBram Moolenaar    endif
34251b0f370SBram Moolenaar  endif
34351b0f370SBram Moolenaar
344*de1a8314SBram Moolenaar  " Contains breakpoints that have been placed, key is the number.
345e09ba7baSBram Moolenaar  let s:breakpoints = {}
3461b9645deSBram Moolenaar
3471b9645deSBram Moolenaar  augroup TermDebug
3481b9645deSBram Moolenaar    au BufRead * call s:BufRead()
3491b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
3501b9645deSBram Moolenaar  augroup END
35132c67ba7SBram Moolenaar
352b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
353b3307b5eSBram Moolenaar  " window.
35432c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
35532c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
35632c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
35732c67ba7SBram Moolenaar  endif
358c572da5fSBram Moolenaarendfunc
359c572da5fSBram Moolenaar
360b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
361b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
362b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
363b3307b5eSBram Moolenaar  if s:way == 'prompt'
364b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
365b3307b5eSBram Moolenaar  else
366b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
367b3307b5eSBram Moolenaar  endif
368b3307b5eSBram Moolenaarendfunc
369b3307b5eSBram Moolenaar
370b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
371b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
372b3307b5eSBram Moolenaar  if s:way == 'prompt'
373b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
374b3307b5eSBram Moolenaar  else
375b3307b5eSBram Moolenaar    let do_continue = 0
376b3307b5eSBram Moolenaar    if !s:stopped
377b3307b5eSBram Moolenaar      let do_continue = 1
378b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
379b3307b5eSBram Moolenaar      sleep 10m
380b3307b5eSBram Moolenaar    endif
381b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
382b3307b5eSBram Moolenaar    if do_continue
383b3307b5eSBram Moolenaar      Continue
384b3307b5eSBram Moolenaar    endif
385b3307b5eSBram Moolenaar  endif
386b3307b5eSBram Moolenaarendfunc
387b3307b5eSBram Moolenaar
388b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
389b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
390b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
391b3307b5eSBram Moolenaarendfunc
392b3307b5eSBram Moolenaar
393b3307b5eSBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer.
394b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
395b3307b5eSBram Moolenaar  call ch_log('Interrupting gdb')
396b3307b5eSBram Moolenaar  call job_stop(s:gdbjob, 'int')
397b3307b5eSBram Moolenaarendfunc
398b3307b5eSBram Moolenaar
399b3307b5eSBram Moolenaar" Function called when gdb outputs text.
400b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
401b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
402b3307b5eSBram Moolenaar
403b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
404b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
405b3307b5eSBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' || a:text[0] == '='
406b3307b5eSBram Moolenaar    return
407b3307b5eSBram Moolenaar  endif
408b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
409b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
410b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
411b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
412b3307b5eSBram Moolenaar      unlet s:evalexpr
413b3307b5eSBram Moolenaar      return
414b3307b5eSBram Moolenaar    endif
415b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
416b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
417b3307b5eSBram Moolenaar  else
418b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
419b3307b5eSBram Moolenaar    return
420b3307b5eSBram Moolenaar  endif
421b3307b5eSBram Moolenaar
422b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
423b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
424b3307b5eSBram Moolenaar
425b3307b5eSBram Moolenaar  " Add the output above the current prompt.
426b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
427b3307b5eSBram Moolenaar  set nomodified
428b3307b5eSBram Moolenaar
429b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
430b3307b5eSBram Moolenaarendfunc
431b3307b5eSBram Moolenaar
432b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
433b3307b5eSBram Moolenaar" to the next ", unescaping characters.
434b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
435b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
436b3307b5eSBram Moolenaar    echoerr 'DecodeMessage(): missing quote'
437b3307b5eSBram Moolenaar    return
438b3307b5eSBram Moolenaar  endif
439b3307b5eSBram Moolenaar  let result = ''
440b3307b5eSBram Moolenaar  let i = 1
441b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
442b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
443b3307b5eSBram Moolenaar      let i += 1
444b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
445b3307b5eSBram Moolenaar	" drop \n
446b3307b5eSBram Moolenaar	let i += 1
447b3307b5eSBram Moolenaar	continue
448b3307b5eSBram Moolenaar      endif
449b3307b5eSBram Moolenaar    endif
450b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
451b3307b5eSBram Moolenaar    let i += 1
452b3307b5eSBram Moolenaar  endwhile
453b3307b5eSBram Moolenaar  return result
454b3307b5eSBram Moolenaarendfunc
455b3307b5eSBram Moolenaar
456b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
457fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
458b3623a38SBram Moolenaar  unlet s:gdbwin
459e09ba7baSBram Moolenaar
460b3307b5eSBram Moolenaar  call s:EndDebugCommon()
461b3307b5eSBram Moolenaarendfunc
462b3307b5eSBram Moolenaar
463b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
464e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
465e09ba7baSBram Moolenaar
466b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
467b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
468b3307b5eSBram Moolenaar  endif
469b3307b5eSBram Moolenaar
470b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
471e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
472e09ba7baSBram Moolenaar  call s:DeleteCommands()
473e09ba7baSBram Moolenaar
474e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
475b3307b5eSBram Moolenaar
47638baa3e6SBram Moolenaar  if s:save_columns > 0
47738baa3e6SBram Moolenaar    let &columns = s:save_columns
47838baa3e6SBram Moolenaar  endif
4791b9645deSBram Moolenaar
480246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
481246fe03dSBram Moolenaar    set balloonexpr=
48251b0f370SBram Moolenaar    if has("balloon_eval")
48351b0f370SBram Moolenaar      set noballooneval
484246fe03dSBram Moolenaar    endif
48551b0f370SBram Moolenaar    if has("balloon_eval_term")
48651b0f370SBram Moolenaar      set noballoonevalterm
48751b0f370SBram Moolenaar    endif
48851b0f370SBram Moolenaar  endif
48951b0f370SBram Moolenaar
4901b9645deSBram Moolenaar  au! TermDebug
491fe386641SBram Moolenaarendfunc
492fe386641SBram Moolenaar
493b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
494b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
495b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
496b3307b5eSBram Moolenaar  close
497b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
498b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
499b3307b5eSBram Moolenaar  endif
500b3307b5eSBram Moolenaar
501b3307b5eSBram Moolenaar  call s:EndDebugCommon()
502b3307b5eSBram Moolenaar  unlet s:gdbwin
503b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
504b3307b5eSBram Moolenaarendfunc
505b3307b5eSBram Moolenaar
506fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
507fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
508fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
509fe386641SBram Moolenaar
510fe386641SBram Moolenaar  for msg in msgs
511fe386641SBram Moolenaar    " remove prefixed NL
512fe386641SBram Moolenaar    if msg[0] == "\n"
513fe386641SBram Moolenaar      let msg = msg[1:]
514fe386641SBram Moolenaar    endif
515fe386641SBram Moolenaar    if msg != ''
5161b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
517e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
51845d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
519e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
520e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
521e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
52245d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
52345d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
52445d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
52545d5f26dSBram Moolenaar	call s:HandleError(msg)
526e09ba7baSBram Moolenaar      endif
527e09ba7baSBram Moolenaar    endif
528e09ba7baSBram Moolenaar  endfor
529e09ba7baSBram Moolenaarendfunc
530e09ba7baSBram Moolenaar
531e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
532e09ba7baSBram Moolenaarfunc s:InstallCommands()
533e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
53471137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
535e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
53645d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
537e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
53860e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
53960e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
54060e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
541b3307b5eSBram Moolenaar
542b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
543b3307b5eSBram Moolenaar  if s:way == 'prompt'
544b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
545b3307b5eSBram Moolenaar  else
546b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
547b3307b5eSBram Moolenaar  endif
548b3307b5eSBram Moolenaar
54945d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
55045d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
55145d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
552b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
55371137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
55445d5f26dSBram Moolenaar
55545d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
55645d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
5571b9645deSBram Moolenaar
558f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
55971137fedSBram Moolenaar    call s:InstallWinbar()
56071137fedSBram Moolenaar
56171137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
56271137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
56371137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
56471137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
56571137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
56671137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
56771137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
56871137fedSBram Moolenaar    endif
56971137fedSBram Moolenaar  endif
57071137fedSBram Moolenaarendfunc
57171137fedSBram Moolenaar
57271137fedSBram Moolenaarlet s:winbar_winids = []
57371137fedSBram Moolenaar
57471137fedSBram Moolenaar" Install the window toolbar in the current window.
57571137fedSBram Moolenaarfunc s:InstallWinbar()
576c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
57724a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
57824a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
57924a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
58024a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
58160e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
58224a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
58371137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
584c4b533e1SBram Moolenaar  endif
585e09ba7baSBram Moolenaarendfunc
586e09ba7baSBram Moolenaar
587e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
588e09ba7baSBram Moolenaarfunc s:DeleteCommands()
589e09ba7baSBram Moolenaar  delcommand Break
59071137fedSBram Moolenaar  delcommand Clear
591e09ba7baSBram Moolenaar  delcommand Step
59245d5f26dSBram Moolenaar  delcommand Over
593e09ba7baSBram Moolenaar  delcommand Finish
59460e73f2aSBram Moolenaar  delcommand Run
59560e73f2aSBram Moolenaar  delcommand Arguments
59660e73f2aSBram Moolenaar  delcommand Stop
597e09ba7baSBram Moolenaar  delcommand Continue
59845d5f26dSBram Moolenaar  delcommand Evaluate
59945d5f26dSBram Moolenaar  delcommand Gdb
60045d5f26dSBram Moolenaar  delcommand Program
601b3623a38SBram Moolenaar  delcommand Source
60271137fedSBram Moolenaar  delcommand Winbar
60345d5f26dSBram Moolenaar
60445d5f26dSBram Moolenaar  nunmap K
6051b9645deSBram Moolenaar
6061b9645deSBram Moolenaar  if has('menu')
60771137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
60871137fedSBram Moolenaar    let curwinid = win_getid(winnr())
60971137fedSBram Moolenaar    for winid in s:winbar_winids
61071137fedSBram Moolenaar      if win_gotoid(winid)
6111b9645deSBram Moolenaar	aunmenu WinBar.Step
6121b9645deSBram Moolenaar	aunmenu WinBar.Next
6131b9645deSBram Moolenaar	aunmenu WinBar.Finish
6141b9645deSBram Moolenaar	aunmenu WinBar.Cont
61560e73f2aSBram Moolenaar	aunmenu WinBar.Stop
6161b9645deSBram Moolenaar	aunmenu WinBar.Eval
6171b9645deSBram Moolenaar      endif
61871137fedSBram Moolenaar    endfor
61971137fedSBram Moolenaar    call win_gotoid(curwinid)
62071137fedSBram Moolenaar    let s:winbar_winids = []
62171137fedSBram Moolenaar
62271137fedSBram Moolenaar    if exists('s:saved_mousemodel')
62371137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
62471137fedSBram Moolenaar      unlet s:saved_mousemodel
62571137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
62671137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
62771137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
62871137fedSBram Moolenaar      aunmenu PopUp.Evaluate
62971137fedSBram Moolenaar    endif
63071137fedSBram Moolenaar  endif
6311b9645deSBram Moolenaar
63245d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
63345d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
63445d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
63545d5f26dSBram Moolenaar  endfor
63638baa3e6SBram Moolenaar  sign undefine debugPC
63738baa3e6SBram Moolenaar  sign undefine debugBreakpoint
63845d5f26dSBram Moolenaar  unlet s:breakpoints
639e09ba7baSBram Moolenaarendfunc
640e09ba7baSBram Moolenaar
641e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
642e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
64360e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
64460e73f2aSBram Moolenaar  " Interrupt to make it work.
64560e73f2aSBram Moolenaar  let do_continue = 0
64660e73f2aSBram Moolenaar  if !s:stopped
64760e73f2aSBram Moolenaar    let do_continue = 1
648b3307b5eSBram Moolenaar    if s:way == 'prompt'
649b3307b5eSBram Moolenaar      " Need to send a signal to get the UI to listen.  Strangely this is only
650b3307b5eSBram Moolenaar      " needed once.
651b3307b5eSBram Moolenaar      call job_stop(s:gdbjob, 'int')
652b3307b5eSBram Moolenaar    else
65360e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
654b3307b5eSBram Moolenaar    endif
65560e73f2aSBram Moolenaar    sleep 10m
65660e73f2aSBram Moolenaar  endif
65760e73f2aSBram Moolenaar  call s:SendCommand('-break-insert --source '
65860e73f2aSBram Moolenaar	\ . fnameescape(expand('%:p')) . ' --line ' . line('.'))
65960e73f2aSBram Moolenaar  if do_continue
66060e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
66160e73f2aSBram Moolenaar  endif
662e09ba7baSBram Moolenaarendfunc
663e09ba7baSBram Moolenaar
66471137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
66571137fedSBram Moolenaarfunc s:ClearBreakpoint()
666e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
667e09ba7baSBram Moolenaar  let lnum = line('.')
668e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
669e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
670b3307b5eSBram Moolenaar      call s:SendCommand('-break-delete ' . key)
671e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
672e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
673e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
674e09ba7baSBram Moolenaar      break
675e09ba7baSBram Moolenaar    endif
676e09ba7baSBram Moolenaar  endfor
677e09ba7baSBram Moolenaarendfunc
678e09ba7baSBram Moolenaar
67960e73f2aSBram Moolenaarfunc s:Run(args)
68060e73f2aSBram Moolenaar  if a:args != ''
68160e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
68260e73f2aSBram Moolenaar  endif
68360e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
68460e73f2aSBram Moolenaarendfunc
68560e73f2aSBram Moolenaar
68651b0f370SBram Moolenaarfunc s:SendEval(expr)
68751b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
68851b0f370SBram Moolenaar  let s:evalexpr = a:expr
68951b0f370SBram Moolenaarendfunc
69051b0f370SBram Moolenaar
69145d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
69245d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
69345d5f26dSBram Moolenaar  if a:arg != ''
69445d5f26dSBram Moolenaar    let expr = a:arg
69545d5f26dSBram Moolenaar  elseif a:range == 2
69645d5f26dSBram Moolenaar    let pos = getcurpos()
69745d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
69845d5f26dSBram Moolenaar    let regt = getregtype('v')
69945d5f26dSBram Moolenaar    normal! gv"vy
70045d5f26dSBram Moolenaar    let expr = @v
70145d5f26dSBram Moolenaar    call setpos('.', pos)
70245d5f26dSBram Moolenaar    call setreg('v', reg, regt)
70345d5f26dSBram Moolenaar  else
70445d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
70545d5f26dSBram Moolenaar  endif
70622f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
70751b0f370SBram Moolenaar  call s:SendEval(expr)
70845d5f26dSBram Moolenaarendfunc
70945d5f26dSBram Moolenaar
71022f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
71151b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
71251b0f370SBram Moolenaar
71345d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
71445d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
7151b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
7161b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
71751b0f370SBram Moolenaar  if s:evalFromBalloonExpr
71851b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
71951b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
72051b0f370SBram Moolenaar    else
72151b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
72251b0f370SBram Moolenaar    endif
72351b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
72451b0f370SBram Moolenaar  else
7251b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
72651b0f370SBram Moolenaar  endif
7271b9645deSBram Moolenaar
7287f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
7291b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
73022f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
73151b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
73251b0f370SBram Moolenaar  else
73351b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
7341b9645deSBram Moolenaar  endif
73545d5f26dSBram Moolenaarendfunc
73645d5f26dSBram Moolenaar
73751b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
73851b0f370SBram Moolenaar" if there is any.
73951b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
740b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
741b3307b5eSBram Moolenaar    return
742b3307b5eSBram Moolenaar  endif
743b3307b5eSBram Moolenaar  if !s:stopped
744b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
745b3307b5eSBram Moolenaar    " mouse triggers a balloon.
74651b0f370SBram Moolenaar    return
74751b0f370SBram Moolenaar  endif
74851b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
74951b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
75022f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
75122f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
75251b0f370SBram Moolenaar  return ''
75351b0f370SBram Moolenaarendfunc
75451b0f370SBram Moolenaar
75545d5f26dSBram Moolenaar" Handle an error.
75645d5f26dSBram Moolenaarfunc s:HandleError(msg)
75722f1d0e3SBram Moolenaar  if s:ignoreEvalError
75851b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
75922f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
76022f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
76151b0f370SBram Moolenaar    return
76251b0f370SBram Moolenaar  endif
76345d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
76445d5f26dSBram Moolenaarendfunc
76545d5f26dSBram Moolenaar
766b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
767b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
768c4b533e1SBram Moolenaar    new
769b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
770c4b533e1SBram Moolenaar    call s:InstallWinbar()
771c4b533e1SBram Moolenaar  endif
772c4b533e1SBram Moolenaarendfunc
773c4b533e1SBram Moolenaar
774e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
775e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
776e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
777fe386641SBram Moolenaar  let wid = win_getid(winnr())
778fe386641SBram Moolenaar
77960e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
78060e73f2aSBram Moolenaar    let s:stopped = 1
78160e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
78260e73f2aSBram Moolenaar    let s:stopped = 0
78360e73f2aSBram Moolenaar  endif
78460e73f2aSBram Moolenaar
785b3307b5eSBram Moolenaar  call s:GotoSourcewinOrCreateIt()
786c4b533e1SBram Moolenaar
787e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
7881b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
789e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
790fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
7911b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
792fe386641SBram Moolenaar	if &modified
793fe386641SBram Moolenaar	  " TODO: find existing window
794fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
795b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
796c4b533e1SBram Moolenaar	  call s:InstallWinbar()
797fe386641SBram Moolenaar	else
798fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
799fe386641SBram Moolenaar	endif
800fe386641SBram Moolenaar      endif
801fe386641SBram Moolenaar      exe lnum
80201164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
8031b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
804fe386641SBram Moolenaar      setlocal signcolumn=yes
805fe386641SBram Moolenaar    endif
806fe386641SBram Moolenaar  else
807fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
808fe386641SBram Moolenaar  endif
809fe386641SBram Moolenaar
810fe386641SBram Moolenaar  call win_gotoid(wid)
811e09ba7baSBram Moolenaarendfunc
812e09ba7baSBram Moolenaar
813*de1a8314SBram Moolenaarfunc s:CreateBreakpoint(nr)
814*de1a8314SBram Moolenaar  if !exists("s:BreakpointSigns")
815*de1a8314SBram Moolenaar    let s:BreakpointSigns = []
816*de1a8314SBram Moolenaar  endif
817*de1a8314SBram Moolenaar  if index(s:BreakpointSigns, a:nr) == -1
818*de1a8314SBram Moolenaar    call add(s:BreakpointSigns, a:nr)
819*de1a8314SBram Moolenaar    exe "sign define debugBreakpoint". a:nr . " text=" . a:nr . " texthl=debugBreakpoint"
820*de1a8314SBram Moolenaar  endif
821*de1a8314SBram Moolenaarendfunc
822*de1a8314SBram Moolenaar
823e09ba7baSBram Moolenaar" Handle setting a breakpoint
824e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
825e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
826e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
827e09ba7baSBram Moolenaar  if nr == 0
828e09ba7baSBram Moolenaar    return
829fe386641SBram Moolenaar  endif
830*de1a8314SBram Moolenaar  call s:CreateBreakpoint(nr)
831e09ba7baSBram Moolenaar
832e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
833e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
834e09ba7baSBram Moolenaar  else
835e09ba7baSBram Moolenaar    let entry = {}
836e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
837fe386641SBram Moolenaar  endif
838e09ba7baSBram Moolenaar
839e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
840e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
841e09ba7baSBram Moolenaar  let entry['fname'] = fname
842e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
8431b9645deSBram Moolenaar
8441b9645deSBram Moolenaar  if bufloaded(fname)
8451b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
8461b9645deSBram Moolenaar  endif
8471b9645deSBram Moolenaarendfunc
8481b9645deSBram Moolenaar
8491b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
850*de1a8314SBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname']
8511b9645deSBram Moolenaar  let a:entry['placed'] = 1
852e09ba7baSBram Moolenaarendfunc
853e09ba7baSBram Moolenaar
854e09ba7baSBram Moolenaar" Handle deleting a breakpoint
855e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
856e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
857e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
858e09ba7baSBram Moolenaar  if nr == 0
859e09ba7baSBram Moolenaar    return
860e09ba7baSBram Moolenaar  endif
8611b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
8621b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
8631b9645deSBram Moolenaar    if has_key(entry, 'placed')
864e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
8651b9645deSBram Moolenaar      unlet entry['placed']
8661b9645deSBram Moolenaar    endif
867e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
8681b9645deSBram Moolenaar  endif
869c572da5fSBram Moolenaarendfunc
8701b9645deSBram Moolenaar
8711b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
8721b9645deSBram Moolenaarfunc s:BufRead()
8731b9645deSBram Moolenaar  let fname = expand('<afile>:p')
8741b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
8751b9645deSBram Moolenaar    if entry['fname'] == fname
8761b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
8771b9645deSBram Moolenaar    endif
8781b9645deSBram Moolenaar  endfor
8791b9645deSBram Moolenaarendfunc
8801b9645deSBram Moolenaar
8811b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
8821b9645deSBram Moolenaarfunc s:BufUnloaded()
8831b9645deSBram Moolenaar  let fname = expand('<afile>:p')
8841b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
8851b9645deSBram Moolenaar    if entry['fname'] == fname
8861b9645deSBram Moolenaar      let entry['placed'] = 0
8871b9645deSBram Moolenaar    endif
8881b9645deSBram Moolenaar  endfor
8891b9645deSBram Moolenaarendfunc
8901b9645deSBram Moolenaar
891