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
1014551c0a9SBram 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
2754551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
2764551c0a9SBram 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
4024551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
4034551c0a9SBram Moolenaar" breakpoint.
404b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
4052ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
4062ed890f1SBram Moolenaar  if has('win32')
4072ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
4082ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
4094551c0a9SBram Moolenaar    if s:pid == 0
4104551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
4114551c0a9SBram Moolenaar    else
4124551c0a9SBram Moolenaar      call debugbreak(s:pid)
4134551c0a9SBram Moolenaar    endif
4142ed890f1SBram Moolenaar  else
4152ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
4162ed890f1SBram Moolenaar  endif
417b3307b5eSBram Moolenaarendfunc
418b3307b5eSBram Moolenaar
419b3307b5eSBram Moolenaar" Function called when gdb outputs text.
420b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
421b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
422b3307b5eSBram Moolenaar
423b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
424b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
425a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
426b3307b5eSBram Moolenaar    return
427b3307b5eSBram Moolenaar  endif
428b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
429b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
430b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
431b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
432b3307b5eSBram Moolenaar      unlet s:evalexpr
433b3307b5eSBram Moolenaar      return
434b3307b5eSBram Moolenaar    endif
435b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
436b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
437b3307b5eSBram Moolenaar  else
438b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
439b3307b5eSBram Moolenaar    return
440b3307b5eSBram Moolenaar  endif
441b3307b5eSBram Moolenaar
442b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
443b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
444b3307b5eSBram Moolenaar
445b3307b5eSBram Moolenaar  " Add the output above the current prompt.
446b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
4474551c0a9SBram Moolenaar  set modified
448b3307b5eSBram Moolenaar
449b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
450b3307b5eSBram Moolenaarendfunc
451b3307b5eSBram Moolenaar
452b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
453b3307b5eSBram Moolenaar" to the next ", unescaping characters.
454b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
455b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
456a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
457b3307b5eSBram Moolenaar    return
458b3307b5eSBram Moolenaar  endif
459b3307b5eSBram Moolenaar  let result = ''
460b3307b5eSBram Moolenaar  let i = 1
461b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
462b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
463b3307b5eSBram Moolenaar      let i += 1
464b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
465b3307b5eSBram Moolenaar	" drop \n
466b3307b5eSBram Moolenaar	let i += 1
467b3307b5eSBram Moolenaar	continue
468b3307b5eSBram Moolenaar      endif
469b3307b5eSBram Moolenaar    endif
470b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
471b3307b5eSBram Moolenaar    let i += 1
472b3307b5eSBram Moolenaar  endwhile
473b3307b5eSBram Moolenaar  return result
474b3307b5eSBram Moolenaarendfunc
475b3307b5eSBram Moolenaar
476a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
477a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
478a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
479a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
480a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
481a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
482a15b0a93SBram Moolenaar  endif
483a15b0a93SBram Moolenaar  return name
484a15b0a93SBram Moolenaarendfunc
485a15b0a93SBram Moolenaar
486b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
487fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
488b3623a38SBram Moolenaar  unlet s:gdbwin
489e09ba7baSBram Moolenaar
490b3307b5eSBram Moolenaar  call s:EndDebugCommon()
491b3307b5eSBram Moolenaarendfunc
492b3307b5eSBram Moolenaar
493b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
494e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
495e09ba7baSBram Moolenaar
496b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
497b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
498b3307b5eSBram Moolenaar  endif
499b3307b5eSBram Moolenaar
500b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
501e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
502e09ba7baSBram Moolenaar  call s:DeleteCommands()
503e09ba7baSBram Moolenaar
504e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
505b3307b5eSBram Moolenaar
50638baa3e6SBram Moolenaar  if s:save_columns > 0
50738baa3e6SBram Moolenaar    let &columns = s:save_columns
50838baa3e6SBram Moolenaar  endif
5091b9645deSBram Moolenaar
510246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
511246fe03dSBram Moolenaar    set balloonexpr=
51251b0f370SBram Moolenaar    if has("balloon_eval")
51351b0f370SBram Moolenaar      set noballooneval
514246fe03dSBram Moolenaar    endif
51551b0f370SBram Moolenaar    if has("balloon_eval_term")
51651b0f370SBram Moolenaar      set noballoonevalterm
51751b0f370SBram Moolenaar    endif
51851b0f370SBram Moolenaar  endif
51951b0f370SBram Moolenaar
5201b9645deSBram Moolenaar  au! TermDebug
521fe386641SBram Moolenaarendfunc
522fe386641SBram Moolenaar
523b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
524b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
525b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
5264551c0a9SBram Moolenaar  set nomodified
527b3307b5eSBram Moolenaar  close
528b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
529b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
530b3307b5eSBram Moolenaar  endif
531b3307b5eSBram Moolenaar
532b3307b5eSBram Moolenaar  call s:EndDebugCommon()
533b3307b5eSBram Moolenaar  unlet s:gdbwin
534b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
535b3307b5eSBram Moolenaarendfunc
536b3307b5eSBram Moolenaar
537fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
538fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
539fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
540fe386641SBram Moolenaar
541fe386641SBram Moolenaar  for msg in msgs
542fe386641SBram Moolenaar    " remove prefixed NL
543fe386641SBram Moolenaar    if msg[0] == "\n"
544fe386641SBram Moolenaar      let msg = msg[1:]
545fe386641SBram Moolenaar    endif
546fe386641SBram Moolenaar    if msg != ''
5471b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
548e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
54945d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
550e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
551e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
552e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
5534551c0a9SBram Moolenaar      elseif msg =~ '^=thread-group-started'
5544551c0a9SBram Moolenaar	call s:HandleProgramRun(msg)
55545d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
55645d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
55745d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
55845d5f26dSBram Moolenaar	call s:HandleError(msg)
559e09ba7baSBram Moolenaar      endif
560e09ba7baSBram Moolenaar    endif
561e09ba7baSBram Moolenaar  endfor
562e09ba7baSBram Moolenaarendfunc
563e09ba7baSBram Moolenaar
564e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
565e09ba7baSBram Moolenaarfunc s:InstallCommands()
566e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
56771137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
568e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
56945d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
570e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
57160e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
57260e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
57360e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
574b3307b5eSBram Moolenaar
575b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
576b3307b5eSBram Moolenaar  if s:way == 'prompt'
577b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
578b3307b5eSBram Moolenaar  else
579b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
580b3307b5eSBram Moolenaar  endif
581b3307b5eSBram Moolenaar
58245d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
58345d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
58445d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
585b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
58671137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
58745d5f26dSBram Moolenaar
58845d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
58945d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
5901b9645deSBram Moolenaar
591f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
59271137fedSBram Moolenaar    call s:InstallWinbar()
59371137fedSBram Moolenaar
59471137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
59571137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
59671137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
59771137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
59871137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
59971137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
60071137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
60171137fedSBram Moolenaar    endif
60271137fedSBram Moolenaar  endif
60371137fedSBram Moolenaarendfunc
60471137fedSBram Moolenaar
60571137fedSBram Moolenaarlet s:winbar_winids = []
60671137fedSBram Moolenaar
60771137fedSBram Moolenaar" Install the window toolbar in the current window.
60871137fedSBram Moolenaarfunc s:InstallWinbar()
609c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
61024a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
61124a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
61224a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
61324a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
61460e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
61524a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
61671137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
617c4b533e1SBram Moolenaar  endif
618e09ba7baSBram Moolenaarendfunc
619e09ba7baSBram Moolenaar
620e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
621e09ba7baSBram Moolenaarfunc s:DeleteCommands()
622e09ba7baSBram Moolenaar  delcommand Break
62371137fedSBram Moolenaar  delcommand Clear
624e09ba7baSBram Moolenaar  delcommand Step
62545d5f26dSBram Moolenaar  delcommand Over
626e09ba7baSBram Moolenaar  delcommand Finish
62760e73f2aSBram Moolenaar  delcommand Run
62860e73f2aSBram Moolenaar  delcommand Arguments
62960e73f2aSBram Moolenaar  delcommand Stop
630e09ba7baSBram Moolenaar  delcommand Continue
63145d5f26dSBram Moolenaar  delcommand Evaluate
63245d5f26dSBram Moolenaar  delcommand Gdb
63345d5f26dSBram Moolenaar  delcommand Program
634b3623a38SBram Moolenaar  delcommand Source
63571137fedSBram Moolenaar  delcommand Winbar
63645d5f26dSBram Moolenaar
63745d5f26dSBram Moolenaar  nunmap K
6381b9645deSBram Moolenaar
6391b9645deSBram Moolenaar  if has('menu')
64071137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
64171137fedSBram Moolenaar    let curwinid = win_getid(winnr())
64271137fedSBram Moolenaar    for winid in s:winbar_winids
64371137fedSBram Moolenaar      if win_gotoid(winid)
6441b9645deSBram Moolenaar	aunmenu WinBar.Step
6451b9645deSBram Moolenaar	aunmenu WinBar.Next
6461b9645deSBram Moolenaar	aunmenu WinBar.Finish
6471b9645deSBram Moolenaar	aunmenu WinBar.Cont
64860e73f2aSBram Moolenaar	aunmenu WinBar.Stop
6491b9645deSBram Moolenaar	aunmenu WinBar.Eval
6501b9645deSBram Moolenaar      endif
65171137fedSBram Moolenaar    endfor
65271137fedSBram Moolenaar    call win_gotoid(curwinid)
65371137fedSBram Moolenaar    let s:winbar_winids = []
65471137fedSBram Moolenaar
65571137fedSBram Moolenaar    if exists('s:saved_mousemodel')
65671137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
65771137fedSBram Moolenaar      unlet s:saved_mousemodel
65871137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
65971137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
66071137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
66171137fedSBram Moolenaar      aunmenu PopUp.Evaluate
66271137fedSBram Moolenaar    endif
66371137fedSBram Moolenaar  endif
6641b9645deSBram Moolenaar
66545d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
66645d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
66745d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
66845d5f26dSBram Moolenaar  endfor
66945d5f26dSBram Moolenaar  unlet s:breakpoints
670a15b0a93SBram Moolenaar
671a15b0a93SBram Moolenaar  sign undefine debugPC
672a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
673a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
674a15b0a93SBram Moolenaar  endfor
6754551c0a9SBram Moolenaar  let s:BreakpointSigns = []
676e09ba7baSBram Moolenaarendfunc
677e09ba7baSBram Moolenaar
678e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
679e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
68060e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
68160e73f2aSBram Moolenaar  " Interrupt to make it work.
68260e73f2aSBram Moolenaar  let do_continue = 0
68360e73f2aSBram Moolenaar  if !s:stopped
68460e73f2aSBram Moolenaar    let do_continue = 1
685b3307b5eSBram Moolenaar    if s:way == 'prompt'
6864551c0a9SBram Moolenaar      call s:PromptInterrupt()
687b3307b5eSBram Moolenaar    else
68860e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
689b3307b5eSBram Moolenaar    endif
69060e73f2aSBram Moolenaar    sleep 10m
69160e73f2aSBram Moolenaar  endif
692a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
693a15b0a93SBram Moolenaar  call s:SendCommand('-break-insert '
694a15b0a93SBram Moolenaar	\ . fnameescape(expand('%:p')) . ':' . line('.'))
69560e73f2aSBram Moolenaar  if do_continue
69660e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
69760e73f2aSBram Moolenaar  endif
698e09ba7baSBram Moolenaarendfunc
699e09ba7baSBram Moolenaar
70071137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
70171137fedSBram Moolenaarfunc s:ClearBreakpoint()
702e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
703e09ba7baSBram Moolenaar  let lnum = line('.')
704e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
705e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
706b3307b5eSBram Moolenaar      call s:SendCommand('-break-delete ' . key)
707e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
708e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
709e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
710e09ba7baSBram Moolenaar      break
711e09ba7baSBram Moolenaar    endif
712e09ba7baSBram Moolenaar  endfor
713e09ba7baSBram Moolenaarendfunc
714e09ba7baSBram Moolenaar
71560e73f2aSBram Moolenaarfunc s:Run(args)
71660e73f2aSBram Moolenaar  if a:args != ''
71760e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
71860e73f2aSBram Moolenaar  endif
71960e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
72060e73f2aSBram Moolenaarendfunc
72160e73f2aSBram Moolenaar
72251b0f370SBram Moolenaarfunc s:SendEval(expr)
72351b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
72451b0f370SBram Moolenaar  let s:evalexpr = a:expr
72551b0f370SBram Moolenaarendfunc
72651b0f370SBram Moolenaar
72745d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
72845d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
72945d5f26dSBram Moolenaar  if a:arg != ''
73045d5f26dSBram Moolenaar    let expr = a:arg
73145d5f26dSBram Moolenaar  elseif a:range == 2
73245d5f26dSBram Moolenaar    let pos = getcurpos()
73345d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
73445d5f26dSBram Moolenaar    let regt = getregtype('v')
73545d5f26dSBram Moolenaar    normal! gv"vy
73645d5f26dSBram Moolenaar    let expr = @v
73745d5f26dSBram Moolenaar    call setpos('.', pos)
73845d5f26dSBram Moolenaar    call setreg('v', reg, regt)
73945d5f26dSBram Moolenaar  else
74045d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
74145d5f26dSBram Moolenaar  endif
74222f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
74351b0f370SBram Moolenaar  call s:SendEval(expr)
74445d5f26dSBram Moolenaarendfunc
74545d5f26dSBram Moolenaar
74622f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
74751b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
74851b0f370SBram Moolenaar
74945d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
75045d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
7511b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
7521b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
75351b0f370SBram Moolenaar  if s:evalFromBalloonExpr
75451b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
75551b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
75651b0f370SBram Moolenaar    else
75751b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
75851b0f370SBram Moolenaar    endif
75951b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
76051b0f370SBram Moolenaar  else
7611b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
76251b0f370SBram Moolenaar  endif
7631b9645deSBram Moolenaar
7647f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
7651b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
76622f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
76751b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
76851b0f370SBram Moolenaar  else
76951b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
7701b9645deSBram Moolenaar  endif
77145d5f26dSBram Moolenaarendfunc
77245d5f26dSBram Moolenaar
77351b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
77451b0f370SBram Moolenaar" if there is any.
77551b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
776b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
777b3307b5eSBram Moolenaar    return
778b3307b5eSBram Moolenaar  endif
779b3307b5eSBram Moolenaar  if !s:stopped
780b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
781b3307b5eSBram Moolenaar    " mouse triggers a balloon.
78251b0f370SBram Moolenaar    return
78351b0f370SBram Moolenaar  endif
78451b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
78551b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
78622f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
78722f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
78851b0f370SBram Moolenaar  return ''
78951b0f370SBram Moolenaarendfunc
79051b0f370SBram Moolenaar
79145d5f26dSBram Moolenaar" Handle an error.
79245d5f26dSBram Moolenaarfunc s:HandleError(msg)
79322f1d0e3SBram Moolenaar  if s:ignoreEvalError
79451b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
79522f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
79622f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
79751b0f370SBram Moolenaar    return
79851b0f370SBram Moolenaar  endif
79945d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
80045d5f26dSBram Moolenaarendfunc
80145d5f26dSBram Moolenaar
802b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
803b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
804c4b533e1SBram Moolenaar    new
805b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
806c4b533e1SBram Moolenaar    call s:InstallWinbar()
807c4b533e1SBram Moolenaar  endif
808c4b533e1SBram Moolenaarendfunc
809c4b533e1SBram Moolenaar
810e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
811e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
812e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
813fe386641SBram Moolenaar  let wid = win_getid(winnr())
814fe386641SBram Moolenaar
81560e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
8164551c0a9SBram Moolenaar    call ch_log('program stopped')
81760e73f2aSBram Moolenaar    let s:stopped = 1
81860e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
8194551c0a9SBram Moolenaar    call ch_log('program running')
82060e73f2aSBram Moolenaar    let s:stopped = 0
82160e73f2aSBram Moolenaar  endif
82260e73f2aSBram Moolenaar
823a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
824a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
825a15b0a93SBram Moolenaar  else
826a15b0a93SBram Moolenaar    let fname = ''
827a15b0a93SBram Moolenaar  endif
8281b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
829e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
830fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
8314551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
8321b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
833fe386641SBram Moolenaar	if &modified
834fe386641SBram Moolenaar	  " TODO: find existing window
835fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
836b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
837c4b533e1SBram Moolenaar	  call s:InstallWinbar()
838fe386641SBram Moolenaar	else
839fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
840fe386641SBram Moolenaar	endif
841fe386641SBram Moolenaar      endif
842fe386641SBram Moolenaar      exe lnum
84301164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
8441b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
845fe386641SBram Moolenaar      setlocal signcolumn=yes
846fe386641SBram Moolenaar    endif
8474551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
848fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
849fe386641SBram Moolenaar  endif
850fe386641SBram Moolenaar
851fe386641SBram Moolenaar  call win_gotoid(wid)
852e09ba7baSBram Moolenaarendfunc
853e09ba7baSBram Moolenaar
854de1a8314SBram Moolenaarlet s:BreakpointSigns = []
855a15b0a93SBram Moolenaar
856a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr)
857de1a8314SBram Moolenaar  if index(s:BreakpointSigns, a:nr) == -1
858de1a8314SBram Moolenaar    call add(s:BreakpointSigns, a:nr)
859de1a8314SBram Moolenaar    exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint"
860de1a8314SBram Moolenaar  endif
861de1a8314SBram Moolenaarendfunc
862de1a8314SBram Moolenaar
863e09ba7baSBram Moolenaar" Handle setting a breakpoint
864e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
865e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
866*6dccc962SBram Moolenaar  if a:msg !~ 'fullname='
867*6dccc962SBram Moolenaar    " a watch does not have a file name
868*6dccc962SBram Moolenaar    return
869*6dccc962SBram Moolenaar  endif
870*6dccc962SBram Moolenaar
871e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
872e09ba7baSBram Moolenaar  if nr == 0
873e09ba7baSBram Moolenaar    return
874fe386641SBram Moolenaar  endif
875de1a8314SBram Moolenaar  call s:CreateBreakpoint(nr)
876e09ba7baSBram Moolenaar
877e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
878e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
879e09ba7baSBram Moolenaar  else
880e09ba7baSBram Moolenaar    let entry = {}
881e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
882fe386641SBram Moolenaar  endif
883e09ba7baSBram Moolenaar
884a15b0a93SBram Moolenaar  let fname = s:GetFullname(a:msg)
885e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
886e09ba7baSBram Moolenaar  let entry['fname'] = fname
887e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
8881b9645deSBram Moolenaar
8891b9645deSBram Moolenaar  if bufloaded(fname)
8901b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
8911b9645deSBram Moolenaar  endif
8921b9645deSBram Moolenaarendfunc
8931b9645deSBram Moolenaar
8941b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
895de1a8314SBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname']
8961b9645deSBram Moolenaar  let a:entry['placed'] = 1
897e09ba7baSBram Moolenaarendfunc
898e09ba7baSBram Moolenaar
899e09ba7baSBram Moolenaar" Handle deleting a breakpoint
900e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
901e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
902e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
903e09ba7baSBram Moolenaar  if nr == 0
904e09ba7baSBram Moolenaar    return
905e09ba7baSBram Moolenaar  endif
9061b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
9071b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
9081b9645deSBram Moolenaar    if has_key(entry, 'placed')
909e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
9101b9645deSBram Moolenaar      unlet entry['placed']
9111b9645deSBram Moolenaar    endif
912e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
9131b9645deSBram Moolenaar  endif
914c572da5fSBram Moolenaarendfunc
9151b9645deSBram Moolenaar
9164551c0a9SBram Moolenaar" Handle the debugged program starting to run.
9174551c0a9SBram Moolenaar" Will store the process ID in s:pid
9184551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
9194551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
9204551c0a9SBram Moolenaar  if nr == 0
9214551c0a9SBram Moolenaar    return
9224551c0a9SBram Moolenaar  endif
9234551c0a9SBram Moolenaar  let s:pid = nr
9244551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
9254551c0a9SBram Moolenaarendfunc
9264551c0a9SBram Moolenaar
9271b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
9281b9645deSBram Moolenaarfunc s:BufRead()
9291b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9301b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9311b9645deSBram Moolenaar    if entry['fname'] == fname
9321b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
9331b9645deSBram Moolenaar    endif
9341b9645deSBram Moolenaar  endfor
9351b9645deSBram Moolenaarendfunc
9361b9645deSBram Moolenaar
9371b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
9381b9645deSBram Moolenaarfunc s:BufUnloaded()
9391b9645deSBram Moolenaar  let fname = expand('<afile>:p')
9401b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
9411b9645deSBram Moolenaar    if entry['fname'] == fname
9421b9645deSBram Moolenaar      let entry['placed'] = 0
9431b9645deSBram Moolenaar    endif
9441b9645deSBram Moolenaar  endfor
9451b9645deSBram Moolenaarendfunc
946