2020command -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'
2525endif
2626
2727" Sign used to highlight the line where the program has stopped.
28+ " There can be only one.
2829sign 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+
2937if &background == ' light'
30- hi debugPC term = reverse ctermbg= lightblue guibg= lightblue
38+ hi default debugPC term = reverse ctermbg= lightblue guibg= lightblue
3139else
32- hi debugPC term = reverse ctermbg= darkblue guibg= darkblue
40+ hi default debugPC term = reverse ctermbg= darkblue guibg= darkblue
3341endif
34- let s: pc_id = 12
42+ hi default debugBreakpoint term = reverse ctermbg = red guibg = red
3543
3644func 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 = {}
7992endfunc
8093
8194func 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)
85105endfunc
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]
128238endfunc
0 commit comments