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
101*4551c0a9SBram Moolenaar  let s:pid = 0
102b3623a38SBram Moolenaar
103b3307b5eSBram Moolenaar  " Uncomment this line to write logging in "debuglog".
104b3307b5eSBram Moolenaar  " call ch_logfile('debuglog', 'w')
105b3307b5eSBram Moolenaar
106b3307b5eSBram Moolenaar  let s:sourcewin = win_getid(winnr())
107fe386641SBram Moolenaar  let s:startsigncolumn = &signcolumn
108fe386641SBram Moolenaar
10924a98a0eSBram Moolenaar  let s:save_columns = 0
11024a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
11124a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
11238baa3e6SBram Moolenaar      let s:save_columns = &columns
11338baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
11424a98a0eSBram Moolenaar    endif
115b3307b5eSBram Moolenaar    let s:vertical = 1
11638baa3e6SBram Moolenaar  else
117b3307b5eSBram Moolenaar    let s:vertical = 0
11838baa3e6SBram Moolenaar  endif
11938baa3e6SBram Moolenaar
120b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
121b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
122b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
123b3307b5eSBram Moolenaar    let s:way = 'terminal'
124b3307b5eSBram Moolenaar  else
125b3307b5eSBram Moolenaar    let s:way = 'prompt'
126b3307b5eSBram Moolenaar  endif
127b3307b5eSBram Moolenaar
128b3307b5eSBram Moolenaar  if s:way == 'prompt'
129b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
130b3307b5eSBram Moolenaar  else
131b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
132b3307b5eSBram Moolenaar  endif
133b3307b5eSBram Moolenaarendfunc
134b3307b5eSBram Moolenaar
135b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
136b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
137fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
138b3307b5eSBram Moolenaar	\ 'term_name': 'debugged program',
139b3307b5eSBram Moolenaar	\ 'vertical': s:vertical,
140fe386641SBram Moolenaar	\ })
141fe386641SBram Moolenaar  if s:ptybuf == 0
142fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
143fe386641SBram Moolenaar    return
144fe386641SBram Moolenaar  endif
145fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
14645d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
147b3307b5eSBram Moolenaar  if s:vertical
14851b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
14951b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
15051b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
15151b0f370SBram Moolenaar  endif
152fe386641SBram Moolenaar
153fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
154fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
155fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
156fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
157fe386641SBram Moolenaar	\ 'hidden': 1,
158fe386641SBram Moolenaar	\ })
159fe386641SBram Moolenaar  if s:commbuf == 0
160fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
161fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
162fe386641SBram Moolenaar    return
163fe386641SBram Moolenaar  endif
164fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
165c572da5fSBram Moolenaar
166c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
167c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
16832c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
16932c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
17032c67ba7SBram Moolenaar
17132c67ba7SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
172b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
17360e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
174b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndTermDebug'),
175fe386641SBram Moolenaar	\ 'term_finish': 'close',
176c572da5fSBram Moolenaar	\ })
17760e73f2aSBram Moolenaar  if s:gdbbuf == 0
178fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
179fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
180fe386641SBram Moolenaar    exe 'bwipe! ' . s:commbuf
181fe386641SBram Moolenaar    return
182fe386641SBram Moolenaar  endif
18345d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
184fe386641SBram Moolenaar
18532c67ba7SBram Moolenaar  " Set arguments to be run
18632c67ba7SBram Moolenaar  if len(proc_args)
18732c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
18832c67ba7SBram Moolenaar  endif
18932c67ba7SBram Moolenaar
190fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
19160e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
19260e73f2aSBram Moolenaar
1933e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
1943e4b84d0SBram Moolenaar  " why the debugger doesn't work.
1953e4b84d0SBram Moolenaar  let try_count = 0
1963e4b84d0SBram Moolenaar  while 1
1973e4b84d0SBram Moolenaar    let response = ''
198b3623a38SBram Moolenaar    for lnum in range(1,200)
1993e4b84d0SBram Moolenaar      if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi '
2003e4b84d0SBram Moolenaar	let response = term_getline(s:gdbbuf, lnum + 1)
2013e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
202f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
2033e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:ptybuf
2043e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:commbuf
2053e4b84d0SBram Moolenaar	  return
2063e4b84d0SBram Moolenaar	endif
2073e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
2083e4b84d0SBram Moolenaar	  " Success!
2093e4b84d0SBram Moolenaar	  break
2103e4b84d0SBram Moolenaar	endif
2113e4b84d0SBram Moolenaar      endif
2123e4b84d0SBram Moolenaar    endfor
2133e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2143e4b84d0SBram Moolenaar      break
2153e4b84d0SBram Moolenaar    endif
2163e4b84d0SBram Moolenaar    let try_count += 1
2173e4b84d0SBram Moolenaar    if try_count > 100
2183e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2193e4b84d0SBram Moolenaar      break
2203e4b84d0SBram Moolenaar    endif
2213e4b84d0SBram Moolenaar    sleep 10m
2223e4b84d0SBram Moolenaar  endwhile
2233e4b84d0SBram Moolenaar
22460e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
22560e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
22660e73f2aSBram Moolenaar  " running.
22760e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
228b3307b5eSBram Moolenaar  " Older gdb uses a different command.
229b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
230e09ba7baSBram Moolenaar
231f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
232f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
233b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
234f3ba14ffSBram Moolenaar
235b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
236b3307b5eSBram Moolenaarendfunc
237b3307b5eSBram Moolenaar
238b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
239b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
240b3307b5eSBram Moolenaar  if s:vertical
241b3307b5eSBram Moolenaar    vertical new
242b3307b5eSBram Moolenaar  else
243b3307b5eSBram Moolenaar    new
244b3307b5eSBram Moolenaar  endif
245b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
246b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
247b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
248b3307b5eSBram Moolenaar  set buftype=prompt
249b3307b5eSBram Moolenaar  file gdb
250b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
251b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
252b3307b5eSBram Moolenaar
253b3307b5eSBram Moolenaar  if s:vertical
254b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
255b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
256b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
257b3307b5eSBram Moolenaar  endif
258b3307b5eSBram Moolenaar
259b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
260b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
261b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
262b3307b5eSBram Moolenaar
263b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
264b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
265b3307b5eSBram Moolenaar
266b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
267b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
268b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
269b3307b5eSBram Moolenaar	\ })
270b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
271b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
272b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
273b3307b5eSBram Moolenaar    return
274b3307b5eSBram Moolenaar  endif
275*4551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
276*4551c0a9SBram Moolenaar  set modified
277b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
278b3307b5eSBram Moolenaar
279b3307b5eSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only
280b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
281b3307b5eSBram Moolenaar  " target is running.
282b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
283b3307b5eSBram Moolenaar  " Older gdb uses a different command.
284b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
285b3307b5eSBram Moolenaar
286b3307b5eSBram Moolenaar  let s:ptybuf = 0
287b3307b5eSBram Moolenaar  if has('win32')
288b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
289b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
290b3307b5eSBram Moolenaar  elseif has('terminal')
291b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
292b3307b5eSBram Moolenaar    " gdb window.
293b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
294b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
295b3307b5eSBram Moolenaar	  \ })
296b3307b5eSBram Moolenaar    if s:ptybuf == 0
297b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
298b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
299b3307b5eSBram Moolenaar      return
300b3307b5eSBram Moolenaar    endif
301b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
302b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
303b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
304b3307b5eSBram Moolenaar
305b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
306b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
307b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
308b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
309b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
310b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
311b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
312b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
313b3307b5eSBram Moolenaar  else
314b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
315b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
316b3307b5eSBram Moolenaar  endif
317b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
318b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
319b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
320b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
321b3307b5eSBram Moolenaar
322b3307b5eSBram Moolenaar  " Set arguments to be run
323b3307b5eSBram Moolenaar  if len(proc_args)
324b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
325b3307b5eSBram Moolenaar  endif
326b3307b5eSBram Moolenaar
327b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
328b3307b5eSBram Moolenaar  startinsert
329b3307b5eSBram Moolenaarendfunc
330b3307b5eSBram Moolenaar
331b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
33238baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
33338baa3e6SBram Moolenaar  " There can be only one.
33438baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
33538baa3e6SBram Moolenaar
33645d5f26dSBram Moolenaar  " Install debugger commands in the text window.
337b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
338e09ba7baSBram Moolenaar  call s:InstallCommands()
33945d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
340e09ba7baSBram Moolenaar
34151b0f370SBram Moolenaar  " Enable showing a balloon with eval info
342246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
343246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
34451b0f370SBram Moolenaar    if has("balloon_eval")
34551b0f370SBram Moolenaar      set ballooneval
346246fe03dSBram Moolenaar    endif
34751b0f370SBram Moolenaar    if has("balloon_eval_term")
34851b0f370SBram Moolenaar      set balloonevalterm
34951b0f370SBram Moolenaar    endif
35051b0f370SBram Moolenaar  endif
35151b0f370SBram Moolenaar
352de1a8314SBram Moolenaar  " Contains breakpoints that have been placed, key is the number.
353e09ba7baSBram Moolenaar  let s:breakpoints = {}
3541b9645deSBram Moolenaar
3551b9645deSBram Moolenaar  augroup TermDebug
3561b9645deSBram Moolenaar    au BufRead * call s:BufRead()
3571b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
358f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
3591b9645deSBram Moolenaar  augroup END
36032c67ba7SBram Moolenaar
361b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
362b3307b5eSBram Moolenaar  " window.
36332c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
36432c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
36532c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
36632c67ba7SBram Moolenaar  endif
367c572da5fSBram Moolenaarendfunc
368c572da5fSBram Moolenaar
369b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
370b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
371b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
372b3307b5eSBram Moolenaar  if s:way == 'prompt'
373b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
374b3307b5eSBram Moolenaar  else
375b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
376b3307b5eSBram Moolenaar  endif
377b3307b5eSBram Moolenaarendfunc
378b3307b5eSBram Moolenaar
379b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
380b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
381b3307b5eSBram Moolenaar  if s:way == 'prompt'
382b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
383b3307b5eSBram Moolenaar  else
384b3307b5eSBram Moolenaar    let do_continue = 0
385b3307b5eSBram Moolenaar    if !s:stopped
386b3307b5eSBram Moolenaar      let do_continue = 1
387b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
388b3307b5eSBram Moolenaar      sleep 10m
389b3307b5eSBram Moolenaar    endif
390b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
391b3307b5eSBram Moolenaar    if do_continue
392b3307b5eSBram Moolenaar      Continue
393b3307b5eSBram Moolenaar    endif
394b3307b5eSBram Moolenaar  endif
395b3307b5eSBram Moolenaarendfunc
396b3307b5eSBram Moolenaar
397b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
398b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
399b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
400b3307b5eSBram Moolenaarendfunc
401b3307b5eSBram Moolenaar
402*4551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
403*4551c0a9SBram Moolenaar" breakpoint.
404b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
405*4551c0a9SBram Moolenaar  if s:pid == 0
406*4551c0a9SBram Moolenaar    echoerr 'Cannot interrupt gdb, did not find a process ID'
407*4551c0a9SBram Moolenaar  else
408b3307b5eSBram Moolenaar    call ch_log('Interrupting gdb')
409*4551c0a9SBram Moolenaar    " Using job_stop(s:gdbjob, 'int') does not work.
410*4551c0a9SBram Moolenaar    call debugbreak(s:pid)
411*4551c0a9SBram Moolenaar  endif
412b3307b5eSBram Moolenaarendfunc
413b3307b5eSBram Moolenaar
414b3307b5eSBram Moolenaar" Function called when gdb outputs text.
415b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
416b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
417b3307b5eSBram Moolenaar
418b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
419b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
420a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
421b3307b5eSBram Moolenaar    return
422b3307b5eSBram Moolenaar  endif
423b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
424b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
425b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
426b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
427b3307b5eSBram Moolenaar      unlet s:evalexpr
428b3307b5eSBram Moolenaar      return
429b3307b5eSBram Moolenaar    endif
430b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
431b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
432b3307b5eSBram Moolenaar  else
433b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
434b3307b5eSBram Moolenaar    return
435b3307b5eSBram Moolenaar  endif
436b3307b5eSBram Moolenaar
437b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
438b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
439b3307b5eSBram Moolenaar
440b3307b5eSBram Moolenaar  " Add the output above the current prompt.
441b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
442*4551c0a9SBram Moolenaar  set modified
443b3307b5eSBram Moolenaar
444b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
445b3307b5eSBram Moolenaarendfunc
446b3307b5eSBram Moolenaar
447b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
448b3307b5eSBram Moolenaar" to the next ", unescaping characters.
449b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
450b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
451a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
452b3307b5eSBram Moolenaar    return
453b3307b5eSBram Moolenaar  endif
454b3307b5eSBram Moolenaar  let result = ''
455b3307b5eSBram Moolenaar  let i = 1
456b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
457b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
458b3307b5eSBram Moolenaar      let i += 1
459b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
460b3307b5eSBram Moolenaar	" drop \n
461b3307b5eSBram Moolenaar	let i += 1
462b3307b5eSBram Moolenaar	continue
463b3307b5eSBram Moolenaar      endif
464b3307b5eSBram Moolenaar    endif
465b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
466b3307b5eSBram Moolenaar    let i += 1
467b3307b5eSBram Moolenaar  endwhile
468b3307b5eSBram Moolenaar  return result
469b3307b5eSBram Moolenaarendfunc
470b3307b5eSBram Moolenaar
471a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
472a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
473a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
474a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
475a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
476a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
477a15b0a93SBram Moolenaar  endif
478a15b0a93SBram Moolenaar  return name
479a15b0a93SBram Moolenaarendfunc
480a15b0a93SBram Moolenaar
481b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
482fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
483b3623a38SBram Moolenaar  unlet s:gdbwin
484e09ba7baSBram Moolenaar
485b3307b5eSBram Moolenaar  call s:EndDebugCommon()
486b3307b5eSBram Moolenaarendfunc
487b3307b5eSBram Moolenaar
488b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
489e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
490e09ba7baSBram Moolenaar
491b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
492b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
493b3307b5eSBram Moolenaar  endif
494b3307b5eSBram Moolenaar
495b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
496e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
497e09ba7baSBram Moolenaar  call s:DeleteCommands()
498e09ba7baSBram Moolenaar
499e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
500b3307b5eSBram Moolenaar
50138baa3e6SBram Moolenaar  if s:save_columns > 0
50238baa3e6SBram Moolenaar    let &columns = s:save_columns
50338baa3e6SBram Moolenaar  endif
5041b9645deSBram Moolenaar
505246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
506246fe03dSBram Moolenaar    set balloonexpr=
50751b0f370SBram Moolenaar    if has("balloon_eval")
50851b0f370SBram Moolenaar      set noballooneval
509246fe03dSBram Moolenaar    endif
51051b0f370SBram Moolenaar    if has("balloon_eval_term")
51151b0f370SBram Moolenaar      set noballoonevalterm
51251b0f370SBram Moolenaar    endif
51351b0f370SBram Moolenaar  endif
51451b0f370SBram Moolenaar
5151b9645deSBram Moolenaar  au! TermDebug
516fe386641SBram Moolenaarendfunc
517fe386641SBram Moolenaar
518b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
519b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
520b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
521*4551c0a9SBram Moolenaar  set nomodified
522b3307b5eSBram Moolenaar  close
523b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
524b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
525b3307b5eSBram Moolenaar  endif
526b3307b5eSBram Moolenaar
527b3307b5eSBram Moolenaar  call s:EndDebugCommon()
528b3307b5eSBram Moolenaar  unlet s:gdbwin
529b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
530b3307b5eSBram Moolenaarendfunc
531b3307b5eSBram Moolenaar
532fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
533fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
534fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
535fe386641SBram Moolenaar
536fe386641SBram Moolenaar  for msg in msgs
537fe386641SBram Moolenaar    " remove prefixed NL
538fe386641SBram Moolenaar    if msg[0] == "\n"
539fe386641SBram Moolenaar      let msg = msg[1:]
540fe386641SBram Moolenaar    endif
541fe386641SBram Moolenaar    if msg != ''
5421b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
543e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
54445d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
545e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
546e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
547e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
548*4551c0a9SBram Moolenaar      elseif msg =~ '^=thread-group-started'
549*4551c0a9SBram Moolenaar	call s:HandleProgramRun(msg)
55045d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
55145d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
55245d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
55345d5f26dSBram Moolenaar	call s:HandleError(msg)
554e09ba7baSBram Moolenaar      endif
555e09ba7baSBram Moolenaar    endif
556e09ba7baSBram Moolenaar  endfor
557e09ba7baSBram Moolenaarendfunc
558e09ba7baSBram Moolenaar
559e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
560e09ba7baSBram Moolenaarfunc s:InstallCommands()
561e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
56271137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
563e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
56445d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
565e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
56660e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
56760e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
56860e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
569b3307b5eSBram Moolenaar
570b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
571b3307b5eSBram Moolenaar  if s:way == 'prompt'
572b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
573b3307b5eSBram Moolenaar  else
574b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
575b3307b5eSBram Moolenaar  endif
576b3307b5eSBram Moolenaar
57745d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
57845d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
57945d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
580b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
58171137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
58245d5f26dSBram Moolenaar
58345d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
58445d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
5851b9645deSBram Moolenaar
586f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
58771137fedSBram Moolenaar    call s:InstallWinbar()
58871137fedSBram Moolenaar
58971137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
59071137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
59171137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
59271137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
59371137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
59471137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
59571137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
59671137fedSBram Moolenaar    endif
59771137fedSBram Moolenaar  endif
59871137fedSBram Moolenaarendfunc
59971137fedSBram Moolenaar
60071137fedSBram Moolenaarlet s:winbar_winids = []
60171137fedSBram Moolenaar
60271137fedSBram Moolenaar" Install the window toolbar in the current window.
60371137fedSBram Moolenaarfunc s:InstallWinbar()
604c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
60524a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
60624a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
60724a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
60824a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
60960e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
61024a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
61171137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
612c4b533e1SBram Moolenaar  endif
613e09ba7baSBram Moolenaarendfunc
614e09ba7baSBram Moolenaar
615e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
616e09ba7baSBram Moolenaarfunc s:DeleteCommands()
617e09ba7baSBram Moolenaar  delcommand Break
61871137fedSBram Moolenaar  delcommand Clear
619e09ba7baSBram Moolenaar  delcommand Step
62045d5f26dSBram Moolenaar  delcommand Over
621e09ba7baSBram Moolenaar  delcommand Finish
62260e73f2aSBram Moolenaar  delcommand Run
62360e73f2aSBram Moolenaar  delcommand Arguments
62460e73f2aSBram Moolenaar  delcommand Stop
625e09ba7baSBram Moolenaar  delcommand Continue
62645d5f26dSBram Moolenaar  delcommand Evaluate
62745d5f26dSBram Moolenaar  delcommand Gdb
62845d5f26dSBram Moolenaar  delcommand Program
629b3623a38SBram Moolenaar  delcommand Source
63071137fedSBram Moolenaar  delcommand Winbar
63145d5f26dSBram Moolenaar
63245d5f26dSBram Moolenaar  nunmap K
6331b9645deSBram Moolenaar
6341b9645deSBram Moolenaar  if has('menu')
63571137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
63671137fedSBram Moolenaar    let curwinid = win_getid(winnr())
63771137fedSBram Moolenaar    for winid in s:winbar_winids
63871137fedSBram Moolenaar      if win_gotoid(winid)
6391b9645deSBram Moolenaar	aunmenu WinBar.Step
6401b9645deSBram Moolenaar	aunmenu WinBar.Next
6411b9645deSBram Moolenaar	aunmenu WinBar.Finish
6421b9645deSBram Moolenaar	aunmenu WinBar.Cont
64360e73f2aSBram Moolenaar	aunmenu WinBar.Stop
6441b9645deSBram Moolenaar	aunmenu WinBar.Eval
6451b9645deSBram Moolenaar      endif
64671137fedSBram Moolenaar    endfor
64771137fedSBram Moolenaar    call win_gotoid(curwinid)
64871137fedSBram Moolenaar    let s:winbar_winids = []
64971137fedSBram Moolenaar
65071137fedSBram Moolenaar    if exists('s:saved_mousemodel')
65171137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
65271137fedSBram Moolenaar      unlet s:saved_mousemodel
65371137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
65471137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
65571137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
65671137fedSBram Moolenaar      aunmenu PopUp.Evaluate
65771137fedSBram Moolenaar    endif
65871137fedSBram Moolenaar  endif
6591b9645deSBram Moolenaar
66045d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
66145d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
66245d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
66345d5f26dSBram Moolenaar  endfor
66445d5f26dSBram Moolenaar  unlet s:breakpoints
665a15b0a93SBram Moolenaar
666a15b0a93SBram Moolenaar  sign undefine debugPC
667a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
668a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
669a15b0a93SBram Moolenaar  endfor
670*4551c0a9SBram Moolenaar  let s:BreakpointSigns = []
671e09ba7baSBram Moolenaarendfunc
672e09ba7baSBram Moolenaar
673e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
674e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
67560e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
67660e73f2aSBram Moolenaar  " Interrupt to make it work.
67760e73f2aSBram Moolenaar  let do_continue = 0
67860e73f2aSBram Moolenaar  if !s:stopped
67960e73f2aSBram Moolenaar    let do_continue = 1
680b3307b5eSBram Moolenaar    if s:way == 'prompt'
681*4551c0a9SBram Moolenaar      call s:PromptInterrupt()
682b3307b5eSBram Moolenaar    else
68360e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
684b3307b5eSBram Moolenaar    endif
68560e73f2aSBram Moolenaar    sleep 10m
68660e73f2aSBram Moolenaar  endif
687a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
688a15b0a93SBram Moolenaar  call s:SendCommand('-break-insert '
689a15b0a93SBram Moolenaar	\ . fnameescape(expand('%:p')) . ':' . line('.'))
69060e73f2aSBram Moolenaar  if do_continue
69160e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
69260e73f2aSBram Moolenaar  endif
693e09ba7baSBram Moolenaarendfunc
694e09ba7baSBram Moolenaar
69571137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
69671137fedSBram Moolenaarfunc s:ClearBreakpoint()
697e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
698e09ba7baSBram Moolenaar  let lnum = line('.')
699e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
700e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
701b3307b5eSBram Moolenaar      call s:SendCommand('-break-delete ' . key)
702e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
703e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
704e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
705e09ba7baSBram Moolenaar      break
706e09ba7baSBram Moolenaar    endif
707e09ba7baSBram Moolenaar  endfor
708e09ba7baSBram Moolenaarendfunc
709e09ba7baSBram Moolenaar
71060e73f2aSBram Moolenaarfunc s:Run(args)
71160e73f2aSBram Moolenaar  if a:args != ''
71260e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
71360e73f2aSBram Moolenaar  endif
71460e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
71560e73f2aSBram Moolenaarendfunc
71660e73f2aSBram Moolenaar
71751b0f370SBram Moolenaarfunc s:SendEval(expr)
71851b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
71951b0f370SBram Moolenaar  let s:evalexpr = a:expr
72051b0f370SBram Moolenaarendfunc
72151b0f370SBram Moolenaar
72245d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
72345d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
72445d5f26dSBram Moolenaar  if a:arg != ''
72545d5f26dSBram Moolenaar    let expr = a:arg
72645d5f26dSBram Moolenaar  elseif a:range == 2
72745d5f26dSBram Moolenaar    let pos = getcurpos()
72845d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
72945d5f26dSBram Moolenaar    let regt = getregtype('v')
73045d5f26dSBram Moolenaar    normal! gv"vy
73145d5f26dSBram Moolenaar    let expr = @v
73245d5f26dSBram Moolenaar    call setpos('.', pos)
73345d5f26dSBram Moolenaar    call setreg('v', reg, regt)
73445d5f26dSBram Moolenaar  else
73545d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
73645d5f26dSBram Moolenaar  endif
73722f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
73851b0f370SBram Moolenaar  call s:SendEval(expr)
73945d5f26dSBram Moolenaarendfunc
74045d5f26dSBram Moolenaar
74122f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
74251b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
74351b0f370SBram Moolenaar
74445d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
74545d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
7461b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
7471b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
74851b0f370SBram Moolenaar  if s:evalFromBalloonExpr
74951b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
75051b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
75151b0f370SBram Moolenaar    else
75251b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
75351b0f370SBram Moolenaar    endif
75451b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
75551b0f370SBram Moolenaar  else
7561b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
75751b0f370SBram Moolenaar  endif
7581b9645deSBram Moolenaar
7597f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
7601b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
76122f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
76251b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
76351b0f370SBram Moolenaar  else
76451b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
7651b9645deSBram Moolenaar  endif
76645d5f26dSBram Moolenaarendfunc
76745d5f26dSBram Moolenaar
76851b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
76951b0f370SBram Moolenaar" if there is any.
77051b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
771b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
772b3307b5eSBram Moolenaar    return
773b3307b5eSBram Moolenaar  endif
774b3307b5eSBram Moolenaar  if !s:stopped
775b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
776b3307b5eSBram Moolenaar    " mouse triggers a balloon.
77751b0f370SBram Moolenaar    return
77851b0f370SBram Moolenaar  endif
77951b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
78051b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
78122f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
78222f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
78351b0f370SBram Moolenaar  return ''
78451b0f370SBram Moolenaarendfunc
78551b0f370SBram Moolenaar
78645d5f26dSBram Moolenaar" Handle an error.
78745d5f26dSBram Moolenaarfunc s:HandleError(msg)
78822f1d0e3SBram Moolenaar  if s:ignoreEvalError
78951b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
79022f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
79122f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
79251b0f370SBram Moolenaar    return
79351b0f370SBram Moolenaar  endif
79445d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
79545d5f26dSBram Moolenaarendfunc
79645d5f26dSBram Moolenaar
797b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
798b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
799c4b533e1SBram Moolenaar    new
800b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
801c4b533e1SBram Moolenaar    call s:InstallWinbar()
802c4b533e1SBram Moolenaar  endif
803c4b533e1SBram Moolenaarendfunc
804c4b533e1SBram Moolenaar
805e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
806e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
807e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
808fe386641SBram Moolenaar  let wid = win_getid(winnr())
809fe386641SBram Moolenaar
81060e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
811*4551c0a9SBram Moolenaar    call ch_log('program stopped')
81260e73f2aSBram Moolenaar    let s:stopped = 1
81360e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
814*4551c0a9SBram Moolenaar    call ch_log('program running')
81560e73f2aSBram Moolenaar    let s:stopped = 0
81660e73f2aSBram Moolenaar  endif
81760e73f2aSBram Moolenaar
818a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
819a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
820a15b0a93SBram Moolenaar  else
821a15b0a93SBram Moolenaar    let fname = ''
822a15b0a93SBram Moolenaar  endif
8231b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
824e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
825fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
826*4551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
8271b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
828fe386641SBram Moolenaar	if &modified
829fe386641SBram Moolenaar	  " TODO: find existing window
830fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
831b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
832c4b533e1SBram Moolenaar	  call s:InstallWinbar()
833fe386641SBram Moolenaar	else
834fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
835fe386641SBram Moolenaar	endif
836fe386641SBram Moolenaar      endif
837fe386641SBram Moolenaar      exe lnum
83801164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
8391b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
840fe386641SBram Moolenaar      setlocal signcolumn=yes
841fe386641SBram Moolenaar    endif
842*4551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
843fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
844fe386641SBram Moolenaar  endif
845fe386641SBram Moolenaar
846fe386641SBram Moolenaar  call win_gotoid(wid)
847e09ba7baSBram Moolenaarendfunc
848e09ba7baSBram Moolenaar
849de1a8314SBram Moolenaarlet s:BreakpointSigns = []
850a15b0a93SBram Moolenaar
851a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr)
852de1a8314SBram Moolenaar  if index(s:BreakpointSigns, a:nr) == -1
853de1a8314SBram Moolenaar    call add(s:BreakpointSigns, a:nr)
854de1a8314SBram Moolenaar    exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint"
855de1a8314SBram Moolenaar  endif
856de1a8314SBram Moolenaarendfunc
857de1a8314SBram Moolenaar
858e09ba7baSBram Moolenaar" Handle setting a breakpoint
859e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
860e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
861e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
862e09ba7baSBram Moolenaar  if nr == 0
863e09ba7baSBram Moolenaar    return
864fe386641SBram Moolenaar  endif
865de1a8314SBram Moolenaar  call s:CreateBreakpoint(nr)
866e09ba7baSBram Moolenaar
867e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
868e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
869e09ba7baSBram Moolenaar  else
870e09ba7baSBram Moolenaar    let entry = {}
871e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
872fe386641SBram Moolenaar  endif
873e09ba7baSBram Moolenaar
874a15b0a93SBram Moolenaar  let fname = s:GetFullname(a:msg)
875e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
876e09ba7baSBram Moolenaar  let entry['fname'] = fname
877e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
8781b9645deSBram Moolenaar
8791b9645deSBram Moolenaar  if bufloaded(fname)
8801b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
8811b9645deSBram Moolenaar  endif
8821b9645deSBram Moolenaarendfunc
8831b9645deSBram Moolenaar
8841b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
885de1a8314SBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname']
8861b9645deSBram Moolenaar  let a:entry['placed'] = 1
887e09ba7baSBram Moolenaarendfunc
888e09ba7baSBram Moolenaar
889e09ba7baSBram Moolenaar" Handle deleting a breakpoint
890e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
891e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
892e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
893e09ba7baSBram Moolenaar  if nr == 0
894e09ba7baSBram Moolenaar    return
895e09ba7baSBram Moolenaar  endif
8961b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
8971b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
8981b9645deSBram Moolenaar    if has_key(entry, 'placed')
899e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
9001b9645deSBram Moolenaar      unlet entry['placed']
9011b9645deSBram Moolenaar    endif
902e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
9031b9645deSBram Moolenaar  endif
904c572da5fSBram Moolenaarendfunc
9051b9645deSBram Moolenaar
906*4551c0a9SBram Moolenaar" Handle the debugged program starting to run.
907*4551c0a9SBram Moolenaar" Will store the process ID in s:pid
908*4551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
909*4551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
910*4551c0a9SBram Moolenaar  if nr == 0
911*4551c0a9SBram Moolenaar    return
912*4551c0a9SBram Moolenaar  endif
913*4551c0a9SBram Moolenaar  let s:pid = nr
914*4551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
915*4551c0a9SBram Moolenaarendfunc
916*4551c0a9SBram Moolenaar
9171b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
9181b9645deSBram Moolenaarfunc s:BufRead()
9191b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9201b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9211b9645deSBram Moolenaar    if entry['fname'] == fname
9221b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
9231b9645deSBram Moolenaar    endif
9241b9645deSBram Moolenaar  endfor
9251b9645deSBram Moolenaarendfunc
9261b9645deSBram Moolenaar
9271b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
9281b9645deSBram Moolenaarfunc s:BufUnloaded()
9291b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9301b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9311b9645deSBram Moolenaar    if entry['fname'] == fname
9321b9645deSBram Moolenaar      let entry['placed'] = 0
9331b9645deSBram Moolenaar    endif
9341b9645deSBram Moolenaar  endfor
9351b9645deSBram Moolenaarendfunc
936