forked from aniani/vim
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.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
*terminal.txt* For Vim version 8.0. Last change: 2017 Aug 29
|
*terminal.txt* For Vim version 8.0. Last change: 2017 Sep 09
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
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
|
|||||||
started connected to the terminal emulator. For example, to run a shell: >
|
started connected to the terminal emulator. For example, to run a shell: >
|
||||||
:term bash
|
:term bash
|
||||||
|
|
||||||
Or to run a debugger: >
|
Or to run build command: >
|
||||||
:term gdb vim
|
:term make myprogram
|
||||||
|
|
||||||
The job runs asynchronously from Vim, the window will be updated to show
|
The job runs asynchronously from Vim, the window will be updated to show
|
||||||
output from the job, also while editing in any other window.
|
output from the job, also while editing in another window.
|
||||||
|
|
||||||
|
|
||||||
Typing ~
|
Typing ~
|
||||||
@@ -109,7 +109,8 @@ Syntax ~
|
|||||||
|
|
||||||
If [range] is given the specified lines are used as
|
If [range] is given the specified lines are used as
|
||||||
input for the job. It will not be possible to type
|
input for the job. It will not be possible to type
|
||||||
keys in the terminal window.
|
keys in the terminal window. For MS-Windows see the
|
||||||
|
++eof argument below.
|
||||||
|
|
||||||
Two comma separated numbers are used as "rows,cols".
|
Two comma separated numbers are used as "rows,cols".
|
||||||
E.g. `:24,80gdb` opens a terminal with 24 rows and 80
|
E.g. `:24,80gdb` opens a terminal with 24 rows and 80
|
||||||
@@ -133,14 +134,15 @@ Syntax ~
|
|||||||
height.
|
height.
|
||||||
++cols={width} Use {width} for the terminal window
|
++cols={width} Use {width} for the terminal window
|
||||||
width.
|
width.
|
||||||
++eof={text} when using [range], text to send after
|
++eof={text} when using [range]: text to send after
|
||||||
the last line was written. The default
|
the last line was written. Cannot
|
||||||
is to send CTRL-D. A CR is appended.
|
contain white space. A CR is
|
||||||
|
appended. For MS-Windows the default
|
||||||
|
is to send CTRL-D.
|
||||||
E.g. for a shell use "++eof=exit" and
|
E.g. for a shell use "++eof=exit" and
|
||||||
for Python "++eof=exit()". Special
|
for Python "++eof=exit()". Special
|
||||||
codes can be used like with `:map`,
|
codes can be used like with `:map`,
|
||||||
e.g. "<C-Z>" for CTRL-Z.
|
e.g. "<C-Z>" for CTRL-Z.
|
||||||
{only on MS-Windows}
|
|
||||||
|
|
||||||
If you want to use more options use the |term_start()|
|
If you want to use more options use the |term_start()|
|
||||||
function.
|
function.
|
||||||
@@ -303,33 +305,90 @@ term_scrape() inspect terminal screen
|
|||||||
3. Debugging *terminal-debug*
|
3. Debugging *terminal-debug*
|
||||||
|
|
||||||
The Terminal debugging plugin can be used to debug a program with gdb and view
|
The Terminal debugging plugin can be used to debug a program with gdb and view
|
||||||
the source code in a Vim window.
|
the source code in a Vim window. Since this is completely contained inside
|
||||||
|
Vim this also works remotely over an ssh connection.
|
||||||
|
|
||||||
|
|
||||||
|
Starting ~
|
||||||
|
|
||||||
Load the plugin with this command: >
|
Load the plugin with this command: >
|
||||||
packadd termdebug
|
packadd termdebug
|
||||||
|
< *:Termdebug*
|
||||||
To start debugging use `:TermDebug` folowed by the command name, for example: >
|
To start debugging use `:TermDebug` folowed by the command name, for example: >
|
||||||
:TermDebug vim
|
:TermDebug vim
|
||||||
|
|
||||||
This opens two windows:
|
This opens two windows:
|
||||||
- A terminal window in which "gdb vim" is executed. Here you can directly
|
- A terminal window in which "gdb vim" is executed. Here you can directly
|
||||||
interact with gdb.
|
interact with gdb. The buffer name is "!gdb".
|
||||||
- A terminal window for the executed program. When "run" is used in gdb the
|
- A terminal window for the executed program. When "run" is used in gdb the
|
||||||
program I/O will happen in this window, so that it does not interfere with
|
program I/O will happen in this window, so that it does not interfere with
|
||||||
controlling gdb.
|
controlling gdb. The buffer name is "gdb program".
|
||||||
The current window is used to show the source code. When gdb jumps to a
|
|
||||||
source file location this window will display the code, if possible. Values
|
The current window is used to show the source code. When gdb pauses the
|
||||||
of variables can be inspected, breakpoints set and cleared, etc.
|
source file location will be displayed, if possible. A sign is used to
|
||||||
|
highlight the current position (using highlight group debugPC).
|
||||||
|
|
||||||
|
If the buffer in the current window is modified, another window will be opened
|
||||||
|
to display the current gdb position.
|
||||||
|
|
||||||
|
Focus the terminal of the executed program to interact with it. This works
|
||||||
|
the same as any command running in a terminal window.
|
||||||
|
|
||||||
When the debugger ends the two opened windows are closed.
|
When the debugger ends the two opened windows are closed.
|
||||||
|
|
||||||
|
|
||||||
|
Stepping through code ~
|
||||||
|
|
||||||
|
Put focus on the gdb window to type commands there. Some common ones are:
|
||||||
|
- CTRL-C interrupt the program
|
||||||
|
- next execute the current line and stop at the next line
|
||||||
|
- step execute the current line and stop at the next statement, entering
|
||||||
|
functions
|
||||||
|
- finish execute until leaving the current function
|
||||||
|
- where show the stack
|
||||||
|
- frame N go to the Nth stack frame
|
||||||
|
- continue continue execution
|
||||||
|
|
||||||
|
In the window showing the source code some commands can passed to gdb:
|
||||||
|
- Break set a breakpoint at the current line; a sign will be displayed
|
||||||
|
- Delete delete a breakpoint at the current line
|
||||||
|
- Step execute the gdb "step" command
|
||||||
|
- NNext execute the gdb "next" command (:Next is a Vim command)
|
||||||
|
- Finish execute the gdb "finish" command
|
||||||
|
- Continue execute the gdb "continue" command
|
||||||
|
|
||||||
|
|
||||||
|
Communication ~
|
||||||
|
|
||||||
|
There is another, hidden, buffer, which is used for Vim to communicate with
|
||||||
|
gdb. The buffer name is "gdb communication". Do not delete this buffer, it
|
||||||
|
will break the debugger.
|
||||||
|
|
||||||
|
|
||||||
Customizing ~
|
Customizing ~
|
||||||
|
|
||||||
g:debugger The debugger command. Default "gdb".
|
To change the name of the gdb command, set the "termdebugger" variable before
|
||||||
|
invoking `:Termdebug`: >
|
||||||
|
let termdebugger = "mygdb"
|
||||||
|
Only debuggers fully compatible with gdb will work. Vim uses the GDB/MI
|
||||||
|
interface.
|
||||||
|
|
||||||
|
The color of the signs can be adjusted with these highlight groups:
|
||||||
|
- debugPC the current position
|
||||||
|
- debugBreakpoint a breakpoint
|
||||||
|
|
||||||
|
The defaults are, when 'background' is "light":
|
||||||
|
hi debugPC term=reverse ctermbg=lightblue guibg=lightblue
|
||||||
|
hi debugBreakpoint term=reverse ctermbg=red guibg=red
|
||||||
|
|
||||||
|
When 'background' is "dark":
|
||||||
|
hi debugPC term=reverse ctermbg=darkblue guibg=darkblue
|
||||||
|
hi debugBreakpoint term=reverse ctermbg=red guibg=red
|
||||||
|
|
||||||
|
|
||||||
TODO
|
NOT WORKING YET: ~
|
||||||
|
|
||||||
|
Values of variables can be inspected, etc.
|
||||||
|
|
||||||
|
|
||||||
vim:tw=78:ts=8:ft=help:norl:
|
vim:tw=78:ts=8:ft=help:norl:
|
||||||
|
182
runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
vendored
182
runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
vendored
@@ -20,18 +20,26 @@
|
|||||||
command -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>)
|
command -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>)
|
||||||
|
|
||||||
" Name of the gdb command, defaults to "gdb".
|
" Name of the gdb command, defaults to "gdb".
|
||||||
if !exists('debugger')
|
if !exists('termdebugger')
|
||||||
let debugger = 'gdb'
|
let termdebugger = 'gdb'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" Sign used to highlight the line where the program has stopped.
|
" Sign used to highlight the line where the program has stopped.
|
||||||
|
" There can be only one.
|
||||||
sign define debugPC linehl=debugPC
|
sign define debugPC linehl=debugPC
|
||||||
if &background == 'light'
|
|
||||||
hi debugPC term=reverse ctermbg=lightblue guibg=lightblue
|
|
||||||
else
|
|
||||||
hi debugPC term=reverse ctermbg=darkblue guibg=darkblue
|
|
||||||
endif
|
|
||||||
let s:pc_id = 12
|
let s:pc_id = 12
|
||||||
|
let s:break_id = 13
|
||||||
|
|
||||||
|
" Sign used to indicate a breakpoint.
|
||||||
|
" Can be used multiple times.
|
||||||
|
sign define debugBreakpoint text=>> texthl=debugBreakpoint
|
||||||
|
|
||||||
|
if &background == 'light'
|
||||||
|
hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue
|
||||||
|
else
|
||||||
|
hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue
|
||||||
|
endif
|
||||||
|
hi default debugBreakpoint term=reverse ctermbg=red guibg=red
|
||||||
|
|
||||||
func s:StartDebug(cmd)
|
func s:StartDebug(cmd)
|
||||||
let s:startwin = win_getid(winnr())
|
let s:startwin = win_getid(winnr())
|
||||||
@@ -61,7 +69,7 @@ func s:StartDebug(cmd)
|
|||||||
let commpty = job_info(term_getjob(s:commbuf))['tty_out']
|
let commpty = job_info(term_getjob(s:commbuf))['tty_out']
|
||||||
|
|
||||||
" Open a terminal window to run the debugger.
|
" Open a terminal window to run the debugger.
|
||||||
let cmd = [g:debugger, '-tty', pty, a:cmd]
|
let cmd = [g:termdebugger, '-tty', pty, a:cmd]
|
||||||
echomsg 'executing "' . join(cmd) . '"'
|
echomsg 'executing "' . join(cmd) . '"'
|
||||||
let gdbbuf = term_start(cmd, {
|
let gdbbuf = term_start(cmd, {
|
||||||
\ 'exit_cb': function('s:EndDebug'),
|
\ 'exit_cb': function('s:EndDebug'),
|
||||||
@@ -76,12 +84,24 @@ func s:StartDebug(cmd)
|
|||||||
|
|
||||||
" Connect gdb to the communication pty, using the GDB/MI interface
|
" Connect gdb to the communication pty, using the GDB/MI interface
|
||||||
call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r")
|
call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r")
|
||||||
|
|
||||||
|
" Install debugger commands.
|
||||||
|
call s:InstallCommands()
|
||||||
|
|
||||||
|
let s:breakpoints = {}
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func s:EndDebug(job, status)
|
func s:EndDebug(job, status)
|
||||||
exe 'bwipe! ' . s:ptybuf
|
exe 'bwipe! ' . s:ptybuf
|
||||||
exe 'bwipe! ' . s:commbuf
|
exe 'bwipe! ' . s:commbuf
|
||||||
call setwinvar(s:startwin, '&signcolumn', s:startsigncolumn)
|
|
||||||
|
let curwinid = win_getid(winnr())
|
||||||
|
|
||||||
|
call win_gotoid(s:startwin)
|
||||||
|
let &signcolumn = s:startsigncolumn
|
||||||
|
call s:DeleteCommands()
|
||||||
|
|
||||||
|
call win_gotoid(curwinid)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" Handle a message received from gdb on the GDB/MI interface.
|
" Handle a message received from gdb on the GDB/MI interface.
|
||||||
@@ -95,34 +115,124 @@ func s:CommOutput(chan, msg)
|
|||||||
endif
|
endif
|
||||||
if msg != ''
|
if msg != ''
|
||||||
if msg =~ '^\*\(stopped\|running\)'
|
if msg =~ '^\*\(stopped\|running\)'
|
||||||
let wid = win_getid(winnr())
|
call s:HandleCursor(msg)
|
||||||
|
elseif msg =~ '^\^done,bkpt='
|
||||||
if win_gotoid(s:startwin)
|
call s:HandleNewBreakpoint(msg)
|
||||||
if msg =~ '^\*stopped'
|
elseif msg =~ '^=breakpoint-deleted,'
|
||||||
" TODO: proper parsing
|
call s:HandleBreakpointDelete(msg)
|
||||||
let fname = substitute(msg, '.*fullname="\([^"]*\)".*', '\1', '')
|
|
||||||
let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
|
|
||||||
if lnum =~ '^[0-9]*$'
|
|
||||||
if expand('%:h') != fname
|
|
||||||
if &modified
|
|
||||||
" TODO: find existing window
|
|
||||||
exe 'split ' . fnameescape(fname)
|
|
||||||
let s:startwin = win_getid(winnr())
|
|
||||||
else
|
|
||||||
exe 'edit ' . fnameescape(fname)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
exe lnum
|
|
||||||
exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname)
|
|
||||||
setlocal signcolumn=yes
|
|
||||||
endif
|
|
||||||
else
|
|
||||||
exe 'sign unplace ' . s:pc_id
|
|
||||||
endif
|
|
||||||
|
|
||||||
call win_gotoid(wid)
|
|
||||||
endif
|
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
endfor
|
endfor
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" Install commands in the current window to control the debugger.
|
||||||
|
func s:InstallCommands()
|
||||||
|
command Break call s:SetBreakpoint()
|
||||||
|
command Delete call s:DeleteBreakpoint()
|
||||||
|
command Step call s:SendCommand('-exec-step')
|
||||||
|
command NNext call s:SendCommand('-exec-next')
|
||||||
|
command Finish call s:SendCommand('-exec-finish')
|
||||||
|
command Continue call s:SendCommand('-exec-continue')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Delete installed debugger commands in the current window.
|
||||||
|
func s:DeleteCommands()
|
||||||
|
delcommand Break
|
||||||
|
delcommand Delete
|
||||||
|
delcommand Step
|
||||||
|
delcommand NNext
|
||||||
|
delcommand Finish
|
||||||
|
delcommand Continue
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" :Break - Set a breakpoint at the cursor position.
|
||||||
|
func s:SetBreakpoint()
|
||||||
|
call term_sendkeys(s:commbuf, '-break-insert --source '
|
||||||
|
\ . fnameescape(expand('%:p')) . ' --line ' . line('.') . "\r")
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" :Delete - Delete a breakpoint at the cursor position.
|
||||||
|
func s:DeleteBreakpoint()
|
||||||
|
let fname = fnameescape(expand('%:p'))
|
||||||
|
let lnum = line('.')
|
||||||
|
for [key, val] in items(s:breakpoints)
|
||||||
|
if val['fname'] == fname && val['lnum'] == lnum
|
||||||
|
call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r")
|
||||||
|
" Assume this always wors, the reply is simply "^done".
|
||||||
|
exe 'sign unplace ' . (s:break_id + key)
|
||||||
|
unlet s:breakpoints[key]
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" :Next, :Continue, etc - send a command to gdb
|
||||||
|
func s:SendCommand(cmd)
|
||||||
|
call term_sendkeys(s:commbuf, a:cmd . "\r")
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Handle stopping and running message from gdb.
|
||||||
|
" Will update the sign that shows the current position.
|
||||||
|
func s:HandleCursor(msg)
|
||||||
|
let wid = win_getid(winnr())
|
||||||
|
|
||||||
|
if win_gotoid(s:startwin)
|
||||||
|
if a:msg =~ '^\*stopped'
|
||||||
|
let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
|
||||||
|
let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
|
||||||
|
if lnum =~ '^[0-9]*$'
|
||||||
|
if expand('%:h') != fname
|
||||||
|
if &modified
|
||||||
|
" TODO: find existing window
|
||||||
|
exe 'split ' . fnameescape(fname)
|
||||||
|
let s:startwin = win_getid(winnr())
|
||||||
|
else
|
||||||
|
exe 'edit ' . fnameescape(fname)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
exe lnum
|
||||||
|
exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname)
|
||||||
|
setlocal signcolumn=yes
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
exe 'sign unplace ' . s:pc_id
|
||||||
|
endif
|
||||||
|
|
||||||
|
call win_gotoid(wid)
|
||||||
|
endif
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Handle setting a breakpoint
|
||||||
|
" Will update the sign that shows the breakpoint
|
||||||
|
func s:HandleNewBreakpoint(msg)
|
||||||
|
let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
|
||||||
|
if nr == 0
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(s:breakpoints, nr)
|
||||||
|
let entry = s:breakpoints[nr]
|
||||||
|
else
|
||||||
|
let entry = {}
|
||||||
|
let s:breakpoints[nr] = entry
|
||||||
|
endif
|
||||||
|
|
||||||
|
let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
|
||||||
|
let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
|
||||||
|
|
||||||
|
exe 'sign place ' . (s:break_id + nr) . ' line=' . lnum . ' name=debugBreakpoint file=' . fnameescape(fname)
|
||||||
|
|
||||||
|
let entry['fname'] = fname
|
||||||
|
let entry['lnum'] = lnum
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Handle deleting a breakpoint
|
||||||
|
" Will remove the sign that shows the breakpoint
|
||||||
|
func s:HandleBreakpointDelete(msg)
|
||||||
|
let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
|
||||||
|
if nr == 0
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
exe 'sign unplace ' . (s:break_id + nr)
|
||||||
|
unlet s:breakpoints[nr]
|
||||||
|
endfunc
|
||||||
|
@@ -769,6 +769,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1085,
|
||||||
/**/
|
/**/
|
||||||
1084,
|
1084,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user