Skip to content

Commit e09ba7b

Browse files
committed
patch 8.0.1085: terminal debugger can't set breakpoints
Problem: The terminal debugger can't set breakpoints. Solution: Add :Break and :Delete commands. Also commands for stepping through code.
1 parent 7be9b50 commit e09ba7b

3 files changed

Lines changed: 221 additions & 50 deletions

File tree

runtime/doc/terminal.txt

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*terminal.txt* For Vim version 8.0. Last change: 2017 Aug 29
1+
*terminal.txt* For Vim version 8.0. Last change: 2017 Sep 09
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -30,11 +30,11 @@ This feature is for running a terminal emulator in a Vim window. A job can be
3030
started connected to the terminal emulator. For example, to run a shell: >
3131
:term bash
3232
33-
Or to run a debugger: >
34-
:term gdb vim
33+
Or to run build command: >
34+
:term make myprogram
3535
3636
The job runs asynchronously from Vim, the window will be updated to show
37-
output from the job, also while editing in any other window.
37+
output from the job, also while editing in another window.
3838

3939

4040
Typing ~
@@ -109,7 +109,8 @@ Syntax ~
109109

110110
If [range] is given the specified lines are used as
111111
input for the job. It will not be possible to type
112-
keys in the terminal window.
112+
keys in the terminal window. For MS-Windows see the
113+
++eof argument below.
113114

114115
Two comma separated numbers are used as "rows,cols".
115116
E.g. `:24,80gdb` opens a terminal with 24 rows and 80
@@ -133,14 +134,15 @@ Syntax ~
133134
height.
134135
++cols={width} Use {width} for the terminal window
135136
width.
136-
++eof={text} when using [range], text to send after
137-
the last line was written. The default
138-
is to send CTRL-D. A CR is appended.
137+
++eof={text} when using [range]: text to send after
138+
the last line was written. Cannot
139+
contain white space. A CR is
140+
appended. For MS-Windows the default
141+
is to send CTRL-D.
139142
E.g. for a shell use "++eof=exit" and
140143
for Python "++eof=exit()". Special
141144
codes can be used like with `:map`,
142145
e.g. "<C-Z>" for CTRL-Z.
143-
{only on MS-Windows}
144146

145147
If you want to use more options use the |term_start()|
146148
function.
@@ -303,33 +305,90 @@ term_scrape() inspect terminal screen
303305
3. Debugging *terminal-debug*
304306

305307
The Terminal debugging plugin can be used to debug a program with gdb and view
306-
the source code in a Vim window.
308+
the source code in a Vim window. Since this is completely contained inside
309+
Vim this also works remotely over an ssh connection.
310+
311+
312+
Starting ~
307313

308314
Load the plugin with this command: >
309315
packadd termdebug
310-
316+
< *:Termdebug*
311317
To start debugging use `:TermDebug` folowed by the command name, for example: >
312318
:TermDebug vim
313319
314320
This opens two windows:
315321
- A terminal window in which "gdb vim" is executed. Here you can directly
316-
interact with gdb.
322+
interact with gdb. The buffer name is "!gdb".
317323
- A terminal window for the executed program. When "run" is used in gdb the
318324
program I/O will happen in this window, so that it does not interfere with
319-
controlling gdb.
320-
The current window is used to show the source code. When gdb jumps to a
321-
source file location this window will display the code, if possible. Values
322-
of variables can be inspected, breakpoints set and cleared, etc.
325+
controlling gdb. The buffer name is "gdb program".
326+
327+
The current window is used to show the source code. When gdb pauses the
328+
source file location will be displayed, if possible. A sign is used to
329+
highlight the current position (using highlight group debugPC).
330+
331+
If the buffer in the current window is modified, another window will be opened
332+
to display the current gdb position.
333+
334+
Focus the terminal of the executed program to interact with it. This works
335+
the same as any command running in a terminal window.
323336

324337
When the debugger ends the two opened windows are closed.
325338

326339

340+
Stepping through code ~
341+
342+
Put focus on the gdb window to type commands there. Some common ones are:
343+
- CTRL-C interrupt the program
344+
- next execute the current line and stop at the next line
345+
- step execute the current line and stop at the next statement, entering
346+
functions
347+
- finish execute until leaving the current function
348+
- where show the stack
349+
- frame N go to the Nth stack frame
350+
- continue continue execution
351+
352+
In the window showing the source code some commands can passed to gdb:
353+
- Break set a breakpoint at the current line; a sign will be displayed
354+
- Delete delete a breakpoint at the current line
355+
- Step execute the gdb "step" command
356+
- NNext execute the gdb "next" command (:Next is a Vim command)
357+
- Finish execute the gdb "finish" command
358+
- Continue execute the gdb "continue" command
359+
360+
361+
Communication ~
362+
363+
There is another, hidden, buffer, which is used for Vim to communicate with
364+
gdb. The buffer name is "gdb communication". Do not delete this buffer, it
365+
will break the debugger.
366+
367+
327368
Customizing ~
328369

329-
g:debugger The debugger command. Default "gdb".
370+
To change the name of the gdb command, set the "termdebugger" variable before
371+
invoking `:Termdebug`: >
372+
let termdebugger = "mygdb"
373+
Only debuggers fully compatible with gdb will work. Vim uses the GDB/MI
374+
interface.
375+
376+
The color of the signs can be adjusted with these highlight groups:
377+
- debugPC the current position
378+
- debugBreakpoint a breakpoint
379+
380+
The defaults are, when 'background' is "light":
381+
hi debugPC term=reverse ctermbg=lightblue guibg=lightblue
382+
hi debugBreakpoint term=reverse ctermbg=red guibg=red
383+
384+
When 'background' is "dark":
385+
hi debugPC term=reverse ctermbg=darkblue guibg=darkblue
386+
hi debugBreakpoint term=reverse ctermbg=red guibg=red
387+
330388

389+
NOT WORKING YET: ~
331390

332-
TODO
391+
Values of variables can be inspected, etc.
333392

334393

335394
vim:tw=78:ts=8:ft=help:norl:

runtime/pack/dist/opt/termdebug/plugin/termdebug.vim

Lines changed: 142 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,26 @@
2020
command -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>)
2121

2222
" Name of the gdb command, defaults to "gdb".
23-
if !exists('debugger')
24-
let debugger = 'gdb'
23+
if !exists('termdebugger')
24+
let termdebugger = 'gdb'
2525
endif
2626

2727
" Sign used to highlight the line where the program has stopped.
28+
" There can be only one.
2829
sign define debugPC linehl=debugPC
30+
let s:pc_id = 12
31+
let s:break_id = 13
32+
33+
" Sign used to indicate a breakpoint.
34+
" Can be used multiple times.
35+
sign define debugBreakpoint text=>> texthl=debugBreakpoint
36+
2937
if &background == 'light'
30-
hi debugPC term=reverse ctermbg=lightblue guibg=lightblue
38+
hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue
3139
else
32-
hi debugPC term=reverse ctermbg=darkblue guibg=darkblue
40+
hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue
3341
endif
34-
let s:pc_id = 12
42+
hi default debugBreakpoint term=reverse ctermbg=red guibg=red
3543

3644
func s:StartDebug(cmd)
3745
let s:startwin = win_getid(winnr())
@@ -61,7 +69,7 @@ func s:StartDebug(cmd)
6169
let commpty = job_info(term_getjob(s:commbuf))['tty_out']
6270

6371
" Open a terminal window to run the debugger.
64-
let cmd = [g:debugger, '-tty', pty, a:cmd]
72+
let cmd = [g:termdebugger, '-tty', pty, a:cmd]
6573
echomsg 'executing "' . join(cmd) . '"'
6674
let gdbbuf = term_start(cmd, {
6775
\ 'exit_cb': function('s:EndDebug'),
@@ -76,12 +84,24 @@ func s:StartDebug(cmd)
7684

7785
" Connect gdb to the communication pty, using the GDB/MI interface
7886
call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r")
87+
88+
" Install debugger commands.
89+
call s:InstallCommands()
90+
91+
let s:breakpoints = {}
7992
endfunc
8093

8194
func s:EndDebug(job, status)
8295
exe 'bwipe! ' . s:ptybuf
8396
exe 'bwipe! ' . s:commbuf
84-
call setwinvar(s:startwin, '&signcolumn', s:startsigncolumn)
97+
98+
let curwinid = win_getid(winnr())
99+
100+
call win_gotoid(s:startwin)
101+
let &signcolumn = s:startsigncolumn
102+
call s:DeleteCommands()
103+
104+
call win_gotoid(curwinid)
85105
endfunc
86106

87107
" Handle a message received from gdb on the GDB/MI interface.
@@ -95,34 +115,124 @@ func s:CommOutput(chan, msg)
95115
endif
96116
if msg != ''
97117
if msg =~ '^\*\(stopped\|running\)'
98-
let wid = win_getid(winnr())
99-
100-
if win_gotoid(s:startwin)
101-
if msg =~ '^\*stopped'
102-
" TODO: proper parsing
103-
let fname = substitute(msg, '.*fullname="\([^"]*\)".*', '\1', '')
104-
let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
105-
if lnum =~ '^[0-9]*$'
106-
if expand('%:h') != fname
107-
if &modified
108-
" TODO: find existing window
109-
exe 'split ' . fnameescape(fname)
110-
let s:startwin = win_getid(winnr())
111-
else
112-
exe 'edit ' . fnameescape(fname)
113-
endif
114-
endif
115-
exe lnum
116-
exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname)
117-
setlocal signcolumn=yes
118-
endif
118+
call s:HandleCursor(msg)
119+
elseif msg =~ '^\^done,bkpt='
120+
call s:HandleNewBreakpoint(msg)
121+
elseif msg =~ '^=breakpoint-deleted,'
122+
call s:HandleBreakpointDelete(msg)
123+
endif
124+
endif
125+
endfor
126+
endfunc
127+
128+
" Install commands in the current window to control the debugger.
129+
func s:InstallCommands()
130+
command Break call s:SetBreakpoint()
131+
command Delete call s:DeleteBreakpoint()
132+
command Step call s:SendCommand('-exec-step')
133+
command NNext call s:SendCommand('-exec-next')
134+
command Finish call s:SendCommand('-exec-finish')
135+
command Continue call s:SendCommand('-exec-continue')
136+
endfunc
137+
138+
" Delete installed debugger commands in the current window.
139+
func s:DeleteCommands()
140+
delcommand Break
141+
delcommand Delete
142+
delcommand Step
143+
delcommand NNext
144+
delcommand Finish
145+
delcommand Continue
146+
endfunc
147+
148+
" :Break - Set a breakpoint at the cursor position.
149+
func s:SetBreakpoint()
150+
call term_sendkeys(s:commbuf, '-break-insert --source '
151+
\ . fnameescape(expand('%:p')) . ' --line ' . line('.') . "\r")
152+
endfunc
153+
154+
" :Delete - Delete a breakpoint at the cursor position.
155+
func s:DeleteBreakpoint()
156+
let fname = fnameescape(expand('%:p'))
157+
let lnum = line('.')
158+
for [key, val] in items(s:breakpoints)
159+
if val['fname'] == fname && val['lnum'] == lnum
160+
call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r")
161+
" Assume this always wors, the reply is simply "^done".
162+
exe 'sign unplace ' . (s:break_id + key)
163+
unlet s:breakpoints[key]
164+
break
165+
endif
166+
endfor
167+
endfunc
168+
169+
" :Next, :Continue, etc - send a command to gdb
170+
func s:SendCommand(cmd)
171+
call term_sendkeys(s:commbuf, a:cmd . "\r")
172+
endfunc
173+
174+
" Handle stopping and running message from gdb.
175+
" Will update the sign that shows the current position.
176+
func s:HandleCursor(msg)
177+
let wid = win_getid(winnr())
178+
179+
if win_gotoid(s:startwin)
180+
if a:msg =~ '^\*stopped'
181+
let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
182+
let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
183+
if lnum =~ '^[0-9]*$'
184+
if expand('%:h') != fname
185+
if &modified
186+
" TODO: find existing window
187+
exe 'split ' . fnameescape(fname)
188+
let s:startwin = win_getid(winnr())
119189
else
120-
exe 'sign unplace ' . s:pc_id
190+
exe 'edit ' . fnameescape(fname)
121191
endif
122-
123-
call win_gotoid(wid)
124192
endif
193+
exe lnum
194+
exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname)
195+
setlocal signcolumn=yes
125196
endif
197+
else
198+
exe 'sign unplace ' . s:pc_id
126199
endif
127-
endfor
200+
201+
call win_gotoid(wid)
202+
endif
203+
endfunc
204+
205+
" Handle setting a breakpoint
206+
" Will update the sign that shows the breakpoint
207+
func s:HandleNewBreakpoint(msg)
208+
let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
209+
if nr == 0
210+
return
211+
endif
212+
213+
if has_key(s:breakpoints, nr)
214+
let entry = s:breakpoints[nr]
215+
else
216+
let entry = {}
217+
let s:breakpoints[nr] = entry
218+
endif
219+
220+
let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
221+
let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
222+
223+
exe 'sign place ' . (s:break_id + nr) . ' line=' . lnum . ' name=debugBreakpoint file=' . fnameescape(fname)
224+
225+
let entry['fname'] = fname
226+
let entry['lnum'] = lnum
227+
endfunc
228+
229+
" Handle deleting a breakpoint
230+
" Will remove the sign that shows the breakpoint
231+
func s:HandleBreakpointDelete(msg)
232+
let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
233+
if nr == 0
234+
return
235+
endif
236+
exe 'sign unplace ' . (s:break_id + nr)
237+
unlet s:breakpoints[nr]
128238
endfunc

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,8 @@ static char *(features[]) =
769769

770770
static int included_patches[] =
771771
{ /* Add new patch number below this line */
772+
/**/
773+
1085,
772774
/**/
773775
1084,
774776
/**/

0 commit comments

Comments
 (0)