1fe386641SBram Moolenaar" Debugger plugin using gdb. 2c572da5fSBram Moolenaar" 3b3307b5eSBram Moolenaar" Author: Bram Moolenaar 4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license" 5b3307b5eSBram Moolenaar" Last Update: 2018 Jun 3 6c572da5fSBram Moolenaar" 7b3307b5eSBram Moolenaar" WORK IN PROGRESS - Only the basics work 8b3307b5eSBram Moolenaar" Note: On MS-Windows you need a recent version of gdb. The one included with 9b3307b5eSBram Moolenaar" MingW is too old (7.6.1). 10b3307b5eSBram Moolenaar" I used version 7.12 from http://www.equation.com/servlet/equation.cmd?fa=gdb 11fe386641SBram Moolenaar" 12b3307b5eSBram Moolenaar" There are two ways to run gdb: 13b3307b5eSBram Moolenaar" - In a terminal window; used if possible, does not work on MS-Windows 14b3307b5eSBram Moolenaar" Not used when g:termdebug_use_prompt is set to 1. 15b3307b5eSBram Moolenaar" - Using a "prompt" buffer; may use a terminal window for the program 16b3307b5eSBram Moolenaar" 17b3307b5eSBram Moolenaar" For both the current window is used to view source code and shows the 18b3307b5eSBram Moolenaar" current statement from gdb. 19b3307b5eSBram Moolenaar" 20b3307b5eSBram Moolenaar" USING A TERMINAL WINDOW 21b3307b5eSBram Moolenaar" 22b3307b5eSBram Moolenaar" Opens two visible terminal windows: 23b3307b5eSBram Moolenaar" 1. runs a pty for the debugged program, as with ":term NONE" 24b3307b5eSBram Moolenaar" 2. runs gdb, passing the pty of the debugged program 25fe386641SBram Moolenaar" A third terminal window is hidden, it is used for communication with gdb. 26fe386641SBram Moolenaar" 27b3307b5eSBram Moolenaar" USING A PROMPT BUFFER 28b3307b5eSBram Moolenaar" 29b3307b5eSBram Moolenaar" Opens a window with a prompt buffer to communicate with gdb. 30b3307b5eSBram Moolenaar" Gdb is run as a job with callbacks for I/O. 31b3307b5eSBram Moolenaar" On Unix another terminal window is opened to run the debugged program 32b3307b5eSBram Moolenaar" On MS-Windows a separate console is opened to run the debugged program 33b3307b5eSBram Moolenaar" 34fe386641SBram Moolenaar" The communication with gdb uses GDB/MI. See: 35fe386641SBram Moolenaar" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html 36c572da5fSBram Moolenaar 37b3307b5eSBram Moolenaar" In case this gets sourced twice. 3837c64c78SBram Moolenaarif exists(':Termdebug') 3937c64c78SBram Moolenaar finish 4037c64c78SBram Moolenaarendif 4137c64c78SBram Moolenaar 42b3307b5eSBram Moolenaar" Need either the +terminal feature or +channel and the prompt buffer. 43b3307b5eSBram Moolenaar" The terminal feature does not work with gdb on win32. 44b3307b5eSBram Moolenaarif has('terminal') && !has('win32') 45b3307b5eSBram Moolenaar let s:way = 'terminal' 46b3307b5eSBram Moolenaarelseif has('channel') && exists('*prompt_setprompt') 47b3307b5eSBram Moolenaar let s:way = 'prompt' 48b3307b5eSBram Moolenaarelse 49b3307b5eSBram Moolenaar if has('terminal') 50b3307b5eSBram Moolenaar let s:err = 'Cannot debug, missing prompt buffer support' 51b3307b5eSBram Moolenaar else 52b3307b5eSBram Moolenaar let s:err = 'Cannot debug, +channel feature is not supported' 53b3307b5eSBram Moolenaar endif 54b3307b5eSBram Moolenaar command -nargs=* -complete=file -bang Termdebug echoerr s:err 55b3307b5eSBram Moolenaar command -nargs=+ -complete=file -bang TermdebugCommand echoerr s:err 56b3307b5eSBram Moolenaar finish 57b3307b5eSBram Moolenaarendif 5860e73f2aSBram Moolenaar 59fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim". 60fe386641SBram Moolenaar" To end type "quit" in the gdb window. 6132c67ba7SBram Moolenaarcommand -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>) 6232c67ba7SBram Moolenaarcommand -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>) 63c572da5fSBram Moolenaar 64fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb". 65e09ba7baSBram Moolenaarif !exists('termdebugger') 66e09ba7baSBram Moolenaar let termdebugger = 'gdb' 67c572da5fSBram Moolenaarendif 68c572da5fSBram Moolenaar 69fe386641SBram Moolenaarlet s:pc_id = 12 70*de1a8314SBram Moolenaarlet s:break_id = 13 " breakpoint number is added to this 7160e73f2aSBram Moolenaarlet s:stopped = 1 72e09ba7baSBram Moolenaar 73e09ba7baSBram Moolenaarif &background == 'light' 74e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue 75e09ba7baSBram Moolenaarelse 76e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue 77e09ba7baSBram Moolenaarendif 78e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 79fe386641SBram Moolenaar 8032c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...) 8132c67ba7SBram Moolenaar " First argument is the command to debug, second core file or process ID. 8232c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) 8332c67ba7SBram Moolenaarendfunc 8432c67ba7SBram Moolenaar 8532c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...) 8632c67ba7SBram Moolenaar " First argument is the command to debug, rest are run arguments. 8732c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang}) 8832c67ba7SBram Moolenaarendfunc 8932c67ba7SBram Moolenaar 9032c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict) 91b3623a38SBram Moolenaar if exists('s:gdbwin') 92b3623a38SBram Moolenaar echoerr 'Terminal debugger already running' 93b3623a38SBram Moolenaar return 94b3623a38SBram Moolenaar endif 95b3307b5eSBram Moolenaar let s:ptywin = 0 96b3623a38SBram Moolenaar 97b3307b5eSBram Moolenaar " Uncomment this line to write logging in "debuglog". 98b3307b5eSBram Moolenaar " call ch_logfile('debuglog', 'w') 99b3307b5eSBram Moolenaar 100b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 101fe386641SBram Moolenaar let s:startsigncolumn = &signcolumn 102fe386641SBram Moolenaar 10324a98a0eSBram Moolenaar let s:save_columns = 0 10424a98a0eSBram Moolenaar if exists('g:termdebug_wide') 10524a98a0eSBram Moolenaar if &columns < g:termdebug_wide 10638baa3e6SBram Moolenaar let s:save_columns = &columns 10738baa3e6SBram Moolenaar let &columns = g:termdebug_wide 10824a98a0eSBram Moolenaar endif 109b3307b5eSBram Moolenaar let s:vertical = 1 11038baa3e6SBram Moolenaar else 111b3307b5eSBram Moolenaar let s:vertical = 0 11238baa3e6SBram Moolenaar endif 11338baa3e6SBram Moolenaar 114b3307b5eSBram Moolenaar " Override using a terminal window by setting g:termdebug_use_prompt to 1. 115b3307b5eSBram Moolenaar let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt 116b3307b5eSBram Moolenaar if has('terminal') && !has('win32') && !use_prompt 117b3307b5eSBram Moolenaar let s:way = 'terminal' 118b3307b5eSBram Moolenaar else 119b3307b5eSBram Moolenaar let s:way = 'prompt' 120b3307b5eSBram Moolenaar endif 121b3307b5eSBram Moolenaar 122b3307b5eSBram Moolenaar if s:way == 'prompt' 123b3307b5eSBram Moolenaar call s:StartDebug_prompt(a:dict) 124b3307b5eSBram Moolenaar else 125b3307b5eSBram Moolenaar call s:StartDebug_term(a:dict) 126b3307b5eSBram Moolenaar endif 127b3307b5eSBram Moolenaarendfunc 128b3307b5eSBram Moolenaar 129b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 130b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 131fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 132b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 133b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 134fe386641SBram Moolenaar \ }) 135fe386641SBram Moolenaar if s:ptybuf == 0 136fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 137fe386641SBram Moolenaar return 138fe386641SBram Moolenaar endif 139fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 14045d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 141b3307b5eSBram Moolenaar if s:vertical 14251b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 14351b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 14451b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 14551b0f370SBram Moolenaar endif 146fe386641SBram Moolenaar 147fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 148fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 149fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 150fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 151fe386641SBram Moolenaar \ 'hidden': 1, 152fe386641SBram Moolenaar \ }) 153fe386641SBram Moolenaar if s:commbuf == 0 154fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 155fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 156fe386641SBram Moolenaar return 157fe386641SBram Moolenaar endif 158fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 159c572da5fSBram Moolenaar 160c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 161c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 16232c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 16332c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 16432c67ba7SBram Moolenaar 16532c67ba7SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 166b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 16760e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 168b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndTermDebug'), 169fe386641SBram Moolenaar \ 'term_finish': 'close', 170c572da5fSBram Moolenaar \ }) 17160e73f2aSBram Moolenaar if s:gdbbuf == 0 172fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 173fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 174fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 175fe386641SBram Moolenaar return 176fe386641SBram Moolenaar endif 17745d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 178fe386641SBram Moolenaar 17932c67ba7SBram Moolenaar " Set arguments to be run 18032c67ba7SBram Moolenaar if len(proc_args) 18132c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 18232c67ba7SBram Moolenaar endif 18332c67ba7SBram Moolenaar 184fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 18560e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 18660e73f2aSBram Moolenaar 1873e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 1883e4b84d0SBram Moolenaar " why the debugger doesn't work. 1893e4b84d0SBram Moolenaar let try_count = 0 1903e4b84d0SBram Moolenaar while 1 1913e4b84d0SBram Moolenaar let response = '' 192b3623a38SBram Moolenaar for lnum in range(1,200) 1933e4b84d0SBram Moolenaar if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi ' 1943e4b84d0SBram Moolenaar let response = term_getline(s:gdbbuf, lnum + 1) 1953e4b84d0SBram Moolenaar if response =~ 'Undefined command' 196f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 1973e4b84d0SBram Moolenaar exe 'bwipe! ' . s:ptybuf 1983e4b84d0SBram Moolenaar exe 'bwipe! ' . s:commbuf 1993e4b84d0SBram Moolenaar return 2003e4b84d0SBram Moolenaar endif 2013e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2023e4b84d0SBram Moolenaar " Success! 2033e4b84d0SBram Moolenaar break 2043e4b84d0SBram Moolenaar endif 2053e4b84d0SBram Moolenaar endif 2063e4b84d0SBram Moolenaar endfor 2073e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2083e4b84d0SBram Moolenaar break 2093e4b84d0SBram Moolenaar endif 2103e4b84d0SBram Moolenaar let try_count += 1 2113e4b84d0SBram Moolenaar if try_count > 100 2123e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2133e4b84d0SBram Moolenaar break 2143e4b84d0SBram Moolenaar endif 2153e4b84d0SBram Moolenaar sleep 10m 2163e4b84d0SBram Moolenaar endwhile 2173e4b84d0SBram Moolenaar 21860e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 21960e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 22060e73f2aSBram Moolenaar " running. 22160e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 222b3307b5eSBram Moolenaar " Older gdb uses a different command. 223b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 224e09ba7baSBram Moolenaar 225f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 226f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 227b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 228f3ba14ffSBram Moolenaar 229b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 230b3307b5eSBram Moolenaarendfunc 231b3307b5eSBram Moolenaar 232b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 233b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 234b3307b5eSBram Moolenaar if s:vertical 235b3307b5eSBram Moolenaar vertical new 236b3307b5eSBram Moolenaar else 237b3307b5eSBram Moolenaar new 238b3307b5eSBram Moolenaar endif 239b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 240b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 241b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 242b3307b5eSBram Moolenaar set buftype=prompt 243b3307b5eSBram Moolenaar file gdb 244b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 245b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 246b3307b5eSBram Moolenaar 247b3307b5eSBram Moolenaar if s:vertical 248b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 249b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 250b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 251b3307b5eSBram Moolenaar endif 252b3307b5eSBram Moolenaar 253b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 254b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 255b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 256b3307b5eSBram Moolenaar 257b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 258b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 259b3307b5eSBram Moolenaar 260b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 261b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 262b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 263b3307b5eSBram Moolenaar \ }) 264b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 265b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 266b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 267b3307b5eSBram Moolenaar return 268b3307b5eSBram Moolenaar endif 269b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 270b3307b5eSBram Moolenaar 271b3307b5eSBram Moolenaar " Interpret commands while the target is running. This should usualy only 272b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 273b3307b5eSBram Moolenaar " target is running. 274b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 275b3307b5eSBram Moolenaar " Older gdb uses a different command. 276b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 277b3307b5eSBram Moolenaar 278b3307b5eSBram Moolenaar let s:ptybuf = 0 279b3307b5eSBram Moolenaar if has('win32') 280b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 281b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 282b3307b5eSBram Moolenaar elseif has('terminal') 283b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 284b3307b5eSBram Moolenaar " gdb window. 285b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 286b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 287b3307b5eSBram Moolenaar \ }) 288b3307b5eSBram Moolenaar if s:ptybuf == 0 289b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 290b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 291b3307b5eSBram Moolenaar return 292b3307b5eSBram Moolenaar endif 293b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 294b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 295b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 296b3307b5eSBram Moolenaar 297b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 298b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 299b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 300b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 301b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 302b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 303b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 304b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 305b3307b5eSBram Moolenaar else 306b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 307b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 308b3307b5eSBram Moolenaar endif 309b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 310b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 311b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 312b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 313b3307b5eSBram Moolenaar 314b3307b5eSBram Moolenaar " Set arguments to be run 315b3307b5eSBram Moolenaar if len(proc_args) 316b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 317b3307b5eSBram Moolenaar endif 318b3307b5eSBram Moolenaar 319b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 320b3307b5eSBram Moolenaar startinsert 321b3307b5eSBram Moolenaarendfunc 322b3307b5eSBram Moolenaar 323b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 32438baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 32538baa3e6SBram Moolenaar " There can be only one. 32638baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 32738baa3e6SBram Moolenaar 32845d5f26dSBram Moolenaar " Install debugger commands in the text window. 329b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 330e09ba7baSBram Moolenaar call s:InstallCommands() 33145d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 332e09ba7baSBram Moolenaar 33351b0f370SBram Moolenaar " Enable showing a balloon with eval info 334246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 335246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 33651b0f370SBram Moolenaar if has("balloon_eval") 33751b0f370SBram Moolenaar set ballooneval 338246fe03dSBram Moolenaar endif 33951b0f370SBram Moolenaar if has("balloon_eval_term") 34051b0f370SBram Moolenaar set balloonevalterm 34151b0f370SBram Moolenaar endif 34251b0f370SBram Moolenaar endif 34351b0f370SBram Moolenaar 344*de1a8314SBram Moolenaar " Contains breakpoints that have been placed, key is the number. 345e09ba7baSBram Moolenaar let s:breakpoints = {} 3461b9645deSBram Moolenaar 3471b9645deSBram Moolenaar augroup TermDebug 3481b9645deSBram Moolenaar au BufRead * call s:BufRead() 3491b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 3501b9645deSBram Moolenaar augroup END 35132c67ba7SBram Moolenaar 352b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 353b3307b5eSBram Moolenaar " window. 35432c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 35532c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 35632c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 35732c67ba7SBram Moolenaar endif 358c572da5fSBram Moolenaarendfunc 359c572da5fSBram Moolenaar 360b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 361b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 362b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 363b3307b5eSBram Moolenaar if s:way == 'prompt' 364b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 365b3307b5eSBram Moolenaar else 366b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 367b3307b5eSBram Moolenaar endif 368b3307b5eSBram Moolenaarendfunc 369b3307b5eSBram Moolenaar 370b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 371b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 372b3307b5eSBram Moolenaar if s:way == 'prompt' 373b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 374b3307b5eSBram Moolenaar else 375b3307b5eSBram Moolenaar let do_continue = 0 376b3307b5eSBram Moolenaar if !s:stopped 377b3307b5eSBram Moolenaar let do_continue = 1 378b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 379b3307b5eSBram Moolenaar sleep 10m 380b3307b5eSBram Moolenaar endif 381b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 382b3307b5eSBram Moolenaar if do_continue 383b3307b5eSBram Moolenaar Continue 384b3307b5eSBram Moolenaar endif 385b3307b5eSBram Moolenaar endif 386b3307b5eSBram Moolenaarendfunc 387b3307b5eSBram Moolenaar 388b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 389b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 390b3307b5eSBram Moolenaar call s:SendCommand(a:text) 391b3307b5eSBram Moolenaarendfunc 392b3307b5eSBram Moolenaar 393b3307b5eSBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer. 394b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 395b3307b5eSBram Moolenaar call ch_log('Interrupting gdb') 396b3307b5eSBram Moolenaar call job_stop(s:gdbjob, 'int') 397b3307b5eSBram Moolenaarendfunc 398b3307b5eSBram Moolenaar 399b3307b5eSBram Moolenaar" Function called when gdb outputs text. 400b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 401b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 402b3307b5eSBram Moolenaar 403b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 404b3307b5eSBram Moolenaar " Drop status and echo'd commands. 405b3307b5eSBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' || a:text[0] == '=' 406b3307b5eSBram Moolenaar return 407b3307b5eSBram Moolenaar endif 408b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 409b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 410b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 411b3307b5eSBram Moolenaar " Silently drop evaluation errors. 412b3307b5eSBram Moolenaar unlet s:evalexpr 413b3307b5eSBram Moolenaar return 414b3307b5eSBram Moolenaar endif 415b3307b5eSBram Moolenaar elseif a:text[0] == '~' 416b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 417b3307b5eSBram Moolenaar else 418b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 419b3307b5eSBram Moolenaar return 420b3307b5eSBram Moolenaar endif 421b3307b5eSBram Moolenaar 422b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 423b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 424b3307b5eSBram Moolenaar 425b3307b5eSBram Moolenaar " Add the output above the current prompt. 426b3307b5eSBram Moolenaar call append(line('$') - 1, text) 427b3307b5eSBram Moolenaar set nomodified 428b3307b5eSBram Moolenaar 429b3307b5eSBram Moolenaar call win_gotoid(curwinid) 430b3307b5eSBram Moolenaarendfunc 431b3307b5eSBram Moolenaar 432b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 433b3307b5eSBram Moolenaar" to the next ", unescaping characters. 434b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 435b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 436b3307b5eSBram Moolenaar echoerr 'DecodeMessage(): missing quote' 437b3307b5eSBram Moolenaar return 438b3307b5eSBram Moolenaar endif 439b3307b5eSBram Moolenaar let result = '' 440b3307b5eSBram Moolenaar let i = 1 441b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 442b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 443b3307b5eSBram Moolenaar let i += 1 444b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 445b3307b5eSBram Moolenaar " drop \n 446b3307b5eSBram Moolenaar let i += 1 447b3307b5eSBram Moolenaar continue 448b3307b5eSBram Moolenaar endif 449b3307b5eSBram Moolenaar endif 450b3307b5eSBram Moolenaar let result .= a:quotedText[i] 451b3307b5eSBram Moolenaar let i += 1 452b3307b5eSBram Moolenaar endwhile 453b3307b5eSBram Moolenaar return result 454b3307b5eSBram Moolenaarendfunc 455b3307b5eSBram Moolenaar 456b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 457fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 458b3623a38SBram Moolenaar unlet s:gdbwin 459e09ba7baSBram Moolenaar 460b3307b5eSBram Moolenaar call s:EndDebugCommon() 461b3307b5eSBram Moolenaarendfunc 462b3307b5eSBram Moolenaar 463b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 464e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 465e09ba7baSBram Moolenaar 466b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 467b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 468b3307b5eSBram Moolenaar endif 469b3307b5eSBram Moolenaar 470b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 471e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 472e09ba7baSBram Moolenaar call s:DeleteCommands() 473e09ba7baSBram Moolenaar 474e09ba7baSBram Moolenaar call win_gotoid(curwinid) 475b3307b5eSBram Moolenaar 47638baa3e6SBram Moolenaar if s:save_columns > 0 47738baa3e6SBram Moolenaar let &columns = s:save_columns 47838baa3e6SBram Moolenaar endif 4791b9645deSBram Moolenaar 480246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 481246fe03dSBram Moolenaar set balloonexpr= 48251b0f370SBram Moolenaar if has("balloon_eval") 48351b0f370SBram Moolenaar set noballooneval 484246fe03dSBram Moolenaar endif 48551b0f370SBram Moolenaar if has("balloon_eval_term") 48651b0f370SBram Moolenaar set noballoonevalterm 48751b0f370SBram Moolenaar endif 48851b0f370SBram Moolenaar endif 48951b0f370SBram Moolenaar 4901b9645deSBram Moolenaar au! TermDebug 491fe386641SBram Moolenaarendfunc 492fe386641SBram Moolenaar 493b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 494b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 495b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 496b3307b5eSBram Moolenaar close 497b3307b5eSBram Moolenaar if curwinid != s:gdbwin 498b3307b5eSBram Moolenaar call win_gotoid(curwinid) 499b3307b5eSBram Moolenaar endif 500b3307b5eSBram Moolenaar 501b3307b5eSBram Moolenaar call s:EndDebugCommon() 502b3307b5eSBram Moolenaar unlet s:gdbwin 503b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 504b3307b5eSBram Moolenaarendfunc 505b3307b5eSBram Moolenaar 506fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 507fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 508fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 509fe386641SBram Moolenaar 510fe386641SBram Moolenaar for msg in msgs 511fe386641SBram Moolenaar " remove prefixed NL 512fe386641SBram Moolenaar if msg[0] == "\n" 513fe386641SBram Moolenaar let msg = msg[1:] 514fe386641SBram Moolenaar endif 515fe386641SBram Moolenaar if msg != '' 5161b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 517e09ba7baSBram Moolenaar call s:HandleCursor(msg) 51845d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 519e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 520e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 521e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 52245d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 52345d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 52445d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 52545d5f26dSBram Moolenaar call s:HandleError(msg) 526e09ba7baSBram Moolenaar endif 527e09ba7baSBram Moolenaar endif 528e09ba7baSBram Moolenaar endfor 529e09ba7baSBram Moolenaarendfunc 530e09ba7baSBram Moolenaar 531e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 532e09ba7baSBram Moolenaarfunc s:InstallCommands() 533e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 53471137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 535e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 53645d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 537e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 53860e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 53960e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 54060e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 541b3307b5eSBram Moolenaar 542b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 543b3307b5eSBram Moolenaar if s:way == 'prompt' 544b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 545b3307b5eSBram Moolenaar else 546b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 547b3307b5eSBram Moolenaar endif 548b3307b5eSBram Moolenaar 54945d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 55045d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 55145d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 552b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 55371137fedSBram Moolenaar command Winbar call s:InstallWinbar() 55445d5f26dSBram Moolenaar 55545d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 55645d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 5571b9645deSBram Moolenaar 558f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 55971137fedSBram Moolenaar call s:InstallWinbar() 56071137fedSBram Moolenaar 56171137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 56271137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 56371137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 56471137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 56571137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 56671137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 56771137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 56871137fedSBram Moolenaar endif 56971137fedSBram Moolenaar endif 57071137fedSBram Moolenaarendfunc 57171137fedSBram Moolenaar 57271137fedSBram Moolenaarlet s:winbar_winids = [] 57371137fedSBram Moolenaar 57471137fedSBram Moolenaar" Install the window toolbar in the current window. 57571137fedSBram Moolenaarfunc s:InstallWinbar() 576c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 57724a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 57824a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 57924a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 58024a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 58160e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 58224a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 58371137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 584c4b533e1SBram Moolenaar endif 585e09ba7baSBram Moolenaarendfunc 586e09ba7baSBram Moolenaar 587e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 588e09ba7baSBram Moolenaarfunc s:DeleteCommands() 589e09ba7baSBram Moolenaar delcommand Break 59071137fedSBram Moolenaar delcommand Clear 591e09ba7baSBram Moolenaar delcommand Step 59245d5f26dSBram Moolenaar delcommand Over 593e09ba7baSBram Moolenaar delcommand Finish 59460e73f2aSBram Moolenaar delcommand Run 59560e73f2aSBram Moolenaar delcommand Arguments 59660e73f2aSBram Moolenaar delcommand Stop 597e09ba7baSBram Moolenaar delcommand Continue 59845d5f26dSBram Moolenaar delcommand Evaluate 59945d5f26dSBram Moolenaar delcommand Gdb 60045d5f26dSBram Moolenaar delcommand Program 601b3623a38SBram Moolenaar delcommand Source 60271137fedSBram Moolenaar delcommand Winbar 60345d5f26dSBram Moolenaar 60445d5f26dSBram Moolenaar nunmap K 6051b9645deSBram Moolenaar 6061b9645deSBram Moolenaar if has('menu') 60771137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 60871137fedSBram Moolenaar let curwinid = win_getid(winnr()) 60971137fedSBram Moolenaar for winid in s:winbar_winids 61071137fedSBram Moolenaar if win_gotoid(winid) 6111b9645deSBram Moolenaar aunmenu WinBar.Step 6121b9645deSBram Moolenaar aunmenu WinBar.Next 6131b9645deSBram Moolenaar aunmenu WinBar.Finish 6141b9645deSBram Moolenaar aunmenu WinBar.Cont 61560e73f2aSBram Moolenaar aunmenu WinBar.Stop 6161b9645deSBram Moolenaar aunmenu WinBar.Eval 6171b9645deSBram Moolenaar endif 61871137fedSBram Moolenaar endfor 61971137fedSBram Moolenaar call win_gotoid(curwinid) 62071137fedSBram Moolenaar let s:winbar_winids = [] 62171137fedSBram Moolenaar 62271137fedSBram Moolenaar if exists('s:saved_mousemodel') 62371137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 62471137fedSBram Moolenaar unlet s:saved_mousemodel 62571137fedSBram Moolenaar aunmenu PopUp.-SEP3- 62671137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 62771137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 62871137fedSBram Moolenaar aunmenu PopUp.Evaluate 62971137fedSBram Moolenaar endif 63071137fedSBram Moolenaar endif 6311b9645deSBram Moolenaar 63245d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 63345d5f26dSBram Moolenaar for key in keys(s:breakpoints) 63445d5f26dSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 63545d5f26dSBram Moolenaar endfor 63638baa3e6SBram Moolenaar sign undefine debugPC 63738baa3e6SBram Moolenaar sign undefine debugBreakpoint 63845d5f26dSBram Moolenaar unlet s:breakpoints 639e09ba7baSBram Moolenaarendfunc 640e09ba7baSBram Moolenaar 641e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 642e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 64360e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 64460e73f2aSBram Moolenaar " Interrupt to make it work. 64560e73f2aSBram Moolenaar let do_continue = 0 64660e73f2aSBram Moolenaar if !s:stopped 64760e73f2aSBram Moolenaar let do_continue = 1 648b3307b5eSBram Moolenaar if s:way == 'prompt' 649b3307b5eSBram Moolenaar " Need to send a signal to get the UI to listen. Strangely this is only 650b3307b5eSBram Moolenaar " needed once. 651b3307b5eSBram Moolenaar call job_stop(s:gdbjob, 'int') 652b3307b5eSBram Moolenaar else 65360e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 654b3307b5eSBram Moolenaar endif 65560e73f2aSBram Moolenaar sleep 10m 65660e73f2aSBram Moolenaar endif 65760e73f2aSBram Moolenaar call s:SendCommand('-break-insert --source ' 65860e73f2aSBram Moolenaar \ . fnameescape(expand('%:p')) . ' --line ' . line('.')) 65960e73f2aSBram Moolenaar if do_continue 66060e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 66160e73f2aSBram Moolenaar endif 662e09ba7baSBram Moolenaarendfunc 663e09ba7baSBram Moolenaar 66471137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 66571137fedSBram Moolenaarfunc s:ClearBreakpoint() 666e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 667e09ba7baSBram Moolenaar let lnum = line('.') 668e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 669e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 670b3307b5eSBram Moolenaar call s:SendCommand('-break-delete ' . key) 671e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 672e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 673e09ba7baSBram Moolenaar unlet s:breakpoints[key] 674e09ba7baSBram Moolenaar break 675e09ba7baSBram Moolenaar endif 676e09ba7baSBram Moolenaar endfor 677e09ba7baSBram Moolenaarendfunc 678e09ba7baSBram Moolenaar 67960e73f2aSBram Moolenaarfunc s:Run(args) 68060e73f2aSBram Moolenaar if a:args != '' 68160e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 68260e73f2aSBram Moolenaar endif 68360e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 68460e73f2aSBram Moolenaarendfunc 68560e73f2aSBram Moolenaar 68651b0f370SBram Moolenaarfunc s:SendEval(expr) 68751b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 68851b0f370SBram Moolenaar let s:evalexpr = a:expr 68951b0f370SBram Moolenaarendfunc 69051b0f370SBram Moolenaar 69145d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 69245d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 69345d5f26dSBram Moolenaar if a:arg != '' 69445d5f26dSBram Moolenaar let expr = a:arg 69545d5f26dSBram Moolenaar elseif a:range == 2 69645d5f26dSBram Moolenaar let pos = getcurpos() 69745d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 69845d5f26dSBram Moolenaar let regt = getregtype('v') 69945d5f26dSBram Moolenaar normal! gv"vy 70045d5f26dSBram Moolenaar let expr = @v 70145d5f26dSBram Moolenaar call setpos('.', pos) 70245d5f26dSBram Moolenaar call setreg('v', reg, regt) 70345d5f26dSBram Moolenaar else 70445d5f26dSBram Moolenaar let expr = expand('<cexpr>') 70545d5f26dSBram Moolenaar endif 70622f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 70751b0f370SBram Moolenaar call s:SendEval(expr) 70845d5f26dSBram Moolenaarendfunc 70945d5f26dSBram Moolenaar 71022f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 71151b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 71251b0f370SBram Moolenaar 71345d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 71445d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 7151b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 7161b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 71751b0f370SBram Moolenaar if s:evalFromBalloonExpr 71851b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 71951b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 72051b0f370SBram Moolenaar else 72151b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 72251b0f370SBram Moolenaar endif 72351b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 72451b0f370SBram Moolenaar else 7251b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 72651b0f370SBram Moolenaar endif 7271b9645deSBram Moolenaar 7287f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 7291b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 73022f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 73151b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 73251b0f370SBram Moolenaar else 73351b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 7341b9645deSBram Moolenaar endif 73545d5f26dSBram Moolenaarendfunc 73645d5f26dSBram Moolenaar 73751b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 73851b0f370SBram Moolenaar" if there is any. 73951b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 740b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 741b3307b5eSBram Moolenaar return 742b3307b5eSBram Moolenaar endif 743b3307b5eSBram Moolenaar if !s:stopped 744b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 745b3307b5eSBram Moolenaar " mouse triggers a balloon. 74651b0f370SBram Moolenaar return 74751b0f370SBram Moolenaar endif 74851b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 74951b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 75022f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 75122f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 75251b0f370SBram Moolenaar return '' 75351b0f370SBram Moolenaarendfunc 75451b0f370SBram Moolenaar 75545d5f26dSBram Moolenaar" Handle an error. 75645d5f26dSBram Moolenaarfunc s:HandleError(msg) 75722f1d0e3SBram Moolenaar if s:ignoreEvalError 75851b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 75922f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 76022f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 76151b0f370SBram Moolenaar return 76251b0f370SBram Moolenaar endif 76345d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 76445d5f26dSBram Moolenaarendfunc 76545d5f26dSBram Moolenaar 766b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 767b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 768c4b533e1SBram Moolenaar new 769b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 770c4b533e1SBram Moolenaar call s:InstallWinbar() 771c4b533e1SBram Moolenaar endif 772c4b533e1SBram Moolenaarendfunc 773c4b533e1SBram Moolenaar 774e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 775e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 776e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 777fe386641SBram Moolenaar let wid = win_getid(winnr()) 778fe386641SBram Moolenaar 77960e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 78060e73f2aSBram Moolenaar let s:stopped = 1 78160e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 78260e73f2aSBram Moolenaar let s:stopped = 0 78360e73f2aSBram Moolenaar endif 78460e73f2aSBram Moolenaar 785b3307b5eSBram Moolenaar call s:GotoSourcewinOrCreateIt() 786c4b533e1SBram Moolenaar 787e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 7881b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 789e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 790fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 7911b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 792fe386641SBram Moolenaar if &modified 793fe386641SBram Moolenaar " TODO: find existing window 794fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 795b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 796c4b533e1SBram Moolenaar call s:InstallWinbar() 797fe386641SBram Moolenaar else 798fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 799fe386641SBram Moolenaar endif 800fe386641SBram Moolenaar endif 801fe386641SBram Moolenaar exe lnum 80201164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 8031b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 804fe386641SBram Moolenaar setlocal signcolumn=yes 805fe386641SBram Moolenaar endif 806fe386641SBram Moolenaar else 807fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 808fe386641SBram Moolenaar endif 809fe386641SBram Moolenaar 810fe386641SBram Moolenaar call win_gotoid(wid) 811e09ba7baSBram Moolenaarendfunc 812e09ba7baSBram Moolenaar 813*de1a8314SBram Moolenaarfunc s:CreateBreakpoint(nr) 814*de1a8314SBram Moolenaar if !exists("s:BreakpointSigns") 815*de1a8314SBram Moolenaar let s:BreakpointSigns = [] 816*de1a8314SBram Moolenaar endif 817*de1a8314SBram Moolenaar if index(s:BreakpointSigns, a:nr) == -1 818*de1a8314SBram Moolenaar call add(s:BreakpointSigns, a:nr) 819*de1a8314SBram Moolenaar exe "sign define debugBreakpoint". a:nr . " text=" . a:nr . " texthl=debugBreakpoint" 820*de1a8314SBram Moolenaar endif 821*de1a8314SBram Moolenaarendfunc 822*de1a8314SBram Moolenaar 823e09ba7baSBram Moolenaar" Handle setting a breakpoint 824e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 825e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 826e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 827e09ba7baSBram Moolenaar if nr == 0 828e09ba7baSBram Moolenaar return 829fe386641SBram Moolenaar endif 830*de1a8314SBram Moolenaar call s:CreateBreakpoint(nr) 831e09ba7baSBram Moolenaar 832e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 833e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 834e09ba7baSBram Moolenaar else 835e09ba7baSBram Moolenaar let entry = {} 836e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 837fe386641SBram Moolenaar endif 838e09ba7baSBram Moolenaar 839e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 840e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 841e09ba7baSBram Moolenaar let entry['fname'] = fname 842e09ba7baSBram Moolenaar let entry['lnum'] = lnum 8431b9645deSBram Moolenaar 8441b9645deSBram Moolenaar if bufloaded(fname) 8451b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 8461b9645deSBram Moolenaar endif 8471b9645deSBram Moolenaarendfunc 8481b9645deSBram Moolenaar 8491b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry) 850*de1a8314SBram Moolenaar exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname'] 8511b9645deSBram Moolenaar let a:entry['placed'] = 1 852e09ba7baSBram Moolenaarendfunc 853e09ba7baSBram Moolenaar 854e09ba7baSBram Moolenaar" Handle deleting a breakpoint 855e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 856e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 857e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 858e09ba7baSBram Moolenaar if nr == 0 859e09ba7baSBram Moolenaar return 860e09ba7baSBram Moolenaar endif 8611b9645deSBram Moolenaar if has_key(s:breakpoints, nr) 8621b9645deSBram Moolenaar let entry = s:breakpoints[nr] 8631b9645deSBram Moolenaar if has_key(entry, 'placed') 864e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 8651b9645deSBram Moolenaar unlet entry['placed'] 8661b9645deSBram Moolenaar endif 867e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 8681b9645deSBram Moolenaar endif 869c572da5fSBram Moolenaarendfunc 8701b9645deSBram Moolenaar 8711b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 8721b9645deSBram Moolenaarfunc s:BufRead() 8731b9645deSBram Moolenaar let fname = expand('<afile>:p') 8741b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 8751b9645deSBram Moolenaar if entry['fname'] == fname 8761b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 8771b9645deSBram Moolenaar endif 8781b9645deSBram Moolenaar endfor 8791b9645deSBram Moolenaarendfunc 8801b9645deSBram Moolenaar 8811b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 8821b9645deSBram Moolenaarfunc s:BufUnloaded() 8831b9645deSBram Moolenaar let fname = expand('<afile>:p') 8841b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 8851b9645deSBram Moolenaar if entry['fname'] == fname 8861b9645deSBram Moolenaar let entry['placed'] = 0 8871b9645deSBram Moolenaar endif 8881b9645deSBram Moolenaar endfor 8891b9645deSBram Moolenaarendfunc 8901b9645deSBram Moolenaar 891