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
10801164a65SBram Moolenaar  " If you get an error "undefined command" your GDB is too old.
10960e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
11060e73f2aSBram Moolenaar
11160e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
11260e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
11360e73f2aSBram Moolenaar  " running.
11460e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
115e09ba7baSBram Moolenaar
11638baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
11738baa3e6SBram Moolenaar  " There can be only one.
11838baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
11938baa3e6SBram Moolenaar
12038baa3e6SBram Moolenaar  " Sign used to indicate a breakpoint.
12138baa3e6SBram Moolenaar  " Can be used multiple times.
12238baa3e6SBram Moolenaar  sign define debugBreakpoint text=>> texthl=debugBreakpoint
12338baa3e6SBram Moolenaar
12445d5f26dSBram Moolenaar  " Install debugger commands in the text window.
12545d5f26dSBram Moolenaar  call win_gotoid(s:startwin)
126e09ba7baSBram Moolenaar  call s:InstallCommands()
12745d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
128e09ba7baSBram Moolenaar
12951b0f370SBram Moolenaar  " Enable showing a balloon with eval info
130246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
131246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
13251b0f370SBram Moolenaar    if has("balloon_eval")
13351b0f370SBram Moolenaar      set ballooneval
134246fe03dSBram Moolenaar    endif
13551b0f370SBram Moolenaar    if has("balloon_eval_term")
13651b0f370SBram Moolenaar      set balloonevalterm
13751b0f370SBram Moolenaar    endif
13851b0f370SBram Moolenaar  endif
13951b0f370SBram Moolenaar
140e09ba7baSBram Moolenaar  let s:breakpoints = {}
1411b9645deSBram Moolenaar
1421b9645deSBram Moolenaar  augroup TermDebug
1431b9645deSBram Moolenaar    au BufRead * call s:BufRead()
1441b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
1451b9645deSBram Moolenaar  augroup END
146c572da5fSBram Moolenaarendfunc
147c572da5fSBram Moolenaar
148c572da5fSBram Moolenaarfunc s:EndDebug(job, status)
149c572da5fSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
150fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
151e09ba7baSBram Moolenaar
152e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
153e09ba7baSBram Moolenaar
154e09ba7baSBram Moolenaar  call win_gotoid(s:startwin)
155e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
156e09ba7baSBram Moolenaar  call s:DeleteCommands()
157e09ba7baSBram Moolenaar
158e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
15938baa3e6SBram Moolenaar  if s:save_columns > 0
16038baa3e6SBram Moolenaar    let &columns = s:save_columns
16138baa3e6SBram Moolenaar  endif
1621b9645deSBram Moolenaar
163246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
164246fe03dSBram Moolenaar    set balloonexpr=
16551b0f370SBram Moolenaar    if has("balloon_eval")
16651b0f370SBram Moolenaar      set noballooneval
167246fe03dSBram Moolenaar    endif
16851b0f370SBram Moolenaar    if has("balloon_eval_term")
16951b0f370SBram Moolenaar      set noballoonevalterm
17051b0f370SBram Moolenaar    endif
17151b0f370SBram Moolenaar  endif
17251b0f370SBram Moolenaar
1731b9645deSBram Moolenaar  au! TermDebug
174fe386641SBram Moolenaarendfunc
175fe386641SBram Moolenaar
176fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
177fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
178fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
179fe386641SBram Moolenaar
180fe386641SBram Moolenaar  for msg in msgs
181fe386641SBram Moolenaar    " remove prefixed NL
182fe386641SBram Moolenaar    if msg[0] == "\n"
183fe386641SBram Moolenaar      let msg = msg[1:]
184fe386641SBram Moolenaar    endif
185fe386641SBram Moolenaar    if msg != ''
1861b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
187e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
18845d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
189e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
190e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
191e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
19245d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
19345d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
19445d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
19545d5f26dSBram Moolenaar	call s:HandleError(msg)
196e09ba7baSBram Moolenaar      endif
197e09ba7baSBram Moolenaar    endif
198e09ba7baSBram Moolenaar  endfor
199e09ba7baSBram Moolenaarendfunc
200e09ba7baSBram Moolenaar
201e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
202e09ba7baSBram Moolenaarfunc s:InstallCommands()
203e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
204*71137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
205e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
20645d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
207e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
20860e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
20960e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
21060e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
211e09ba7baSBram Moolenaar  command Continue call s:SendCommand('-exec-continue')
21245d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
21345d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
21445d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
215*71137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
21645d5f26dSBram Moolenaar
21745d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
21845d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
2191b9645deSBram Moolenaar
220f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
221*71137fedSBram Moolenaar    call s:InstallWinbar()
222*71137fedSBram Moolenaar
223*71137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
224*71137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
225*71137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
226*71137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
227*71137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
228*71137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
229*71137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
230*71137fedSBram Moolenaar    endif
231*71137fedSBram Moolenaar  endif
232*71137fedSBram Moolenaarendfunc
233*71137fedSBram Moolenaar
234*71137fedSBram Moolenaarlet s:winbar_winids = []
235*71137fedSBram Moolenaar
236*71137fedSBram Moolenaar" Install the window toolbar in the current window.
237*71137fedSBram Moolenaarfunc s:InstallWinbar()
23824a98a0eSBram Moolenaar  nnoremenu WinBar.Step   :Step<CR>
23924a98a0eSBram Moolenaar  nnoremenu WinBar.Next   :Over<CR>
24024a98a0eSBram Moolenaar  nnoremenu WinBar.Finish :Finish<CR>
24124a98a0eSBram Moolenaar  nnoremenu WinBar.Cont   :Continue<CR>
24260e73f2aSBram Moolenaar  nnoremenu WinBar.Stop   :Stop<CR>
24324a98a0eSBram Moolenaar  nnoremenu WinBar.Eval   :Evaluate<CR>
244*71137fedSBram Moolenaar  call add(s:winbar_winids, win_getid(winnr()))
245e09ba7baSBram Moolenaarendfunc
246e09ba7baSBram Moolenaar
247e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
248e09ba7baSBram Moolenaarfunc s:DeleteCommands()
249e09ba7baSBram Moolenaar  delcommand Break
250*71137fedSBram Moolenaar  delcommand Clear
251e09ba7baSBram Moolenaar  delcommand Step
25245d5f26dSBram Moolenaar  delcommand Over
253e09ba7baSBram Moolenaar  delcommand Finish
25460e73f2aSBram Moolenaar  delcommand Run
25560e73f2aSBram Moolenaar  delcommand Arguments
25660e73f2aSBram Moolenaar  delcommand Stop
257e09ba7baSBram Moolenaar  delcommand Continue
25845d5f26dSBram Moolenaar  delcommand Evaluate
25945d5f26dSBram Moolenaar  delcommand Gdb
26045d5f26dSBram Moolenaar  delcommand Program
261*71137fedSBram Moolenaar  delcommand Winbar
26245d5f26dSBram Moolenaar
26345d5f26dSBram Moolenaar  nunmap K
2641b9645deSBram Moolenaar
2651b9645deSBram Moolenaar  if has('menu')
266*71137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
267*71137fedSBram Moolenaar    let curwinid = win_getid(winnr())
268*71137fedSBram Moolenaar    for winid in s:winbar_winids
269*71137fedSBram Moolenaar      if win_gotoid(winid)
2701b9645deSBram Moolenaar	aunmenu WinBar.Step
2711b9645deSBram Moolenaar	aunmenu WinBar.Next
2721b9645deSBram Moolenaar	aunmenu WinBar.Finish
2731b9645deSBram Moolenaar	aunmenu WinBar.Cont
27460e73f2aSBram Moolenaar	aunmenu WinBar.Stop
2751b9645deSBram Moolenaar	aunmenu WinBar.Eval
2761b9645deSBram Moolenaar      endif
277*71137fedSBram Moolenaar    endfor
278*71137fedSBram Moolenaar    call win_gotoid(curwinid)
279*71137fedSBram Moolenaar    let s:winbar_winids = []
280*71137fedSBram Moolenaar
281*71137fedSBram Moolenaar    if exists('s:saved_mousemodel')
282*71137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
283*71137fedSBram Moolenaar      unlet s:saved_mousemodel
284*71137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
285*71137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
286*71137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
287*71137fedSBram Moolenaar      aunmenu PopUp.Evaluate
288*71137fedSBram Moolenaar    endif
289*71137fedSBram Moolenaar  endif
2901b9645deSBram Moolenaar
29145d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
29245d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
29345d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
29445d5f26dSBram Moolenaar  endfor
29538baa3e6SBram Moolenaar  sign undefine debugPC
29638baa3e6SBram Moolenaar  sign undefine debugBreakpoint
29745d5f26dSBram Moolenaar  unlet s:breakpoints
298e09ba7baSBram Moolenaarendfunc
299e09ba7baSBram Moolenaar
300e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
301e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
30260e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
30360e73f2aSBram Moolenaar  " Interrupt to make it work.
30460e73f2aSBram Moolenaar  let do_continue = 0
30560e73f2aSBram Moolenaar  if !s:stopped
30660e73f2aSBram Moolenaar    let do_continue = 1
30760e73f2aSBram Moolenaar    call s:SendCommand('-exec-interrupt')
30860e73f2aSBram Moolenaar    sleep 10m
30960e73f2aSBram Moolenaar  endif
31060e73f2aSBram Moolenaar  call s:SendCommand('-break-insert --source '
31160e73f2aSBram Moolenaar	\ . fnameescape(expand('%:p')) . ' --line ' . line('.'))
31260e73f2aSBram Moolenaar  if do_continue
31360e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
31460e73f2aSBram Moolenaar  endif
315e09ba7baSBram Moolenaarendfunc
316e09ba7baSBram Moolenaar
317*71137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
318*71137fedSBram Moolenaarfunc s:ClearBreakpoint()
319e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
320e09ba7baSBram Moolenaar  let lnum = line('.')
321e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
322e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
323e09ba7baSBram Moolenaar      call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r")
324e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
325e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
326e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
327e09ba7baSBram Moolenaar      break
328e09ba7baSBram Moolenaar    endif
329e09ba7baSBram Moolenaar  endfor
330e09ba7baSBram Moolenaarendfunc
331e09ba7baSBram Moolenaar
332e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb
333e09ba7baSBram Moolenaarfunc s:SendCommand(cmd)
334e09ba7baSBram Moolenaar  call term_sendkeys(s:commbuf, a:cmd . "\r")
335e09ba7baSBram Moolenaarendfunc
336e09ba7baSBram Moolenaar
33760e73f2aSBram Moolenaarfunc s:Run(args)
33860e73f2aSBram Moolenaar  if a:args != ''
33960e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
34060e73f2aSBram Moolenaar  endif
34160e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
34260e73f2aSBram Moolenaarendfunc
34360e73f2aSBram Moolenaar
34451b0f370SBram Moolenaarfunc s:SendEval(expr)
34551b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
34651b0f370SBram Moolenaar  let s:evalexpr = a:expr
34751b0f370SBram Moolenaarendfunc
34851b0f370SBram Moolenaar
34945d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
35045d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
35145d5f26dSBram Moolenaar  if a:arg != ''
35245d5f26dSBram Moolenaar    let expr = a:arg
35345d5f26dSBram Moolenaar  elseif a:range == 2
35445d5f26dSBram Moolenaar    let pos = getcurpos()
35545d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
35645d5f26dSBram Moolenaar    let regt = getregtype('v')
35745d5f26dSBram Moolenaar    normal! gv"vy
35845d5f26dSBram Moolenaar    let expr = @v
35945d5f26dSBram Moolenaar    call setpos('.', pos)
36045d5f26dSBram Moolenaar    call setreg('v', reg, regt)
36145d5f26dSBram Moolenaar  else
36245d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
36345d5f26dSBram Moolenaar  endif
36422f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
36551b0f370SBram Moolenaar  call s:SendEval(expr)
36645d5f26dSBram Moolenaarendfunc
36745d5f26dSBram Moolenaar
36822f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
36951b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
37051b0f370SBram Moolenaar
37145d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
37245d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
3731b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
3741b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
37551b0f370SBram Moolenaar  if s:evalFromBalloonExpr
37651b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
37751b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
37851b0f370SBram Moolenaar    else
37951b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
38051b0f370SBram Moolenaar    endif
38151b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
38251b0f370SBram Moolenaar  else
3831b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
38451b0f370SBram Moolenaar  endif
3851b9645deSBram Moolenaar
3867f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
3871b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
38822f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
38951b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
39051b0f370SBram Moolenaar  else
39151b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
3921b9645deSBram Moolenaar  endif
39345d5f26dSBram Moolenaarendfunc
39445d5f26dSBram Moolenaar
39551b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
39651b0f370SBram Moolenaar" if there is any.
39751b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
39851b0f370SBram Moolenaar  if v:beval_winid != s:startwin
39951b0f370SBram Moolenaar    return
40051b0f370SBram Moolenaar  endif
40151b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
40251b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
40322f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
40422f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
40551b0f370SBram Moolenaar  return ''
40651b0f370SBram Moolenaarendfunc
40751b0f370SBram Moolenaar
40845d5f26dSBram Moolenaar" Handle an error.
40945d5f26dSBram Moolenaarfunc s:HandleError(msg)
41022f1d0e3SBram Moolenaar  if s:ignoreEvalError
41151b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
41222f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
41322f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
41451b0f370SBram Moolenaar    return
41551b0f370SBram Moolenaar  endif
41645d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
41745d5f26dSBram Moolenaarendfunc
41845d5f26dSBram Moolenaar
419e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
420e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
421e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
422fe386641SBram Moolenaar  let wid = win_getid(winnr())
423fe386641SBram Moolenaar
42460e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
42560e73f2aSBram Moolenaar    let s:stopped = 1
42660e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
42760e73f2aSBram Moolenaar    let s:stopped = 0
42860e73f2aSBram Moolenaar  endif
42960e73f2aSBram Moolenaar
430fe386641SBram Moolenaar  if win_gotoid(s:startwin)
431e09ba7baSBram Moolenaar    let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
4321b9645deSBram Moolenaar    if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
433e09ba7baSBram Moolenaar      let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
434fe386641SBram Moolenaar      if lnum =~ '^[0-9]*$'
4351b9645deSBram Moolenaar	if expand('%:p') != fnamemodify(fname, ':p')
436fe386641SBram Moolenaar	  if &modified
437fe386641SBram Moolenaar	    " TODO: find existing window
438fe386641SBram Moolenaar	    exe 'split ' . fnameescape(fname)
439fe386641SBram Moolenaar	    let s:startwin = win_getid(winnr())
440fe386641SBram Moolenaar	  else
441fe386641SBram Moolenaar	    exe 'edit ' . fnameescape(fname)
442fe386641SBram Moolenaar	  endif
443fe386641SBram Moolenaar	endif
444fe386641SBram Moolenaar	exe lnum
44501164a65SBram Moolenaar	exe 'sign unplace ' . s:pc_id
4461b9645deSBram Moolenaar	exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
447fe386641SBram Moolenaar	setlocal signcolumn=yes
448fe386641SBram Moolenaar      endif
449fe386641SBram Moolenaar    else
450fe386641SBram Moolenaar      exe 'sign unplace ' . s:pc_id
451fe386641SBram Moolenaar    endif
452fe386641SBram Moolenaar
453fe386641SBram Moolenaar    call win_gotoid(wid)
454fe386641SBram Moolenaar  endif
455e09ba7baSBram Moolenaarendfunc
456e09ba7baSBram Moolenaar
457e09ba7baSBram Moolenaar" Handle setting a breakpoint
458e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
459e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
460e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
461e09ba7baSBram Moolenaar  if nr == 0
462e09ba7baSBram Moolenaar    return
463fe386641SBram Moolenaar  endif
464e09ba7baSBram Moolenaar
465e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
466e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
467e09ba7baSBram Moolenaar  else
468e09ba7baSBram Moolenaar    let entry = {}
469e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
470fe386641SBram Moolenaar  endif
471e09ba7baSBram Moolenaar
472e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
473e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
474e09ba7baSBram Moolenaar  let entry['fname'] = fname
475e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
4761b9645deSBram Moolenaar
4771b9645deSBram Moolenaar  if bufloaded(fname)
4781b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
4791b9645deSBram Moolenaar  endif
4801b9645deSBram Moolenaarendfunc
4811b9645deSBram Moolenaar
4821b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
4831b9645deSBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname']
4841b9645deSBram Moolenaar  let a:entry['placed'] = 1
485e09ba7baSBram Moolenaarendfunc
486e09ba7baSBram Moolenaar
487e09ba7baSBram Moolenaar" Handle deleting a breakpoint
488e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
489e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
490e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
491e09ba7baSBram Moolenaar  if nr == 0
492e09ba7baSBram Moolenaar    return
493e09ba7baSBram Moolenaar  endif
4941b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
4951b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
4961b9645deSBram Moolenaar    if has_key(entry, 'placed')
497e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
4981b9645deSBram Moolenaar      unlet entry['placed']
4991b9645deSBram Moolenaar    endif
500e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
5011b9645deSBram Moolenaar  endif
502c572da5fSBram Moolenaarendfunc
5031b9645deSBram Moolenaar
5041b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
5051b9645deSBram Moolenaarfunc s:BufRead()
5061b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5071b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5081b9645deSBram Moolenaar    if entry['fname'] == fname
5091b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
5101b9645deSBram Moolenaar    endif
5111b9645deSBram Moolenaar  endfor
5121b9645deSBram Moolenaarendfunc
5131b9645deSBram Moolenaar
5141b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
5151b9645deSBram Moolenaarfunc s:BufUnloaded()
5161b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5171b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5181b9645deSBram Moolenaar    if entry['fname'] == fname
5191b9645deSBram Moolenaar      let entry['placed'] = 0
5201b9645deSBram Moolenaar    endif
5211b9645deSBram Moolenaar  endfor
5221b9645deSBram Moolenaarendfunc
5231b9645deSBram Moolenaar
524