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 70de1a8314SBram Moolenaarlet s:break_id = 13 " breakpoint number is added to this 7160e73f2aSBram Moolenaarlet s:stopped = 1 72e09ba7baSBram Moolenaar 73f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new) 74f07f9e73SBram Moolenaar let default = a:init ? 'default ' : '' 75f07f9e73SBram Moolenaar if a:new ==# 'light' && a:old !=# 'light' 76f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue" 77f07f9e73SBram Moolenaar elseif a:new ==# 'dark' && a:old !=# 'dark' 78f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue" 79e09ba7baSBram Moolenaar endif 80f07f9e73SBram Moolenaarendfunc 81f07f9e73SBram Moolenaar 82f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background) 83e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 84fe386641SBram Moolenaar 8532c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...) 8632c67ba7SBram Moolenaar " First argument is the command to debug, second core file or process ID. 8732c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) 8832c67ba7SBram Moolenaarendfunc 8932c67ba7SBram Moolenaar 9032c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...) 9132c67ba7SBram Moolenaar " First argument is the command to debug, rest are run arguments. 9232c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang}) 9332c67ba7SBram Moolenaarendfunc 9432c67ba7SBram Moolenaar 9532c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict) 96b3623a38SBram Moolenaar if exists('s:gdbwin') 97b3623a38SBram Moolenaar echoerr 'Terminal debugger already running' 98b3623a38SBram Moolenaar return 99b3623a38SBram Moolenaar endif 100b3307b5eSBram Moolenaar let s:ptywin = 0 101b3623a38SBram Moolenaar 102b3307b5eSBram Moolenaar " Uncomment this line to write logging in "debuglog". 103b3307b5eSBram Moolenaar " call ch_logfile('debuglog', 'w') 104b3307b5eSBram Moolenaar 105b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 106fe386641SBram Moolenaar let s:startsigncolumn = &signcolumn 107fe386641SBram Moolenaar 10824a98a0eSBram Moolenaar let s:save_columns = 0 10924a98a0eSBram Moolenaar if exists('g:termdebug_wide') 11024a98a0eSBram Moolenaar if &columns < g:termdebug_wide 11138baa3e6SBram Moolenaar let s:save_columns = &columns 11238baa3e6SBram Moolenaar let &columns = g:termdebug_wide 11324a98a0eSBram Moolenaar endif 114b3307b5eSBram Moolenaar let s:vertical = 1 11538baa3e6SBram Moolenaar else 116b3307b5eSBram Moolenaar let s:vertical = 0 11738baa3e6SBram Moolenaar endif 11838baa3e6SBram Moolenaar 119b3307b5eSBram Moolenaar " Override using a terminal window by setting g:termdebug_use_prompt to 1. 120b3307b5eSBram Moolenaar let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt 121b3307b5eSBram Moolenaar if has('terminal') && !has('win32') && !use_prompt 122b3307b5eSBram Moolenaar let s:way = 'terminal' 123b3307b5eSBram Moolenaar else 124b3307b5eSBram Moolenaar let s:way = 'prompt' 125b3307b5eSBram Moolenaar endif 126b3307b5eSBram Moolenaar 127b3307b5eSBram Moolenaar if s:way == 'prompt' 128b3307b5eSBram Moolenaar call s:StartDebug_prompt(a:dict) 129b3307b5eSBram Moolenaar else 130b3307b5eSBram Moolenaar call s:StartDebug_term(a:dict) 131b3307b5eSBram Moolenaar endif 132b3307b5eSBram Moolenaarendfunc 133b3307b5eSBram Moolenaar 134b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 135b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 136fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 137b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 138b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 139fe386641SBram Moolenaar \ }) 140fe386641SBram Moolenaar if s:ptybuf == 0 141fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 142fe386641SBram Moolenaar return 143fe386641SBram Moolenaar endif 144fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 14545d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 146b3307b5eSBram Moolenaar if s:vertical 14751b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 14851b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 14951b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 15051b0f370SBram Moolenaar endif 151fe386641SBram Moolenaar 152fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 153fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 154fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 155fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 156fe386641SBram Moolenaar \ 'hidden': 1, 157fe386641SBram Moolenaar \ }) 158fe386641SBram Moolenaar if s:commbuf == 0 159fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 160fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 161fe386641SBram Moolenaar return 162fe386641SBram Moolenaar endif 163fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 164c572da5fSBram Moolenaar 165c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 166c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 16732c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 16832c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 16932c67ba7SBram Moolenaar 17032c67ba7SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 171b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 17260e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 173b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndTermDebug'), 174fe386641SBram Moolenaar \ 'term_finish': 'close', 175c572da5fSBram Moolenaar \ }) 17660e73f2aSBram Moolenaar if s:gdbbuf == 0 177fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 178fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 179fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 180fe386641SBram Moolenaar return 181fe386641SBram Moolenaar endif 18245d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 183fe386641SBram Moolenaar 18432c67ba7SBram Moolenaar " Set arguments to be run 18532c67ba7SBram Moolenaar if len(proc_args) 18632c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 18732c67ba7SBram Moolenaar endif 18832c67ba7SBram Moolenaar 189fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 19060e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 19160e73f2aSBram Moolenaar 1923e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 1933e4b84d0SBram Moolenaar " why the debugger doesn't work. 1943e4b84d0SBram Moolenaar let try_count = 0 1953e4b84d0SBram Moolenaar while 1 1963e4b84d0SBram Moolenaar let response = '' 197b3623a38SBram Moolenaar for lnum in range(1,200) 1983e4b84d0SBram Moolenaar if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi ' 1993e4b84d0SBram Moolenaar let response = term_getline(s:gdbbuf, lnum + 1) 2003e4b84d0SBram Moolenaar if response =~ 'Undefined command' 201f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 2023e4b84d0SBram Moolenaar exe 'bwipe! ' . s:ptybuf 2033e4b84d0SBram Moolenaar exe 'bwipe! ' . s:commbuf 2043e4b84d0SBram Moolenaar return 2053e4b84d0SBram Moolenaar endif 2063e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2073e4b84d0SBram Moolenaar " Success! 2083e4b84d0SBram Moolenaar break 2093e4b84d0SBram Moolenaar endif 2103e4b84d0SBram Moolenaar endif 2113e4b84d0SBram Moolenaar endfor 2123e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2133e4b84d0SBram Moolenaar break 2143e4b84d0SBram Moolenaar endif 2153e4b84d0SBram Moolenaar let try_count += 1 2163e4b84d0SBram Moolenaar if try_count > 100 2173e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2183e4b84d0SBram Moolenaar break 2193e4b84d0SBram Moolenaar endif 2203e4b84d0SBram Moolenaar sleep 10m 2213e4b84d0SBram Moolenaar endwhile 2223e4b84d0SBram Moolenaar 22360e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 22460e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 22560e73f2aSBram Moolenaar " running. 22660e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 227b3307b5eSBram Moolenaar " Older gdb uses a different command. 228b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 229e09ba7baSBram Moolenaar 230f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 231f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 232b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 233f3ba14ffSBram Moolenaar 234b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 235b3307b5eSBram Moolenaarendfunc 236b3307b5eSBram Moolenaar 237b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 238b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 239b3307b5eSBram Moolenaar if s:vertical 240b3307b5eSBram Moolenaar vertical new 241b3307b5eSBram Moolenaar else 242b3307b5eSBram Moolenaar new 243b3307b5eSBram Moolenaar endif 244b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 245b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 246b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 247b3307b5eSBram Moolenaar set buftype=prompt 248b3307b5eSBram Moolenaar file gdb 249b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 250b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 251b3307b5eSBram Moolenaar 252b3307b5eSBram Moolenaar if s:vertical 253b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 254b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 255b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 256b3307b5eSBram Moolenaar endif 257b3307b5eSBram Moolenaar 258b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 259b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 260b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 261b3307b5eSBram Moolenaar 262b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 263b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 264b3307b5eSBram Moolenaar 265b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 266b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 267b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 268b3307b5eSBram Moolenaar \ }) 269b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 270b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 271b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 272b3307b5eSBram Moolenaar return 273b3307b5eSBram Moolenaar endif 274b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 275b3307b5eSBram Moolenaar 276b3307b5eSBram Moolenaar " Interpret commands while the target is running. This should usualy only 277b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 278b3307b5eSBram Moolenaar " target is running. 279b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 280b3307b5eSBram Moolenaar " Older gdb uses a different command. 281b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 282b3307b5eSBram Moolenaar 283b3307b5eSBram Moolenaar let s:ptybuf = 0 284b3307b5eSBram Moolenaar if has('win32') 285b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 286b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 287b3307b5eSBram Moolenaar elseif has('terminal') 288b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 289b3307b5eSBram Moolenaar " gdb window. 290b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 291b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 292b3307b5eSBram Moolenaar \ }) 293b3307b5eSBram Moolenaar if s:ptybuf == 0 294b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 295b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 296b3307b5eSBram Moolenaar return 297b3307b5eSBram Moolenaar endif 298b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 299b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 300b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 301b3307b5eSBram Moolenaar 302b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 303b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 304b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 305b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 306b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 307b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 308b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 309b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 310b3307b5eSBram Moolenaar else 311b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 312b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 313b3307b5eSBram Moolenaar endif 314b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 315b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 316b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 317b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 318b3307b5eSBram Moolenaar 319b3307b5eSBram Moolenaar " Set arguments to be run 320b3307b5eSBram Moolenaar if len(proc_args) 321b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 322b3307b5eSBram Moolenaar endif 323b3307b5eSBram Moolenaar 324b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 325b3307b5eSBram Moolenaar startinsert 326b3307b5eSBram Moolenaarendfunc 327b3307b5eSBram Moolenaar 328b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 32938baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 33038baa3e6SBram Moolenaar " There can be only one. 33138baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 33238baa3e6SBram Moolenaar 33345d5f26dSBram Moolenaar " Install debugger commands in the text window. 334b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 335e09ba7baSBram Moolenaar call s:InstallCommands() 33645d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 337e09ba7baSBram Moolenaar 33851b0f370SBram Moolenaar " Enable showing a balloon with eval info 339246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 340246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 34151b0f370SBram Moolenaar if has("balloon_eval") 34251b0f370SBram Moolenaar set ballooneval 343246fe03dSBram Moolenaar endif 34451b0f370SBram Moolenaar if has("balloon_eval_term") 34551b0f370SBram Moolenaar set balloonevalterm 34651b0f370SBram Moolenaar endif 34751b0f370SBram Moolenaar endif 34851b0f370SBram Moolenaar 349de1a8314SBram Moolenaar " Contains breakpoints that have been placed, key is the number. 350e09ba7baSBram Moolenaar let s:breakpoints = {} 3511b9645deSBram Moolenaar 3521b9645deSBram Moolenaar augroup TermDebug 3531b9645deSBram Moolenaar au BufRead * call s:BufRead() 3541b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 355f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 3561b9645deSBram Moolenaar augroup END 35732c67ba7SBram Moolenaar 358b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 359b3307b5eSBram Moolenaar " window. 36032c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 36132c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 36232c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 36332c67ba7SBram Moolenaar endif 364c572da5fSBram Moolenaarendfunc 365c572da5fSBram Moolenaar 366b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 367b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 368b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 369b3307b5eSBram Moolenaar if s:way == 'prompt' 370b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 371b3307b5eSBram Moolenaar else 372b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 373b3307b5eSBram Moolenaar endif 374b3307b5eSBram Moolenaarendfunc 375b3307b5eSBram Moolenaar 376b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 377b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 378b3307b5eSBram Moolenaar if s:way == 'prompt' 379b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 380b3307b5eSBram Moolenaar else 381b3307b5eSBram Moolenaar let do_continue = 0 382b3307b5eSBram Moolenaar if !s:stopped 383b3307b5eSBram Moolenaar let do_continue = 1 384b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 385b3307b5eSBram Moolenaar sleep 10m 386b3307b5eSBram Moolenaar endif 387b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 388b3307b5eSBram Moolenaar if do_continue 389b3307b5eSBram Moolenaar Continue 390b3307b5eSBram Moolenaar endif 391b3307b5eSBram Moolenaar endif 392b3307b5eSBram Moolenaarendfunc 393b3307b5eSBram Moolenaar 394b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 395b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 396b3307b5eSBram Moolenaar call s:SendCommand(a:text) 397b3307b5eSBram Moolenaarendfunc 398b3307b5eSBram Moolenaar 399b3307b5eSBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer. 400b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 401b3307b5eSBram Moolenaar call ch_log('Interrupting gdb') 402b3307b5eSBram Moolenaar call job_stop(s:gdbjob, 'int') 403b3307b5eSBram Moolenaarendfunc 404b3307b5eSBram Moolenaar 405b3307b5eSBram Moolenaar" Function called when gdb outputs text. 406b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 407b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 408b3307b5eSBram Moolenaar 409b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 410b3307b5eSBram Moolenaar " Drop status and echo'd commands. 411*a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 412b3307b5eSBram Moolenaar return 413b3307b5eSBram Moolenaar endif 414b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 415b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 416b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 417b3307b5eSBram Moolenaar " Silently drop evaluation errors. 418b3307b5eSBram Moolenaar unlet s:evalexpr 419b3307b5eSBram Moolenaar return 420b3307b5eSBram Moolenaar endif 421b3307b5eSBram Moolenaar elseif a:text[0] == '~' 422b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 423b3307b5eSBram Moolenaar else 424b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 425b3307b5eSBram Moolenaar return 426b3307b5eSBram Moolenaar endif 427b3307b5eSBram Moolenaar 428b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 429b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 430b3307b5eSBram Moolenaar 431b3307b5eSBram Moolenaar " Add the output above the current prompt. 432b3307b5eSBram Moolenaar call append(line('$') - 1, text) 433b3307b5eSBram Moolenaar set nomodified 434b3307b5eSBram Moolenaar 435b3307b5eSBram Moolenaar call win_gotoid(curwinid) 436b3307b5eSBram Moolenaarendfunc 437b3307b5eSBram Moolenaar 438b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 439b3307b5eSBram Moolenaar" to the next ", unescaping characters. 440b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 441b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 442*a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 443b3307b5eSBram Moolenaar return 444b3307b5eSBram Moolenaar endif 445b3307b5eSBram Moolenaar let result = '' 446b3307b5eSBram Moolenaar let i = 1 447b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 448b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 449b3307b5eSBram Moolenaar let i += 1 450b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 451b3307b5eSBram Moolenaar " drop \n 452b3307b5eSBram Moolenaar let i += 1 453b3307b5eSBram Moolenaar continue 454b3307b5eSBram Moolenaar endif 455b3307b5eSBram Moolenaar endif 456b3307b5eSBram Moolenaar let result .= a:quotedText[i] 457b3307b5eSBram Moolenaar let i += 1 458b3307b5eSBram Moolenaar endwhile 459b3307b5eSBram Moolenaar return result 460b3307b5eSBram Moolenaarendfunc 461b3307b5eSBram Moolenaar 462*a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 463*a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 464*a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 465*a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 466*a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 467*a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 468*a15b0a93SBram Moolenaar endif 469*a15b0a93SBram Moolenaar return name 470*a15b0a93SBram Moolenaarendfunc 471*a15b0a93SBram Moolenaar 472b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 473fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 474b3623a38SBram Moolenaar unlet s:gdbwin 475e09ba7baSBram Moolenaar 476b3307b5eSBram Moolenaar call s:EndDebugCommon() 477b3307b5eSBram Moolenaarendfunc 478b3307b5eSBram Moolenaar 479b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 480e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 481e09ba7baSBram Moolenaar 482b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 483b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 484b3307b5eSBram Moolenaar endif 485b3307b5eSBram Moolenaar 486b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 487e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 488e09ba7baSBram Moolenaar call s:DeleteCommands() 489e09ba7baSBram Moolenaar 490e09ba7baSBram Moolenaar call win_gotoid(curwinid) 491b3307b5eSBram Moolenaar 49238baa3e6SBram Moolenaar if s:save_columns > 0 49338baa3e6SBram Moolenaar let &columns = s:save_columns 49438baa3e6SBram Moolenaar endif 4951b9645deSBram Moolenaar 496246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 497246fe03dSBram Moolenaar set balloonexpr= 49851b0f370SBram Moolenaar if has("balloon_eval") 49951b0f370SBram Moolenaar set noballooneval 500246fe03dSBram Moolenaar endif 50151b0f370SBram Moolenaar if has("balloon_eval_term") 50251b0f370SBram Moolenaar set noballoonevalterm 50351b0f370SBram Moolenaar endif 50451b0f370SBram Moolenaar endif 50551b0f370SBram Moolenaar 5061b9645deSBram Moolenaar au! TermDebug 507fe386641SBram Moolenaarendfunc 508fe386641SBram Moolenaar 509b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 510b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 511b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 512b3307b5eSBram Moolenaar close 513b3307b5eSBram Moolenaar if curwinid != s:gdbwin 514b3307b5eSBram Moolenaar call win_gotoid(curwinid) 515b3307b5eSBram Moolenaar endif 516b3307b5eSBram Moolenaar 517b3307b5eSBram Moolenaar call s:EndDebugCommon() 518b3307b5eSBram Moolenaar unlet s:gdbwin 519b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 520b3307b5eSBram Moolenaarendfunc 521b3307b5eSBram Moolenaar 522fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 523fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 524fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 525fe386641SBram Moolenaar 526fe386641SBram Moolenaar for msg in msgs 527fe386641SBram Moolenaar " remove prefixed NL 528fe386641SBram Moolenaar if msg[0] == "\n" 529fe386641SBram Moolenaar let msg = msg[1:] 530fe386641SBram Moolenaar endif 531fe386641SBram Moolenaar if msg != '' 5321b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 533e09ba7baSBram Moolenaar call s:HandleCursor(msg) 53445d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 535e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 536e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 537e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 53845d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 53945d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 54045d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 54145d5f26dSBram Moolenaar call s:HandleError(msg) 542e09ba7baSBram Moolenaar endif 543e09ba7baSBram Moolenaar endif 544e09ba7baSBram Moolenaar endfor 545e09ba7baSBram Moolenaarendfunc 546e09ba7baSBram Moolenaar 547e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 548e09ba7baSBram Moolenaarfunc s:InstallCommands() 549e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 55071137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 551e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 55245d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 553e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 55460e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 55560e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 55660e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 557b3307b5eSBram Moolenaar 558b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 559b3307b5eSBram Moolenaar if s:way == 'prompt' 560b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 561b3307b5eSBram Moolenaar else 562b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 563b3307b5eSBram Moolenaar endif 564b3307b5eSBram Moolenaar 56545d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 56645d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 56745d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 568b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 56971137fedSBram Moolenaar command Winbar call s:InstallWinbar() 57045d5f26dSBram Moolenaar 57145d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 57245d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 5731b9645deSBram Moolenaar 574f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 57571137fedSBram Moolenaar call s:InstallWinbar() 57671137fedSBram Moolenaar 57771137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 57871137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 57971137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 58071137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 58171137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 58271137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 58371137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 58471137fedSBram Moolenaar endif 58571137fedSBram Moolenaar endif 58671137fedSBram Moolenaarendfunc 58771137fedSBram Moolenaar 58871137fedSBram Moolenaarlet s:winbar_winids = [] 58971137fedSBram Moolenaar 59071137fedSBram Moolenaar" Install the window toolbar in the current window. 59171137fedSBram Moolenaarfunc s:InstallWinbar() 592c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 59324a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 59424a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 59524a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 59624a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 59760e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 59824a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 59971137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 600c4b533e1SBram Moolenaar endif 601e09ba7baSBram Moolenaarendfunc 602e09ba7baSBram Moolenaar 603e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 604e09ba7baSBram Moolenaarfunc s:DeleteCommands() 605e09ba7baSBram Moolenaar delcommand Break 60671137fedSBram Moolenaar delcommand Clear 607e09ba7baSBram Moolenaar delcommand Step 60845d5f26dSBram Moolenaar delcommand Over 609e09ba7baSBram Moolenaar delcommand Finish 61060e73f2aSBram Moolenaar delcommand Run 61160e73f2aSBram Moolenaar delcommand Arguments 61260e73f2aSBram Moolenaar delcommand Stop 613e09ba7baSBram Moolenaar delcommand Continue 61445d5f26dSBram Moolenaar delcommand Evaluate 61545d5f26dSBram Moolenaar delcommand Gdb 61645d5f26dSBram Moolenaar delcommand Program 617b3623a38SBram Moolenaar delcommand Source 61871137fedSBram Moolenaar delcommand Winbar 61945d5f26dSBram Moolenaar 62045d5f26dSBram Moolenaar nunmap K 6211b9645deSBram Moolenaar 6221b9645deSBram Moolenaar if has('menu') 62371137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 62471137fedSBram Moolenaar let curwinid = win_getid(winnr()) 62571137fedSBram Moolenaar for winid in s:winbar_winids 62671137fedSBram Moolenaar if win_gotoid(winid) 6271b9645deSBram Moolenaar aunmenu WinBar.Step 6281b9645deSBram Moolenaar aunmenu WinBar.Next 6291b9645deSBram Moolenaar aunmenu WinBar.Finish 6301b9645deSBram Moolenaar aunmenu WinBar.Cont 63160e73f2aSBram Moolenaar aunmenu WinBar.Stop 6321b9645deSBram Moolenaar aunmenu WinBar.Eval 6331b9645deSBram Moolenaar endif 63471137fedSBram Moolenaar endfor 63571137fedSBram Moolenaar call win_gotoid(curwinid) 63671137fedSBram Moolenaar let s:winbar_winids = [] 63771137fedSBram Moolenaar 63871137fedSBram Moolenaar if exists('s:saved_mousemodel') 63971137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 64071137fedSBram Moolenaar unlet s:saved_mousemodel 64171137fedSBram Moolenaar aunmenu PopUp.-SEP3- 64271137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 64371137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 64471137fedSBram Moolenaar aunmenu PopUp.Evaluate 64571137fedSBram Moolenaar endif 64671137fedSBram Moolenaar endif 6471b9645deSBram Moolenaar 64845d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 64945d5f26dSBram Moolenaar for key in keys(s:breakpoints) 65045d5f26dSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 65145d5f26dSBram Moolenaar endfor 65245d5f26dSBram Moolenaar unlet s:breakpoints 653*a15b0a93SBram Moolenaar 654*a15b0a93SBram Moolenaar sign undefine debugPC 655*a15b0a93SBram Moolenaar for val in s:BreakpointSigns 656*a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 657*a15b0a93SBram Moolenaar endfor 658*a15b0a93SBram Moolenaar unlet s:BreakpointSigns 659e09ba7baSBram Moolenaarendfunc 660e09ba7baSBram Moolenaar 661e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 662e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 66360e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 66460e73f2aSBram Moolenaar " Interrupt to make it work. 66560e73f2aSBram Moolenaar let do_continue = 0 66660e73f2aSBram Moolenaar if !s:stopped 66760e73f2aSBram Moolenaar let do_continue = 1 668b3307b5eSBram Moolenaar if s:way == 'prompt' 669b3307b5eSBram Moolenaar " Need to send a signal to get the UI to listen. Strangely this is only 670b3307b5eSBram Moolenaar " needed once. 671b3307b5eSBram Moolenaar call job_stop(s:gdbjob, 'int') 672b3307b5eSBram Moolenaar else 67360e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 674b3307b5eSBram Moolenaar endif 67560e73f2aSBram Moolenaar sleep 10m 67660e73f2aSBram Moolenaar endif 677*a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 678*a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 679*a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 68060e73f2aSBram Moolenaar if do_continue 68160e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 68260e73f2aSBram Moolenaar endif 683e09ba7baSBram Moolenaarendfunc 684e09ba7baSBram Moolenaar 68571137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 68671137fedSBram Moolenaarfunc s:ClearBreakpoint() 687e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 688e09ba7baSBram Moolenaar let lnum = line('.') 689e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 690e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 691b3307b5eSBram Moolenaar call s:SendCommand('-break-delete ' . key) 692e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 693e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 694e09ba7baSBram Moolenaar unlet s:breakpoints[key] 695e09ba7baSBram Moolenaar break 696e09ba7baSBram Moolenaar endif 697e09ba7baSBram Moolenaar endfor 698e09ba7baSBram Moolenaarendfunc 699e09ba7baSBram Moolenaar 70060e73f2aSBram Moolenaarfunc s:Run(args) 70160e73f2aSBram Moolenaar if a:args != '' 70260e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 70360e73f2aSBram Moolenaar endif 70460e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 70560e73f2aSBram Moolenaarendfunc 70660e73f2aSBram Moolenaar 70751b0f370SBram Moolenaarfunc s:SendEval(expr) 70851b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 70951b0f370SBram Moolenaar let s:evalexpr = a:expr 71051b0f370SBram Moolenaarendfunc 71151b0f370SBram Moolenaar 71245d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 71345d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 71445d5f26dSBram Moolenaar if a:arg != '' 71545d5f26dSBram Moolenaar let expr = a:arg 71645d5f26dSBram Moolenaar elseif a:range == 2 71745d5f26dSBram Moolenaar let pos = getcurpos() 71845d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 71945d5f26dSBram Moolenaar let regt = getregtype('v') 72045d5f26dSBram Moolenaar normal! gv"vy 72145d5f26dSBram Moolenaar let expr = @v 72245d5f26dSBram Moolenaar call setpos('.', pos) 72345d5f26dSBram Moolenaar call setreg('v', reg, regt) 72445d5f26dSBram Moolenaar else 72545d5f26dSBram Moolenaar let expr = expand('<cexpr>') 72645d5f26dSBram Moolenaar endif 72722f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 72851b0f370SBram Moolenaar call s:SendEval(expr) 72945d5f26dSBram Moolenaarendfunc 73045d5f26dSBram Moolenaar 73122f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 73251b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 73351b0f370SBram Moolenaar 73445d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 73545d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 7361b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 7371b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 73851b0f370SBram Moolenaar if s:evalFromBalloonExpr 73951b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 74051b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 74151b0f370SBram Moolenaar else 74251b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 74351b0f370SBram Moolenaar endif 74451b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 74551b0f370SBram Moolenaar else 7461b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 74751b0f370SBram Moolenaar endif 7481b9645deSBram Moolenaar 7497f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 7501b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 75122f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 75251b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 75351b0f370SBram Moolenaar else 75451b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 7551b9645deSBram Moolenaar endif 75645d5f26dSBram Moolenaarendfunc 75745d5f26dSBram Moolenaar 75851b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 75951b0f370SBram Moolenaar" if there is any. 76051b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 761b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 762b3307b5eSBram Moolenaar return 763b3307b5eSBram Moolenaar endif 764b3307b5eSBram Moolenaar if !s:stopped 765b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 766b3307b5eSBram Moolenaar " mouse triggers a balloon. 76751b0f370SBram Moolenaar return 76851b0f370SBram Moolenaar endif 76951b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 77051b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 77122f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 77222f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 77351b0f370SBram Moolenaar return '' 77451b0f370SBram Moolenaarendfunc 77551b0f370SBram Moolenaar 77645d5f26dSBram Moolenaar" Handle an error. 77745d5f26dSBram Moolenaarfunc s:HandleError(msg) 77822f1d0e3SBram Moolenaar if s:ignoreEvalError 77951b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 78022f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 78122f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 78251b0f370SBram Moolenaar return 78351b0f370SBram Moolenaar endif 78445d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 78545d5f26dSBram Moolenaarendfunc 78645d5f26dSBram Moolenaar 787b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 788b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 789c4b533e1SBram Moolenaar new 790b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 791c4b533e1SBram Moolenaar call s:InstallWinbar() 792c4b533e1SBram Moolenaar endif 793c4b533e1SBram Moolenaarendfunc 794c4b533e1SBram Moolenaar 795e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 796e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 797e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 798fe386641SBram Moolenaar let wid = win_getid(winnr()) 799fe386641SBram Moolenaar 80060e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 80160e73f2aSBram Moolenaar let s:stopped = 1 80260e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 80360e73f2aSBram Moolenaar let s:stopped = 0 80460e73f2aSBram Moolenaar endif 80560e73f2aSBram Moolenaar 806b3307b5eSBram Moolenaar call s:GotoSourcewinOrCreateIt() 807c4b533e1SBram Moolenaar 808*a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 809*a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 810*a15b0a93SBram Moolenaar else 811*a15b0a93SBram Moolenaar let fname = '' 812*a15b0a93SBram Moolenaar endif 8131b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 814e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 815fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 8161b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 817fe386641SBram Moolenaar if &modified 818fe386641SBram Moolenaar " TODO: find existing window 819fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 820b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 821c4b533e1SBram Moolenaar call s:InstallWinbar() 822fe386641SBram Moolenaar else 823fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 824fe386641SBram Moolenaar endif 825fe386641SBram Moolenaar endif 826fe386641SBram Moolenaar exe lnum 82701164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 8281b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 829fe386641SBram Moolenaar setlocal signcolumn=yes 830fe386641SBram Moolenaar endif 831fe386641SBram Moolenaar else 832fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 833fe386641SBram Moolenaar endif 834fe386641SBram Moolenaar 835fe386641SBram Moolenaar call win_gotoid(wid) 836e09ba7baSBram Moolenaarendfunc 837e09ba7baSBram Moolenaar 838de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 839*a15b0a93SBram Moolenaar 840*a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr) 841de1a8314SBram Moolenaar if index(s:BreakpointSigns, a:nr) == -1 842de1a8314SBram Moolenaar call add(s:BreakpointSigns, a:nr) 843de1a8314SBram Moolenaar exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint" 844de1a8314SBram Moolenaar endif 845de1a8314SBram Moolenaarendfunc 846de1a8314SBram Moolenaar 847e09ba7baSBram Moolenaar" Handle setting a breakpoint 848e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 849e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 850e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 851e09ba7baSBram Moolenaar if nr == 0 852e09ba7baSBram Moolenaar return 853fe386641SBram Moolenaar endif 854de1a8314SBram Moolenaar call s:CreateBreakpoint(nr) 855e09ba7baSBram Moolenaar 856e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 857e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 858e09ba7baSBram Moolenaar else 859e09ba7baSBram Moolenaar let entry = {} 860e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 861fe386641SBram Moolenaar endif 862e09ba7baSBram Moolenaar 863*a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 864e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 865e09ba7baSBram Moolenaar let entry['fname'] = fname 866e09ba7baSBram Moolenaar let entry['lnum'] = lnum 8671b9645deSBram Moolenaar 8681b9645deSBram Moolenaar if bufloaded(fname) 8691b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 8701b9645deSBram Moolenaar endif 8711b9645deSBram Moolenaarendfunc 8721b9645deSBram Moolenaar 8731b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry) 874de1a8314SBram Moolenaar exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname'] 8751b9645deSBram Moolenaar let a:entry['placed'] = 1 876e09ba7baSBram Moolenaarendfunc 877e09ba7baSBram Moolenaar 878e09ba7baSBram Moolenaar" Handle deleting a breakpoint 879e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 880e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 881e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 882e09ba7baSBram Moolenaar if nr == 0 883e09ba7baSBram Moolenaar return 884e09ba7baSBram Moolenaar endif 8851b9645deSBram Moolenaar if has_key(s:breakpoints, nr) 8861b9645deSBram Moolenaar let entry = s:breakpoints[nr] 8871b9645deSBram Moolenaar if has_key(entry, 'placed') 888e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 8891b9645deSBram Moolenaar unlet entry['placed'] 8901b9645deSBram Moolenaar endif 891e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 8921b9645deSBram Moolenaar endif 893c572da5fSBram Moolenaarendfunc 8941b9645deSBram Moolenaar 8951b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 8961b9645deSBram Moolenaarfunc s:BufRead() 8971b9645deSBram Moolenaar let fname = expand('<afile>:p') 8981b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 8991b9645deSBram Moolenaar if entry['fname'] == fname 9001b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 9011b9645deSBram Moolenaar endif 9021b9645deSBram Moolenaar endfor 9031b9645deSBram Moolenaarendfunc 9041b9645deSBram Moolenaar 9051b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 9061b9645deSBram Moolenaarfunc s:BufUnloaded() 9071b9645deSBram Moolenaar let fname = expand('<afile>:p') 9081b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9091b9645deSBram Moolenaar if entry['fname'] == fname 9101b9645deSBram Moolenaar let entry['placed'] = 0 9111b9645deSBram Moolenaar endif 9121b9645deSBram Moolenaar endfor 9131b9645deSBram Moolenaarendfunc 914