1fe386641SBram Moolenaar" Debugger plugin using gdb.
2c572da5fSBram Moolenaar"
3c572da5fSBram Moolenaar" WORK IN PROGRESS - much doesn't work yet
4c572da5fSBram Moolenaar"
5fe386641SBram Moolenaar" Open two visible terminal windows:
6c572da5fSBram Moolenaar" 1. run a pty, as with ":term NONE"
7c572da5fSBram Moolenaar" 2. run gdb, passing the pty
8fe386641SBram Moolenaar" The current window is used to view source code and follows gdb.
9fe386641SBram Moolenaar"
10fe386641SBram Moolenaar" A third terminal window is hidden, it is used for communication with gdb.
11fe386641SBram Moolenaar"
12fe386641SBram Moolenaar" The communication with gdb uses GDB/MI.  See:
13fe386641SBram Moolenaar" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html
14c572da5fSBram Moolenaar"
15c572da5fSBram Moolenaar" Author: Bram Moolenaar
16fe386641SBram Moolenaar" Copyright: Vim license applies, see ":help license"
17c572da5fSBram Moolenaar
1837c64c78SBram Moolenaar" In case this gets loaded twice.
1937c64c78SBram Moolenaarif exists(':Termdebug')
2037c64c78SBram Moolenaar  finish
2137c64c78SBram Moolenaarendif
2237c64c78SBram Moolenaar
2360e73f2aSBram Moolenaar" Uncomment this line to write logging in "debuglog".
2460e73f2aSBram Moolenaar" call ch_logfile('debuglog', 'w')
2560e73f2aSBram Moolenaar
26fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim".
27fe386641SBram Moolenaar" To end type "quit" in the gdb window.
28*b3623a38SBram Moolenaarcommand -nargs=* -complete=file Termdebug call s:StartDebug(<f-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
46*b3623a38SBram Moolenaarfunc s:StartDebug(...)
47*b3623a38SBram Moolenaar  if exists('s:gdbwin')
48*b3623a38SBram Moolenaar    echoerr 'Terminal debugger already running'
49*b3623a38SBram Moolenaar    return
50*b3623a38SBram Moolenaar  endif
51*b3623a38SBram Moolenaar
52fe386641SBram Moolenaar  let s:startwin = win_getid(winnr())
53fe386641SBram Moolenaar  let s:startsigncolumn = &signcolumn
54fe386641SBram Moolenaar
5524a98a0eSBram Moolenaar  let s:save_columns = 0
5624a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
5724a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
5838baa3e6SBram Moolenaar      let s:save_columns = &columns
5938baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
6024a98a0eSBram Moolenaar    endif
6138baa3e6SBram Moolenaar    let vertical = 1
6238baa3e6SBram Moolenaar  else
6338baa3e6SBram Moolenaar    let vertical = 0
6438baa3e6SBram Moolenaar  endif
6538baa3e6SBram Moolenaar
66c572da5fSBram Moolenaar  " Open a terminal window without a job, to run the debugged program
67fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
68fe386641SBram Moolenaar	\ 'term_name': 'gdb program',
6938baa3e6SBram Moolenaar	\ 'vertical': vertical,
70fe386641SBram Moolenaar	\ })
71fe386641SBram Moolenaar  if s:ptybuf == 0
72fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
73fe386641SBram Moolenaar    return
74fe386641SBram Moolenaar  endif
75fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
7645d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
7751b0f370SBram Moolenaar  if vertical
7851b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
7951b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
8051b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
8151b0f370SBram Moolenaar  endif
82fe386641SBram Moolenaar
83fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
84fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
85fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
86fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
87fe386641SBram Moolenaar	\ 'hidden': 1,
88fe386641SBram Moolenaar	\ })
89fe386641SBram Moolenaar  if s:commbuf == 0
90fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
91fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
92fe386641SBram Moolenaar    return
93fe386641SBram Moolenaar  endif
94fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
95c572da5fSBram Moolenaar
96c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
97c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
98*b3623a38SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + a:000
99c572da5fSBram Moolenaar  echomsg 'executing "' . join(cmd) . '"'
10060e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
101c572da5fSBram Moolenaar	\ 'exit_cb': function('s:EndDebug'),
102fe386641SBram Moolenaar	\ 'term_finish': 'close',
103c572da5fSBram Moolenaar	\ })
10460e73f2aSBram Moolenaar  if s:gdbbuf == 0
105fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
106fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
107fe386641SBram Moolenaar    exe 'bwipe! ' . s:commbuf
108fe386641SBram Moolenaar    return
109fe386641SBram Moolenaar  endif
11045d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
111fe386641SBram Moolenaar
112fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
11360e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
11460e73f2aSBram Moolenaar
1153e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
1163e4b84d0SBram Moolenaar  " why the debugger doesn't work.
1173e4b84d0SBram Moolenaar  let try_count = 0
1183e4b84d0SBram Moolenaar  while 1
1193e4b84d0SBram Moolenaar    let response = ''
120*b3623a38SBram Moolenaar    for lnum in range(1,200)
1213e4b84d0SBram Moolenaar      if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi '
1223e4b84d0SBram Moolenaar	let response = term_getline(s:gdbbuf, lnum + 1)
1233e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
124f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
1253e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:ptybuf
1263e4b84d0SBram Moolenaar	  exe 'bwipe! ' . s:commbuf
1273e4b84d0SBram Moolenaar	  return
1283e4b84d0SBram Moolenaar	endif
1293e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
1303e4b84d0SBram Moolenaar	  " Success!
1313e4b84d0SBram Moolenaar	  break
1323e4b84d0SBram Moolenaar	endif
1333e4b84d0SBram Moolenaar      endif
1343e4b84d0SBram Moolenaar    endfor
1353e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
1363e4b84d0SBram Moolenaar      break
1373e4b84d0SBram Moolenaar    endif
1383e4b84d0SBram Moolenaar    let try_count += 1
1393e4b84d0SBram Moolenaar    if try_count > 100
1403e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
1413e4b84d0SBram Moolenaar      break
1423e4b84d0SBram Moolenaar    endif
1433e4b84d0SBram Moolenaar    sleep 10m
1443e4b84d0SBram Moolenaar  endwhile
1453e4b84d0SBram Moolenaar
14660e73f2aSBram Moolenaar  " Interpret commands while the target is running.  This should usualy only be
14760e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
14860e73f2aSBram Moolenaar  " running.
14960e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
150e09ba7baSBram Moolenaar
151f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
152f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
153f3ba14ffSBram Moolenaar  call s:SendCommand('-gdb-set pagination off')
154f3ba14ffSBram Moolenaar
15538baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
15638baa3e6SBram Moolenaar  " There can be only one.
15738baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
15838baa3e6SBram Moolenaar
15938baa3e6SBram Moolenaar  " Sign used to indicate a breakpoint.
16038baa3e6SBram Moolenaar  " Can be used multiple times.
16138baa3e6SBram Moolenaar  sign define debugBreakpoint text=>> texthl=debugBreakpoint
16238baa3e6SBram Moolenaar
16345d5f26dSBram Moolenaar  " Install debugger commands in the text window.
16445d5f26dSBram Moolenaar  call win_gotoid(s:startwin)
165e09ba7baSBram Moolenaar  call s:InstallCommands()
16645d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
167e09ba7baSBram Moolenaar
16851b0f370SBram Moolenaar  " Enable showing a balloon with eval info
169246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
170246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
17151b0f370SBram Moolenaar    if has("balloon_eval")
17251b0f370SBram Moolenaar      set ballooneval
173246fe03dSBram Moolenaar    endif
17451b0f370SBram Moolenaar    if has("balloon_eval_term")
17551b0f370SBram Moolenaar      set balloonevalterm
17651b0f370SBram Moolenaar    endif
17751b0f370SBram Moolenaar  endif
17851b0f370SBram Moolenaar
179e09ba7baSBram Moolenaar  let s:breakpoints = {}
1801b9645deSBram Moolenaar
1811b9645deSBram Moolenaar  augroup TermDebug
1821b9645deSBram Moolenaar    au BufRead * call s:BufRead()
1831b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
1841b9645deSBram Moolenaar  augroup END
185c572da5fSBram Moolenaarendfunc
186c572da5fSBram Moolenaar
187c572da5fSBram Moolenaarfunc s:EndDebug(job, status)
188c572da5fSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
189fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
190*b3623a38SBram Moolenaar  unlet s:gdbwin
191e09ba7baSBram Moolenaar
192e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
193e09ba7baSBram Moolenaar
194e09ba7baSBram Moolenaar  call win_gotoid(s:startwin)
195e09ba7baSBram Moolenaar  let &signcolumn = s:startsigncolumn
196e09ba7baSBram Moolenaar  call s:DeleteCommands()
197e09ba7baSBram Moolenaar
198e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
19938baa3e6SBram Moolenaar  if s:save_columns > 0
20038baa3e6SBram Moolenaar    let &columns = s:save_columns
20138baa3e6SBram Moolenaar  endif
2021b9645deSBram Moolenaar
203246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
204246fe03dSBram Moolenaar    set balloonexpr=
20551b0f370SBram Moolenaar    if has("balloon_eval")
20651b0f370SBram Moolenaar      set noballooneval
207246fe03dSBram Moolenaar    endif
20851b0f370SBram Moolenaar    if has("balloon_eval_term")
20951b0f370SBram Moolenaar      set noballoonevalterm
21051b0f370SBram Moolenaar    endif
21151b0f370SBram Moolenaar  endif
21251b0f370SBram Moolenaar
2131b9645deSBram Moolenaar  au! TermDebug
214fe386641SBram Moolenaarendfunc
215fe386641SBram Moolenaar
216fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
217fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
218fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
219fe386641SBram Moolenaar
220fe386641SBram Moolenaar  for msg in msgs
221fe386641SBram Moolenaar    " remove prefixed NL
222fe386641SBram Moolenaar    if msg[0] == "\n"
223fe386641SBram Moolenaar      let msg = msg[1:]
224fe386641SBram Moolenaar    endif
225fe386641SBram Moolenaar    if msg != ''
2261b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
227e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
22845d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
229e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
230e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
231e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
23245d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
23345d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
23445d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
23545d5f26dSBram Moolenaar	call s:HandleError(msg)
236e09ba7baSBram Moolenaar      endif
237e09ba7baSBram Moolenaar    endif
238e09ba7baSBram Moolenaar  endfor
239e09ba7baSBram Moolenaarendfunc
240e09ba7baSBram Moolenaar
241e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
242e09ba7baSBram Moolenaarfunc s:InstallCommands()
243e09ba7baSBram Moolenaar  command Break call s:SetBreakpoint()
24471137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
245e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
24645d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
247e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
24860e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
24960e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
25060e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
251e09ba7baSBram Moolenaar  command Continue call s:SendCommand('-exec-continue')
25245d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
25345d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
25445d5f26dSBram Moolenaar  command Program call win_gotoid(s:ptywin)
255c4b533e1SBram Moolenaar  command Source call s:GotoStartwinOrCreateIt()
25671137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
25745d5f26dSBram Moolenaar
25845d5f26dSBram Moolenaar  " TODO: can the K mapping be restored?
25945d5f26dSBram Moolenaar  nnoremap K :Evaluate<CR>
2601b9645deSBram Moolenaar
261f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
26271137fedSBram Moolenaar    call s:InstallWinbar()
26371137fedSBram Moolenaar
26471137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
26571137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
26671137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
26771137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
26871137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
26971137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
27071137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
27171137fedSBram Moolenaar    endif
27271137fedSBram Moolenaar  endif
27371137fedSBram Moolenaarendfunc
27471137fedSBram Moolenaar
27571137fedSBram Moolenaarlet s:winbar_winids = []
27671137fedSBram Moolenaar
27771137fedSBram Moolenaar" Install the window toolbar in the current window.
27871137fedSBram Moolenaarfunc s:InstallWinbar()
279c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
28024a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
28124a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
28224a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
28324a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
28460e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
28524a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
28671137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
287c4b533e1SBram Moolenaar  endif
288e09ba7baSBram Moolenaarendfunc
289e09ba7baSBram Moolenaar
290e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
291e09ba7baSBram Moolenaarfunc s:DeleteCommands()
292e09ba7baSBram Moolenaar  delcommand Break
29371137fedSBram Moolenaar  delcommand Clear
294e09ba7baSBram Moolenaar  delcommand Step
29545d5f26dSBram Moolenaar  delcommand Over
296e09ba7baSBram Moolenaar  delcommand Finish
29760e73f2aSBram Moolenaar  delcommand Run
29860e73f2aSBram Moolenaar  delcommand Arguments
29960e73f2aSBram Moolenaar  delcommand Stop
300e09ba7baSBram Moolenaar  delcommand Continue
30145d5f26dSBram Moolenaar  delcommand Evaluate
30245d5f26dSBram Moolenaar  delcommand Gdb
30345d5f26dSBram Moolenaar  delcommand Program
304*b3623a38SBram Moolenaar  delcommand Source
30571137fedSBram Moolenaar  delcommand Winbar
30645d5f26dSBram Moolenaar
30745d5f26dSBram Moolenaar  nunmap K
3081b9645deSBram Moolenaar
3091b9645deSBram Moolenaar  if has('menu')
31071137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
31171137fedSBram Moolenaar    let curwinid = win_getid(winnr())
31271137fedSBram Moolenaar    for winid in s:winbar_winids
31371137fedSBram Moolenaar      if win_gotoid(winid)
3141b9645deSBram Moolenaar	aunmenu WinBar.Step
3151b9645deSBram Moolenaar	aunmenu WinBar.Next
3161b9645deSBram Moolenaar	aunmenu WinBar.Finish
3171b9645deSBram Moolenaar	aunmenu WinBar.Cont
31860e73f2aSBram Moolenaar	aunmenu WinBar.Stop
3191b9645deSBram Moolenaar	aunmenu WinBar.Eval
3201b9645deSBram Moolenaar      endif
32171137fedSBram Moolenaar    endfor
32271137fedSBram Moolenaar    call win_gotoid(curwinid)
32371137fedSBram Moolenaar    let s:winbar_winids = []
32471137fedSBram Moolenaar
32571137fedSBram Moolenaar    if exists('s:saved_mousemodel')
32671137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
32771137fedSBram Moolenaar      unlet s:saved_mousemodel
32871137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
32971137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
33071137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
33171137fedSBram Moolenaar      aunmenu PopUp.Evaluate
33271137fedSBram Moolenaar    endif
33371137fedSBram Moolenaar  endif
3341b9645deSBram Moolenaar
33545d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
33645d5f26dSBram Moolenaar  for key in keys(s:breakpoints)
33745d5f26dSBram Moolenaar    exe 'sign unplace ' . (s:break_id + key)
33845d5f26dSBram Moolenaar  endfor
33938baa3e6SBram Moolenaar  sign undefine debugPC
34038baa3e6SBram Moolenaar  sign undefine debugBreakpoint
34145d5f26dSBram Moolenaar  unlet s:breakpoints
342e09ba7baSBram Moolenaarendfunc
343e09ba7baSBram Moolenaar
344e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
345e09ba7baSBram Moolenaarfunc s:SetBreakpoint()
34660e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
34760e73f2aSBram Moolenaar  " Interrupt to make it work.
34860e73f2aSBram Moolenaar  let do_continue = 0
34960e73f2aSBram Moolenaar  if !s:stopped
35060e73f2aSBram Moolenaar    let do_continue = 1
35160e73f2aSBram Moolenaar    call s:SendCommand('-exec-interrupt')
35260e73f2aSBram Moolenaar    sleep 10m
35360e73f2aSBram Moolenaar  endif
35460e73f2aSBram Moolenaar  call s:SendCommand('-break-insert --source '
35560e73f2aSBram Moolenaar	\ . fnameescape(expand('%:p')) . ' --line ' . line('.'))
35660e73f2aSBram Moolenaar  if do_continue
35760e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
35860e73f2aSBram Moolenaar  endif
359e09ba7baSBram Moolenaarendfunc
360e09ba7baSBram Moolenaar
36171137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
36271137fedSBram Moolenaarfunc s:ClearBreakpoint()
363e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
364e09ba7baSBram Moolenaar  let lnum = line('.')
365e09ba7baSBram Moolenaar  for [key, val] in items(s:breakpoints)
366e09ba7baSBram Moolenaar    if val['fname'] == fname && val['lnum'] == lnum
367e09ba7baSBram Moolenaar      call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r")
368e09ba7baSBram Moolenaar      " Assume this always wors, the reply is simply "^done".
369e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + key)
370e09ba7baSBram Moolenaar      unlet s:breakpoints[key]
371e09ba7baSBram Moolenaar      break
372e09ba7baSBram Moolenaar    endif
373e09ba7baSBram Moolenaar  endfor
374e09ba7baSBram Moolenaarendfunc
375e09ba7baSBram Moolenaar
376e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb
377e09ba7baSBram Moolenaarfunc s:SendCommand(cmd)
378e09ba7baSBram Moolenaar  call term_sendkeys(s:commbuf, a:cmd . "\r")
379e09ba7baSBram Moolenaarendfunc
380e09ba7baSBram Moolenaar
38160e73f2aSBram Moolenaarfunc s:Run(args)
38260e73f2aSBram Moolenaar  if a:args != ''
38360e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
38460e73f2aSBram Moolenaar  endif
38560e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
38660e73f2aSBram Moolenaarendfunc
38760e73f2aSBram Moolenaar
38851b0f370SBram Moolenaarfunc s:SendEval(expr)
38951b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
39051b0f370SBram Moolenaar  let s:evalexpr = a:expr
39151b0f370SBram Moolenaarendfunc
39251b0f370SBram Moolenaar
39345d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
39445d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
39545d5f26dSBram Moolenaar  if a:arg != ''
39645d5f26dSBram Moolenaar    let expr = a:arg
39745d5f26dSBram Moolenaar  elseif a:range == 2
39845d5f26dSBram Moolenaar    let pos = getcurpos()
39945d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
40045d5f26dSBram Moolenaar    let regt = getregtype('v')
40145d5f26dSBram Moolenaar    normal! gv"vy
40245d5f26dSBram Moolenaar    let expr = @v
40345d5f26dSBram Moolenaar    call setpos('.', pos)
40445d5f26dSBram Moolenaar    call setreg('v', reg, regt)
40545d5f26dSBram Moolenaar  else
40645d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
40745d5f26dSBram Moolenaar  endif
40822f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
40951b0f370SBram Moolenaar  call s:SendEval(expr)
41045d5f26dSBram Moolenaarendfunc
41145d5f26dSBram Moolenaar
41222f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
41351b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
41451b0f370SBram Moolenaar
41545d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
41645d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
4171b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
4181b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
41951b0f370SBram Moolenaar  if s:evalFromBalloonExpr
42051b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
42151b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
42251b0f370SBram Moolenaar    else
42351b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
42451b0f370SBram Moolenaar    endif
42551b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
42651b0f370SBram Moolenaar  else
4271b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
42851b0f370SBram Moolenaar  endif
4291b9645deSBram Moolenaar
4307f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
4311b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
43222f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
43351b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
43451b0f370SBram Moolenaar  else
43551b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
4361b9645deSBram Moolenaar  endif
43745d5f26dSBram Moolenaarendfunc
43845d5f26dSBram Moolenaar
43951b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
44051b0f370SBram Moolenaar" if there is any.
44151b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
44251b0f370SBram Moolenaar  if v:beval_winid != s:startwin
44351b0f370SBram Moolenaar    return
44451b0f370SBram Moolenaar  endif
44551b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
44651b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
44722f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
44822f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
44951b0f370SBram Moolenaar  return ''
45051b0f370SBram Moolenaarendfunc
45151b0f370SBram Moolenaar
45245d5f26dSBram Moolenaar" Handle an error.
45345d5f26dSBram Moolenaarfunc s:HandleError(msg)
45422f1d0e3SBram Moolenaar  if s:ignoreEvalError
45551b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
45622f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
45722f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
45851b0f370SBram Moolenaar    return
45951b0f370SBram Moolenaar  endif
46045d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
46145d5f26dSBram Moolenaarendfunc
46245d5f26dSBram Moolenaar
463c4b533e1SBram Moolenaarfunc s:GotoStartwinOrCreateIt()
464c4b533e1SBram Moolenaar  if !win_gotoid(s:startwin)
465c4b533e1SBram Moolenaar    new
466c4b533e1SBram Moolenaar    let s:startwin = win_getid(winnr())
467c4b533e1SBram Moolenaar    call s:InstallWinbar()
468c4b533e1SBram Moolenaar  endif
469c4b533e1SBram Moolenaarendfunc
470c4b533e1SBram Moolenaar
471e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
472e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
473e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
474fe386641SBram Moolenaar  let wid = win_getid(winnr())
475fe386641SBram Moolenaar
47660e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
47760e73f2aSBram Moolenaar    let s:stopped = 1
47860e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
47960e73f2aSBram Moolenaar    let s:stopped = 0
48060e73f2aSBram Moolenaar  endif
48160e73f2aSBram Moolenaar
482c4b533e1SBram Moolenaar  call s:GotoStartwinOrCreateIt()
483c4b533e1SBram Moolenaar
484e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
4851b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
486e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
487fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
4881b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
489fe386641SBram Moolenaar	if &modified
490fe386641SBram Moolenaar	  " TODO: find existing window
491fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
492fe386641SBram Moolenaar	  let s:startwin = win_getid(winnr())
493c4b533e1SBram Moolenaar	  call s:InstallWinbar()
494fe386641SBram Moolenaar	else
495fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
496fe386641SBram Moolenaar	endif
497fe386641SBram Moolenaar      endif
498fe386641SBram Moolenaar      exe lnum
49901164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
5001b9645deSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
501fe386641SBram Moolenaar      setlocal signcolumn=yes
502fe386641SBram Moolenaar    endif
503fe386641SBram Moolenaar  else
504fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
505fe386641SBram Moolenaar  endif
506fe386641SBram Moolenaar
507fe386641SBram Moolenaar  call win_gotoid(wid)
508e09ba7baSBram Moolenaarendfunc
509e09ba7baSBram Moolenaar
510e09ba7baSBram Moolenaar" Handle setting a breakpoint
511e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
512e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
513e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
514e09ba7baSBram Moolenaar  if nr == 0
515e09ba7baSBram Moolenaar    return
516fe386641SBram Moolenaar  endif
517e09ba7baSBram Moolenaar
518e09ba7baSBram Moolenaar  if has_key(s:breakpoints, nr)
519e09ba7baSBram Moolenaar    let entry = s:breakpoints[nr]
520e09ba7baSBram Moolenaar  else
521e09ba7baSBram Moolenaar    let entry = {}
522e09ba7baSBram Moolenaar    let s:breakpoints[nr] = entry
523fe386641SBram Moolenaar  endif
524e09ba7baSBram Moolenaar
525e09ba7baSBram Moolenaar  let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
526e09ba7baSBram Moolenaar  let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
527e09ba7baSBram Moolenaar  let entry['fname'] = fname
528e09ba7baSBram Moolenaar  let entry['lnum'] = lnum
5291b9645deSBram Moolenaar
5301b9645deSBram Moolenaar  if bufloaded(fname)
5311b9645deSBram Moolenaar    call s:PlaceSign(nr, entry)
5321b9645deSBram Moolenaar  endif
5331b9645deSBram Moolenaarendfunc
5341b9645deSBram Moolenaar
5351b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry)
5361b9645deSBram Moolenaar  exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname']
5371b9645deSBram Moolenaar  let a:entry['placed'] = 1
538e09ba7baSBram Moolenaarendfunc
539e09ba7baSBram Moolenaar
540e09ba7baSBram Moolenaar" Handle deleting a breakpoint
541e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
542e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
543e09ba7baSBram Moolenaar  let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
544e09ba7baSBram Moolenaar  if nr == 0
545e09ba7baSBram Moolenaar    return
546e09ba7baSBram Moolenaar  endif
5471b9645deSBram Moolenaar  if has_key(s:breakpoints, nr)
5481b9645deSBram Moolenaar    let entry = s:breakpoints[nr]
5491b9645deSBram Moolenaar    if has_key(entry, 'placed')
550e09ba7baSBram Moolenaar      exe 'sign unplace ' . (s:break_id + nr)
5511b9645deSBram Moolenaar      unlet entry['placed']
5521b9645deSBram Moolenaar    endif
553e09ba7baSBram Moolenaar    unlet s:breakpoints[nr]
5541b9645deSBram Moolenaar  endif
555c572da5fSBram Moolenaarendfunc
5561b9645deSBram Moolenaar
5571b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
5581b9645deSBram Moolenaarfunc s:BufRead()
5591b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5601b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5611b9645deSBram Moolenaar    if entry['fname'] == fname
5621b9645deSBram Moolenaar      call s:PlaceSign(nr, entry)
5631b9645deSBram Moolenaar    endif
5641b9645deSBram Moolenaar  endfor
5651b9645deSBram Moolenaarendfunc
5661b9645deSBram Moolenaar
5671b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
5681b9645deSBram Moolenaarfunc s:BufUnloaded()
5691b9645deSBram Moolenaar  let fname = expand('<afile>:p')
5701b9645deSBram Moolenaar  for [nr, entry] in items(s:breakpoints)
5711b9645deSBram Moolenaar    if entry['fname'] == fname
5721b9645deSBram Moolenaar      let entry['placed'] = 0
5731b9645deSBram Moolenaar    endif
5741b9645deSBram Moolenaar  endfor
5751b9645deSBram Moolenaarendfunc
5761b9645deSBram Moolenaar
577