]> granicus.if.org Git - vim/commitdiff
patch 8.0.1085: terminal debugger can't set breakpoints v8.0.1085
authorBram Moolenaar <Bram@vim.org>
Sat, 9 Sep 2017 20:19:47 +0000 (22:19 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 9 Sep 2017 20:19:47 +0000 (22:19 +0200)
Problem:    The terminal debugger can't set breakpoints.
Solution:   Add :Break and :Delete commands.  Also commands for stepping
            through code.

runtime/doc/terminal.txt
runtime/pack/dist/opt/termdebug/plugin/termdebug.vim
src/version.c

index 7be6927a0bcd6839020891ef4193340548e1a691..3489142cefcc895bf79330a0bf38d29bcabd50c5 100644 (file)
@@ -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
@@ -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: >
      :term bash
 
-Or to run a debugger: >
-     :term gdb vim
+Or to run build command: >
+     :term make myprogram
 
 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 ~
@@ -109,7 +109,8 @@ Syntax ~
 
                        If [range] is given the specified lines are used as
                        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".
                        E.g. `:24,80gdb` opens a terminal with 24 rows and 80
@@ -133,14 +134,15 @@ Syntax ~
                                        height.
                        ++cols={width}  Use {width} for the terminal window
                                        width.
-                       ++eof={text}    when using [range], text to send after
-                                       the last line was written. The default
-                                       is to send CTRL-D.  A CR is appended.
+                       ++eof={text}    when using [range]: text to send after
+                                       the last line was written. Cannot
+                                       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
                                        for Python "++eof=exit()".  Special
                                        codes can be used like with `:map`,
                                        e.g. "<C-Z>" for CTRL-Z.
-                                       {only on MS-Windows}
 
                        If you want to use more options use the |term_start()|
                        function.
@@ -303,33 +305,90 @@ term_scrape()             inspect terminal screen
 3. Debugging                                   *terminal-debug*
 
 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: >
        packadd termdebug
-
+<                                                      *:Termdebug*
 To start debugging use `:TermDebug` folowed by the command name, for example: >
        :TermDebug vim
 
 This opens two windows:
 - 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
   program I/O will happen in this window, so that it does not interfere with
-  controlling gdb.
-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
-of variables can be inspected, breakpoints set and cleared, etc.
+  controlling gdb.  The buffer name is "gdb program".
+
+The current window is used to show the source code.  When gdb pauses the
+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.
 
 
+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 ~
 
-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
+
 
+NOT WORKING YET: ~
 
-TODO
+Values of variables can be inspected, etc.
 
 
  vim:tw=78:ts=8:ft=help:norl:
index f69990b52bfa695edc5bb9a9714518c62167a185..1ee887aa55dfa773763c5a2d30b9812897827eec 100644 (file)
 command -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>)
 
 " Name of the gdb command, defaults to "gdb".
-if !exists('debugger')
-  let debugger = 'gdb'
+if !exists('termdebugger')
+  let termdebugger = 'gdb'
 endif
 
 " Sign used to highlight the line where the program has stopped.
+" There can be only one.
 sign define debugPC linehl=debugPC
+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 debugPC term=reverse ctermbg=lightblue guibg=lightblue
+  hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue
 else
-  hi debugPC term=reverse ctermbg=darkblue guibg=darkblue
+  hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue
 endif
-let s:pc_id = 12
+hi default debugBreakpoint term=reverse ctermbg=red guibg=red
 
 func s:StartDebug(cmd)
   let s:startwin = win_getid(winnr())
@@ -61,7 +69,7 @@ func s:StartDebug(cmd)
   let commpty = job_info(term_getjob(s:commbuf))['tty_out']
 
   " 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) . '"'
   let gdbbuf = term_start(cmd, {
        \ 'exit_cb': function('s:EndDebug'),
@@ -76,12 +84,24 @@ func s:StartDebug(cmd)
 
   " Connect gdb to the communication pty, using the GDB/MI interface
   call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r")
+
+  " Install debugger commands.
+  call s:InstallCommands()
+
+  let s:breakpoints = {}
 endfunc
 
 func s:EndDebug(job, status)
   exe 'bwipe! ' . s:ptybuf
   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
 
 " Handle a message received from gdb on the GDB/MI interface.
@@ -95,34 +115,124 @@ func s:CommOutput(chan, msg)
     endif
     if msg != ''
       if msg =~ '^\*\(stopped\|running\)'
-       let wid = win_getid(winnr())
-
-       if win_gotoid(s:startwin)
-         if msg =~ '^\*stopped'
-           " TODO: proper parsing
-           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
+       call s:HandleCursor(msg)
+      elseif msg =~ '^\^done,bkpt='
+       call s:HandleNewBreakpoint(msg)
+      elseif msg =~ '^=breakpoint-deleted,'
+       call s:HandleBreakpointDelete(msg)
+      endif
+    endif
+  endfor
+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 'sign unplace ' . s:pc_id
+           exe 'edit ' . fnameescape(fname)
          endif
-
-         call win_gotoid(wid)
        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
-  endfor
+
+    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
index acf6e29d8d70a2e7409f1cf12697b9c7be2c144f..cb5963424ca189ce80891376aac62be0351a72c2 100644 (file)
@@ -769,6 +769,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1085,
 /**/
     1084,
 /**/