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
70de1a8314SBram Moolenaarlet s:break_id = 13  " breakpoint number is added to this
7160e73f2aSBram Moolenaarlet s:stopped = 1
72e09ba7baSBram Moolenaar
73f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new)
74f07f9e73SBram Moolenaar  let default = a:init ? 'default ' : ''
75f07f9e73SBram Moolenaar  if a:new ==# 'light' && a:old !=# 'light'
76f07f9e73SBram Moolenaar    exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue"
77f07f9e73SBram Moolenaar  elseif a:new ==# 'dark' && a:old !=# 'dark'
78f07f9e73SBram Moolenaar    exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue"
79e09ba7baSBram Moolenaar  endif
80f07f9e73SBram Moolenaarendfunc
81f07f9e73SBram Moolenaar
82f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background)
83e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red
84fe386641SBram Moolenaar
8532c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...)
8632c67ba7SBram Moolenaar  " First argument is the command to debug, second core file or process ID.
8732c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang})
8832c67ba7SBram Moolenaarendfunc
8932c67ba7SBram Moolenaar
9032c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...)
9132c67ba7SBram Moolenaar  " First argument is the command to debug, rest are run arguments.
9232c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang})
9332c67ba7SBram Moolenaarendfunc
9432c67ba7SBram Moolenaar
9532c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict)
96b3623a38SBram Moolenaar  if exists('s:gdbwin')
97b3623a38SBram Moolenaar    echoerr 'Terminal debugger already running'
98b3623a38SBram Moolenaar    return
99b3623a38SBram Moolenaar  endif
100b3307b5eSBram Moolenaar  let s:ptywin = 0
101b3623a38SBram Moolenaar
102b3307b5eSBram Moolenaar  " Uncomment this line to write logging in "debuglog".
103b3307b5eSBram Moolenaar  " call ch_logfile('debuglog', 'w')
104b3307b5eSBram Moolenaar
105b3307b5eSBram Moolenaar  let s:sourcewin = win_getid(winnr())
106fe386641SBram Moolenaar  let s:startsigncolumn = &signcolumn
107fe386641SBram Moolenaar
10824a98a0eSBram Moolenaar  let s:save_columns = 0
10924a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
11024a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
11138baa3e6SBram Moolenaar      let s:save_columns = &columns
11238baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
11324a98a0eSBram Moolenaar    endif
114b3307b5eSBram Moolenaar    let s:vertical = 1
11538baa3e6SBram Moolenaar  else
116b3307b5eSBram Moolenaar    let s:vertical = 0
11738baa3e6SBram Moolenaar  endif
11838baa3e6SBram Moolenaar
119b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
120b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
121b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
122b3307b5eSBram Moolenaar    let s:way = 'terminal'
123b3307b5eSBram Moolenaar  else
124b3307b5eSBram Moolenaar    let s:way = 'prompt'
125b3307b5eSBram Moolenaar  endif
126b3307b5eSBram Moolenaar
127b3307b5eSBram Moolenaar  if s:way == 'prompt'
128b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
129b3307b5eSBram Moolenaar  else
130b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
131b3307b5eSBram Moolenaar  endif
132b3307b5eSBram Moolenaarendfunc
133b3307b5eSBram Moolenaar
134b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
135b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
136fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
137b3307b5eSBram Moolenaar	\ 'term_name': 'debugged program',
138b3307b5eSBram Moolenaar	\ 'vertical': s:vertical,
139fe386641SBram Moolenaar	\ })
140fe386641SBram Moolenaar  if s:ptybuf == 0
141fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
142fe386641SBram Moolenaar    return
143fe386641SBram Moolenaar  endif
144fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
14545d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
146b3307b5eSBram Moolenaar  if s:vertical
14751b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
14851b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
14951b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
15051b0f370SBram Moolenaar  endif
151fe386641SBram Moolenaar
152fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
153fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
154fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
155fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
156fe386641SBram Moolenaar	\ 'hidden': 1,
157fe386641SBram Moolenaar	\ })
158fe386641SBram Moolenaar  if s:commbuf == 0
159fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
160fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
161fe386641SBram Moolenaar    return
162fe386641SBram Moolenaar  endif
163fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
164c572da5fSBram Moolenaar
165c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
166c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
16732c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
16832c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
16932c67ba7SBram Moolenaar
17032c67ba7SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
171b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
17260e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
173b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndTermDebug'),
174fe386641SBram Moolenaar	\ 'term_finish': 'close',
175c572da5fSBram Moolenaar	\ })
17660e73f2aSBram Moolenaar  if s:gdbbuf == 0
177fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
178fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
179fe386641SBram Moolenaar    exe 'bwipe! ' . s:commbuf
180fe386641SBram Moolenaar    return
181fe386641SBram Moolenaar  endif
18245d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
183fe386641SBram Moolenaar
18432c67ba7SBram Moolenaar  " Set arguments to be run
18532c67ba7SBram Moolenaar  if len(proc_args)
18632c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
18732c67ba7SBram Moolenaar  endif
18832c67ba7SBram Moolenaar
189fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
19060e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
19160e73f2aSBram Moolenaar
1923e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
1933e4b84d0SBram Moolenaar  " why the debugger doesn't work.
1943e4b84d0SBram Moolenaar  let try_count = 0
1953e4b84d0SBram Moolenaar  while 1
1963e4b84d0SBram Moolenaar    let response = ''
197b3623a38SBram Moolenaar    for lnum in range(1,200)
1983e4b84d0SBram Moolenaar      if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi '
1993e4b84d0SBram Moolenaar	let response = term_getline(s:gdbbuf, lnum + 1)
2003e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
201f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
2023e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:ptybuf
2033e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:commbuf
2043e4b84d0SBram Moolenaar	  return
2053e4b84d0SBram Moolenaar	endif
2063e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
2073e4b84d0SBram Moolenaar	  " Success!
2083e4b84d0SBram Moolenaar	  break
2093e4b84d0SBram Moolenaar	endif
2103e4b84d0SBram Moolenaar      endif
2113e4b84d0SBram Moolenaar    endfor
2123e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2133e4b84d0SBram Moolenaar      break
2143e4b84d0SBram Moolenaar    endif
2153e4b84d0SBram Moolenaar    let try_count += 1
2163e4b84d0SBram Moolenaar    if try_count > 100
2173e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2183e4b84d0SBram Moolenaar      break
2193e4b84d0SBram Moolenaar    endif
2203e4b84d0SBram Moolenaar    sleep 10m
2213e4b84d0SBram Moolenaar  endwhile
2223e4b84d0SBram Moolenaar
22360e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
22460e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
22560e73f2aSBram Moolenaar  " running.
22660e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
227b3307b5eSBram Moolenaar  " Older gdb uses a different command.
228b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
229e09ba7baSBram Moolenaar
230f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
231f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
232b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
233f3ba14ffSBram Moolenaar
234b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
235b3307b5eSBram Moolenaarendfunc
236b3307b5eSBram Moolenaar
237b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
238b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
239b3307b5eSBram Moolenaar  if s:vertical
240b3307b5eSBram Moolenaar    vertical new
241b3307b5eSBram Moolenaar  else
242b3307b5eSBram Moolenaar    new
243b3307b5eSBram Moolenaar  endif
244b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
245b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
246b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
247b3307b5eSBram Moolenaar  set buftype=prompt
248b3307b5eSBram Moolenaar  file gdb
249b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
250b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
251b3307b5eSBram Moolenaar
252b3307b5eSBram Moolenaar  if s:vertical
253b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
254b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
255b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
256b3307b5eSBram Moolenaar  endif
257b3307b5eSBram Moolenaar
258b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
259b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
260b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
261b3307b5eSBram Moolenaar
262b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
263b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
264b3307b5eSBram Moolenaar
265b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
266b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
267b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
268b3307b5eSBram Moolenaar	\ })
269b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
270b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
271b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
272b3307b5eSBram Moolenaar    return
273b3307b5eSBram Moolenaar  endif
274b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
275b3307b5eSBram Moolenaar
276b3307b5eSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only
277b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
278b3307b5eSBram Moolenaar  " target is running.
279b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
280b3307b5eSBram Moolenaar  " Older gdb uses a different command.
281b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
282b3307b5eSBram Moolenaar
283b3307b5eSBram Moolenaar  let s:ptybuf = 0
284b3307b5eSBram Moolenaar  if has('win32')
285b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
286b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
287b3307b5eSBram Moolenaar  elseif has('terminal')
288b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
289b3307b5eSBram Moolenaar    " gdb window.
290b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
291b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
292b3307b5eSBram Moolenaar	  \ })
293b3307b5eSBram Moolenaar    if s:ptybuf == 0
294b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
295b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
296b3307b5eSBram Moolenaar      return
297b3307b5eSBram Moolenaar    endif
298b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
299b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
300b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
301b3307b5eSBram Moolenaar
302b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
303b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
304b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
305b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
306b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
307b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
308b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
309b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
310b3307b5eSBram Moolenaar  else
311b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
312b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
313b3307b5eSBram Moolenaar  endif
314b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
315b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
316b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
317b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
318b3307b5eSBram Moolenaar
319b3307b5eSBram Moolenaar  " Set arguments to be run
320b3307b5eSBram Moolenaar  if len(proc_args)
321b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
322b3307b5eSBram Moolenaar  endif
323b3307b5eSBram Moolenaar
324b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
325b3307b5eSBram Moolenaar  startinsert
326b3307b5eSBram Moolenaarendfunc
327b3307b5eSBram Moolenaar
328b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
32938baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
33038baa3e6SBram Moolenaar  " There can be only one.
33138baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
33238baa3e6SBram Moolenaar
33345d5f26dSBram Moolenaar  " Install debugger commands in the text window.
334b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
335e09ba7baSBram Moolenaar  call s:InstallCommands()
33645d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
337e09ba7baSBram Moolenaar
33851b0f370SBram Moolenaar  " Enable showing a balloon with eval info
339246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
340246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
34151b0f370SBram Moolenaar    if has("balloon_eval")
34251b0f370SBram Moolenaar      set ballooneval
343246fe03dSBram Moolenaar    endif
34451b0f370SBram Moolenaar    if has("balloon_eval_term")
34551b0f370SBram Moolenaar      set balloonevalterm
34651b0f370SBram Moolenaar    endif
34751b0f370SBram Moolenaar  endif
34851b0f370SBram Moolenaar
349de1a8314SBram Moolenaar  " Contains breakpoints that have been placed, key is the number.
350e09ba7baSBram Moolenaar  let s:breakpoints = {}
3511b9645deSBram Moolenaar
3521b9645deSBram Moolenaar  augroup TermDebug
3531b9645deSBram Moolenaar    au BufRead * call s:BufRead()
3541b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
355f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
3561b9645deSBram Moolenaar  augroup END
35732c67ba7SBram Moolenaar
358b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
359b3307b5eSBram Moolenaar  " window.
36032c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
36132c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
36232c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
36332c67ba7SBram Moolenaar  endif
364c572da5fSBram Moolenaarendfunc
365c572da5fSBram Moolenaar
366b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
367b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
368b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
369b3307b5eSBram Moolenaar  if s:way == 'prompt'
370b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
371b3307b5eSBram Moolenaar  else
372b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
373b3307b5eSBram Moolenaar  endif
374b3307b5eSBram Moolenaarendfunc
375b3307b5eSBram Moolenaar
376b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
377b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
378b3307b5eSBram Moolenaar  if s:way == 'prompt'
379b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
380b3307b5eSBram Moolenaar  else
381b3307b5eSBram Moolenaar    let do_continue = 0
382b3307b5eSBram Moolenaar    if !s:stopped
383b3307b5eSBram Moolenaar      let do_continue = 1
384b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
385b3307b5eSBram Moolenaar      sleep 10m
386b3307b5eSBram Moolenaar    endif
387b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
388b3307b5eSBram Moolenaar    if do_continue
389b3307b5eSBram Moolenaar      Continue
390b3307b5eSBram Moolenaar    endif
391b3307b5eSBram Moolenaar  endif
392b3307b5eSBram Moolenaarendfunc
393b3307b5eSBram Moolenaar
394b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
395b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
396b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
397b3307b5eSBram Moolenaarendfunc
398b3307b5eSBram Moolenaar
399b3307b5eSBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer.
400b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
401b3307b5eSBram Moolenaar  call ch_log('Interrupting gdb')
402b3307b5eSBram Moolenaar  call job_stop(s:gdbjob, 'int')
403b3307b5eSBram Moolenaarendfunc
404b3307b5eSBram Moolenaar
405b3307b5eSBram Moolenaar" Function called when gdb outputs text.
406b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
407b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
408b3307b5eSBram Moolenaar
409b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
410b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
411*a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
412b3307b5eSBram Moolenaar    return
413b3307b5eSBram Moolenaar  endif
414b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
415b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
416b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
417b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
418b3307b5eSBram Moolenaar      unlet s:evalexpr
419b3307b5eSBram Moolenaar      return
420b3307b5eSBram Moolenaar    endif
421b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
422b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
423b3307b5eSBram Moolenaar  else
424b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
425b3307b5eSBram Moolenaar    return
426b3307b5eSBram Moolenaar  endif
427b3307b5eSBram Moolenaar
428b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
429b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
430b3307b5eSBram Moolenaar
431b3307b5eSBram Moolenaar  " Add the output above the current prompt.
432b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
433b3307b5eSBram Moolenaar  set nomodified
434b3307b5eSBram Moolenaar
435b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
436b3307b5eSBram Moolenaarendfunc
437b3307b5eSBram Moolenaar
438b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
439b3307b5eSBram Moolenaar" to the next ", unescaping characters.
440b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
441b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
442*a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
443b3307b5eSBram Moolenaar    return
444b3307b5eSBram Moolenaar  endif
445b3307b5eSBram Moolenaar  let result = ''
446b3307b5eSBram Moolenaar  let i = 1
447b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
448b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
449b3307b5eSBram Moolenaar      let i += 1
450b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
451b3307b5eSBram Moolenaar	" drop \n
452b3307b5eSBram Moolenaar	let i += 1
453b3307b5eSBram Moolenaar	continue
454b3307b5eSBram Moolenaar      endif
455b3307b5eSBram Moolenaar    endif
456b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
457b3307b5eSBram Moolenaar    let i += 1
458b3307b5eSBram Moolenaar  endwhile
459b3307b5eSBram Moolenaar  return result
460b3307b5eSBram Moolenaarendfunc
461b3307b5eSBram Moolenaar
462*a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
463*a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
464*a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
465*a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
466*a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
467*a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
468*a15b0a93SBram Moolenaar  endif
469*a15b0a93SBram Moolenaar  return name
470*a15b0a93SBram Moolenaarendfunc
471*a15b0a93SBram Moolenaar
472b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
473fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
474b3623a38SBram Moolenaar  unlet s:gdbwin
475e09ba7baSBram Moolenaar
476b3307b5eSBram Moolenaar  call s:EndDebugCommon()
477b3307b5eSBram Moolenaarendfunc
478b3307b5eSBram Moolenaar
479b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
480e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
481e09ba7baSBram Moolenaar
482b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
483b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
484b3307b5eSBram Moolenaar  endif
485b3307b5eSBram Moolenaar
486b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
487e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
488e09ba7baSBram Moolenaar  call s:DeleteCommands()
489e09ba7baSBram Moolenaar
490e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
491b3307b5eSBram Moolenaar
49238baa3e6SBram Moolenaar  if s:save_columns > 0
49338baa3e6SBram Moolenaar    let &columns = s:save_columns
49438baa3e6SBram Moolenaar  endif
4951b9645deSBram Moolenaar
496246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
497246fe03dSBram Moolenaar    set balloonexpr=
49851b0f370SBram Moolenaar    if has("balloon_eval")
49951b0f370SBram Moolenaar      set noballooneval
500246fe03dSBram Moolenaar    endif
50151b0f370SBram Moolenaar    if has("balloon_eval_term")
50251b0f370SBram Moolenaar      set noballoonevalterm
50351b0f370SBram Moolenaar    endif
50451b0f370SBram Moolenaar  endif
50551b0f370SBram Moolenaar
5061b9645deSBram Moolenaar  au! TermDebug
507fe386641SBram Moolenaarendfunc
508fe386641SBram Moolenaar
509b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
510b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
511b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
512b3307b5eSBram Moolenaar  close
513b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
514b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
515b3307b5eSBram Moolenaar  endif
516b3307b5eSBram Moolenaar
517b3307b5eSBram Moolenaar  call s:EndDebugCommon()
518b3307b5eSBram Moolenaar  unlet s:gdbwin
519b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
520b3307b5eSBram Moolenaarendfunc
521b3307b5eSBram Moolenaar
522fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
523fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
524fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
525fe386641SBram Moolenaar
526fe386641SBram Moolenaar  for msg in msgs
527fe386641SBram Moolenaar    " remove prefixed NL
528fe386641SBram Moolenaar    if msg[0] == "\n"
529fe386641SBram Moolenaar      let msg = msg[1:]
530fe386641SBram Moolenaar    endif
531fe386641SBram Moolenaar    if msg != ''
5321b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
533e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
53445d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
535e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
536e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
537e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
53845d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
53945d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
54045d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
54145d5f26dSBram Moolenaar	call s:HandleError(msg)
542e09ba7baSBram Moolenaar      endif
543e09ba7baSBram Moolenaar    endif
544e09ba7baSBram Moolenaar  endfor
545e09ba7baSBram Moolenaarendfunc
546e09ba7baSBram Moolenaar
547e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
548e09ba7baSBram Moolenaarfunc s:InstallCommands()
549e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
55071137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
551e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
55245d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
553e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
55460e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
55560e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
55660e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
557b3307b5eSBram Moolenaar
558b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
559b3307b5eSBram Moolenaar  if s:way == 'prompt'
560b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
561b3307b5eSBram Moolenaar  else
562b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
563b3307b5eSBram Moolenaar  endif
564b3307b5eSBram Moolenaar
56545d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
56645d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
56745d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
568b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
56971137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
57045d5f26dSBram Moolenaar
57145d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
57245d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
5731b9645deSBram Moolenaar
574f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
57571137fedSBram Moolenaar    call s:InstallWinbar()
57671137fedSBram Moolenaar
57771137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
57871137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
57971137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
58071137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
58171137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
58271137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
58371137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
58471137fedSBram Moolenaar    endif
58571137fedSBram Moolenaar  endif
58671137fedSBram Moolenaarendfunc
58771137fedSBram Moolenaar
58871137fedSBram Moolenaarlet s:winbar_winids = []
58971137fedSBram Moolenaar
59071137fedSBram Moolenaar" Install the window toolbar in the current window.
59171137fedSBram Moolenaarfunc s:InstallWinbar()
592c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
59324a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
59424a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
59524a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
59624a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
59760e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
59824a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
59971137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
600c4b533e1SBram Moolenaar  endif
601e09ba7baSBram Moolenaarendfunc
602e09ba7baSBram Moolenaar
603e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
604e09ba7baSBram Moolenaarfunc s:DeleteCommands()
605e09ba7baSBram Moolenaar  delcommand Break
60671137fedSBram Moolenaar  delcommand Clear
607e09ba7baSBram Moolenaar  delcommand Step
60845d5f26dSBram Moolenaar  delcommand Over
609e09ba7baSBram Moolenaar  delcommand Finish
61060e73f2aSBram Moolenaar  delcommand Run
61160e73f2aSBram Moolenaar  delcommand Arguments
61260e73f2aSBram Moolenaar  delcommand Stop
613e09ba7baSBram Moolenaar  delcommand Continue
61445d5f26dSBram Moolenaar  delcommand Evaluate
61545d5f26dSBram Moolenaar  delcommand Gdb
61645d5f26dSBram Moolenaar  delcommand Program
617b3623a38SBram Moolenaar  delcommand Source
61871137fedSBram Moolenaar  delcommand Winbar
61945d5f26dSBram Moolenaar
62045d5f26dSBram Moolenaar  nunmap K
6211b9645deSBram Moolenaar
6221b9645deSBram Moolenaar  if has('menu')
62371137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
62471137fedSBram Moolenaar    let curwinid = win_getid(winnr())
62571137fedSBram Moolenaar    for winid in s:winbar_winids
62671137fedSBram Moolenaar      if win_gotoid(winid)
6271b9645deSBram Moolenaar	aunmenu WinBar.Step
6281b9645deSBram Moolenaar	aunmenu WinBar.Next
6291b9645deSBram Moolenaar	aunmenu WinBar.Finish
6301b9645deSBram Moolenaar	aunmenu WinBar.Cont
63160e73f2aSBram Moolenaar	aunmenu WinBar.Stop
6321b9645deSBram Moolenaar	aunmenu WinBar.Eval
6331b9645deSBram Moolenaar      endif
63471137fedSBram Moolenaar    endfor
63571137fedSBram Moolenaar    call win_gotoid(curwinid)
63671137fedSBram Moolenaar    let s:winbar_winids = []
63771137fedSBram Moolenaar
63871137fedSBram Moolenaar    if exists('s:saved_mousemodel')
63971137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
64071137fedSBram Moolenaar      unlet s:saved_mousemodel
64171137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
64271137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
64371137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
64471137fedSBram Moolenaar      aunmenu PopUp.Evaluate
64571137fedSBram Moolenaar    endif
64671137fedSBram Moolenaar  endif
6471b9645deSBram Moolenaar
64845d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
64945d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
65045d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
65145d5f26dSBram Moolenaar  endfor
65245d5f26dSBram Moolenaar  unlet s:breakpoints
653*a15b0a93SBram Moolenaar
654*a15b0a93SBram Moolenaar  sign undefine debugPC
655*a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
656*a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
657*a15b0a93SBram Moolenaar  endfor
658*a15b0a93SBram Moolenaar  unlet s:BreakpointSigns
659e09ba7baSBram Moolenaarendfunc
660e09ba7baSBram Moolenaar
661e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
662e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
66360e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
66460e73f2aSBram Moolenaar  " Interrupt to make it work.
66560e73f2aSBram Moolenaar  let do_continue = 0
66660e73f2aSBram Moolenaar  if !s:stopped
66760e73f2aSBram Moolenaar    let do_continue = 1
668b3307b5eSBram Moolenaar    if s:way == 'prompt'
669b3307b5eSBram Moolenaar      " Need to send a signal to get the UI to listen.  Strangely this is only
670b3307b5eSBram Moolenaar      " needed once.
671b3307b5eSBram Moolenaar      call job_stop(s:gdbjob, 'int')
672b3307b5eSBram Moolenaar    else
67360e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
674b3307b5eSBram Moolenaar    endif
67560e73f2aSBram Moolenaar    sleep 10m
67660e73f2aSBram Moolenaar  endif
677*a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
678*a15b0a93SBram Moolenaar  call s:SendCommand('-break-insert '
679*a15b0a93SBram Moolenaar	\ . fnameescape(expand('%:p')) . ':' . line('.'))
68060e73f2aSBram Moolenaar  if do_continue
68160e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
68260e73f2aSBram Moolenaar  endif
683e09ba7baSBram Moolenaarendfunc
684e09ba7baSBram Moolenaar
68571137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
68671137fedSBram Moolenaarfunc s:ClearBreakpoint()
687e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
688e09ba7baSBram Moolenaar  let lnum = line('.')
689e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
690e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
691b3307b5eSBram Moolenaar      call s:SendCommand('-break-delete ' . key)
692e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
693e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
694e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
695e09ba7baSBram Moolenaar      break
696e09ba7baSBram Moolenaar    endif
697e09ba7baSBram Moolenaar  endfor
698e09ba7baSBram Moolenaarendfunc
699e09ba7baSBram Moolenaar
70060e73f2aSBram Moolenaarfunc s:Run(args)
70160e73f2aSBram Moolenaar  if a:args != ''
70260e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
70360e73f2aSBram Moolenaar  endif
70460e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
70560e73f2aSBram Moolenaarendfunc
70660e73f2aSBram Moolenaar
70751b0f370SBram Moolenaarfunc s:SendEval(expr)
70851b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
70951b0f370SBram Moolenaar  let s:evalexpr = a:expr
71051b0f370SBram Moolenaarendfunc
71151b0f370SBram Moolenaar
71245d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
71345d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
71445d5f26dSBram Moolenaar  if a:arg != ''
71545d5f26dSBram Moolenaar    let expr = a:arg
71645d5f26dSBram Moolenaar  elseif a:range == 2
71745d5f26dSBram Moolenaar    let pos = getcurpos()
71845d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
71945d5f26dSBram Moolenaar    let regt = getregtype('v')
72045d5f26dSBram Moolenaar    normal! gv"vy
72145d5f26dSBram Moolenaar    let expr = @v
72245d5f26dSBram Moolenaar    call setpos('.', pos)
72345d5f26dSBram Moolenaar    call setreg('v', reg, regt)
72445d5f26dSBram Moolenaar  else
72545d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
72645d5f26dSBram Moolenaar  endif
72722f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
72851b0f370SBram Moolenaar  call s:SendEval(expr)
72945d5f26dSBram Moolenaarendfunc
73045d5f26dSBram Moolenaar
73122f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
73251b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
73351b0f370SBram Moolenaar
73445d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
73545d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
7361b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
7371b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
73851b0f370SBram Moolenaar  if s:evalFromBalloonExpr
73951b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
74051b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
74151b0f370SBram Moolenaar    else
74251b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
74351b0f370SBram Moolenaar    endif
74451b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
74551b0f370SBram Moolenaar  else
7461b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
74751b0f370SBram Moolenaar  endif
7481b9645deSBram Moolenaar
7497f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
7501b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
75122f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
75251b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
75351b0f370SBram Moolenaar  else
75451b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
7551b9645deSBram Moolenaar  endif
75645d5f26dSBram Moolenaarendfunc
75745d5f26dSBram Moolenaar
75851b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
75951b0f370SBram Moolenaar" if there is any.
76051b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
761b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
762b3307b5eSBram Moolenaar    return
763b3307b5eSBram Moolenaar  endif
764b3307b5eSBram Moolenaar  if !s:stopped
765b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
766b3307b5eSBram Moolenaar    " mouse triggers a balloon.
76751b0f370SBram Moolenaar    return
76851b0f370SBram Moolenaar  endif
76951b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
77051b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
77122f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
77222f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
77351b0f370SBram Moolenaar  return ''
77451b0f370SBram Moolenaarendfunc
77551b0f370SBram Moolenaar
77645d5f26dSBram Moolenaar" Handle an error.
77745d5f26dSBram Moolenaarfunc s:HandleError(msg)
77822f1d0e3SBram Moolenaar  if s:ignoreEvalError
77951b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
78022f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
78122f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
78251b0f370SBram Moolenaar    return
78351b0f370SBram Moolenaar  endif
78445d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
78545d5f26dSBram Moolenaarendfunc
78645d5f26dSBram Moolenaar
787b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
788b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
789c4b533e1SBram Moolenaar    new
790b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
791c4b533e1SBram Moolenaar    call s:InstallWinbar()
792c4b533e1SBram Moolenaar  endif
793c4b533e1SBram Moolenaarendfunc
794c4b533e1SBram Moolenaar
795e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
796e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
797e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
798fe386641SBram Moolenaar  let wid = win_getid(winnr())
799fe386641SBram Moolenaar
80060e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
80160e73f2aSBram Moolenaar    let s:stopped = 1
80260e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
80360e73f2aSBram Moolenaar    let s:stopped = 0
80460e73f2aSBram Moolenaar  endif
80560e73f2aSBram Moolenaar
806b3307b5eSBram Moolenaar  call s:GotoSourcewinOrCreateIt()
807c4b533e1SBram Moolenaar
808*a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
809*a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
810*a15b0a93SBram Moolenaar  else
811*a15b0a93SBram Moolenaar    let fname = ''
812*a15b0a93SBram Moolenaar  endif
8131b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
814e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
815fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
8161b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
817fe386641SBram Moolenaar	if &modified
818fe386641SBram Moolenaar	  " TODO: find existing window
819fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
820b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
821c4b533e1SBram Moolenaar	  call s:InstallWinbar()
822fe386641SBram Moolenaar	else
823fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
824fe386641SBram Moolenaar	endif
825fe386641SBram Moolenaar      endif
826fe386641SBram Moolenaar      exe lnum
82701164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
8281b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
829fe386641SBram Moolenaar      setlocal signcolumn=yes
830fe386641SBram Moolenaar    endif
831fe386641SBram Moolenaar  else
832fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
833fe386641SBram Moolenaar  endif
834fe386641SBram Moolenaar
835fe386641SBram Moolenaar  call win_gotoid(wid)
836e09ba7baSBram Moolenaarendfunc
837e09ba7baSBram Moolenaar
838de1a8314SBram Moolenaarlet s:BreakpointSigns = []
839*a15b0a93SBram Moolenaar
840*a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr)
841de1a8314SBram Moolenaar  if index(s:BreakpointSigns, a:nr) == -1
842de1a8314SBram Moolenaar    call add(s:BreakpointSigns, a:nr)
843de1a8314SBram Moolenaar    exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint"
844de1a8314SBram Moolenaar  endif
845de1a8314SBram Moolenaarendfunc
846de1a8314SBram Moolenaar
847e09ba7baSBram Moolenaar" Handle setting a breakpoint
848e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
849e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
850e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
851e09ba7baSBram Moolenaar  if nr == 0
852e09ba7baSBram Moolenaar    return
853fe386641SBram Moolenaar  endif
854de1a8314SBram Moolenaar  call s:CreateBreakpoint(nr)
855e09ba7baSBram Moolenaar
856e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
857e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
858e09ba7baSBram Moolenaar  else
859e09ba7baSBram Moolenaar    let entry = {}
860e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
861fe386641SBram Moolenaar  endif
862e09ba7baSBram Moolenaar
863*a15b0a93SBram Moolenaar  let fname = s:GetFullname(a:msg)
864e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
865e09ba7baSBram Moolenaar  let entry['fname'] = fname
866e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
8671b9645deSBram Moolenaar
8681b9645deSBram Moolenaar  if bufloaded(fname)
8691b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
8701b9645deSBram Moolenaar  endif
8711b9645deSBram Moolenaarendfunc
8721b9645deSBram Moolenaar
8731b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
874de1a8314SBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname']
8751b9645deSBram Moolenaar  let a:entry['placed'] = 1
876e09ba7baSBram Moolenaarendfunc
877e09ba7baSBram Moolenaar
878e09ba7baSBram Moolenaar" Handle deleting a breakpoint
879e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
880e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
881e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
882e09ba7baSBram Moolenaar  if nr == 0
883e09ba7baSBram Moolenaar    return
884e09ba7baSBram Moolenaar  endif
8851b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
8861b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
8871b9645deSBram Moolenaar    if has_key(entry, 'placed')
888e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
8891b9645deSBram Moolenaar      unlet entry['placed']
8901b9645deSBram Moolenaar    endif
891e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
8921b9645deSBram Moolenaar  endif
893c572da5fSBram Moolenaarendfunc
8941b9645deSBram Moolenaar
8951b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
8961b9645deSBram Moolenaarfunc s:BufRead()
8971b9645deSBram Moolenaar  let fname = expand('<afile>:p')
8981b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
8991b9645deSBram Moolenaar    if entry['fname'] == fname
9001b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
9011b9645deSBram Moolenaar    endif
9021b9645deSBram Moolenaar  endfor
9031b9645deSBram Moolenaarendfunc
9041b9645deSBram Moolenaar
9051b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
9061b9645deSBram Moolenaarfunc s:BufUnloaded()
9071b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9081b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9091b9645deSBram Moolenaar    if entry['fname'] == fname
9101b9645deSBram Moolenaar      let entry['placed'] = 0
9111b9645deSBram Moolenaar    endif
9121b9645deSBram Moolenaar  endfor
9131b9645deSBram Moolenaarendfunc
914