1fe386641SBram Moolenaar" Debugger plugin using gdb.
2c572da5fSBram Moolenaar"
3c572da5fSBram Moolenaar" WORK IN PROGRESS - much doesn't work yet
4c572da5fSBram Moolenaar"
5fe386641SBram Moolenaar" Open two visible terminal windows:
6c572da5fSBram Moolenaar" 1. run a pty, as with ":term NONE"
7c572da5fSBram Moolenaar" 2. run gdb, passing the pty
8fe386641SBram Moolenaar" The current window is used to view source code and follows gdb.
9fe386641SBram Moolenaar"
10fe386641SBram Moolenaar" A third terminal window is hidden, it is used for communication with gdb.
11fe386641SBram Moolenaar"
12fe386641SBram Moolenaar" The communication with gdb uses GDB/MI.  See:
13fe386641SBram Moolenaar" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html
14c572da5fSBram Moolenaar"
15c572da5fSBram Moolenaar" Author: Bram Moolenaar
16fe386641SBram Moolenaar" Copyright: Vim license applies, see ":help license"
17c572da5fSBram Moolenaar
1837c64c78SBram Moolenaar" In case this gets loaded twice.
1937c64c78SBram Moolenaarif exists(':Termdebug')
2037c64c78SBram Moolenaar  finish
2137c64c78SBram Moolenaarendif
2237c64c78SBram Moolenaar
2360e73f2aSBram Moolenaar" Uncomment this line to write logging in "debuglog".
2460e73f2aSBram Moolenaar" call ch_logfile('debuglog', 'w')
2560e73f2aSBram Moolenaar
26fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim".
27fe386641SBram Moolenaar" To end type "quit" in the gdb window.
28*32c67ba7SBram Moolenaarcommand -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>)
29*32c67ba7SBram Moolenaarcommand -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>)
30c572da5fSBram Moolenaar
31fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb".
32e09ba7baSBram Moolenaarif !exists('termdebugger')
33e09ba7baSBram Moolenaar  let termdebugger = 'gdb'
34c572da5fSBram Moolenaarendif
35c572da5fSBram Moolenaar
36fe386641SBram Moolenaarlet s:pc_id = 12
37e09ba7baSBram Moolenaarlet s:break_id = 13
3860e73f2aSBram Moolenaarlet s:stopped = 1
39e09ba7baSBram Moolenaar
40e09ba7baSBram Moolenaarif &background == 'light'
41e09ba7baSBram Moolenaar  hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue
42e09ba7baSBram Moolenaarelse
43e09ba7baSBram Moolenaar  hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue
44e09ba7baSBram Moolenaarendif
45e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red
46fe386641SBram Moolenaar
47*32c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...)
48*32c67ba7SBram Moolenaar  " First argument is the command to debug, second core file or process ID.
49*32c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang})
50*32c67ba7SBram Moolenaarendfunc
51*32c67ba7SBram Moolenaar
52*32c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...)
53*32c67ba7SBram Moolenaar  " First argument is the command to debug, rest are run arguments.
54*32c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang})
55*32c67ba7SBram Moolenaarendfunc
56*32c67ba7SBram Moolenaar
57*32c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict)
58b3623a38SBram Moolenaar  if exists('s:gdbwin')
59b3623a38SBram Moolenaar    echoerr 'Terminal debugger already running'
60b3623a38SBram Moolenaar    return
61b3623a38SBram Moolenaar  endif
62b3623a38SBram Moolenaar
63fe386641SBram Moolenaar  let s:startwin = win_getid(winnr())
64fe386641SBram Moolenaar  let s:startsigncolumn = &signcolumn
65fe386641SBram Moolenaar
6624a98a0eSBram Moolenaar  let s:save_columns = 0
6724a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
6824a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
6938baa3e6SBram Moolenaar      let s:save_columns = &columns
7038baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
7124a98a0eSBram Moolenaar    endif
7238baa3e6SBram Moolenaar    let vertical = 1
7338baa3e6SBram Moolenaar  else
7438baa3e6SBram Moolenaar    let vertical = 0
7538baa3e6SBram Moolenaar  endif
7638baa3e6SBram Moolenaar
77c572da5fSBram Moolenaar  " Open a terminal window without a job, to run the debugged program
78fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
79fe386641SBram Moolenaar	\ 'term_name': 'gdb program',
8038baa3e6SBram Moolenaar	\ 'vertical': vertical,
81fe386641SBram Moolenaar	\ })
82fe386641SBram Moolenaar  if s:ptybuf == 0
83fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
84fe386641SBram Moolenaar    return
85fe386641SBram Moolenaar  endif
86fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
8745d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
8851b0f370SBram Moolenaar  if vertical
8951b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
9051b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
9151b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
9251b0f370SBram Moolenaar  endif
93fe386641SBram Moolenaar
94fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
95fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
96fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
97fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
98fe386641SBram Moolenaar	\ 'hidden': 1,
99fe386641SBram Moolenaar	\ })
100fe386641SBram Moolenaar  if s:commbuf == 0
101fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
102fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
103fe386641SBram Moolenaar    return
104fe386641SBram Moolenaar  endif
105fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
106c572da5fSBram Moolenaar
107c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
108c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
109*32c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
110*32c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
111*32c67ba7SBram Moolenaar
112*32c67ba7SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
113c572da5fSBram Moolenaar  echomsg 'executing "' . join(cmd) . '"'
11460e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
115c572da5fSBram Moolenaar	\ 'exit_cb': function('s:EndDebug'),
116fe386641SBram Moolenaar	\ 'term_finish': 'close',
117c572da5fSBram Moolenaar	\ })
11860e73f2aSBram Moolenaar  if s:gdbbuf == 0
119fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
120fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
121fe386641SBram Moolenaar    exe 'bwipe! ' . s:commbuf
122fe386641SBram Moolenaar    return
123fe386641SBram Moolenaar  endif
12445d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
125fe386641SBram Moolenaar
126*32c67ba7SBram Moolenaar  " Set arguments to be run
127*32c67ba7SBram Moolenaar  if len(proc_args)
128*32c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
129*32c67ba7SBram Moolenaar  endif
130*32c67ba7SBram Moolenaar
131fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
13260e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
13360e73f2aSBram Moolenaar
1343e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
1353e4b84d0SBram Moolenaar  " why the debugger doesn't work.
1363e4b84d0SBram Moolenaar  let try_count = 0
1373e4b84d0SBram Moolenaar  while 1
1383e4b84d0SBram Moolenaar    let response = ''
139b3623a38SBram Moolenaar    for lnum in range(1,200)
1403e4b84d0SBram Moolenaar      if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi '
1413e4b84d0SBram Moolenaar	let response = term_getline(s:gdbbuf, lnum + 1)
1423e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
143f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
1443e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:ptybuf
1453e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:commbuf
1463e4b84d0SBram Moolenaar	  return
1473e4b84d0SBram Moolenaar	endif
1483e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
1493e4b84d0SBram Moolenaar	  " Success!
1503e4b84d0SBram Moolenaar	  break
1513e4b84d0SBram Moolenaar	endif
1523e4b84d0SBram Moolenaar      endif
1533e4b84d0SBram Moolenaar    endfor
1543e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
1553e4b84d0SBram Moolenaar      break
1563e4b84d0SBram Moolenaar    endif
1573e4b84d0SBram Moolenaar    let try_count += 1
1583e4b84d0SBram Moolenaar    if try_count > 100
1593e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
1603e4b84d0SBram Moolenaar      break
1613e4b84d0SBram Moolenaar    endif
1623e4b84d0SBram Moolenaar    sleep 10m
1633e4b84d0SBram Moolenaar  endwhile
1643e4b84d0SBram Moolenaar
16560e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
16660e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
16760e73f2aSBram Moolenaar  " running.
16860e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
169e09ba7baSBram Moolenaar
170f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
171f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
172f3ba14ffSBram Moolenaar  call s:SendCommand('-gdb-set pagination off')
173f3ba14ffSBram Moolenaar
17438baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
17538baa3e6SBram Moolenaar  " There can be only one.
17638baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
17738baa3e6SBram Moolenaar
17838baa3e6SBram Moolenaar  " Sign used to indicate a breakpoint.
17938baa3e6SBram Moolenaar  " Can be used multiple times.
18038baa3e6SBram Moolenaar  sign define debugBreakpoint text=>> texthl=debugBreakpoint
18138baa3e6SBram Moolenaar
18245d5f26dSBram Moolenaar  " Install debugger commands in the text window.
18345d5f26dSBram Moolenaar  call win_gotoid(s:startwin)
184e09ba7baSBram Moolenaar  call s:InstallCommands()
18545d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
186e09ba7baSBram Moolenaar
18751b0f370SBram Moolenaar  " Enable showing a balloon with eval info
188246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
189246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
19051b0f370SBram Moolenaar    if has("balloon_eval")
19151b0f370SBram Moolenaar      set ballooneval
192246fe03dSBram Moolenaar    endif
19351b0f370SBram Moolenaar    if has("balloon_eval_term")
19451b0f370SBram Moolenaar      set balloonevalterm
19551b0f370SBram Moolenaar    endif
19651b0f370SBram Moolenaar  endif
19751b0f370SBram Moolenaar
198e09ba7baSBram Moolenaar  let s:breakpoints = {}
1991b9645deSBram Moolenaar
2001b9645deSBram Moolenaar  augroup TermDebug
2011b9645deSBram Moolenaar    au BufRead * call s:BufRead()
2021b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
2031b9645deSBram Moolenaar  augroup END
204*32c67ba7SBram Moolenaar
205*32c67ba7SBram Moolenaar  " Run the command if the bang attribute was given
206*32c67ba7SBram Moolenaar  " and got to the window
207*32c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
208*32c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
209*32c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
210*32c67ba7SBram Moolenaar  endif
211*32c67ba7SBram Moolenaar
212c572da5fSBram Moolenaarendfunc
213c572da5fSBram Moolenaar
214c572da5fSBram Moolenaarfunc s:EndDebug(job, status)
215c572da5fSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
216fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
217b3623a38SBram Moolenaar  unlet s:gdbwin
218e09ba7baSBram Moolenaar
219e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
220e09ba7baSBram Moolenaar
221e09ba7baSBram Moolenaar  call win_gotoid(s:startwin)
222e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
223e09ba7baSBram Moolenaar  call s:DeleteCommands()
224e09ba7baSBram Moolenaar
225e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
22638baa3e6SBram Moolenaar  if s:save_columns > 0
22738baa3e6SBram Moolenaar    let &columns = s:save_columns
22838baa3e6SBram Moolenaar  endif
2291b9645deSBram Moolenaar
230246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
231246fe03dSBram Moolenaar    set balloonexpr=
23251b0f370SBram Moolenaar    if has("balloon_eval")
23351b0f370SBram Moolenaar      set noballooneval
234246fe03dSBram Moolenaar    endif
23551b0f370SBram Moolenaar    if has("balloon_eval_term")
23651b0f370SBram Moolenaar      set noballoonevalterm
23751b0f370SBram Moolenaar    endif
23851b0f370SBram Moolenaar  endif
23951b0f370SBram Moolenaar
2401b9645deSBram Moolenaar  au! TermDebug
241fe386641SBram Moolenaarendfunc
242fe386641SBram Moolenaar
243fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
244fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
245fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
246fe386641SBram Moolenaar
247fe386641SBram Moolenaar  for msg in msgs
248fe386641SBram Moolenaar    " remove prefixed NL
249fe386641SBram Moolenaar    if msg[0] == "\n"
250fe386641SBram Moolenaar      let msg = msg[1:]
251fe386641SBram Moolenaar    endif
252fe386641SBram Moolenaar    if msg != ''
2531b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
254e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
25545d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
256e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
257e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
258e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
25945d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
26045d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
26145d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
26245d5f26dSBram Moolenaar	call s:HandleError(msg)
263e09ba7baSBram Moolenaar      endif
264e09ba7baSBram Moolenaar    endif
265e09ba7baSBram Moolenaar  endfor
266e09ba7baSBram Moolenaarendfunc
267e09ba7baSBram Moolenaar
268e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
269e09ba7baSBram Moolenaarfunc s:InstallCommands()
270e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
27171137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
272e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
27345d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
274e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
27560e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
27660e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
27760e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
278e09ba7baSBram Moolenaar  command Continue call s:SendCommand('-exec-continue')
27945d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
28045d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
28145d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
282c4b533e1SBram Moolenaar  command Source call s:GotoStartwinOrCreateIt()
28371137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
28445d5f26dSBram Moolenaar
28545d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
28645d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
2871b9645deSBram Moolenaar
288f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
28971137fedSBram Moolenaar    call s:InstallWinbar()
29071137fedSBram Moolenaar
29171137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
29271137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
29371137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
29471137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
29571137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
29671137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
29771137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
29871137fedSBram Moolenaar    endif
29971137fedSBram Moolenaar  endif
30071137fedSBram Moolenaarendfunc
30171137fedSBram Moolenaar
30271137fedSBram Moolenaarlet s:winbar_winids = []
30371137fedSBram Moolenaar
30471137fedSBram Moolenaar" Install the window toolbar in the current window.
30571137fedSBram Moolenaarfunc s:InstallWinbar()
306c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
30724a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
30824a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
30924a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
31024a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
31160e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
31224a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
31371137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
314c4b533e1SBram Moolenaar  endif
315e09ba7baSBram Moolenaarendfunc
316e09ba7baSBram Moolenaar
317e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
318e09ba7baSBram Moolenaarfunc s:DeleteCommands()
319e09ba7baSBram Moolenaar  delcommand Break
32071137fedSBram Moolenaar  delcommand Clear
321e09ba7baSBram Moolenaar  delcommand Step
32245d5f26dSBram Moolenaar  delcommand Over
323e09ba7baSBram Moolenaar  delcommand Finish
32460e73f2aSBram Moolenaar  delcommand Run
32560e73f2aSBram Moolenaar  delcommand Arguments
32660e73f2aSBram Moolenaar  delcommand Stop
327e09ba7baSBram Moolenaar  delcommand Continue
32845d5f26dSBram Moolenaar  delcommand Evaluate
32945d5f26dSBram Moolenaar  delcommand Gdb
33045d5f26dSBram Moolenaar  delcommand Program
331b3623a38SBram Moolenaar  delcommand Source
33271137fedSBram Moolenaar  delcommand Winbar
33345d5f26dSBram Moolenaar
33445d5f26dSBram Moolenaar  nunmap K
3351b9645deSBram Moolenaar
3361b9645deSBram Moolenaar  if has('menu')
33771137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
33871137fedSBram Moolenaar    let curwinid = win_getid(winnr())
33971137fedSBram Moolenaar    for winid in s:winbar_winids
34071137fedSBram Moolenaar      if win_gotoid(winid)
3411b9645deSBram Moolenaar	aunmenu WinBar.Step
3421b9645deSBram Moolenaar	aunmenu WinBar.Next
3431b9645deSBram Moolenaar	aunmenu WinBar.Finish
3441b9645deSBram Moolenaar	aunmenu WinBar.Cont
34560e73f2aSBram Moolenaar	aunmenu WinBar.Stop
3461b9645deSBram Moolenaar	aunmenu WinBar.Eval
3471b9645deSBram Moolenaar      endif
34871137fedSBram Moolenaar    endfor
34971137fedSBram Moolenaar    call win_gotoid(curwinid)
35071137fedSBram Moolenaar    let s:winbar_winids = []
35171137fedSBram Moolenaar
35271137fedSBram Moolenaar    if exists('s:saved_mousemodel')
35371137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
35471137fedSBram Moolenaar      unlet s:saved_mousemodel
35571137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
35671137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
35771137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
35871137fedSBram Moolenaar      aunmenu PopUp.Evaluate
35971137fedSBram Moolenaar    endif
36071137fedSBram Moolenaar  endif
3611b9645deSBram Moolenaar
36245d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
36345d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
36445d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
36545d5f26dSBram Moolenaar  endfor
36638baa3e6SBram Moolenaar  sign undefine debugPC
36738baa3e6SBram Moolenaar  sign undefine debugBreakpoint
36845d5f26dSBram Moolenaar  unlet s:breakpoints
369e09ba7baSBram Moolenaarendfunc
370e09ba7baSBram Moolenaar
371e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
372e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
37360e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
37460e73f2aSBram Moolenaar  " Interrupt to make it work.
37560e73f2aSBram Moolenaar  let do_continue = 0
37660e73f2aSBram Moolenaar  if !s:stopped
37760e73f2aSBram Moolenaar    let do_continue = 1
37860e73f2aSBram Moolenaar    call s:SendCommand('-exec-interrupt')
37960e73f2aSBram Moolenaar    sleep 10m
38060e73f2aSBram Moolenaar  endif
38160e73f2aSBram Moolenaar  call s:SendCommand('-break-insert --source '
38260e73f2aSBram Moolenaar	\ . fnameescape(expand('%:p')) . ' --line ' . line('.'))
38360e73f2aSBram Moolenaar  if do_continue
38460e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
38560e73f2aSBram Moolenaar  endif
386e09ba7baSBram Moolenaarendfunc
387e09ba7baSBram Moolenaar
38871137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
38971137fedSBram Moolenaarfunc s:ClearBreakpoint()
390e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
391e09ba7baSBram Moolenaar  let lnum = line('.')
392e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
393e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
394e09ba7baSBram Moolenaar      call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r")
395e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
396e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
397e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
398e09ba7baSBram Moolenaar      break
399e09ba7baSBram Moolenaar    endif
400e09ba7baSBram Moolenaar  endfor
401e09ba7baSBram Moolenaarendfunc
402e09ba7baSBram Moolenaar
403e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb
404e09ba7baSBram Moolenaarfunc s:SendCommand(cmd)
405e09ba7baSBram Moolenaar  call term_sendkeys(s:commbuf, a:cmd . "\r")
406e09ba7baSBram Moolenaarendfunc
407e09ba7baSBram Moolenaar
40860e73f2aSBram Moolenaarfunc s:Run(args)
40960e73f2aSBram Moolenaar  if a:args != ''
41060e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
41160e73f2aSBram Moolenaar  endif
41260e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
41360e73f2aSBram Moolenaarendfunc
41460e73f2aSBram Moolenaar
41551b0f370SBram Moolenaarfunc s:SendEval(expr)
41651b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
41751b0f370SBram Moolenaar  let s:evalexpr = a:expr
41851b0f370SBram Moolenaarendfunc
41951b0f370SBram Moolenaar
42045d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
42145d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
42245d5f26dSBram Moolenaar  if a:arg != ''
42345d5f26dSBram Moolenaar    let expr = a:arg
42445d5f26dSBram Moolenaar  elseif a:range == 2
42545d5f26dSBram Moolenaar    let pos = getcurpos()
42645d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
42745d5f26dSBram Moolenaar    let regt = getregtype('v')
42845d5f26dSBram Moolenaar    normal! gv"vy
42945d5f26dSBram Moolenaar    let expr = @v
43045d5f26dSBram Moolenaar    call setpos('.', pos)
43145d5f26dSBram Moolenaar    call setreg('v', reg, regt)
43245d5f26dSBram Moolenaar  else
43345d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
43445d5f26dSBram Moolenaar  endif
43522f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
43651b0f370SBram Moolenaar  call s:SendEval(expr)
43745d5f26dSBram Moolenaarendfunc
43845d5f26dSBram Moolenaar
43922f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
44051b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
44151b0f370SBram Moolenaar
44245d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
44345d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
4441b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
4451b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
44651b0f370SBram Moolenaar  if s:evalFromBalloonExpr
44751b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
44851b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
44951b0f370SBram Moolenaar    else
45051b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
45151b0f370SBram Moolenaar    endif
45251b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
45351b0f370SBram Moolenaar  else
4541b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
45551b0f370SBram Moolenaar  endif
4561b9645deSBram Moolenaar
4577f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
4581b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
45922f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
46051b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
46151b0f370SBram Moolenaar  else
46251b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
4631b9645deSBram Moolenaar  endif
46445d5f26dSBram Moolenaarendfunc
46545d5f26dSBram Moolenaar
46651b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
46751b0f370SBram Moolenaar" if there is any.
46851b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
46951b0f370SBram Moolenaar  if v:beval_winid != s:startwin
47051b0f370SBram Moolenaar    return
47151b0f370SBram Moolenaar  endif
47251b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
47351b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
47422f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
47522f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
47651b0f370SBram Moolenaar  return ''
47751b0f370SBram Moolenaarendfunc
47851b0f370SBram Moolenaar
47945d5f26dSBram Moolenaar" Handle an error.
48045d5f26dSBram Moolenaarfunc s:HandleError(msg)
48122f1d0e3SBram Moolenaar  if s:ignoreEvalError
48251b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
48322f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
48422f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
48551b0f370SBram Moolenaar    return
48651b0f370SBram Moolenaar  endif
48745d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
48845d5f26dSBram Moolenaarendfunc
48945d5f26dSBram Moolenaar
490c4b533e1SBram Moolenaarfunc s:GotoStartwinOrCreateIt()
491c4b533e1SBram Moolenaar  if !win_gotoid(s:startwin)
492c4b533e1SBram Moolenaar    new
493c4b533e1SBram Moolenaar    let s:startwin = win_getid(winnr())
494c4b533e1SBram Moolenaar    call s:InstallWinbar()
495c4b533e1SBram Moolenaar  endif
496c4b533e1SBram Moolenaarendfunc
497c4b533e1SBram Moolenaar
498e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
499e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
500e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
501fe386641SBram Moolenaar  let wid = win_getid(winnr())
502fe386641SBram Moolenaar
50360e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
50460e73f2aSBram Moolenaar    let s:stopped = 1
50560e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
50660e73f2aSBram Moolenaar    let s:stopped = 0
50760e73f2aSBram Moolenaar  endif
50860e73f2aSBram Moolenaar
509c4b533e1SBram Moolenaar  call s:GotoStartwinOrCreateIt()
510c4b533e1SBram Moolenaar
511e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
5121b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
513e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
514fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
5151b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
516fe386641SBram Moolenaar	if &modified
517fe386641SBram Moolenaar	  " TODO: find existing window
518fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
519fe386641SBram Moolenaar	  let s:startwin = win_getid(winnr())
520c4b533e1SBram Moolenaar	  call s:InstallWinbar()
521fe386641SBram Moolenaar	else
522fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
523fe386641SBram Moolenaar	endif
524fe386641SBram Moolenaar      endif
525fe386641SBram Moolenaar      exe lnum
52601164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
5271b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
528fe386641SBram Moolenaar      setlocal signcolumn=yes
529fe386641SBram Moolenaar    endif
530fe386641SBram Moolenaar  else
531fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
532fe386641SBram Moolenaar  endif
533fe386641SBram Moolenaar
534fe386641SBram Moolenaar  call win_gotoid(wid)
535e09ba7baSBram Moolenaarendfunc
536e09ba7baSBram Moolenaar
537e09ba7baSBram Moolenaar" Handle setting a breakpoint
538e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
539e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
540e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
541e09ba7baSBram Moolenaar  if nr == 0
542e09ba7baSBram Moolenaar    return
543fe386641SBram Moolenaar  endif
544e09ba7baSBram Moolenaar
545e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
546e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
547e09ba7baSBram Moolenaar  else
548e09ba7baSBram Moolenaar    let entry = {}
549e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
550fe386641SBram Moolenaar  endif
551e09ba7baSBram Moolenaar
552e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
553e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
554e09ba7baSBram Moolenaar  let entry['fname'] = fname
555e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
5561b9645deSBram Moolenaar
5571b9645deSBram Moolenaar  if bufloaded(fname)
5581b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
5591b9645deSBram Moolenaar  endif
5601b9645deSBram Moolenaarendfunc
5611b9645deSBram Moolenaar
5621b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
5631b9645deSBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname']
5641b9645deSBram Moolenaar  let a:entry['placed'] = 1
565e09ba7baSBram Moolenaarendfunc
566e09ba7baSBram Moolenaar
567e09ba7baSBram Moolenaar" Handle deleting a breakpoint
568e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
569e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
570e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
571e09ba7baSBram Moolenaar  if nr == 0
572e09ba7baSBram Moolenaar    return
573e09ba7baSBram Moolenaar  endif
5741b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
5751b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
5761b9645deSBram Moolenaar    if has_key(entry, 'placed')
577e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
5781b9645deSBram Moolenaar      unlet entry['placed']
5791b9645deSBram Moolenaar    endif
580e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
5811b9645deSBram Moolenaar  endif
582c572da5fSBram Moolenaarendfunc
5831b9645deSBram Moolenaar
5841b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
5851b9645deSBram Moolenaarfunc s:BufRead()
5861b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5871b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5881b9645deSBram Moolenaar    if entry['fname'] == fname
5891b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
5901b9645deSBram Moolenaar    endif
5911b9645deSBram Moolenaar  endfor
5921b9645deSBram Moolenaarendfunc
5931b9645deSBram Moolenaar
5941b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
5951b9645deSBram Moolenaarfunc s:BufUnloaded()
5961b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5971b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5981b9645deSBram Moolenaar    if entry['fname'] == fname
5991b9645deSBram Moolenaar      let entry['placed'] = 0
6001b9645deSBram Moolenaar    endif
6011b9645deSBram Moolenaar  endfor
6021b9645deSBram Moolenaarendfunc
6031b9645deSBram Moolenaar
604