|
1 | | -" Debugger commands. |
| 1 | +" Debugger plugin using gdb. |
2 | 2 | " |
3 | 3 | " WORK IN PROGRESS - much doesn't work yet |
4 | 4 | " |
5 | | -" Open two terminal windows: |
| 5 | +" Open two visible terminal windows: |
6 | 6 | " 1. run a pty, as with ":term NONE" |
7 | 7 | " 2. run gdb, passing the pty |
8 | | -" The current window is used to edit source code and follows gdb. |
| 8 | +" The current window is used to view source code and follows gdb. |
| 9 | +" |
| 10 | +" A third terminal window is hidden, it is used for communication with gdb. |
| 11 | +" |
| 12 | +" The communication with gdb uses GDB/MI. See: |
| 13 | +" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html |
9 | 14 | " |
10 | 15 | " Author: Bram Moolenaar |
11 | | -" Copyright: Vim license applies |
| 16 | +" Copyright: Vim license applies, see ":help license" |
12 | 17 |
|
| 18 | +" The command that starts debugging, e.g. ":Termdebug vim". |
| 19 | +" To end type "quit" in the gdb window. |
13 | 20 | command -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>) |
14 | 21 |
|
| 22 | +" Name of the gdb command, defaults to "gdb". |
15 | 23 | if !exists('debugger') |
16 | 24 | let debugger = 'gdb' |
17 | 25 | endif |
18 | 26 |
|
| 27 | +" Sign used to highlight the line where the program has stopped. |
| 28 | +sign define debugPC linehl=debugPC |
| 29 | +if &background == 'light' |
| 30 | + hi debugPC term=reverse ctermbg=lightblue guibg=lightblue |
| 31 | +else |
| 32 | + hi debugPC term=reverse ctermbg=darkblue guibg=darkblue |
| 33 | +endif |
| 34 | +let s:pc_id = 12 |
| 35 | + |
19 | 36 | func s:StartDebug(cmd) |
| 37 | + let s:startwin = win_getid(winnr()) |
| 38 | + let s:startsigncolumn = &signcolumn |
| 39 | + |
20 | 40 | " Open a terminal window without a job, to run the debugged program |
21 | | - let s:ptybuf = term_start('NONE', {}) |
22 | | - let pty = job_info(term_getjob(s:ptybuf))['tty'] |
| 41 | + let s:ptybuf = term_start('NONE', { |
| 42 | + \ 'term_name': 'gdb program', |
| 43 | + \ }) |
| 44 | + if s:ptybuf == 0 |
| 45 | + echoerr 'Failed to open the program terminal window' |
| 46 | + return |
| 47 | + endif |
| 48 | + let pty = job_info(term_getjob(s:ptybuf))['tty_out'] |
| 49 | + |
| 50 | + " Create a hidden terminal window to communicate with gdb |
| 51 | + let s:commbuf = term_start('NONE', { |
| 52 | + \ 'term_name': 'gdb communication', |
| 53 | + \ 'out_cb': function('s:CommOutput'), |
| 54 | + \ 'hidden': 1, |
| 55 | + \ }) |
| 56 | + if s:commbuf == 0 |
| 57 | + echoerr 'Failed to open the communication terminal window' |
| 58 | + exe 'bwipe! ' . s:ptybuf |
| 59 | + return |
| 60 | + endif |
| 61 | + let commpty = job_info(term_getjob(s:commbuf))['tty_out'] |
23 | 62 |
|
24 | 63 | " Open a terminal window to run the debugger. |
25 | 64 | let cmd = [g:debugger, '-tty', pty, a:cmd] |
26 | 65 | echomsg 'executing "' . join(cmd) . '"' |
27 | 66 | let gdbbuf = term_start(cmd, { |
28 | 67 | \ 'exit_cb': function('s:EndDebug'), |
29 | | - \ 'term_finish': 'close' |
| 68 | + \ 'term_finish': 'close', |
30 | 69 | \ }) |
| 70 | + if gdbbuf == 0 |
| 71 | + echoerr 'Failed to open the gdb terminal window' |
| 72 | + exe 'bwipe! ' . s:ptybuf |
| 73 | + exe 'bwipe! ' . s:commbuf |
| 74 | + return |
| 75 | + endif |
| 76 | + |
| 77 | + " Connect gdb to the communication pty, using the GDB/MI interface |
| 78 | + call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r") |
31 | 79 | endfunc |
32 | 80 |
|
33 | 81 | func s:EndDebug(job, status) |
34 | | - exe 'bwipe! ' . s:ptybuf |
| 82 | + exe 'bwipe! ' . s:ptybuf |
| 83 | + exe 'bwipe! ' . s:commbuf |
| 84 | + call setwinvar(s:startwin, '&signcolumn', s:startsigncolumn) |
| 85 | +endfunc |
| 86 | + |
| 87 | +" Handle a message received from gdb on the GDB/MI interface. |
| 88 | +func s:CommOutput(chan, msg) |
| 89 | + let msgs = split(a:msg, "\r") |
| 90 | + |
| 91 | + for msg in msgs |
| 92 | + " remove prefixed NL |
| 93 | + if msg[0] == "\n" |
| 94 | + let msg = msg[1:] |
| 95 | + endif |
| 96 | + if msg != '' |
| 97 | + 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 |
| 119 | + else |
| 120 | + exe 'sign unplace ' . s:pc_id |
| 121 | + endif |
| 122 | + |
| 123 | + call win_gotoid(wid) |
| 124 | + endif |
| 125 | + endif |
| 126 | + endif |
| 127 | + endfor |
35 | 128 | endfunc |
0 commit comments