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.
28c572da5fSBram Moolenaarcommand -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>)
29c572da5fSBram Moolenaar
30fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb".
31e09ba7baSBram Moolenaarif !exists('termdebugger')
32e09ba7baSBram Moolenaar  let termdebugger = 'gdb'
33c572da5fSBram Moolenaarendif
34c572da5fSBram Moolenaar
35fe386641SBram Moolenaarlet s:pc_id = 12
36e09ba7baSBram Moolenaarlet s:break_id = 13
3760e73f2aSBram Moolenaarlet s:stopped = 1
38e09ba7baSBram Moolenaar
39e09ba7baSBram Moolenaarif &background == 'light'
40e09ba7baSBram Moolenaar  hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue
41e09ba7baSBram Moolenaarelse
42e09ba7baSBram Moolenaar  hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue
43e09ba7baSBram Moolenaarendif
44e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red
45fe386641SBram Moolenaar
46c572da5fSBram Moolenaarfunc s:StartDebug(cmd)
47fe386641SBram Moolenaar  let s:startwin = win_getid(winnr())
48fe386641SBram Moolenaar  let s:startsigncolumn = &signcolumn
49fe386641SBram Moolenaar
5024a98a0eSBram Moolenaar  let s:save_columns = 0
5124a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
5224a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
5338baa3e6SBram Moolenaar      let s:save_columns = &columns
5438baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
5524a98a0eSBram Moolenaar    endif
5638baa3e6SBram Moolenaar    let vertical = 1
5738baa3e6SBram Moolenaar  else
5838baa3e6SBram Moolenaar    let vertical = 0
5938baa3e6SBram Moolenaar  endif
6038baa3e6SBram Moolenaar
61c572da5fSBram Moolenaar  " Open a terminal window without a job, to run the debugged program
62fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
63fe386641SBram Moolenaar	\ 'term_name': 'gdb program',
6438baa3e6SBram Moolenaar	\ 'vertical': vertical,
65fe386641SBram Moolenaar	\ })
66fe386641SBram Moolenaar  if s:ptybuf == 0
67fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
68fe386641SBram Moolenaar    return
69fe386641SBram Moolenaar  endif
70fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
7145d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
7251b0f370SBram Moolenaar  if vertical
7351b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
7451b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
7551b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
7651b0f370SBram Moolenaar  endif
77fe386641SBram Moolenaar
78fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
79fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
80fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
81fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
82fe386641SBram Moolenaar	\ 'hidden': 1,
83fe386641SBram Moolenaar	\ })
84fe386641SBram Moolenaar  if s:commbuf == 0
85fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
86fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
87fe386641SBram Moolenaar    return
88fe386641SBram Moolenaar  endif
89fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
90c572da5fSBram Moolenaar
91c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
92c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
93c3632516SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty, a:cmd]
94c572da5fSBram Moolenaar  echomsg 'executing "' . join(cmd) . '"'
9560e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
96c572da5fSBram Moolenaar	\ 'exit_cb': function('s:EndDebug'),
97fe386641SBram Moolenaar	\ 'term_finish': 'close',
98c572da5fSBram Moolenaar	\ })
9960e73f2aSBram Moolenaar  if s:gdbbuf == 0
100fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
101fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
102fe386641SBram Moolenaar    exe 'bwipe! ' . s:commbuf
103fe386641SBram Moolenaar    return
104fe386641SBram Moolenaar  endif
10545d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
106fe386641SBram Moolenaar
107fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
10860e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
10960e73f2aSBram Moolenaar
1103e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
1113e4b84d0SBram Moolenaar  " why the debugger doesn't work.
1123e4b84d0SBram Moolenaar  let try_count = 0
1133e4b84d0SBram Moolenaar  while 1
1143e4b84d0SBram Moolenaar    let response = ''
1153e4b84d0SBram Moolenaar    for lnum in range(1,20)
1163e4b84d0SBram Moolenaar      if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi '
1173e4b84d0SBram Moolenaar	let response = term_getline(s:gdbbuf, lnum + 1)
1183e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
119*f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
1203e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:ptybuf
1213e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:commbuf
1223e4b84d0SBram Moolenaar	  return
1233e4b84d0SBram Moolenaar	endif
1243e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
1253e4b84d0SBram Moolenaar	  " Success!
1263e4b84d0SBram Moolenaar	  break
1273e4b84d0SBram Moolenaar	endif
1283e4b84d0SBram Moolenaar      endif
1293e4b84d0SBram Moolenaar    endfor
1303e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
1313e4b84d0SBram Moolenaar      break
1323e4b84d0SBram Moolenaar    endif
1333e4b84d0SBram Moolenaar    let try_count += 1
1343e4b84d0SBram Moolenaar    if try_count > 100
1353e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
1363e4b84d0SBram Moolenaar      break
1373e4b84d0SBram Moolenaar    endif
1383e4b84d0SBram Moolenaar    sleep 10m
1393e4b84d0SBram Moolenaar  endwhile
1403e4b84d0SBram Moolenaar
14160e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
14260e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
14360e73f2aSBram Moolenaar  " running.
14460e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
145e09ba7baSBram Moolenaar
146*f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
147*f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
148*f3ba14ffSBram Moolenaar  call s:SendCommand('-gdb-set pagination off')
149*f3ba14ffSBram Moolenaar
15038baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
15138baa3e6SBram Moolenaar  " There can be only one.
15238baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
15338baa3e6SBram Moolenaar
15438baa3e6SBram Moolenaar  " Sign used to indicate a breakpoint.
15538baa3e6SBram Moolenaar  " Can be used multiple times.
15638baa3e6SBram Moolenaar  sign define debugBreakpoint text=>> texthl=debugBreakpoint
15738baa3e6SBram Moolenaar
15845d5f26dSBram Moolenaar  " Install debugger commands in the text window.
15945d5f26dSBram Moolenaar  call win_gotoid(s:startwin)
160e09ba7baSBram Moolenaar  call s:InstallCommands()
16145d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
162e09ba7baSBram Moolenaar
16351b0f370SBram Moolenaar  " Enable showing a balloon with eval info
164246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
165246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
16651b0f370SBram Moolenaar    if has("balloon_eval")
16751b0f370SBram Moolenaar      set ballooneval
168246fe03dSBram Moolenaar    endif
16951b0f370SBram Moolenaar    if has("balloon_eval_term")
17051b0f370SBram Moolenaar      set balloonevalterm
17151b0f370SBram Moolenaar    endif
17251b0f370SBram Moolenaar  endif
17351b0f370SBram Moolenaar
174e09ba7baSBram Moolenaar  let s:breakpoints = {}
1751b9645deSBram Moolenaar
1761b9645deSBram Moolenaar  augroup TermDebug
1771b9645deSBram Moolenaar    au BufRead * call s:BufRead()
1781b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
1791b9645deSBram Moolenaar  augroup END
180c572da5fSBram Moolenaarendfunc
181c572da5fSBram Moolenaar
182c572da5fSBram Moolenaarfunc s:EndDebug(job, status)
183c572da5fSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
184fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
185e09ba7baSBram Moolenaar
186e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
187e09ba7baSBram Moolenaar
188e09ba7baSBram Moolenaar  call win_gotoid(s:startwin)
189e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
190e09ba7baSBram Moolenaar  call s:DeleteCommands()
191e09ba7baSBram Moolenaar
192e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
19338baa3e6SBram Moolenaar  if s:save_columns > 0
19438baa3e6SBram Moolenaar    let &columns = s:save_columns
19538baa3e6SBram Moolenaar  endif
1961b9645deSBram Moolenaar
197246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
198246fe03dSBram Moolenaar    set balloonexpr=
19951b0f370SBram Moolenaar    if has("balloon_eval")
20051b0f370SBram Moolenaar      set noballooneval
201246fe03dSBram Moolenaar    endif
20251b0f370SBram Moolenaar    if has("balloon_eval_term")
20351b0f370SBram Moolenaar      set noballoonevalterm
20451b0f370SBram Moolenaar    endif
20551b0f370SBram Moolenaar  endif
20651b0f370SBram Moolenaar
2071b9645deSBram Moolenaar  au! TermDebug
208fe386641SBram Moolenaarendfunc
209fe386641SBram Moolenaar
210fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
211fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
212fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
213fe386641SBram Moolenaar
214fe386641SBram Moolenaar  for msg in msgs
215fe386641SBram Moolenaar    " remove prefixed NL
216fe386641SBram Moolenaar    if msg[0] == "\n"
217fe386641SBram Moolenaar      let msg = msg[1:]
218fe386641SBram Moolenaar    endif
219fe386641SBram Moolenaar    if msg != ''
2201b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
221e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
22245d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
223e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
224e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
225e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
22645d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
22745d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
22845d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
22945d5f26dSBram Moolenaar	call s:HandleError(msg)
230e09ba7baSBram Moolenaar      endif
231e09ba7baSBram Moolenaar    endif
232e09ba7baSBram Moolenaar  endfor
233e09ba7baSBram Moolenaarendfunc
234e09ba7baSBram Moolenaar
235e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
236e09ba7baSBram Moolenaarfunc s:InstallCommands()
237e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
23871137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
239e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
24045d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
241e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
24260e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
24360e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
24460e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
245e09ba7baSBram Moolenaar  command Continue call s:SendCommand('-exec-continue')
24645d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
24745d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
24845d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
24971137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
25045d5f26dSBram Moolenaar
25145d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
25245d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
2531b9645deSBram Moolenaar
254f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
25571137fedSBram Moolenaar    call s:InstallWinbar()
25671137fedSBram Moolenaar
25771137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
25871137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
25971137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
26071137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
26171137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
26271137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
26371137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
26471137fedSBram Moolenaar    endif
26571137fedSBram Moolenaar  endif
26671137fedSBram Moolenaarendfunc
26771137fedSBram Moolenaar
26871137fedSBram Moolenaarlet s:winbar_winids = []
26971137fedSBram Moolenaar
27071137fedSBram Moolenaar" Install the window toolbar in the current window.
27171137fedSBram Moolenaarfunc s:InstallWinbar()
27224a98a0eSBram Moolenaar  nnoremenu WinBar.Step   :Step<CR>
27324a98a0eSBram Moolenaar  nnoremenu WinBar.Next   :Over<CR>
27424a98a0eSBram Moolenaar  nnoremenu WinBar.Finish :Finish<CR>
27524a98a0eSBram Moolenaar  nnoremenu WinBar.Cont   :Continue<CR>
27660e73f2aSBram Moolenaar  nnoremenu WinBar.Stop   :Stop<CR>
27724a98a0eSBram Moolenaar  nnoremenu WinBar.Eval   :Evaluate<CR>
27871137fedSBram Moolenaar  call add(s:winbar_winids, win_getid(winnr()))
279e09ba7baSBram Moolenaarendfunc
280e09ba7baSBram Moolenaar
281e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
282e09ba7baSBram Moolenaarfunc s:DeleteCommands()
283e09ba7baSBram Moolenaar  delcommand Break
28471137fedSBram Moolenaar  delcommand Clear
285e09ba7baSBram Moolenaar  delcommand Step
28645d5f26dSBram Moolenaar  delcommand Over
287e09ba7baSBram Moolenaar  delcommand Finish
28860e73f2aSBram Moolenaar  delcommand Run
28960e73f2aSBram Moolenaar  delcommand Arguments
29060e73f2aSBram Moolenaar  delcommand Stop
291e09ba7baSBram Moolenaar  delcommand Continue
29245d5f26dSBram Moolenaar  delcommand Evaluate
29345d5f26dSBram Moolenaar  delcommand Gdb
29445d5f26dSBram Moolenaar  delcommand Program
29571137fedSBram Moolenaar  delcommand Winbar
29645d5f26dSBram Moolenaar
29745d5f26dSBram Moolenaar  nunmap K
2981b9645deSBram Moolenaar
2991b9645deSBram Moolenaar  if has('menu')
30071137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
30171137fedSBram Moolenaar    let curwinid = win_getid(winnr())
30271137fedSBram Moolenaar    for winid in s:winbar_winids
30371137fedSBram Moolenaar      if win_gotoid(winid)
3041b9645deSBram Moolenaar	aunmenu WinBar.Step
3051b9645deSBram Moolenaar	aunmenu WinBar.Next
3061b9645deSBram Moolenaar	aunmenu WinBar.Finish
3071b9645deSBram Moolenaar	aunmenu WinBar.Cont
30860e73f2aSBram Moolenaar	aunmenu WinBar.Stop
3091b9645deSBram Moolenaar	aunmenu WinBar.Eval
3101b9645deSBram Moolenaar      endif
31171137fedSBram Moolenaar    endfor
31271137fedSBram Moolenaar    call win_gotoid(curwinid)
31371137fedSBram Moolenaar    let s:winbar_winids = []
31471137fedSBram Moolenaar
31571137fedSBram Moolenaar    if exists('s:saved_mousemodel')
31671137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
31771137fedSBram Moolenaar      unlet s:saved_mousemodel
31871137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
31971137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
32071137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
32171137fedSBram Moolenaar      aunmenu PopUp.Evaluate
32271137fedSBram Moolenaar    endif
32371137fedSBram Moolenaar  endif
3241b9645deSBram Moolenaar
32545d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
32645d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
32745d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
32845d5f26dSBram Moolenaar  endfor
32938baa3e6SBram Moolenaar  sign undefine debugPC
33038baa3e6SBram Moolenaar  sign undefine debugBreakpoint
33145d5f26dSBram Moolenaar  unlet s:breakpoints
332e09ba7baSBram Moolenaarendfunc
333e09ba7baSBram Moolenaar
334e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
335e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
33660e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
33760e73f2aSBram Moolenaar  " Interrupt to make it work.
33860e73f2aSBram Moolenaar  let do_continue = 0
33960e73f2aSBram Moolenaar  if !s:stopped
34060e73f2aSBram Moolenaar    let do_continue = 1
34160e73f2aSBram Moolenaar    call s:SendCommand('-exec-interrupt')
34260e73f2aSBram Moolenaar    sleep 10m
34360e73f2aSBram Moolenaar  endif
34460e73f2aSBram Moolenaar  call s:SendCommand('-break-insert --source '
34560e73f2aSBram Moolenaar	\ . fnameescape(expand('%:p')) . ' --line ' . line('.'))
34660e73f2aSBram Moolenaar  if do_continue
34760e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
34860e73f2aSBram Moolenaar  endif
349e09ba7baSBram Moolenaarendfunc
350e09ba7baSBram Moolenaar
35171137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
35271137fedSBram Moolenaarfunc s:ClearBreakpoint()
353e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
354e09ba7baSBram Moolenaar  let lnum = line('.')
355e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
356e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
357e09ba7baSBram Moolenaar      call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r")
358e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
359e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
360e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
361e09ba7baSBram Moolenaar      break
362e09ba7baSBram Moolenaar    endif
363e09ba7baSBram Moolenaar  endfor
364e09ba7baSBram Moolenaarendfunc
365e09ba7baSBram Moolenaar
366e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb
367e09ba7baSBram Moolenaarfunc s:SendCommand(cmd)
368e09ba7baSBram Moolenaar  call term_sendkeys(s:commbuf, a:cmd . "\r")
369e09ba7baSBram Moolenaarendfunc
370e09ba7baSBram Moolenaar
37160e73f2aSBram Moolenaarfunc s:Run(args)
37260e73f2aSBram Moolenaar  if a:args != ''
37360e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
37460e73f2aSBram Moolenaar  endif
37560e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
37660e73f2aSBram Moolenaarendfunc
37760e73f2aSBram Moolenaar
37851b0f370SBram Moolenaarfunc s:SendEval(expr)
37951b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
38051b0f370SBram Moolenaar  let s:evalexpr = a:expr
38151b0f370SBram Moolenaarendfunc
38251b0f370SBram Moolenaar
38345d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
38445d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
38545d5f26dSBram Moolenaar  if a:arg != ''
38645d5f26dSBram Moolenaar    let expr = a:arg
38745d5f26dSBram Moolenaar  elseif a:range == 2
38845d5f26dSBram Moolenaar    let pos = getcurpos()
38945d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
39045d5f26dSBram Moolenaar    let regt = getregtype('v')
39145d5f26dSBram Moolenaar    normal! gv"vy
39245d5f26dSBram Moolenaar    let expr = @v
39345d5f26dSBram Moolenaar    call setpos('.', pos)
39445d5f26dSBram Moolenaar    call setreg('v', reg, regt)
39545d5f26dSBram Moolenaar  else
39645d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
39745d5f26dSBram Moolenaar  endif
39822f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
39951b0f370SBram Moolenaar  call s:SendEval(expr)
40045d5f26dSBram Moolenaarendfunc
40145d5f26dSBram Moolenaar
40222f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
40351b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
40451b0f370SBram Moolenaar
40545d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
40645d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
4071b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
4081b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
40951b0f370SBram Moolenaar  if s:evalFromBalloonExpr
41051b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
41151b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
41251b0f370SBram Moolenaar    else
41351b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
41451b0f370SBram Moolenaar    endif
41551b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
41651b0f370SBram Moolenaar  else
4171b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
41851b0f370SBram Moolenaar  endif
4191b9645deSBram Moolenaar
4207f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
4211b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
42222f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
42351b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
42451b0f370SBram Moolenaar  else
42551b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
4261b9645deSBram Moolenaar  endif
42745d5f26dSBram Moolenaarendfunc
42845d5f26dSBram Moolenaar
42951b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
43051b0f370SBram Moolenaar" if there is any.
43151b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
43251b0f370SBram Moolenaar  if v:beval_winid != s:startwin
43351b0f370SBram Moolenaar    return
43451b0f370SBram Moolenaar  endif
43551b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
43651b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
43722f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
43822f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
43951b0f370SBram Moolenaar  return ''
44051b0f370SBram Moolenaarendfunc
44151b0f370SBram Moolenaar
44245d5f26dSBram Moolenaar" Handle an error.
44345d5f26dSBram Moolenaarfunc s:HandleError(msg)
44422f1d0e3SBram Moolenaar  if s:ignoreEvalError
44551b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
44622f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
44722f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
44851b0f370SBram Moolenaar    return
44951b0f370SBram Moolenaar  endif
45045d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
45145d5f26dSBram Moolenaarendfunc
45245d5f26dSBram Moolenaar
453e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
454e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
455e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
456fe386641SBram Moolenaar  let wid = win_getid(winnr())
457fe386641SBram Moolenaar
45860e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
45960e73f2aSBram Moolenaar    let s:stopped = 1
46060e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
46160e73f2aSBram Moolenaar    let s:stopped = 0
46260e73f2aSBram Moolenaar  endif
46360e73f2aSBram Moolenaar
464fe386641SBram Moolenaar  if win_gotoid(s:startwin)
465e09ba7baSBram Moolenaar    let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
4661b9645deSBram Moolenaar    if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
467e09ba7baSBram Moolenaar      let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
468fe386641SBram Moolenaar      if lnum =~ '^[0-9]*$'
4691b9645deSBram Moolenaar	if expand('%:p') != fnamemodify(fname, ':p')
470fe386641SBram Moolenaar	  if &modified
471fe386641SBram Moolenaar	    " TODO: find existing window
472fe386641SBram Moolenaar	    exe 'split ' . fnameescape(fname)
473fe386641SBram Moolenaar	    let s:startwin = win_getid(winnr())
474fe386641SBram Moolenaar	  else
475fe386641SBram Moolenaar	    exe 'edit ' . fnameescape(fname)
476fe386641SBram Moolenaar	  endif
477fe386641SBram Moolenaar	endif
478fe386641SBram Moolenaar	exe lnum
47901164a65SBram Moolenaar	exe 'sign unplace ' . s:pc_id
4801b9645deSBram Moolenaar	exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
481fe386641SBram Moolenaar	setlocal signcolumn=yes
482fe386641SBram Moolenaar      endif
483fe386641SBram Moolenaar    else
484fe386641SBram Moolenaar      exe 'sign unplace ' . s:pc_id
485fe386641SBram Moolenaar    endif
486fe386641SBram Moolenaar
487fe386641SBram Moolenaar    call win_gotoid(wid)
488fe386641SBram Moolenaar  endif
489e09ba7baSBram Moolenaarendfunc
490e09ba7baSBram Moolenaar
491e09ba7baSBram Moolenaar" Handle setting a breakpoint
492e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
493e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
494e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
495e09ba7baSBram Moolenaar  if nr == 0
496e09ba7baSBram Moolenaar    return
497fe386641SBram Moolenaar  endif
498e09ba7baSBram Moolenaar
499e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
500e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
501e09ba7baSBram Moolenaar  else
502e09ba7baSBram Moolenaar    let entry = {}
503e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
504fe386641SBram Moolenaar  endif
505e09ba7baSBram Moolenaar
506e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
507e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
508e09ba7baSBram Moolenaar  let entry['fname'] = fname
509e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
5101b9645deSBram Moolenaar
5111b9645deSBram Moolenaar  if bufloaded(fname)
5121b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
5131b9645deSBram Moolenaar  endif
5141b9645deSBram Moolenaarendfunc
5151b9645deSBram Moolenaar
5161b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
5171b9645deSBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname']
5181b9645deSBram Moolenaar  let a:entry['placed'] = 1
519e09ba7baSBram Moolenaarendfunc
520e09ba7baSBram Moolenaar
521e09ba7baSBram Moolenaar" Handle deleting a breakpoint
522e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
523e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
524e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
525e09ba7baSBram Moolenaar  if nr == 0
526e09ba7baSBram Moolenaar    return
527e09ba7baSBram Moolenaar  endif
5281b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
5291b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
5301b9645deSBram Moolenaar    if has_key(entry, 'placed')
531e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
5321b9645deSBram Moolenaar      unlet entry['placed']
5331b9645deSBram Moolenaar    endif
534e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
5351b9645deSBram Moolenaar  endif
536c572da5fSBram Moolenaarendfunc
5371b9645deSBram Moolenaar
5381b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
5391b9645deSBram Moolenaarfunc s:BufRead()
5401b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5411b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5421b9645deSBram Moolenaar    if entry['fname'] == fname
5431b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
5441b9645deSBram Moolenaar    endif
5451b9645deSBram Moolenaar  endfor
5461b9645deSBram Moolenaarendfunc
5471b9645deSBram Moolenaar
5481b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
5491b9645deSBram Moolenaarfunc s:BufUnloaded()
5501b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5511b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5521b9645deSBram Moolenaar    if entry['fname'] == fname
5531b9645deSBram Moolenaar      let entry['placed'] = 0
5541b9645deSBram Moolenaar    endif
5551b9645deSBram Moolenaar  endfor
5561b9645deSBram Moolenaarendfunc
5571b9645deSBram Moolenaar
558