1fe386641SBram Moolenaar" Debugger plugin using gdb.
2c572da5fSBram Moolenaar"
3*b3307b5eSBram Moolenaar" Author: Bram Moolenaar
4*b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license"
5*b3307b5eSBram Moolenaar" Last Update: 2018 Jun 3
6c572da5fSBram Moolenaar"
7*b3307b5eSBram Moolenaar" WORK IN PROGRESS - Only the basics work
8*b3307b5eSBram Moolenaar" Note: On MS-Windows you need a recent version of gdb.  The one included with
9*b3307b5eSBram Moolenaar" MingW is too old (7.6.1).
10*b3307b5eSBram Moolenaar" I used version 7.12 from http://www.equation.com/servlet/equation.cmd?fa=gdb
11fe386641SBram Moolenaar"
12*b3307b5eSBram Moolenaar" There are two ways to run gdb:
13*b3307b5eSBram Moolenaar" - In a terminal window; used if possible, does not work on MS-Windows
14*b3307b5eSBram Moolenaar"   Not used when g:termdebug_use_prompt is set to 1.
15*b3307b5eSBram Moolenaar" - Using a "prompt" buffer; may use a terminal window for the program
16*b3307b5eSBram Moolenaar"
17*b3307b5eSBram Moolenaar" For both the current window is used to view source code and shows the
18*b3307b5eSBram Moolenaar" current statement from gdb.
19*b3307b5eSBram Moolenaar"
20*b3307b5eSBram Moolenaar" USING A TERMINAL WINDOW
21*b3307b5eSBram Moolenaar"
22*b3307b5eSBram Moolenaar" Opens two visible terminal windows:
23*b3307b5eSBram Moolenaar" 1. runs a pty for the debugged program, as with ":term NONE"
24*b3307b5eSBram 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"
27*b3307b5eSBram Moolenaar" USING A PROMPT BUFFER
28*b3307b5eSBram Moolenaar"
29*b3307b5eSBram Moolenaar" Opens a window with a prompt buffer to communicate with gdb.
30*b3307b5eSBram Moolenaar" Gdb is run as a job with callbacks for I/O.
31*b3307b5eSBram Moolenaar" On Unix another terminal window is opened to run the debugged program
32*b3307b5eSBram Moolenaar" On MS-Windows a separate console is opened to run the debugged program
33*b3307b5eSBram 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
37*b3307b5eSBram Moolenaar" In case this gets sourced twice.
3837c64c78SBram Moolenaarif exists(':Termdebug')
3937c64c78SBram Moolenaar  finish
4037c64c78SBram Moolenaarendif
4137c64c78SBram Moolenaar
42*b3307b5eSBram Moolenaar" Need either the +terminal feature or +channel and the prompt buffer.
43*b3307b5eSBram Moolenaar" The terminal feature does not work with gdb on win32.
44*b3307b5eSBram Moolenaarif has('terminal') && !has('win32')
45*b3307b5eSBram Moolenaar  let s:way = 'terminal'
46*b3307b5eSBram Moolenaarelseif has('channel') && exists('*prompt_setprompt')
47*b3307b5eSBram Moolenaar  let s:way = 'prompt'
48*b3307b5eSBram Moolenaarelse
49*b3307b5eSBram Moolenaar  if has('terminal')
50*b3307b5eSBram Moolenaar    let s:err = 'Cannot debug, missing prompt buffer support'
51*b3307b5eSBram Moolenaar  else
52*b3307b5eSBram Moolenaar    let s:err = 'Cannot debug, +channel feature is not supported'
53*b3307b5eSBram Moolenaar  endif
54*b3307b5eSBram Moolenaar  command -nargs=* -complete=file -bang Termdebug echoerr s:err
55*b3307b5eSBram Moolenaar  command -nargs=+ -complete=file -bang TermdebugCommand echoerr s:err
56*b3307b5eSBram Moolenaar  finish
57*b3307b5eSBram 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
70e09ba7baSBram Moolenaarlet s:break_id = 13
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
95*b3307b5eSBram Moolenaar  let s:ptywin = 0
96b3623a38SBram Moolenaar
97*b3307b5eSBram Moolenaar  " Uncomment this line to write logging in "debuglog".
98*b3307b5eSBram Moolenaar  " call ch_logfile('debuglog', 'w')
99*b3307b5eSBram Moolenaar
100*b3307b5eSBram 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
109*b3307b5eSBram Moolenaar    let s:vertical = 1
11038baa3e6SBram Moolenaar  else
111*b3307b5eSBram Moolenaar    let s:vertical = 0
11238baa3e6SBram Moolenaar  endif
11338baa3e6SBram Moolenaar
114*b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
115*b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
116*b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
117*b3307b5eSBram Moolenaar    let s:way = 'terminal'
118*b3307b5eSBram Moolenaar  else
119*b3307b5eSBram Moolenaar    let s:way = 'prompt'
120*b3307b5eSBram Moolenaar  endif
121*b3307b5eSBram Moolenaar
122*b3307b5eSBram Moolenaar  if s:way == 'prompt'
123*b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
124*b3307b5eSBram Moolenaar  else
125*b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
126*b3307b5eSBram Moolenaar  endif
127*b3307b5eSBram Moolenaarendfunc
128*b3307b5eSBram Moolenaar
129*b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
130*b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
131fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
132*b3307b5eSBram Moolenaar	\ 'term_name': 'debugged program',
133*b3307b5eSBram 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())
141*b3307b5eSBram 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
166*b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
16760e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
168*b3307b5eSBram 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')
222*b3307b5eSBram Moolenaar  " Older gdb uses a different command.
223*b3307b5eSBram 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.
227*b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
228f3ba14ffSBram Moolenaar
229*b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
230*b3307b5eSBram Moolenaarendfunc
231*b3307b5eSBram Moolenaar
232*b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
233*b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
234*b3307b5eSBram Moolenaar  if s:vertical
235*b3307b5eSBram Moolenaar    vertical new
236*b3307b5eSBram Moolenaar  else
237*b3307b5eSBram Moolenaar    new
238*b3307b5eSBram Moolenaar  endif
239*b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
240*b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
241*b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
242*b3307b5eSBram Moolenaar  set buftype=prompt
243*b3307b5eSBram Moolenaar  file gdb
244*b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
245*b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
246*b3307b5eSBram Moolenaar
247*b3307b5eSBram Moolenaar  if s:vertical
248*b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
249*b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
250*b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
251*b3307b5eSBram Moolenaar  endif
252*b3307b5eSBram Moolenaar
253*b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
254*b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
255*b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
256*b3307b5eSBram Moolenaar
257*b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
258*b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
259*b3307b5eSBram Moolenaar
260*b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
261*b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
262*b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
263*b3307b5eSBram Moolenaar	\ })
264*b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
265*b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
266*b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
267*b3307b5eSBram Moolenaar    return
268*b3307b5eSBram Moolenaar  endif
269*b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
270*b3307b5eSBram Moolenaar
271*b3307b5eSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only
272*b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
273*b3307b5eSBram Moolenaar  " target is running.
274*b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
275*b3307b5eSBram Moolenaar  " Older gdb uses a different command.
276*b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
277*b3307b5eSBram Moolenaar
278*b3307b5eSBram Moolenaar  let s:ptybuf = 0
279*b3307b5eSBram Moolenaar  if has('win32')
280*b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
281*b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
282*b3307b5eSBram Moolenaar  elseif has('terminal')
283*b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
284*b3307b5eSBram Moolenaar    " gdb window.
285*b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
286*b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
287*b3307b5eSBram Moolenaar	  \ })
288*b3307b5eSBram Moolenaar    if s:ptybuf == 0
289*b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
290*b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
291*b3307b5eSBram Moolenaar      return
292*b3307b5eSBram Moolenaar    endif
293*b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
294*b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
295*b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
296*b3307b5eSBram Moolenaar
297*b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
298*b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
299*b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
300*b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
301*b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
302*b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
303*b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
304*b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
305*b3307b5eSBram Moolenaar  else
306*b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
307*b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
308*b3307b5eSBram Moolenaar  endif
309*b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
310*b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
311*b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
312*b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
313*b3307b5eSBram Moolenaar
314*b3307b5eSBram Moolenaar  " Set arguments to be run
315*b3307b5eSBram Moolenaar  if len(proc_args)
316*b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
317*b3307b5eSBram Moolenaar  endif
318*b3307b5eSBram Moolenaar
319*b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
320*b3307b5eSBram Moolenaar  startinsert
321*b3307b5eSBram Moolenaarendfunc
322*b3307b5eSBram Moolenaar
323*b3307b5eSBram 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
32838baa3e6SBram Moolenaar  " Sign used to indicate a breakpoint.
32938baa3e6SBram Moolenaar  " Can be used multiple times.
33038baa3e6SBram Moolenaar  sign define debugBreakpoint text=>> texthl=debugBreakpoint
33138baa3e6SBram Moolenaar
33245d5f26dSBram Moolenaar  " Install debugger commands in the text window.
333*b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
334e09ba7baSBram Moolenaar  call s:InstallCommands()
33545d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
336e09ba7baSBram Moolenaar
33751b0f370SBram Moolenaar  " Enable showing a balloon with eval info
338246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
339246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
34051b0f370SBram Moolenaar    if has("balloon_eval")
34151b0f370SBram Moolenaar      set ballooneval
342246fe03dSBram Moolenaar    endif
34351b0f370SBram Moolenaar    if has("balloon_eval_term")
34451b0f370SBram Moolenaar      set balloonevalterm
34551b0f370SBram Moolenaar    endif
34651b0f370SBram Moolenaar  endif
34751b0f370SBram Moolenaar
348e09ba7baSBram Moolenaar  let s:breakpoints = {}
3491b9645deSBram Moolenaar
3501b9645deSBram Moolenaar  augroup TermDebug
3511b9645deSBram Moolenaar    au BufRead * call s:BufRead()
3521b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
3531b9645deSBram Moolenaar  augroup END
35432c67ba7SBram Moolenaar
355*b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
356*b3307b5eSBram Moolenaar  " window.
35732c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
35832c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
35932c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
36032c67ba7SBram Moolenaar  endif
361c572da5fSBram Moolenaarendfunc
362c572da5fSBram Moolenaar
363*b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
364*b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
365*b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
366*b3307b5eSBram Moolenaar  if s:way == 'prompt'
367*b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
368*b3307b5eSBram Moolenaar  else
369*b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
370*b3307b5eSBram Moolenaar  endif
371*b3307b5eSBram Moolenaarendfunc
372*b3307b5eSBram Moolenaar
373*b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
374*b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
375*b3307b5eSBram Moolenaar  if s:way == 'prompt'
376*b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
377*b3307b5eSBram Moolenaar  else
378*b3307b5eSBram Moolenaar    let do_continue = 0
379*b3307b5eSBram Moolenaar    if !s:stopped
380*b3307b5eSBram Moolenaar      let do_continue = 1
381*b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
382*b3307b5eSBram Moolenaar      sleep 10m
383*b3307b5eSBram Moolenaar    endif
384*b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
385*b3307b5eSBram Moolenaar    if do_continue
386*b3307b5eSBram Moolenaar      Continue
387*b3307b5eSBram Moolenaar    endif
388*b3307b5eSBram Moolenaar  endif
389*b3307b5eSBram Moolenaarendfunc
390*b3307b5eSBram Moolenaar
391*b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
392*b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
393*b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
394*b3307b5eSBram Moolenaarendfunc
395*b3307b5eSBram Moolenaar
396*b3307b5eSBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer.
397*b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
398*b3307b5eSBram Moolenaar  call ch_log('Interrupting gdb')
399*b3307b5eSBram Moolenaar  call job_stop(s:gdbjob, 'int')
400*b3307b5eSBram Moolenaarendfunc
401*b3307b5eSBram Moolenaar
402*b3307b5eSBram Moolenaar" Function called when gdb outputs text.
403*b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
404*b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
405*b3307b5eSBram Moolenaar
406*b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
407*b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
408*b3307b5eSBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' || a:text[0] == '='
409*b3307b5eSBram Moolenaar    return
410*b3307b5eSBram Moolenaar  endif
411*b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
412*b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
413*b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
414*b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
415*b3307b5eSBram Moolenaar      unlet s:evalexpr
416*b3307b5eSBram Moolenaar      return
417*b3307b5eSBram Moolenaar    endif
418*b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
419*b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
420*b3307b5eSBram Moolenaar  else
421*b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
422*b3307b5eSBram Moolenaar    return
423*b3307b5eSBram Moolenaar  endif
424*b3307b5eSBram Moolenaar
425*b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
426*b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
427*b3307b5eSBram Moolenaar
428*b3307b5eSBram Moolenaar  " Add the output above the current prompt.
429*b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
430*b3307b5eSBram Moolenaar  set nomodified
431*b3307b5eSBram Moolenaar
432*b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
433*b3307b5eSBram Moolenaarendfunc
434*b3307b5eSBram Moolenaar
435*b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
436*b3307b5eSBram Moolenaar" to the next ", unescaping characters.
437*b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
438*b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
439*b3307b5eSBram Moolenaar    echoerr 'DecodeMessage(): missing quote'
440*b3307b5eSBram Moolenaar    return
441*b3307b5eSBram Moolenaar  endif
442*b3307b5eSBram Moolenaar  let result = ''
443*b3307b5eSBram Moolenaar  let i = 1
444*b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
445*b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
446*b3307b5eSBram Moolenaar      let i += 1
447*b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
448*b3307b5eSBram Moolenaar	" drop \n
449*b3307b5eSBram Moolenaar	let i += 1
450*b3307b5eSBram Moolenaar	continue
451*b3307b5eSBram Moolenaar      endif
452*b3307b5eSBram Moolenaar    endif
453*b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
454*b3307b5eSBram Moolenaar    let i += 1
455*b3307b5eSBram Moolenaar  endwhile
456*b3307b5eSBram Moolenaar  return result
457*b3307b5eSBram Moolenaarendfunc
458*b3307b5eSBram Moolenaar
459*b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
460fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
461b3623a38SBram Moolenaar  unlet s:gdbwin
462e09ba7baSBram Moolenaar
463*b3307b5eSBram Moolenaar  call s:EndDebugCommon()
464*b3307b5eSBram Moolenaarendfunc
465*b3307b5eSBram Moolenaar
466*b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
467e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
468e09ba7baSBram Moolenaar
469*b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
470*b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
471*b3307b5eSBram Moolenaar  endif
472*b3307b5eSBram Moolenaar
473*b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
474e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
475e09ba7baSBram Moolenaar  call s:DeleteCommands()
476e09ba7baSBram Moolenaar
477e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
478*b3307b5eSBram Moolenaar
47938baa3e6SBram Moolenaar  if s:save_columns > 0
48038baa3e6SBram Moolenaar    let &columns = s:save_columns
48138baa3e6SBram Moolenaar  endif
4821b9645deSBram Moolenaar
483246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
484246fe03dSBram Moolenaar    set balloonexpr=
48551b0f370SBram Moolenaar    if has("balloon_eval")
48651b0f370SBram Moolenaar      set noballooneval
487246fe03dSBram Moolenaar    endif
48851b0f370SBram Moolenaar    if has("balloon_eval_term")
48951b0f370SBram Moolenaar      set noballoonevalterm
49051b0f370SBram Moolenaar    endif
49151b0f370SBram Moolenaar  endif
49251b0f370SBram Moolenaar
4931b9645deSBram Moolenaar  au! TermDebug
494fe386641SBram Moolenaarendfunc
495fe386641SBram Moolenaar
496*b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
497*b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
498*b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
499*b3307b5eSBram Moolenaar  close
500*b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
501*b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
502*b3307b5eSBram Moolenaar  endif
503*b3307b5eSBram Moolenaar
504*b3307b5eSBram Moolenaar  call s:EndDebugCommon()
505*b3307b5eSBram Moolenaar  unlet s:gdbwin
506*b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
507*b3307b5eSBram Moolenaarendfunc
508*b3307b5eSBram Moolenaar
509fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
510fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
511fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
512fe386641SBram Moolenaar
513fe386641SBram Moolenaar  for msg in msgs
514fe386641SBram Moolenaar    " remove prefixed NL
515fe386641SBram Moolenaar    if msg[0] == "\n"
516fe386641SBram Moolenaar      let msg = msg[1:]
517fe386641SBram Moolenaar    endif
518fe386641SBram Moolenaar    if msg != ''
5191b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
520e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
52145d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
522e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
523e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
524e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
52545d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
52645d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
52745d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
52845d5f26dSBram Moolenaar	call s:HandleError(msg)
529e09ba7baSBram Moolenaar      endif
530e09ba7baSBram Moolenaar    endif
531e09ba7baSBram Moolenaar  endfor
532e09ba7baSBram Moolenaarendfunc
533e09ba7baSBram Moolenaar
534e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
535e09ba7baSBram Moolenaarfunc s:InstallCommands()
536e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
53771137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
538e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
53945d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
540e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
54160e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
54260e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
54360e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
544*b3307b5eSBram Moolenaar
545*b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
546*b3307b5eSBram Moolenaar  if s:way == 'prompt'
547*b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
548*b3307b5eSBram Moolenaar  else
549*b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
550*b3307b5eSBram Moolenaar  endif
551*b3307b5eSBram Moolenaar
55245d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
55345d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
55445d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
555*b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
55671137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
55745d5f26dSBram Moolenaar
55845d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
55945d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
5601b9645deSBram Moolenaar
561f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
56271137fedSBram Moolenaar    call s:InstallWinbar()
56371137fedSBram Moolenaar
56471137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
56571137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
56671137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
56771137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
56871137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
56971137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
57071137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
57171137fedSBram Moolenaar    endif
57271137fedSBram Moolenaar  endif
57371137fedSBram Moolenaarendfunc
57471137fedSBram Moolenaar
57571137fedSBram Moolenaarlet s:winbar_winids = []
57671137fedSBram Moolenaar
57771137fedSBram Moolenaar" Install the window toolbar in the current window.
57871137fedSBram Moolenaarfunc s:InstallWinbar()
579c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
58024a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
58124a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
58224a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
58324a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
58460e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
58524a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
58671137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
587c4b533e1SBram Moolenaar  endif
588e09ba7baSBram Moolenaarendfunc
589e09ba7baSBram Moolenaar
590e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
591e09ba7baSBram Moolenaarfunc s:DeleteCommands()
592e09ba7baSBram Moolenaar  delcommand Break
59371137fedSBram Moolenaar  delcommand Clear
594e09ba7baSBram Moolenaar  delcommand Step
59545d5f26dSBram Moolenaar  delcommand Over
596e09ba7baSBram Moolenaar  delcommand Finish
59760e73f2aSBram Moolenaar  delcommand Run
59860e73f2aSBram Moolenaar  delcommand Arguments
59960e73f2aSBram Moolenaar  delcommand Stop
600e09ba7baSBram Moolenaar  delcommand Continue
60145d5f26dSBram Moolenaar  delcommand Evaluate
60245d5f26dSBram Moolenaar  delcommand Gdb
60345d5f26dSBram Moolenaar  delcommand Program
604b3623a38SBram Moolenaar  delcommand Source
60571137fedSBram Moolenaar  delcommand Winbar
60645d5f26dSBram Moolenaar
60745d5f26dSBram Moolenaar  nunmap K
6081b9645deSBram Moolenaar
6091b9645deSBram Moolenaar  if has('menu')
61071137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
61171137fedSBram Moolenaar    let curwinid = win_getid(winnr())
61271137fedSBram Moolenaar    for winid in s:winbar_winids
61371137fedSBram Moolenaar      if win_gotoid(winid)
6141b9645deSBram Moolenaar	aunmenu WinBar.Step
6151b9645deSBram Moolenaar	aunmenu WinBar.Next
6161b9645deSBram Moolenaar	aunmenu WinBar.Finish
6171b9645deSBram Moolenaar	aunmenu WinBar.Cont
61860e73f2aSBram Moolenaar	aunmenu WinBar.Stop
6191b9645deSBram Moolenaar	aunmenu WinBar.Eval
6201b9645deSBram Moolenaar      endif
62171137fedSBram Moolenaar    endfor
62271137fedSBram Moolenaar    call win_gotoid(curwinid)
62371137fedSBram Moolenaar    let s:winbar_winids = []
62471137fedSBram Moolenaar
62571137fedSBram Moolenaar    if exists('s:saved_mousemodel')
62671137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
62771137fedSBram Moolenaar      unlet s:saved_mousemodel
62871137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
62971137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
63071137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
63171137fedSBram Moolenaar      aunmenu PopUp.Evaluate
63271137fedSBram Moolenaar    endif
63371137fedSBram Moolenaar  endif
6341b9645deSBram Moolenaar
63545d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
63645d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
63745d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
63845d5f26dSBram Moolenaar  endfor
63938baa3e6SBram Moolenaar  sign undefine debugPC
64038baa3e6SBram Moolenaar  sign undefine debugBreakpoint
64145d5f26dSBram Moolenaar  unlet s:breakpoints
642e09ba7baSBram Moolenaarendfunc
643e09ba7baSBram Moolenaar
644e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
645e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
64660e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
64760e73f2aSBram Moolenaar  " Interrupt to make it work.
64860e73f2aSBram Moolenaar  let do_continue = 0
64960e73f2aSBram Moolenaar  if !s:stopped
65060e73f2aSBram Moolenaar    let do_continue = 1
651*b3307b5eSBram Moolenaar    if s:way == 'prompt'
652*b3307b5eSBram Moolenaar      " Need to send a signal to get the UI to listen.  Strangely this is only
653*b3307b5eSBram Moolenaar      " needed once.
654*b3307b5eSBram Moolenaar      call job_stop(s:gdbjob, 'int')
655*b3307b5eSBram Moolenaar    else
65660e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
657*b3307b5eSBram Moolenaar    endif
65860e73f2aSBram Moolenaar    sleep 10m
65960e73f2aSBram Moolenaar  endif
66060e73f2aSBram Moolenaar  call s:SendCommand('-break-insert --source '
66160e73f2aSBram Moolenaar	\ . fnameescape(expand('%:p')) . ' --line ' . line('.'))
66260e73f2aSBram Moolenaar  if do_continue
66360e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
66460e73f2aSBram Moolenaar  endif
665e09ba7baSBram Moolenaarendfunc
666e09ba7baSBram Moolenaar
66771137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
66871137fedSBram Moolenaarfunc s:ClearBreakpoint()
669e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
670e09ba7baSBram Moolenaar  let lnum = line('.')
671e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
672e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
673*b3307b5eSBram Moolenaar      call s:SendCommand('-break-delete ' . key)
674e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
675e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
676e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
677e09ba7baSBram Moolenaar      break
678e09ba7baSBram Moolenaar    endif
679e09ba7baSBram Moolenaar  endfor
680e09ba7baSBram Moolenaarendfunc
681e09ba7baSBram Moolenaar
68260e73f2aSBram Moolenaarfunc s:Run(args)
68360e73f2aSBram Moolenaar  if a:args != ''
68460e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
68560e73f2aSBram Moolenaar  endif
68660e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
68760e73f2aSBram Moolenaarendfunc
68860e73f2aSBram Moolenaar
68951b0f370SBram Moolenaarfunc s:SendEval(expr)
69051b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
69151b0f370SBram Moolenaar  let s:evalexpr = a:expr
69251b0f370SBram Moolenaarendfunc
69351b0f370SBram Moolenaar
69445d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
69545d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
69645d5f26dSBram Moolenaar  if a:arg != ''
69745d5f26dSBram Moolenaar    let expr = a:arg
69845d5f26dSBram Moolenaar  elseif a:range == 2
69945d5f26dSBram Moolenaar    let pos = getcurpos()
70045d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
70145d5f26dSBram Moolenaar    let regt = getregtype('v')
70245d5f26dSBram Moolenaar    normal! gv"vy
70345d5f26dSBram Moolenaar    let expr = @v
70445d5f26dSBram Moolenaar    call setpos('.', pos)
70545d5f26dSBram Moolenaar    call setreg('v', reg, regt)
70645d5f26dSBram Moolenaar  else
70745d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
70845d5f26dSBram Moolenaar  endif
70922f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
71051b0f370SBram Moolenaar  call s:SendEval(expr)
71145d5f26dSBram Moolenaarendfunc
71245d5f26dSBram Moolenaar
71322f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
71451b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
71551b0f370SBram Moolenaar
71645d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
71745d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
7181b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
7191b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
72051b0f370SBram Moolenaar  if s:evalFromBalloonExpr
72151b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
72251b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
72351b0f370SBram Moolenaar    else
72451b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
72551b0f370SBram Moolenaar    endif
72651b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
72751b0f370SBram Moolenaar  else
7281b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
72951b0f370SBram Moolenaar  endif
7301b9645deSBram Moolenaar
7317f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
7321b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
73322f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
73451b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
73551b0f370SBram Moolenaar  else
73651b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
7371b9645deSBram Moolenaar  endif
73845d5f26dSBram Moolenaarendfunc
73945d5f26dSBram Moolenaar
74051b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
74151b0f370SBram Moolenaar" if there is any.
74251b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
743*b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
744*b3307b5eSBram Moolenaar    return
745*b3307b5eSBram Moolenaar  endif
746*b3307b5eSBram Moolenaar  if !s:stopped
747*b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
748*b3307b5eSBram Moolenaar    " mouse triggers a balloon.
74951b0f370SBram Moolenaar    return
75051b0f370SBram Moolenaar  endif
75151b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
75251b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
75322f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
75422f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
75551b0f370SBram Moolenaar  return ''
75651b0f370SBram Moolenaarendfunc
75751b0f370SBram Moolenaar
75845d5f26dSBram Moolenaar" Handle an error.
75945d5f26dSBram Moolenaarfunc s:HandleError(msg)
76022f1d0e3SBram Moolenaar  if s:ignoreEvalError
76151b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
76222f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
76322f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
76451b0f370SBram Moolenaar    return
76551b0f370SBram Moolenaar  endif
76645d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
76745d5f26dSBram Moolenaarendfunc
76845d5f26dSBram Moolenaar
769*b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
770*b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
771c4b533e1SBram Moolenaar    new
772*b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
773c4b533e1SBram Moolenaar    call s:InstallWinbar()
774c4b533e1SBram Moolenaar  endif
775c4b533e1SBram Moolenaarendfunc
776c4b533e1SBram Moolenaar
777e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
778e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
779e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
780fe386641SBram Moolenaar  let wid = win_getid(winnr())
781fe386641SBram Moolenaar
78260e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
78360e73f2aSBram Moolenaar    let s:stopped = 1
78460e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
78560e73f2aSBram Moolenaar    let s:stopped = 0
78660e73f2aSBram Moolenaar  endif
78760e73f2aSBram Moolenaar
788*b3307b5eSBram Moolenaar  call s:GotoSourcewinOrCreateIt()
789c4b533e1SBram Moolenaar
790e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
7911b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
792e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
793fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
7941b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
795fe386641SBram Moolenaar	if &modified
796fe386641SBram Moolenaar	  " TODO: find existing window
797fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
798*b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
799c4b533e1SBram Moolenaar	  call s:InstallWinbar()
800fe386641SBram Moolenaar	else
801fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
802fe386641SBram Moolenaar	endif
803fe386641SBram Moolenaar      endif
804fe386641SBram Moolenaar      exe lnum
80501164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
8061b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
807fe386641SBram Moolenaar      setlocal signcolumn=yes
808fe386641SBram Moolenaar    endif
809fe386641SBram Moolenaar  else
810fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
811fe386641SBram Moolenaar  endif
812fe386641SBram Moolenaar
813fe386641SBram Moolenaar  call win_gotoid(wid)
814e09ba7baSBram Moolenaarendfunc
815e09ba7baSBram Moolenaar
816e09ba7baSBram Moolenaar" Handle setting a breakpoint
817e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
818e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
819e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
820e09ba7baSBram Moolenaar  if nr == 0
821e09ba7baSBram Moolenaar    return
822fe386641SBram Moolenaar  endif
823e09ba7baSBram Moolenaar
824e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
825e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
826e09ba7baSBram Moolenaar  else
827e09ba7baSBram Moolenaar    let entry = {}
828e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
829fe386641SBram Moolenaar  endif
830e09ba7baSBram Moolenaar
831e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
832e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
833e09ba7baSBram Moolenaar  let entry['fname'] = fname
834e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
8351b9645deSBram Moolenaar
8361b9645deSBram Moolenaar  if bufloaded(fname)
8371b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
8381b9645deSBram Moolenaar  endif
8391b9645deSBram Moolenaarendfunc
8401b9645deSBram Moolenaar
8411b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
8421b9645deSBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname']
8431b9645deSBram Moolenaar  let a:entry['placed'] = 1
844e09ba7baSBram Moolenaarendfunc
845e09ba7baSBram Moolenaar
846e09ba7baSBram Moolenaar" Handle deleting a breakpoint
847e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
848e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
849e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
850e09ba7baSBram Moolenaar  if nr == 0
851e09ba7baSBram Moolenaar    return
852e09ba7baSBram Moolenaar  endif
8531b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
8541b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
8551b9645deSBram Moolenaar    if has_key(entry, 'placed')
856e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
8571b9645deSBram Moolenaar      unlet entry['placed']
8581b9645deSBram Moolenaar    endif
859e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
8601b9645deSBram Moolenaar  endif
861c572da5fSBram Moolenaarendfunc
8621b9645deSBram Moolenaar
8631b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
8641b9645deSBram Moolenaarfunc s:BufRead()
8651b9645deSBram Moolenaar  let fname = expand('<afile>:p')
8661b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
8671b9645deSBram Moolenaar    if entry['fname'] == fname
8681b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
8691b9645deSBram Moolenaar    endif
8701b9645deSBram Moolenaar  endfor
8711b9645deSBram Moolenaarendfunc
8721b9645deSBram Moolenaar
8731b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
8741b9645deSBram Moolenaarfunc s:BufUnloaded()
8751b9645deSBram Moolenaar  let fname = expand('<afile>:p')
8761b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
8771b9645deSBram Moolenaar    if entry['fname'] == fname
8781b9645deSBram Moolenaar      let entry['placed'] = 0
8791b9645deSBram Moolenaar    endif
8801b9645deSBram Moolenaar  endfor
8811b9645deSBram Moolenaarendfunc
8821b9645deSBram Moolenaar
883