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'
119f3ba14ffSBram 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
146f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
147f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
148f3ba14ffSBram Moolenaar  call s:SendCommand('-gdb-set pagination off')
149f3ba14ffSBram 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)
249*c4b533e1SBram Moolenaar  command Source call s:GotoStartwinOrCreateIt()
25071137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
25145d5f26dSBram Moolenaar
25245d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
25345d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
2541b9645deSBram Moolenaar
255f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
25671137fedSBram Moolenaar    call s:InstallWinbar()
25771137fedSBram Moolenaar
25871137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
25971137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
26071137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
26171137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
26271137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
26371137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
26471137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
26571137fedSBram Moolenaar    endif
26671137fedSBram Moolenaar  endif
26771137fedSBram Moolenaarendfunc
26871137fedSBram Moolenaar
26971137fedSBram Moolenaarlet s:winbar_winids = []
27071137fedSBram Moolenaar
27171137fedSBram Moolenaar" Install the window toolbar in the current window.
27271137fedSBram Moolenaarfunc s:InstallWinbar()
273*c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
27424a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
27524a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
27624a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
27724a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
27860e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
27924a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
28071137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
281*c4b533e1SBram Moolenaar  endif
282e09ba7baSBram Moolenaarendfunc
283e09ba7baSBram Moolenaar
284e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
285e09ba7baSBram Moolenaarfunc s:DeleteCommands()
286e09ba7baSBram Moolenaar  delcommand Break
28771137fedSBram Moolenaar  delcommand Clear
288e09ba7baSBram Moolenaar  delcommand Step
28945d5f26dSBram Moolenaar  delcommand Over
290e09ba7baSBram Moolenaar  delcommand Finish
29160e73f2aSBram Moolenaar  delcommand Run
29260e73f2aSBram Moolenaar  delcommand Arguments
29360e73f2aSBram Moolenaar  delcommand Stop
294e09ba7baSBram Moolenaar  delcommand Continue
29545d5f26dSBram Moolenaar  delcommand Evaluate
29645d5f26dSBram Moolenaar  delcommand Gdb
29745d5f26dSBram Moolenaar  delcommand Program
29871137fedSBram Moolenaar  delcommand Winbar
29945d5f26dSBram Moolenaar
30045d5f26dSBram Moolenaar  nunmap K
3011b9645deSBram Moolenaar
3021b9645deSBram Moolenaar  if has('menu')
30371137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
30471137fedSBram Moolenaar    let curwinid = win_getid(winnr())
30571137fedSBram Moolenaar    for winid in s:winbar_winids
30671137fedSBram Moolenaar      if win_gotoid(winid)
3071b9645deSBram Moolenaar	aunmenu WinBar.Step
3081b9645deSBram Moolenaar	aunmenu WinBar.Next
3091b9645deSBram Moolenaar	aunmenu WinBar.Finish
3101b9645deSBram Moolenaar	aunmenu WinBar.Cont
31160e73f2aSBram Moolenaar	aunmenu WinBar.Stop
3121b9645deSBram Moolenaar	aunmenu WinBar.Eval
3131b9645deSBram Moolenaar      endif
31471137fedSBram Moolenaar    endfor
31571137fedSBram Moolenaar    call win_gotoid(curwinid)
31671137fedSBram Moolenaar    let s:winbar_winids = []
31771137fedSBram Moolenaar
31871137fedSBram Moolenaar    if exists('s:saved_mousemodel')
31971137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
32071137fedSBram Moolenaar      unlet s:saved_mousemodel
32171137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
32271137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
32371137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
32471137fedSBram Moolenaar      aunmenu PopUp.Evaluate
32571137fedSBram Moolenaar    endif
32671137fedSBram Moolenaar  endif
3271b9645deSBram Moolenaar
32845d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
32945d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
33045d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
33145d5f26dSBram Moolenaar  endfor
33238baa3e6SBram Moolenaar  sign undefine debugPC
33338baa3e6SBram Moolenaar  sign undefine debugBreakpoint
33445d5f26dSBram Moolenaar  unlet s:breakpoints
335e09ba7baSBram Moolenaarendfunc
336e09ba7baSBram Moolenaar
337e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
338e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
33960e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
34060e73f2aSBram Moolenaar  " Interrupt to make it work.
34160e73f2aSBram Moolenaar  let do_continue = 0
34260e73f2aSBram Moolenaar  if !s:stopped
34360e73f2aSBram Moolenaar    let do_continue = 1
34460e73f2aSBram Moolenaar    call s:SendCommand('-exec-interrupt')
34560e73f2aSBram Moolenaar    sleep 10m
34660e73f2aSBram Moolenaar  endif
34760e73f2aSBram Moolenaar  call s:SendCommand('-break-insert --source '
34860e73f2aSBram Moolenaar	\ . fnameescape(expand('%:p')) . ' --line ' . line('.'))
34960e73f2aSBram Moolenaar  if do_continue
35060e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
35160e73f2aSBram Moolenaar  endif
352e09ba7baSBram Moolenaarendfunc
353e09ba7baSBram Moolenaar
35471137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
35571137fedSBram Moolenaarfunc s:ClearBreakpoint()
356e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
357e09ba7baSBram Moolenaar  let lnum = line('.')
358e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
359e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
360e09ba7baSBram Moolenaar      call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r")
361e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
362e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
363e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
364e09ba7baSBram Moolenaar      break
365e09ba7baSBram Moolenaar    endif
366e09ba7baSBram Moolenaar  endfor
367e09ba7baSBram Moolenaarendfunc
368e09ba7baSBram Moolenaar
369e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb
370e09ba7baSBram Moolenaarfunc s:SendCommand(cmd)
371e09ba7baSBram Moolenaar  call term_sendkeys(s:commbuf, a:cmd . "\r")
372e09ba7baSBram Moolenaarendfunc
373e09ba7baSBram Moolenaar
37460e73f2aSBram Moolenaarfunc s:Run(args)
37560e73f2aSBram Moolenaar  if a:args != ''
37660e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
37760e73f2aSBram Moolenaar  endif
37860e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
37960e73f2aSBram Moolenaarendfunc
38060e73f2aSBram Moolenaar
38151b0f370SBram Moolenaarfunc s:SendEval(expr)
38251b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
38351b0f370SBram Moolenaar  let s:evalexpr = a:expr
38451b0f370SBram Moolenaarendfunc
38551b0f370SBram Moolenaar
38645d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
38745d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
38845d5f26dSBram Moolenaar  if a:arg != ''
38945d5f26dSBram Moolenaar    let expr = a:arg
39045d5f26dSBram Moolenaar  elseif a:range == 2
39145d5f26dSBram Moolenaar    let pos = getcurpos()
39245d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
39345d5f26dSBram Moolenaar    let regt = getregtype('v')
39445d5f26dSBram Moolenaar    normal! gv"vy
39545d5f26dSBram Moolenaar    let expr = @v
39645d5f26dSBram Moolenaar    call setpos('.', pos)
39745d5f26dSBram Moolenaar    call setreg('v', reg, regt)
39845d5f26dSBram Moolenaar  else
39945d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
40045d5f26dSBram Moolenaar  endif
40122f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
40251b0f370SBram Moolenaar  call s:SendEval(expr)
40345d5f26dSBram Moolenaarendfunc
40445d5f26dSBram Moolenaar
40522f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
40651b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
40751b0f370SBram Moolenaar
40845d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
40945d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
4101b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
4111b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
41251b0f370SBram Moolenaar  if s:evalFromBalloonExpr
41351b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
41451b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
41551b0f370SBram Moolenaar    else
41651b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
41751b0f370SBram Moolenaar    endif
41851b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
41951b0f370SBram Moolenaar  else
4201b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
42151b0f370SBram Moolenaar  endif
4221b9645deSBram Moolenaar
4237f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
4241b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
42522f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
42651b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
42751b0f370SBram Moolenaar  else
42851b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
4291b9645deSBram Moolenaar  endif
43045d5f26dSBram Moolenaarendfunc
43145d5f26dSBram Moolenaar
43251b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
43351b0f370SBram Moolenaar" if there is any.
43451b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
43551b0f370SBram Moolenaar  if v:beval_winid != s:startwin
43651b0f370SBram Moolenaar    return
43751b0f370SBram Moolenaar  endif
43851b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
43951b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
44022f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
44122f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
44251b0f370SBram Moolenaar  return ''
44351b0f370SBram Moolenaarendfunc
44451b0f370SBram Moolenaar
44545d5f26dSBram Moolenaar" Handle an error.
44645d5f26dSBram Moolenaarfunc s:HandleError(msg)
44722f1d0e3SBram Moolenaar  if s:ignoreEvalError
44851b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
44922f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
45022f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
45151b0f370SBram Moolenaar    return
45251b0f370SBram Moolenaar  endif
45345d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
45445d5f26dSBram Moolenaarendfunc
45545d5f26dSBram Moolenaar
456*c4b533e1SBram Moolenaarfunc s:GotoStartwinOrCreateIt()
457*c4b533e1SBram Moolenaar  if !win_gotoid(s:startwin)
458*c4b533e1SBram Moolenaar    new
459*c4b533e1SBram Moolenaar    let s:startwin = win_getid(winnr())
460*c4b533e1SBram Moolenaar    call s:InstallWinbar()
461*c4b533e1SBram Moolenaar  endif
462*c4b533e1SBram Moolenaarendfunc
463*c4b533e1SBram Moolenaar
464e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
465e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
466e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
467fe386641SBram Moolenaar  let wid = win_getid(winnr())
468fe386641SBram Moolenaar
46960e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
47060e73f2aSBram Moolenaar    let s:stopped = 1
47160e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
47260e73f2aSBram Moolenaar    let s:stopped = 0
47360e73f2aSBram Moolenaar  endif
47460e73f2aSBram Moolenaar
475*c4b533e1SBram Moolenaar  call s:GotoStartwinOrCreateIt()
476*c4b533e1SBram Moolenaar
477e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
4781b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
479e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
480fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
4811b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
482fe386641SBram Moolenaar	if &modified
483fe386641SBram Moolenaar	  " TODO: find existing window
484fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
485fe386641SBram Moolenaar	  let s:startwin = win_getid(winnr())
486*c4b533e1SBram Moolenaar	  call s:InstallWinbar()
487fe386641SBram Moolenaar	else
488fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
489fe386641SBram Moolenaar	endif
490fe386641SBram Moolenaar      endif
491fe386641SBram Moolenaar      exe lnum
49201164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
4931b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
494fe386641SBram Moolenaar      setlocal signcolumn=yes
495fe386641SBram Moolenaar    endif
496fe386641SBram Moolenaar  else
497fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
498fe386641SBram Moolenaar  endif
499fe386641SBram Moolenaar
500fe386641SBram Moolenaar  call win_gotoid(wid)
501e09ba7baSBram Moolenaarendfunc
502e09ba7baSBram Moolenaar
503e09ba7baSBram Moolenaar" Handle setting a breakpoint
504e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
505e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
506e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
507e09ba7baSBram Moolenaar  if nr == 0
508e09ba7baSBram Moolenaar    return
509fe386641SBram Moolenaar  endif
510e09ba7baSBram Moolenaar
511e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
512e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
513e09ba7baSBram Moolenaar  else
514e09ba7baSBram Moolenaar    let entry = {}
515e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
516fe386641SBram Moolenaar  endif
517e09ba7baSBram Moolenaar
518e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
519e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
520e09ba7baSBram Moolenaar  let entry['fname'] = fname
521e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
5221b9645deSBram Moolenaar
5231b9645deSBram Moolenaar  if bufloaded(fname)
5241b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
5251b9645deSBram Moolenaar  endif
5261b9645deSBram Moolenaarendfunc
5271b9645deSBram Moolenaar
5281b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
5291b9645deSBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname']
5301b9645deSBram Moolenaar  let a:entry['placed'] = 1
531e09ba7baSBram Moolenaarendfunc
532e09ba7baSBram Moolenaar
533e09ba7baSBram Moolenaar" Handle deleting a breakpoint
534e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
535e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
536e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
537e09ba7baSBram Moolenaar  if nr == 0
538e09ba7baSBram Moolenaar    return
539e09ba7baSBram Moolenaar  endif
5401b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
5411b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
5421b9645deSBram Moolenaar    if has_key(entry, 'placed')
543e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
5441b9645deSBram Moolenaar      unlet entry['placed']
5451b9645deSBram Moolenaar    endif
546e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
5471b9645deSBram Moolenaar  endif
548c572da5fSBram Moolenaarendfunc
5491b9645deSBram Moolenaar
5501b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
5511b9645deSBram Moolenaarfunc s:BufRead()
5521b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5531b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5541b9645deSBram Moolenaar    if entry['fname'] == fname
5551b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
5561b9645deSBram Moolenaar    endif
5571b9645deSBram Moolenaar  endfor
5581b9645deSBram Moolenaarendfunc
5591b9645deSBram Moolenaar
5601b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
5611b9645deSBram Moolenaarfunc s:BufUnloaded()
5621b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5631b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5641b9645deSBram Moolenaar    if entry['fname'] == fname
5651b9645deSBram Moolenaar      let entry['placed'] = 0
5661b9645deSBram Moolenaar    endif
5671b9645deSBram Moolenaar  endfor
5681b9645deSBram Moolenaarendfunc
5691b9645deSBram Moolenaar
570