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
110*3e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
111*3e4b84d0SBram Moolenaar  " why the debugger doesn't work.
112*3e4b84d0SBram Moolenaar  let try_count = 0
113*3e4b84d0SBram Moolenaar  while 1
114*3e4b84d0SBram Moolenaar    let response = ''
115*3e4b84d0SBram Moolenaar    for lnum in range(1,20)
116*3e4b84d0SBram Moolenaar      if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi '
117*3e4b84d0SBram Moolenaar	let response = term_getline(s:gdbbuf, lnum + 1)
118*3e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
119*3e4b84d0SBram Moolenaar	  echoerr 'Your gdb does not support the Machine Interface feature'
120*3e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:ptybuf
121*3e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:commbuf
122*3e4b84d0SBram Moolenaar	  return
123*3e4b84d0SBram Moolenaar	endif
124*3e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
125*3e4b84d0SBram Moolenaar	  " Success!
126*3e4b84d0SBram Moolenaar	  break
127*3e4b84d0SBram Moolenaar	endif
128*3e4b84d0SBram Moolenaar      endif
129*3e4b84d0SBram Moolenaar    endfor
130*3e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
131*3e4b84d0SBram Moolenaar      break
132*3e4b84d0SBram Moolenaar    endif
133*3e4b84d0SBram Moolenaar    let try_count += 1
134*3e4b84d0SBram Moolenaar    if try_count > 100
135*3e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
136*3e4b84d0SBram Moolenaar      break
137*3e4b84d0SBram Moolenaar    endif
138*3e4b84d0SBram Moolenaar    sleep 10m
139*3e4b84d0SBram Moolenaar  endwhile
140*3e4b84d0SBram 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
14638baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
14738baa3e6SBram Moolenaar  " There can be only one.
14838baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
14938baa3e6SBram Moolenaar
15038baa3e6SBram Moolenaar  " Sign used to indicate a breakpoint.
15138baa3e6SBram Moolenaar  " Can be used multiple times.
15238baa3e6SBram Moolenaar  sign define debugBreakpoint text=>> texthl=debugBreakpoint
15338baa3e6SBram Moolenaar
15445d5f26dSBram Moolenaar  " Install debugger commands in the text window.
15545d5f26dSBram Moolenaar  call win_gotoid(s:startwin)
156e09ba7baSBram Moolenaar  call s:InstallCommands()
15745d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
158e09ba7baSBram Moolenaar
15951b0f370SBram Moolenaar  " Enable showing a balloon with eval info
160246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
161246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
16251b0f370SBram Moolenaar    if has("balloon_eval")
16351b0f370SBram Moolenaar      set ballooneval
164246fe03dSBram Moolenaar    endif
16551b0f370SBram Moolenaar    if has("balloon_eval_term")
16651b0f370SBram Moolenaar      set balloonevalterm
16751b0f370SBram Moolenaar    endif
16851b0f370SBram Moolenaar  endif
16951b0f370SBram Moolenaar
170e09ba7baSBram Moolenaar  let s:breakpoints = {}
1711b9645deSBram Moolenaar
1721b9645deSBram Moolenaar  augroup TermDebug
1731b9645deSBram Moolenaar    au BufRead * call s:BufRead()
1741b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
1751b9645deSBram Moolenaar  augroup END
176c572da5fSBram Moolenaarendfunc
177c572da5fSBram Moolenaar
178c572da5fSBram Moolenaarfunc s:EndDebug(job, status)
179c572da5fSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
180fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
181e09ba7baSBram Moolenaar
182e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
183e09ba7baSBram Moolenaar
184e09ba7baSBram Moolenaar  call win_gotoid(s:startwin)
185e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
186e09ba7baSBram Moolenaar  call s:DeleteCommands()
187e09ba7baSBram Moolenaar
188e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
18938baa3e6SBram Moolenaar  if s:save_columns > 0
19038baa3e6SBram Moolenaar    let &columns = s:save_columns
19138baa3e6SBram Moolenaar  endif
1921b9645deSBram Moolenaar
193246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
194246fe03dSBram Moolenaar    set balloonexpr=
19551b0f370SBram Moolenaar    if has("balloon_eval")
19651b0f370SBram Moolenaar      set noballooneval
197246fe03dSBram Moolenaar    endif
19851b0f370SBram Moolenaar    if has("balloon_eval_term")
19951b0f370SBram Moolenaar      set noballoonevalterm
20051b0f370SBram Moolenaar    endif
20151b0f370SBram Moolenaar  endif
20251b0f370SBram Moolenaar
2031b9645deSBram Moolenaar  au! TermDebug
204fe386641SBram Moolenaarendfunc
205fe386641SBram Moolenaar
206fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
207fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
208fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
209fe386641SBram Moolenaar
210fe386641SBram Moolenaar  for msg in msgs
211fe386641SBram Moolenaar    " remove prefixed NL
212fe386641SBram Moolenaar    if msg[0] == "\n"
213fe386641SBram Moolenaar      let msg = msg[1:]
214fe386641SBram Moolenaar    endif
215fe386641SBram Moolenaar    if msg != ''
2161b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
217e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
21845d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
219e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
220e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
221e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
22245d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
22345d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
22445d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
22545d5f26dSBram Moolenaar	call s:HandleError(msg)
226e09ba7baSBram Moolenaar      endif
227e09ba7baSBram Moolenaar    endif
228e09ba7baSBram Moolenaar  endfor
229e09ba7baSBram Moolenaarendfunc
230e09ba7baSBram Moolenaar
231e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
232e09ba7baSBram Moolenaarfunc s:InstallCommands()
233e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
23471137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
235e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
23645d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
237e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
23860e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
23960e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
24060e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
241e09ba7baSBram Moolenaar  command Continue call s:SendCommand('-exec-continue')
24245d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
24345d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
24445d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
24571137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
24645d5f26dSBram Moolenaar
24745d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
24845d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
2491b9645deSBram Moolenaar
250f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
25171137fedSBram Moolenaar    call s:InstallWinbar()
25271137fedSBram Moolenaar
25371137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
25471137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
25571137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
25671137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
25771137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
25871137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
25971137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
26071137fedSBram Moolenaar    endif
26171137fedSBram Moolenaar  endif
26271137fedSBram Moolenaarendfunc
26371137fedSBram Moolenaar
26471137fedSBram Moolenaarlet s:winbar_winids = []
26571137fedSBram Moolenaar
26671137fedSBram Moolenaar" Install the window toolbar in the current window.
26771137fedSBram Moolenaarfunc s:InstallWinbar()
26824a98a0eSBram Moolenaar  nnoremenu WinBar.Step   :Step<CR>
26924a98a0eSBram Moolenaar  nnoremenu WinBar.Next   :Over<CR>
27024a98a0eSBram Moolenaar  nnoremenu WinBar.Finish :Finish<CR>
27124a98a0eSBram Moolenaar  nnoremenu WinBar.Cont   :Continue<CR>
27260e73f2aSBram Moolenaar  nnoremenu WinBar.Stop   :Stop<CR>
27324a98a0eSBram Moolenaar  nnoremenu WinBar.Eval   :Evaluate<CR>
27471137fedSBram Moolenaar  call add(s:winbar_winids, win_getid(winnr()))
275e09ba7baSBram Moolenaarendfunc
276e09ba7baSBram Moolenaar
277e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
278e09ba7baSBram Moolenaarfunc s:DeleteCommands()
279e09ba7baSBram Moolenaar  delcommand Break
28071137fedSBram Moolenaar  delcommand Clear
281e09ba7baSBram Moolenaar  delcommand Step
28245d5f26dSBram Moolenaar  delcommand Over
283e09ba7baSBram Moolenaar  delcommand Finish
28460e73f2aSBram Moolenaar  delcommand Run
28560e73f2aSBram Moolenaar  delcommand Arguments
28660e73f2aSBram Moolenaar  delcommand Stop
287e09ba7baSBram Moolenaar  delcommand Continue
28845d5f26dSBram Moolenaar  delcommand Evaluate
28945d5f26dSBram Moolenaar  delcommand Gdb
29045d5f26dSBram Moolenaar  delcommand Program
29171137fedSBram Moolenaar  delcommand Winbar
29245d5f26dSBram Moolenaar
29345d5f26dSBram Moolenaar  nunmap K
2941b9645deSBram Moolenaar
2951b9645deSBram Moolenaar  if has('menu')
29671137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
29771137fedSBram Moolenaar    let curwinid = win_getid(winnr())
29871137fedSBram Moolenaar    for winid in s:winbar_winids
29971137fedSBram Moolenaar      if win_gotoid(winid)
3001b9645deSBram Moolenaar	aunmenu WinBar.Step
3011b9645deSBram Moolenaar	aunmenu WinBar.Next
3021b9645deSBram Moolenaar	aunmenu WinBar.Finish
3031b9645deSBram Moolenaar	aunmenu WinBar.Cont
30460e73f2aSBram Moolenaar	aunmenu WinBar.Stop
3051b9645deSBram Moolenaar	aunmenu WinBar.Eval
3061b9645deSBram Moolenaar      endif
30771137fedSBram Moolenaar    endfor
30871137fedSBram Moolenaar    call win_gotoid(curwinid)
30971137fedSBram Moolenaar    let s:winbar_winids = []
31071137fedSBram Moolenaar
31171137fedSBram Moolenaar    if exists('s:saved_mousemodel')
31271137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
31371137fedSBram Moolenaar      unlet s:saved_mousemodel
31471137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
31571137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
31671137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
31771137fedSBram Moolenaar      aunmenu PopUp.Evaluate
31871137fedSBram Moolenaar    endif
31971137fedSBram Moolenaar  endif
3201b9645deSBram Moolenaar
32145d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
32245d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
32345d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
32445d5f26dSBram Moolenaar  endfor
32538baa3e6SBram Moolenaar  sign undefine debugPC
32638baa3e6SBram Moolenaar  sign undefine debugBreakpoint
32745d5f26dSBram Moolenaar  unlet s:breakpoints
328e09ba7baSBram Moolenaarendfunc
329e09ba7baSBram Moolenaar
330e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
331e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
33260e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
33360e73f2aSBram Moolenaar  " Interrupt to make it work.
33460e73f2aSBram Moolenaar  let do_continue = 0
33560e73f2aSBram Moolenaar  if !s:stopped
33660e73f2aSBram Moolenaar    let do_continue = 1
33760e73f2aSBram Moolenaar    call s:SendCommand('-exec-interrupt')
33860e73f2aSBram Moolenaar    sleep 10m
33960e73f2aSBram Moolenaar  endif
34060e73f2aSBram Moolenaar  call s:SendCommand('-break-insert --source '
34160e73f2aSBram Moolenaar	\ . fnameescape(expand('%:p')) . ' --line ' . line('.'))
34260e73f2aSBram Moolenaar  if do_continue
34360e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
34460e73f2aSBram Moolenaar  endif
345e09ba7baSBram Moolenaarendfunc
346e09ba7baSBram Moolenaar
34771137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
34871137fedSBram Moolenaarfunc s:ClearBreakpoint()
349e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
350e09ba7baSBram Moolenaar  let lnum = line('.')
351e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
352e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
353e09ba7baSBram Moolenaar      call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r")
354e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
355e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
356e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
357e09ba7baSBram Moolenaar      break
358e09ba7baSBram Moolenaar    endif
359e09ba7baSBram Moolenaar  endfor
360e09ba7baSBram Moolenaarendfunc
361e09ba7baSBram Moolenaar
362e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb
363e09ba7baSBram Moolenaarfunc s:SendCommand(cmd)
364e09ba7baSBram Moolenaar  call term_sendkeys(s:commbuf, a:cmd . "\r")
365e09ba7baSBram Moolenaarendfunc
366e09ba7baSBram Moolenaar
36760e73f2aSBram Moolenaarfunc s:Run(args)
36860e73f2aSBram Moolenaar  if a:args != ''
36960e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
37060e73f2aSBram Moolenaar  endif
37160e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
37260e73f2aSBram Moolenaarendfunc
37360e73f2aSBram Moolenaar
37451b0f370SBram Moolenaarfunc s:SendEval(expr)
37551b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
37651b0f370SBram Moolenaar  let s:evalexpr = a:expr
37751b0f370SBram Moolenaarendfunc
37851b0f370SBram Moolenaar
37945d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
38045d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
38145d5f26dSBram Moolenaar  if a:arg != ''
38245d5f26dSBram Moolenaar    let expr = a:arg
38345d5f26dSBram Moolenaar  elseif a:range == 2
38445d5f26dSBram Moolenaar    let pos = getcurpos()
38545d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
38645d5f26dSBram Moolenaar    let regt = getregtype('v')
38745d5f26dSBram Moolenaar    normal! gv"vy
38845d5f26dSBram Moolenaar    let expr = @v
38945d5f26dSBram Moolenaar    call setpos('.', pos)
39045d5f26dSBram Moolenaar    call setreg('v', reg, regt)
39145d5f26dSBram Moolenaar  else
39245d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
39345d5f26dSBram Moolenaar  endif
39422f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
39551b0f370SBram Moolenaar  call s:SendEval(expr)
39645d5f26dSBram Moolenaarendfunc
39745d5f26dSBram Moolenaar
39822f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
39951b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
40051b0f370SBram Moolenaar
40145d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
40245d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
4031b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
4041b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
40551b0f370SBram Moolenaar  if s:evalFromBalloonExpr
40651b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
40751b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
40851b0f370SBram Moolenaar    else
40951b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
41051b0f370SBram Moolenaar    endif
41151b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
41251b0f370SBram Moolenaar  else
4131b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
41451b0f370SBram Moolenaar  endif
4151b9645deSBram Moolenaar
4167f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
4171b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
41822f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
41951b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
42051b0f370SBram Moolenaar  else
42151b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
4221b9645deSBram Moolenaar  endif
42345d5f26dSBram Moolenaarendfunc
42445d5f26dSBram Moolenaar
42551b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
42651b0f370SBram Moolenaar" if there is any.
42751b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
42851b0f370SBram Moolenaar  if v:beval_winid != s:startwin
42951b0f370SBram Moolenaar    return
43051b0f370SBram Moolenaar  endif
43151b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
43251b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
43322f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
43422f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
43551b0f370SBram Moolenaar  return ''
43651b0f370SBram Moolenaarendfunc
43751b0f370SBram Moolenaar
43845d5f26dSBram Moolenaar" Handle an error.
43945d5f26dSBram Moolenaarfunc s:HandleError(msg)
44022f1d0e3SBram Moolenaar  if s:ignoreEvalError
44151b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
44222f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
44322f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
44451b0f370SBram Moolenaar    return
44551b0f370SBram Moolenaar  endif
44645d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
44745d5f26dSBram Moolenaarendfunc
44845d5f26dSBram Moolenaar
449e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
450e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
451e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
452fe386641SBram Moolenaar  let wid = win_getid(winnr())
453fe386641SBram Moolenaar
45460e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
45560e73f2aSBram Moolenaar    let s:stopped = 1
45660e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
45760e73f2aSBram Moolenaar    let s:stopped = 0
45860e73f2aSBram Moolenaar  endif
45960e73f2aSBram Moolenaar
460fe386641SBram Moolenaar  if win_gotoid(s:startwin)
461e09ba7baSBram Moolenaar    let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
4621b9645deSBram Moolenaar    if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
463e09ba7baSBram Moolenaar      let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
464fe386641SBram Moolenaar      if lnum =~ '^[0-9]*$'
4651b9645deSBram Moolenaar	if expand('%:p') != fnamemodify(fname, ':p')
466fe386641SBram Moolenaar	  if &modified
467fe386641SBram Moolenaar	    " TODO: find existing window
468fe386641SBram Moolenaar	    exe 'split ' . fnameescape(fname)
469fe386641SBram Moolenaar	    let s:startwin = win_getid(winnr())
470fe386641SBram Moolenaar	  else
471fe386641SBram Moolenaar	    exe 'edit ' . fnameescape(fname)
472fe386641SBram Moolenaar	  endif
473fe386641SBram Moolenaar	endif
474fe386641SBram Moolenaar	exe lnum
47501164a65SBram Moolenaar	exe 'sign unplace ' . s:pc_id
4761b9645deSBram Moolenaar	exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
477fe386641SBram Moolenaar	setlocal signcolumn=yes
478fe386641SBram Moolenaar      endif
479fe386641SBram Moolenaar    else
480fe386641SBram Moolenaar      exe 'sign unplace ' . s:pc_id
481fe386641SBram Moolenaar    endif
482fe386641SBram Moolenaar
483fe386641SBram Moolenaar    call win_gotoid(wid)
484fe386641SBram Moolenaar  endif
485e09ba7baSBram Moolenaarendfunc
486e09ba7baSBram Moolenaar
487e09ba7baSBram Moolenaar" Handle setting a breakpoint
488e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
489e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
490e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
491e09ba7baSBram Moolenaar  if nr == 0
492e09ba7baSBram Moolenaar    return
493fe386641SBram Moolenaar  endif
494e09ba7baSBram Moolenaar
495e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
496e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
497e09ba7baSBram Moolenaar  else
498e09ba7baSBram Moolenaar    let entry = {}
499e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
500fe386641SBram Moolenaar  endif
501e09ba7baSBram Moolenaar
502e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
503e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
504e09ba7baSBram Moolenaar  let entry['fname'] = fname
505e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
5061b9645deSBram Moolenaar
5071b9645deSBram Moolenaar  if bufloaded(fname)
5081b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
5091b9645deSBram Moolenaar  endif
5101b9645deSBram Moolenaarendfunc
5111b9645deSBram Moolenaar
5121b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
5131b9645deSBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname']
5141b9645deSBram Moolenaar  let a:entry['placed'] = 1
515e09ba7baSBram Moolenaarendfunc
516e09ba7baSBram Moolenaar
517e09ba7baSBram Moolenaar" Handle deleting a breakpoint
518e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
519e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
520e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
521e09ba7baSBram Moolenaar  if nr == 0
522e09ba7baSBram Moolenaar    return
523e09ba7baSBram Moolenaar  endif
5241b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
5251b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
5261b9645deSBram Moolenaar    if has_key(entry, 'placed')
527e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
5281b9645deSBram Moolenaar      unlet entry['placed']
5291b9645deSBram Moolenaar    endif
530e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
5311b9645deSBram Moolenaar  endif
532c572da5fSBram Moolenaarendfunc
5331b9645deSBram Moolenaar
5341b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
5351b9645deSBram Moolenaarfunc s:BufRead()
5361b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5371b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5381b9645deSBram Moolenaar    if entry['fname'] == fname
5391b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
5401b9645deSBram Moolenaar    endif
5411b9645deSBram Moolenaar  endfor
5421b9645deSBram Moolenaarendfunc
5431b9645deSBram Moolenaar
5441b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
5451b9645deSBram Moolenaarfunc s:BufUnloaded()
5461b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5471b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5481b9645deSBram Moolenaar    if entry['fname'] == fname
5491b9645deSBram Moolenaar      let entry['placed'] = 0
5501b9645deSBram Moolenaar    endif
5511b9645deSBram Moolenaar  endfor
5521b9645deSBram Moolenaarendfunc
5531b9645deSBram Moolenaar
554