To: vim_dev@googlegroups.com Subject: Patch 8.2.5010 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.5010 Problem: The terminal debugger uses various global variables. Solution: Add a dictionary to hold the terminal debugger preferences. Files: runtime/doc/terminal.txt, runtime/pack/dist/opt/termdebug/plugin/termdebug.vim *** ../vim-8.2.5009/runtime/doc/terminal.txt 2022-05-22 14:48:26.339247289 +0100 --- runtime/doc/terminal.txt 2022-05-23 21:33:55.141039199 +0100 *************** *** 1367,1373 **** *TermdebugStartPre* TermdebugStartPre Before starting debugging. Not triggered if the debugger is already ! running or |g:termdebugger| cannot be executed. *TermdebugStartPost* TermdebugStartPost After debugging has initialized. --- 1367,1373 ---- *TermdebugStartPre* TermdebugStartPre Before starting debugging. Not triggered if the debugger is already ! running or the debugger command cannot be executed. *TermdebugStartPost* TermdebugStartPost After debugging has initialized. *************** *** 1398,1415 **** *termdebug_use_prompt* Prompt mode can be used even when the |+terminal| feature is present with: > let g:termdebug_use_prompt = 1 < *termdebug_map_K* The K key is normally mapped to :Evaluate. If you do not want this use: > let g:termdebug_map_K = 0 - < *termdebug_disasm_window* ! If you want the Asm window shown by default, set this to 1. Setting to ! any value greater than 1 will set the Asm window height to that value: > let g:termdebug_disasm_window = 15 ! < Communication ~ *termdebug-communication* --- 1398,1421 ---- *termdebug_use_prompt* Prompt mode can be used even when the |+terminal| feature is present with: > + let g:termdebug_config['use_prompt'] = 1 + Or if there is no g:termdebug_config: > let g:termdebug_use_prompt = 1 < *termdebug_map_K* The K key is normally mapped to :Evaluate. If you do not want this use: > + let g:termdebug_config['map_K'] = 0 + Or if there is no g:termdebug_config: > let g:termdebug_map_K = 0 < *termdebug_disasm_window* ! If you want the Asm window shown by default, set the flag to 1. ! the "disasm_window_height" entry can be used to set the window height: > ! let g:termdebug_config['disasm_window'] = 1 ! let g:termdebug_config['disasm_window_height'] = 15 ! or, if there is no g:termdebug_config: > let g:termdebug_disasm_window = 15 ! Any value greater than 1 will set the Asm window height to that value: > Communication ~ *termdebug-communication* *************** *** 1426,1440 **** Customizing ~ ! GDB command *termdebug-customizing* *g:termdebugger* ! To change the name of the gdb command, set the "g:termdebugger" variable before ! invoking `:Termdebug`: > let g:termdebugger = "mygdb" If the command needs an argument use a List: > let g:termdebugger = ['rr', 'replay', '--'] ! < *gdb-version* Only debuggers fully compatible with gdb will work. Vim uses the GDB/MI interface. The "new-ui" command requires gdb version 7.12 or later. if you get this error: --- 1432,1467 ---- Customizing ~ + *termdebug-customizing* *g:termdebug_config* + In the past several global variables were used for configuration. These are + deprecated, using the g:termdebug_config dictionary is preferred. When + g:termdebug_config exists the other global variables will not be used. + ! GDB command ~ *g:termdebugger* ! To change the name of the gdb command, set "debugger" entry in ! g:termdebug_config or the "g:termdebugger" variable before invoking ! `:Termdebug`: > ! let g:termdebug_config['command'] = "mygdb" ! Or if there is no g:termdebug_config: > let g:termdebugger = "mygdb" + If the command needs an argument use a List: > + let g:termdebug_config['command'] = ['rr', 'replay', '--'] + Or if there is no g:termdebug_config: > let g:termdebugger = ['rr', 'replay', '--'] ! ! Several arguments will be added to make gdb work well for the debugger. ! If you want to modify them, add a function to filter the argument list: > ! let g:termdebug_config['command_filter'] = MyDebugFilter ! ! If you do not want the arguments to be added, but you do need to set the ! "pty", use a function to add the necessary arguments: > ! let g:termdebug_config['command_add_args'] = MyAddArguments ! The function will be called with the list of arguments so far, and a second ! argument that is the name of the pty. ! *gdb-version* Only debuggers fully compatible with gdb will work. Vim uses the GDB/MI interface. The "new-ui" command requires gdb version 7.12 or later. if you get this error: *************** *** 1442,1449 **** Then your gdb is too old. ! Colors *hl-debugPC* *hl-debugBreakpoint* ! The color of the signs can be adjusted with these highlight groups: - debugPC the current position - debugBreakpoint a breakpoint --- 1469,1476 ---- Then your gdb is too old. ! Colors~ ! *hl-debugPC* *hl-debugBreakpoint* The color of the signs can be adjusted with these highlight groups: - debugPC the current position - debugBreakpoint a breakpoint *************** *** 1473,1478 **** --- 1500,1507 ---- Clear breakpoint `:Clear` Evaluate `:Evaluate` If you don't want this then disable it with: > + let g:termdebug_config['popup'] = 0 + or if there is no g:termdebug_config: > let g:termdebug_popup = 0 *************** *** 1480,1494 **** To change the width of the Vim window when debugging starts and use a vertical split: > let g:termdebug_wide = 163 This will set 'columns' to 163 when `:Termdebug` is used. The value is restored when quitting the debugger. ! If g:termdebug_wide is set and 'columns' is already a greater value, then a vertical split will be used without modifying 'columns'. ! Set g:termdebug_wide to 1 to use a vertical split without ever changing 'columns'. This is useful when the terminal can't be resized by Vim. --- 1509,1525 ---- To change the width of the Vim window when debugging starts and use a vertical split: > + let g:termdebug_config['wide'] = 163 + Or if there is no g:termdebug_config: > let g:termdebug_wide = 163 This will set 'columns' to 163 when `:Termdebug` is used. The value is restored when quitting the debugger. ! If the wide value is set and 'columns' is already a greater value, then a vertical split will be used without modifying 'columns'. ! Set the wide value to 1 to use a vertical split without ever changing 'columns'. This is useful when the terminal can't be resized by Vim. *** ../vim-8.2.5009/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim 2020-08-31 20:58:36.115898718 +0100 --- runtime/pack/dist/opt/termdebug/plugin/termdebug.vim 2022-05-23 21:37:12.244652273 +0100 *************** *** 2,13 **** " " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" ! " Last Change: 2020 Aug 31 " ! " WORK IN PROGRESS - Only the basics work ! " Note: On MS-Windows you need a recent version of gdb. The one included with ! " MingW is too old (7.6.1). ! " I used version 7.12 from http://www.equation.com/servlet/equation.cmd?fa=gdb " " There are two ways to run gdb: " - In a terminal window; used if possible, does not work on MS-Windows --- 2,14 ---- " " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" ! " Last Change: 2022 May 23 " ! " WORK IN PROGRESS - The basics works stable, more to come ! " Note: In general you need at least GDB 7.12 because this provides the ! " frame= response in MI thread-selected events we need to sync stack to file. ! " The one included with "old" MingW is too old (7.6.1), you may upgrade it or ! " use a newer version from http://www.equation.com/servlet/equation.cmd?fa=gdb " " There are two ways to run gdb: " - In a terminal window; used if possible, does not work on MS-Windows *************** *** 64,78 **** command -nargs=* -complete=file -bang Termdebug call s:StartDebug(0, ) command -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(0, ) - " Name of the gdb command, defaults to "gdb". - if !exists('g:termdebugger') - let g:termdebugger = 'gdb' - endif - let s:pc_id = 12 ! let s:break_id = 13 " breakpoint number is added to this let s:stopped = 1 " Take a breakpoint number as used by GDB and turn it into an integer. " The breakpoint may contain a dot: 123.4 -> 123004 " The main breakpoint has a zero subid. --- 65,79 ---- command -nargs=* -complete=file -bang Termdebug call s:StartDebug(0, ) command -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(0, ) let s:pc_id = 12 ! let s:asm_id = 13 ! let s:break_id = 14 " breakpoint number is added to this let s:stopped = 1 + let s:parsing_disasm_msg = 0 + let s:asm_lines = [] + let s:asm_addr = '' + " Take a breakpoint number as used by GDB and turn it into an integer. " The breakpoint may contain a dot: 123.4 -> 123004 " The main breakpoint has a zero subid. *************** *** 91,96 **** --- 92,111 ---- call s:Highlight(1, '', &background) hi default debugBreakpoint term=reverse ctermbg=red guibg=red + hi default debugBreakpointDisabled term=reverse ctermbg=gray guibg=gray + + " Get the command to execute the debugger as a list, defaults to ["gdb"]. + func s:GetCommand() + if exists('g:termdebug_config') + let cmd = get(g:termdebug_config, 'command', 'gdb') + elseif exists('g:termdebugger') + let cmd = g:termdebugger + else + let cmd = 'gdb' + endif + + return type(cmd) == v:t_list ? copy(cmd) : [cmd] + endfunc func s:StartDebug(bang, ...) " First argument is the command to debug, second core file or process ID. *************** *** 107,133 **** echoerr 'Terminal debugger already running, cannot run two' return endif ! if !executable(g:termdebugger) ! echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"' return endif let s:ptywin = 0 let s:pid = 0 " Uncomment this line to write logging in "debuglog". " call ch_logfile('debuglog', 'w') let s:sourcewin = win_getid(winnr()) ! let s:startsigncolumn = &signcolumn let s:save_columns = 0 let s:allleft = 0 ! if exists('g:termdebug_wide') ! if &columns < g:termdebug_wide let s:save_columns = &columns ! let &columns = g:termdebug_wide ! " If we make the Vim window wider, use the whole left halve for the debug " windows. let s:allleft = 1 endif --- 122,164 ---- echoerr 'Terminal debugger already running, cannot run two' return endif ! let gdbcmd = s:GetCommand() ! if !executable(gdbcmd[0]) ! echoerr 'Cannot execute debugger program "' .. gdbcmd[0] .. '"' return endif let s:ptywin = 0 let s:pid = 0 + let s:asmwin = 0 + + if exists('#User#TermdebugStartPre') + doauto User TermdebugStartPre + endif " Uncomment this line to write logging in "debuglog". " call ch_logfile('debuglog', 'w') let s:sourcewin = win_getid(winnr()) ! ! " Remember the old value of 'signcolumn' for each buffer that it's set in, so ! " that we can restore the value for all buffers. ! let b:save_signcolumn = &signcolumn ! let s:signcolumn_buflist = [bufnr()] let s:save_columns = 0 let s:allleft = 0 ! let wide = 0 ! if exists('g:termdebug_config') ! let wide = get(g:termdebug_config, 'wide', 0) ! elseif exists('g:termdebug_wide') ! let wide = g:termdebug_wide ! endif ! if wide > 0 ! if &columns < wide let s:save_columns = &columns ! let &columns = wide ! " If we make the Vim window wider, use the whole left half for the debug " windows. let s:allleft = 1 endif *************** *** 137,143 **** endif " Override using a terminal window by setting g:termdebug_use_prompt to 1. ! let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt if has('terminal') && !has('win32') && !use_prompt let s:way = 'terminal' else --- 168,179 ---- endif " Override using a terminal window by setting g:termdebug_use_prompt to 1. ! let use_prompt = 0 ! if exists('g:termdebug_config') ! let use_prompt = get(g:termdebug_config, 'use_prompt', 0) ! elseif exists('g:termdebug_use_prompt') ! let use_prompt = g:termdebug_use_prompt ! endif if has('terminal') && !has('win32') && !use_prompt let s:way = 'terminal' else *************** *** 149,154 **** --- 185,200 ---- else call s:StartDebug_term(a:dict) endif + + if s:GetDisasmWindow() + let curwinid = win_getid(winnr()) + call s:GotoAsmwinOrCreateIt() + call win_gotoid(curwinid) + endif + + if exists('#User#TermdebugStartPost') + doauto User TermdebugStartPost + endif endfunc " Use when debugger didn't start or ended. *************** *** 158,165 **** unlet! s:gdbwin endfunc func s:StartDebug_term(dict) - " Open a terminal window without a job, to run the debugged program in. let s:ptybuf = term_start('NONE', { \ 'term_name': 'debugged program', \ 'vertical': s:vertical, --- 204,221 ---- unlet! s:gdbwin endfunc + func s:CheckGdbRunning() + let gdbproc = term_getjob(s:gdbbuf) + if gdbproc == v:null || job_status(gdbproc) !=# 'run' + echoerr string(s:GetCommand()[0]) . ' exited unexpectedly' + call s:CloseBuffers() + return '' + endif + return 'ok' + endfunc + + " Open a terminal window without a job, to run the debugged program in. func s:StartDebug_term(dict) let s:ptybuf = term_start('NONE', { \ 'term_name': 'debugged program', \ 'vertical': s:vertical, *************** *** 193,206 **** endif let commpty = job_info(term_getjob(s:commbuf))['tty_out'] - " Open a terminal window to run the debugger. - " Add -quiet to avoid the intro message causing a hit-enter prompt. let gdb_args = get(a:dict, 'gdb_args', []) let proc_args = get(a:dict, 'proc_args', []) ! let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args ! call ch_log('executing "' . join(cmd) . '"') ! let s:gdbbuf = term_start(cmd, { \ 'term_finish': 'close', \ }) if s:gdbbuf == 0 --- 249,286 ---- endif let commpty = job_info(term_getjob(s:commbuf))['tty_out'] let gdb_args = get(a:dict, 'gdb_args', []) let proc_args = get(a:dict, 'proc_args', []) ! let gdb_cmd = s:GetCommand() ! ! if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_add_args') ! let gdb_cmd = g:termdebug_config.command_add_args(gdb_cmd, pty) ! else ! " Add -quiet to avoid the intro message causing a hit-enter prompt. ! let gdb_cmd += ['-quiet'] ! " Disable pagination, it causes everything to stop at the gdb ! let gdb_cmd += ['-iex', 'set pagination off'] ! " Interpret commands while the target is running. This should usually only ! " be exec-interrupt, since many commands don't work properly while the ! " target is running (so execute during startup). ! let gdb_cmd += ['-iex', 'set mi-async on'] ! " Open a terminal window to run the debugger. ! let gdb_cmd += ['-tty', pty] ! " Command executed _after_ startup is done, provides us with the necessary ! " feedback ! let gdb_cmd += ['-ex', 'echo startupdone\n'] ! endif ! ! if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_filter') ! let gdb_cmd = g:termdebug_config.command_filter(gdb_cmd) ! endif ! ! " Adding arguments requested by the user ! let gdb_cmd += gdb_args ! ! call ch_log('executing "' . join(gdb_cmd) . '"') ! let s:gdbbuf = term_start(gdb_cmd, { \ 'term_finish': 'close', \ }) if s:gdbbuf == 0 *************** *** 210,231 **** endif let s:gdbwin = win_getid(winnr()) ! " Set arguments to be run if len(proc_args) ! call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") endif ! " Connect gdb to the communication pty, using the GDB/MI interface ! call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") " Wait for the response to show up, users may not notice the error and wonder " why the debugger doesn't work. let try_count = 0 while 1 ! let gdbproc = term_getjob(s:gdbbuf) ! if gdbproc == v:null || job_status(gdbproc) !=# 'run' ! echoerr string(g:termdebugger) . ' exited unexpectedly' ! call s:CloseBuffers() return endif --- 290,330 ---- endif let s:gdbwin = win_getid(winnr()) ! " Wait for the "startupdone" message before sending any commands. ! let try_count = 0 ! while 1 ! if s:CheckGdbRunning() != 'ok' ! return ! endif ! ! for lnum in range(1, 200) ! if term_getline(s:gdbbuf, lnum) =~ 'startupdone' ! let try_count = 9999 ! break ! endif ! endfor ! let try_count += 1 ! if try_count > 300 ! " done or give up after five seconds ! break ! endif ! sleep 10m ! endwhile ! ! " Set arguments to be run. if len(proc_args) ! call term_sendkeys(s:gdbbuf, 'server set args ' . join(proc_args) . "\r") endif ! " Connect gdb to the communication pty, using the GDB/MI interface. ! " Prefix "server" to avoid adding this to the history. ! call term_sendkeys(s:gdbbuf, 'server new-ui mi ' . commpty . "\r") " Wait for the response to show up, users may not notice the error and wonder " why the debugger doesn't work. let try_count = 0 while 1 ! if s:CheckGdbRunning() != 'ok' return endif *************** *** 234,253 **** let line1 = term_getline(s:gdbbuf, lnum) let line2 = term_getline(s:gdbbuf, lnum + 1) if line1 =~ 'new-ui mi ' ! " response can be in the same line or the next line ! let response = line1 . line2 ! if response =~ 'Undefined command' ! echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' ! call s:CloseBuffers() ! return ! endif ! if response =~ 'New UI allocated' ! " Success! ! break ! endif elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi ' ! " Reading symbols might take a while, try more times ! let try_count -= 1 endif endfor if response =~ 'New UI allocated' --- 333,353 ---- let line1 = term_getline(s:gdbbuf, lnum) let line2 = term_getline(s:gdbbuf, lnum + 1) if line1 =~ 'new-ui mi ' ! " response can be in the same line or the next line ! let response = line1 . line2 ! if response =~ 'Undefined command' ! echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' ! " CHECKME: possibly send a "server show version" here ! call s:CloseBuffers() ! return ! endif ! if response =~ 'New UI allocated' ! " Success! ! break ! endif elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi ' ! " Reading symbols might take a while, try more times ! let try_count -= 1 endif endfor if response =~ 'New UI allocated' *************** *** 261,283 **** sleep 10m endwhile ! " Interpret commands while the target is running. This should usually only be ! " exec-interrupt, since many commands don't work properly while the target is ! " running. ! call s:SendCommand('-gdb-set mi-async on') ! " Older gdb uses a different command. ! call s:SendCommand('-gdb-set target-async on') ! ! " Disable pagination, it causes everything to stop at the gdb ! " "Type to continue" prompt. ! call s:SendCommand('set pagination off') - call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')}) call s:StartDebugCommon(a:dict) endfunc func s:StartDebug_prompt(dict) - " Open a window with a prompt buffer to run gdb in. if s:vertical vertical new else --- 361,376 ---- sleep 10m endwhile ! call job_setoptions(term_getjob(s:gdbbuf), {'exit_cb': function('s:EndTermDebug')}) ! ! " Set the filetype, this can be used to add mappings. ! set filetype=termdebug call s:StartDebugCommon(a:dict) endfunc + " Open a window with a prompt buffer to run gdb in. func s:StartDebug_prompt(dict) if s:vertical vertical new else *************** *** 297,310 **** exe (&columns / 2 - 1) . "wincmd |" endif - " Add -quiet to avoid the intro message causing a hit-enter prompt. let gdb_args = get(a:dict, 'gdb_args', []) let proc_args = get(a:dict, 'proc_args', []) ! let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args ! call ch_log('executing "' . join(cmd) . '"') ! let s:gdbjob = job_start(cmd, { \ 'exit_cb': function('s:EndPromptDebug'), \ 'out_cb': function('s:GdbOutCallback'), \ }) --- 390,415 ---- exe (&columns / 2 - 1) . "wincmd |" endif let gdb_args = get(a:dict, 'gdb_args', []) let proc_args = get(a:dict, 'proc_args', []) ! let gdb_cmd = s:GetCommand() ! " Add -quiet to avoid the intro message causing a hit-enter prompt. ! let gdb_cmd += ['-quiet'] ! " Disable pagination, it causes everything to stop at the gdb, needs to be run early ! let gdb_cmd += ['-iex', 'set pagination off'] ! " Interpret commands while the target is running. This should usually only ! " be exec-interrupt, since many commands don't work properly while the ! " target is running (so execute during startup). ! let gdb_cmd += ['-iex', 'set mi-async on'] ! " directly communicate via mi2 ! let gdb_cmd += ['--interpreter=mi2'] ! ! " Adding arguments requested by the user ! let gdb_cmd += gdb_args ! call ch_log('executing "' . join(gdb_cmd) . '"') ! let s:gdbjob = job_start(gdb_cmd, { \ 'exit_cb': function('s:EndPromptDebug'), \ 'out_cb': function('s:GdbOutCallback'), \ }) *************** *** 315,328 **** endif " Mark the buffer modified so that it's not easy to close. set modified ! let s:gdb_channel = job_getchannel(s:gdbjob) ! ! " Interpret commands while the target is running. This should usually only ! " be exec-interrupt, since many commands don't work properly while the ! " target is running. ! call s:SendCommand('-gdb-set mi-async on') ! " Older gdb uses a different command. ! call s:SendCommand('-gdb-set target-async on') let s:ptybuf = 0 if has('win32') --- 420,426 ---- endif " Mark the buffer modified so that it's not easy to close. set modified ! let s:gdb_channel = job_getchannel(s:gdbjob) let s:ptybuf = 0 if has('win32') *************** *** 352,364 **** call s:SendCommand('set env COLORS = ' . &t_Co) call s:SendCommand('set env VIM_TERMINAL = ' . v:version) else ! " TODO: open a new terminal get get the tty name, pass on to gdb call s:SendCommand('show inferior-tty') endif call s:SendCommand('set print pretty on') call s:SendCommand('set breakpoint pending on') - " Disable pagination, it causes everything to stop at the gdb - call s:SendCommand('set pagination off') " Set arguments to be run if len(proc_args) --- 450,460 ---- call s:SendCommand('set env COLORS = ' . &t_Co) call s:SendCommand('set env VIM_TERMINAL = ' . v:version) else ! " TODO: open a new terminal, get the tty name, pass on to gdb call s:SendCommand('show inferior-tty') endif call s:SendCommand('set print pretty on') call s:SendCommand('set breakpoint pending on') " Set arguments to be run if len(proc_args) *************** *** 372,378 **** func s:StartDebugCommon(dict) " Sign used to highlight the line where the program has stopped. " There can be only one. ! sign define debugPC linehl=debugPC " Install debugger commands in the text window. call win_gotoid(s:sourcewin) --- 468,474 ---- func s:StartDebugCommon(dict) " Sign used to highlight the line where the program has stopped. " There can be only one. ! call sign_define('debugPC', #{linehl: 'debugPC'}) " Install debugger commands in the text window. call win_gotoid(s:sourcewin) *************** *** 412,418 **** " Run the command if the bang attribute was given and got to the debug " window. if get(a:dict, 'bang', 0) ! call s:SendCommand('-exec-run') call win_gotoid(s:ptywin) endif endfunc --- 508,514 ---- " Run the command if the bang attribute was given and got to the debug " window. if get(a:dict, 'bang', 0) ! call s:SendResumingCommand('-exec-run') call win_gotoid(s:ptywin) endif endfunc *************** *** 435,441 **** let do_continue = 0 if !s:stopped let do_continue = 1 ! call s:SendCommand('-exec-interrupt') sleep 10m endif call term_sendkeys(s:gdbbuf, a:cmd . "\r") --- 531,537 ---- let do_continue = 0 if !s:stopped let do_continue = 1 ! Stop sleep 10m endif call term_sendkeys(s:gdbbuf, a:cmd . "\r") *************** *** 445,450 **** --- 541,560 ---- endif endfunc + " Send a command that resumes the program. If the program isn't stopped the + " command is not sent (to avoid a repeated command to cause trouble). + " If the command is sent then reset s:stopped. + func s:SendResumingCommand(cmd) + if s:stopped + " reset s:stopped here, it may take a bit of time before we get a response + let s:stopped = 0 + call ch_log('assume that program is running after this command') + call s:SendCommand(a:cmd) + else + call ch_log('dropping command, program is running: ' . a:cmd) + endif + endfunc + " Function called when entering a line in the prompt buffer. func s:PromptCallback(text) call s:SendCommand(a:text) *************** *** 476,482 **** if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' return endif ! if a:text =~ '^^error,msg=' let text = s:DecodeMessage(a:text[11:]) if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' " Silently drop evaluation errors. --- 586,592 ---- if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' return endif ! if a:text =~ '^\^error,msg=' let text = s:DecodeMessage(a:text[11:]) if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' " Silently drop evaluation errors. *************** *** 501,533 **** endfunc " Decode a message from gdb. quotedText starts with a ", return the text up ! " to the next ", unescaping characters. func s:DecodeMessage(quotedText) if a:quotedText[0] != '"' echoerr 'DecodeMessage(): missing quote in ' . a:quotedText return endif ! let result = '' ! let i = 1 ! while a:quotedText[i] != '"' && i < len(a:quotedText) ! if a:quotedText[i] == '\' ! let i += 1 ! if a:quotedText[i] == 'n' ! " drop \n ! let i += 1 ! continue ! elseif a:quotedText[i] == 't' ! " append \t ! let i += 1 ! let result .= "\t" ! continue ! endif ! endif ! let result .= a:quotedText[i] ! let i += 1 ! endwhile ! return result endfunc " Extract the "name" value from a gdb message with fullname="name". func s:GetFullname(msg) --- 611,643 ---- endfunc " Decode a message from gdb. quotedText starts with a ", return the text up ! " to the next ", unescaping characters: ! " - remove line breaks ! " - change \\t to \t ! " - change \0xhh to \xhh (disabled for now) ! " - change \ooo to octal ! " - change \\ to \ func s:DecodeMessage(quotedText) if a:quotedText[0] != '"' echoerr 'DecodeMessage(): missing quote in ' . a:quotedText return endif ! return a:quotedText ! \ ->substitute('^"\|".*\|\\n', '', 'g') ! \ ->substitute('\\t', "\t", 'g') ! " multi-byte characters arrive in octal form ! " NULL-values must be kept encoded as those break the string otherwise ! \ ->substitute('\\000', s:NullRepl, 'g') ! \ ->substitute('\\\o\o\o', {-> eval('"' .. submatch(0) .. '"')}, 'g') ! " Note: GDB docs also mention hex encodings - the translations below work ! " but we keep them out for performance-reasons until we actually see ! " those in mi-returns ! " \ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') ! " \ ->substitute('\\0x00', s:NullRepl, 'g') ! \ ->substitute('\\\\', '\', 'g') ! \ ->substitute(s:NullRepl, '\\000', 'g') endfunc + const s:NullRepl = 'XXXNULLXXX' " Extract the "name" value from a gdb message with fullname="name". func s:GetFullname(msg) *************** *** 542,548 **** --- 652,671 ---- return name endfunc + " Extract the "addr" value from a gdb message with addr="0x0001234". + func s:GetAsmAddr(msg) + if a:msg !~ 'addr=' + return '' + endif + let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', '')) + return addr + endfunc + func s:EndTermDebug(job, status) + if exists('#User#TermdebugStopPre') + doauto User TermdebugStopPre + endif + exe 'bwipe! ' . s:commbuf unlet s:gdbwin *************** *** 556,563 **** exe 'bwipe! ' . s:ptybuf endif call win_gotoid(s:sourcewin) ! let &signcolumn = s:startsigncolumn call s:DeleteCommands() call win_gotoid(curwinid) --- 679,700 ---- exe 'bwipe! ' . s:ptybuf endif + " Restore 'signcolumn' in all buffers for which it was set. call win_gotoid(s:sourcewin) ! let was_buf = bufnr() ! for bufnr in s:signcolumn_buflist ! if bufexists(bufnr) ! exe bufnr .. "buf" ! if exists('b:save_signcolumn') ! let &signcolumn = b:save_signcolumn ! unlet b:save_signcolumn ! endif ! endif ! endfor ! if bufexists(was_buf) ! exe was_buf .. "buf" ! endif ! call s:DeleteCommands() call win_gotoid(curwinid) *************** *** 576,585 **** --- 713,730 ---- endif endif + if exists('#User#TermdebugStopPost') + doauto User TermdebugStopPost + endif + au! TermDebug endfunc func s:EndPromptDebug(job, status) + if exists('#User#TermdebugStopPre') + doauto User TermdebugStopPre + endif + let curwinid = win_getid(winnr()) call win_gotoid(s:gdbwin) set nomodified *************** *** 593,598 **** --- 738,806 ---- call ch_log("Returning from EndPromptDebug()") endfunc + " Disassembly window - added by Michael Sartain + " + " - CommOutput: disassemble $pc + " - CommOutput: &"disassemble $pc\n" + " - CommOutput: ~"Dump of assembler code for function main(int, char**):\n" + " - CommOutput: ~" 0x0000555556466f69 <+0>:\tpush rbp\n" + " ... + " - CommOutput: ~" 0x0000555556467cd0:\tpop rbp\n" + " - CommOutput: ~" 0x0000555556467cd1:\tret \n" + " - CommOutput: ~"End of assembler dump.\n" + " - CommOutput: ^done + + " - CommOutput: disassemble $pc + " - CommOutput: &"disassemble $pc\n" + " - CommOutput: &"No function contains specified address.\n" + " - CommOutput: ^error,msg="No function contains specified address." + func s:HandleDisasmMsg(msg) + if a:msg =~ '^\^done' + let curwinid = win_getid(winnr()) + if win_gotoid(s:asmwin) + silent normal! gg0"_dG + call setline(1, s:asm_lines) + set nomodified + set filetype=asm + + let lnum = search('^' . s:asm_addr) + if lnum != 0 + call sign_unplace('TermDebug', #{id: s:asm_id}) + call sign_place(s:asm_id, 'TermDebug', 'debugPC', '%', #{lnum: lnum}) + endif + + call win_gotoid(curwinid) + endif + + let s:parsing_disasm_msg = 0 + let s:asm_lines = [] + elseif a:msg =~ '^\^error,msg=' + if s:parsing_disasm_msg == 1 + " Disassemble call ran into an error. This can happen when gdb can't + " find the function frame address, so let's try to disassemble starting + " at current PC + call s:SendCommand('disassemble $pc,+100') + endif + let s:parsing_disasm_msg = 0 + elseif a:msg =~ '\&\"disassemble \$pc' + if a:msg =~ '+100' + " This is our second disasm attempt + let s:parsing_disasm_msg = 2 + endif + else + let value = substitute(a:msg, '^\~\"[ ]*', '', '') + let value = substitute(value, '^=>[ ]*', '', '') + let value = substitute(value, '\\n\"\r$', '', '') + let value = substitute(value, '\\n\"$', '', '') + let value = substitute(value, '\r', '', '') + let value = substitute(value, '\\t', ' ', 'g') + + if value != '' || !empty(s:asm_lines) + call add(s:asm_lines, value) + endif + endif + endfunc + " Handle a message received from gdb on the GDB/MI interface. func s:CommOutput(chan, msg) let msgs = split(a:msg, "\r") *************** *** 602,620 **** if msg[0] == "\n" let msg = msg[1:] endif ! if msg != '' if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' ! call s:HandleCursor(msg) elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' ! call s:HandleNewBreakpoint(msg) elseif msg =~ '^=breakpoint-deleted,' ! call s:HandleBreakpointDelete(msg) elseif msg =~ '^=thread-group-started' ! call s:HandleProgramRun(msg) elseif msg =~ '^\^done,value=' ! call s:HandleEvaluate(msg) elseif msg =~ '^\^error,msg=' ! call s:HandleError(msg) endif endif endfor --- 810,836 ---- if msg[0] == "\n" let msg = msg[1:] endif ! ! if s:parsing_disasm_msg ! call s:HandleDisasmMsg(msg) ! elseif msg != '' if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' ! call s:HandleCursor(msg) elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' ! call s:HandleNewBreakpoint(msg, 0) ! elseif msg =~ '^=breakpoint-modified,' ! call s:HandleNewBreakpoint(msg, 1) elseif msg =~ '^=breakpoint-deleted,' ! call s:HandleBreakpointDelete(msg) elseif msg =~ '^=thread-group-started' ! call s:HandleProgramRun(msg) elseif msg =~ '^\^done,value=' ! call s:HandleEvaluate(msg) elseif msg =~ '^\^error,msg=' ! call s:HandleError(msg) ! elseif msg =~ '^disassemble' ! let s:parsing_disasm_msg = 1 ! let s:asm_lines = [] endif endif endfor *************** *** 637,653 **** command -nargs=? Break call s:SetBreakpoint() command Clear call s:ClearBreakpoint() ! command Step call s:SendCommand('-exec-step') ! command Over call s:SendCommand('-exec-next') ! command Finish call s:SendCommand('-exec-finish') command -nargs=* Run call s:Run() ! command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . ) ! command Stop call s:SendCommand('-exec-interrupt') - " using -exec-continue results in CTRL-C in gdb window not working if s:way == 'prompt' command Continue call s:SendCommand('continue') else command Continue call term_sendkeys(s:gdbbuf, "continue\r") endif --- 853,873 ---- command -nargs=? Break call s:SetBreakpoint() command Clear call s:ClearBreakpoint() ! command Step call s:SendResumingCommand('-exec-step') ! command Over call s:SendResumingCommand('-exec-next') ! command -nargs=? Until call s:Until() ! command Finish call s:SendResumingCommand('-exec-finish') command -nargs=* Run call s:Run() ! command -nargs=* Arguments call s:SendResumingCommand('-exec-arguments ' . ) if s:way == 'prompt' + command Stop call s:PromptInterrupt() command Continue call s:SendCommand('continue') else + command Stop call s:SendCommand('-exec-interrupt') + " using -exec-continue results in CTRL-C in the gdb window not working, + " communicating via commbuf (= use of SendCommand) has the same result + "command Continue call s:SendCommand('-exec-continue') command Continue call term_sendkeys(s:gdbbuf, "continue\r") endif *************** *** 655,663 **** command Gdb call win_gotoid(s:gdbwin) command Program call s:GotoProgram() command Source call s:GotoSourcewinOrCreateIt() command Winbar call s:InstallWinbar() ! if !exists('g:termdebug_map_K') || g:termdebug_map_K let s:k_map_saved = maparg('K', 'n', 0, 1) nnoremap K :Evaluate endif --- 875,890 ---- command Gdb call win_gotoid(s:gdbwin) command Program call s:GotoProgram() command Source call s:GotoSourcewinOrCreateIt() + command Asm call s:GotoAsmwinOrCreateIt() command Winbar call s:InstallWinbar() ! let map = 1 ! if exists('g:termdebug_config') ! let map = get(g:termdebug_config, 'map_K', 1) ! elseif exists('g:termdebug_map_K') ! let map = g:termdebug_map_K ! endif ! if map let s:k_map_saved = maparg('K', 'n', 0, 1) nnoremap K :Evaluate endif *************** *** 665,677 **** if has('menu') && &mouse != '' call s:InstallWinbar() ! if !exists('g:termdebug_popup') || g:termdebug_popup != 0 let s:saved_mousemodel = &mousemodel let &mousemodel = 'popup_setpos' an 1.200 PopUp.-SEP3- an 1.210 PopUp.Set\ breakpoint :Break an 1.220 PopUp.Clear\ breakpoint :Clear ! an 1.230 PopUp.Evaluate :Evaluate endif endif --- 892,911 ---- if has('menu') && &mouse != '' call s:InstallWinbar() ! let popup = 1 ! if exists('g:termdebug_config') ! let popup = get(g:termdebug_config, 'popup', 1) ! elseif exists('g:termdebug_popup') ! let popup = g:termdebug_popup ! endif ! if popup let s:saved_mousemodel = &mousemodel let &mousemodel = 'popup_setpos' an 1.200 PopUp.-SEP3- an 1.210 PopUp.Set\ breakpoint :Break an 1.220 PopUp.Clear\ breakpoint :Clear ! an 1.230 PopUp.Run\ until :Until ! an 1.240 PopUp.Evaluate :Evaluate endif endif *************** *** 699,704 **** --- 933,939 ---- delcommand Clear delcommand Step delcommand Over + delcommand Until delcommand Finish delcommand Run delcommand Arguments *************** *** 708,717 **** delcommand Gdb delcommand Program delcommand Source delcommand Winbar ! if exists('s:k_map_saved') && !empty(s:k_map_saved) ! call mapset('n', 0, s:k_map_saved) unlet s:k_map_saved endif --- 943,957 ---- delcommand Gdb delcommand Program delcommand Source + delcommand Asm delcommand Winbar ! if exists('s:k_map_saved') ! if empty(s:k_map_saved) ! nunmap K ! else ! call mapset(s:k_map_saved) ! endif unlet s:k_map_saved endif *************** *** 737,762 **** aunmenu PopUp.-SEP3- aunmenu PopUp.Set\ breakpoint aunmenu PopUp.Clear\ breakpoint aunmenu PopUp.Evaluate endif endif ! exe 'sign unplace ' . s:pc_id ! for [id, entries] in items(s:breakpoints) ! for subid in keys(entries) ! exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) ! endfor ! endfor unlet s:breakpoints unlet s:breakpoint_locations ! sign undefine debugPC ! for val in s:BreakpointSigns ! exe "sign undefine debugBreakpoint" . val ! endfor let s:BreakpointSigns = [] endfunc " :Break - Set a breakpoint at the cursor position. func s:SetBreakpoint(at) " Setting a breakpoint may not work while the program is running. --- 977,1011 ---- aunmenu PopUp.-SEP3- aunmenu PopUp.Set\ breakpoint aunmenu PopUp.Clear\ breakpoint + aunmenu PopUp.Run\ until aunmenu PopUp.Evaluate endif endif ! call sign_unplace('TermDebug') unlet s:breakpoints unlet s:breakpoint_locations ! call sign_undefine('debugPC') ! call sign_undefine(s:BreakpointSigns->map("'debugBreakpoint' .. v:val")) let s:BreakpointSigns = [] endfunc + " :Until - Execute until past a specified position or current line + func s:Until(at) + if s:stopped + " reset s:stopped here, it may take a bit of time before we get a response + let s:stopped = 0 + call ch_log('assume that program is running after this command') + " Use the fname:lnum format + let at = empty(a:at) ? + \ fnameescape(expand('%:p')) . ':' . line('.') : a:at + call s:SendCommand('-exec-until ' . at) + else + call ch_log('dropping command, program is running: exec-until') + endif + endfunc + " :Break - Set a breakpoint at the cursor position. func s:SetBreakpoint(at) " Setting a breakpoint may not work while the program is running. *************** *** 764,783 **** let do_continue = 0 if !s:stopped let do_continue = 1 ! if s:way == 'prompt' ! call s:PromptInterrupt() ! else ! call s:SendCommand('-exec-interrupt') ! endif sleep 10m endif " Use the fname:lnum format, older gdb can't handle --source. let at = empty(a:at) ? ! \ fnameescape(expand('%:p')) . ':' . line('.') : a:at call s:SendCommand('-break-insert ' . at) if do_continue ! call s:SendCommand('-exec-continue') endif endfunc --- 1013,1028 ---- let do_continue = 0 if !s:stopped let do_continue = 1 ! Stop sleep 10m endif " Use the fname:lnum format, older gdb can't handle --source. let at = empty(a:at) ? ! \ fnameescape(expand('%:p')) . ':' . line('.') : a:at call s:SendCommand('-break-insert ' . at) if do_continue ! Continue endif endfunc *************** *** 788,842 **** let bploc = printf('%s:%d', fname, lnum) if has_key(s:breakpoint_locations, bploc) let idx = 0 for id in s:breakpoint_locations[bploc] if has_key(s:breakpoints, id) ! " Assume this always works, the reply is simply "^done". ! call s:SendCommand('-break-delete ' . id) ! for subid in keys(s:breakpoints[id]) ! exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) ! endfor ! unlet s:breakpoints[id] ! unlet s:breakpoint_locations[bploc][idx] ! break else ! let idx += 1 endif endfor ! if empty(s:breakpoint_locations[bploc]) ! unlet s:breakpoint_locations[bploc] endif endif endfunc func s:Run(args) if a:args != '' ! call s:SendCommand('-exec-arguments ' . a:args) endif ! call s:SendCommand('-exec-run') endfunc func s:SendEval(expr) ! call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') ! let s:evalexpr = a:expr endfunc ! " :Evaluate - evaluate what is under the cursor func s:Evaluate(range, arg) if a:arg != '' ! let expr = a:arg elseif a:range == 2 let pos = getcurpos() let reg = getreg('v', 1, 1) let regt = getregtype('v') normal! gv"vy ! let expr = @v call setpos('.', pos) call setreg('v', reg, regt) else let expr = expand('') endif ! let s:ignoreEvalError = 0 ! call s:SendEval(expr) endfunc let s:ignoreEvalError = 0 --- 1033,1144 ---- let bploc = printf('%s:%d', fname, lnum) if has_key(s:breakpoint_locations, bploc) let idx = 0 + let nr = 0 for id in s:breakpoint_locations[bploc] if has_key(s:breakpoints, id) ! " Assume this always works, the reply is simply "^done". ! call s:SendCommand('-break-delete ' . id) ! for subid in keys(s:breakpoints[id]) ! call sign_unplace('TermDebug', ! \ #{id: s:Breakpoint2SignNumber(id, subid)}) ! endfor ! unlet s:breakpoints[id] ! unlet s:breakpoint_locations[bploc][idx] ! let nr = id ! break else ! let idx += 1 endif endfor ! if nr != 0 ! if empty(s:breakpoint_locations[bploc]) ! unlet s:breakpoint_locations[bploc] ! endif ! echomsg 'Breakpoint ' . id . ' cleared from line ' . lnum . '.' ! else ! echoerr 'Internal error trying to remove breakpoint at line ' . lnum . '!' endif + else + echomsg 'No breakpoint to remove at line ' . lnum . '.' endif endfunc func s:Run(args) if a:args != '' ! call s:SendResumingCommand('-exec-arguments ' . a:args) endif ! call s:SendResumingCommand('-exec-run') endfunc func s:SendEval(expr) ! " check for "likely" boolean expressions, in which case we take it as lhs ! if a:expr =~ "[=!<>]=" ! let exprLHS = a:expr ! else ! " remove text that is likely an assignment ! let exprLHS = substitute(a:expr, ' *=.*', '', '') ! endif ! ! " encoding expression to prevent bad errors ! let expr = a:expr ! let expr = substitute(expr, '\\', '\\\\', 'g') ! let expr = substitute(expr, '"', '\\"', 'g') ! call s:SendCommand('-data-evaluate-expression "' . expr . '"') ! let s:evalexpr = exprLHS endfunc ! " :Evaluate - evaluate what is specified / under the cursor func s:Evaluate(range, arg) + let expr = s:GetEvaluationExpression(a:range, a:arg) + let s:ignoreEvalError = 0 + call s:SendEval(expr) + endfunc + + " get what is specified / under the cursor + func s:GetEvaluationExpression(range, arg) if a:arg != '' ! " user supplied evaluation ! let expr = s:CleanupExpr(a:arg) ! " DSW: replace "likely copy + paste" assignment ! let expr = substitute(expr, '"\([^"]*\)": *', '\1=', 'g') elseif a:range == 2 + " no evaluation but provided but range set let pos = getcurpos() let reg = getreg('v', 1, 1) let regt = getregtype('v') normal! gv"vy ! let expr = s:CleanupExpr(@v) call setpos('.', pos) call setreg('v', reg, regt) else + " no evaluation provided: get from C-expression under cursor + " TODO: allow filetype specific lookup #9057 let expr = expand('') endif ! return expr ! endfunc ! ! " clean up expression that may get in because of range ! " (newlines and surrounding whitespace) ! " As it can also be specified via ex-command for assignments this function ! " may not change the "content" parts (like replacing contained spaces) ! func s:CleanupExpr(expr) ! " replace all embedded newlines/tabs/... ! let expr = substitute(a:expr, '\_s', ' ', 'g') ! ! if &filetype ==# 'cobol' ! " extra cleanup for COBOL: ! " - a semicolon nmay be used instead of a space ! " - a trailing comma or period is ignored as it commonly separates/ends ! " multiple expr ! let expr = substitute(expr, ';', ' ', 'g') ! let expr = substitute(expr, '[,.]\+ *$', '', '') ! endif ! ! " get rid of leading and trailing spaces ! let expr = substitute(expr, '^ *', '', '') ! let expr = substitute(expr, ' *$', '', '') ! return expr endfunc let s:ignoreEvalError = 0 *************** *** 844,851 **** " Handle the result of data-evaluate-expression func s:HandleEvaluate(msg) ! let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') ! let value = substitute(value, '\\"', '"', 'g') if s:evalFromBalloonExpr if s:evalFromBalloonExprResult == '' let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value --- 1146,1164 ---- " Handle the result of data-evaluate-expression func s:HandleEvaluate(msg) ! let value = a:msg ! \ ->substitute('.*value="\(.*\)"', '\1', '') ! \ ->substitute('\\"', '"', 'g') ! \ ->substitute('\\\\', '\\', 'g') ! "\ multi-byte characters arrive in octal form, replace everything but NULL values ! \ ->substitute('\\000', s:NullRepl, 'g') ! \ ->substitute('\\\o\o\o', {-> eval('"' .. submatch(0) .. '"')}, 'g') ! "\ Note: GDB docs also mention hex encodings - the translations below work ! "\ but we keep them out for performance-reasons until we actually see ! "\ those in mi-returns ! "\ ->substitute('\\0x00', s:NullRep, 'g') ! "\ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') ! \ ->substitute(s:NullRepl, '\\000', 'g') if s:evalFromBalloonExpr if s:evalFromBalloonExprResult == '' let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value *************** *** 880,886 **** let s:evalFromBalloonExpr = 1 let s:evalFromBalloonExprResult = '' let s:ignoreEvalError = 1 ! call s:SendEval(v:beval_text) return '' endfunc --- 1193,1200 ---- let s:evalFromBalloonExpr = 1 let s:evalFromBalloonExprResult = '' let s:ignoreEvalError = 1 ! let expr = s:CleanupExpr(v:beval_text) ! call s:SendEval(expr) return '' endfunc *************** *** 892,898 **** let s:evalFromBalloonExpr = 0 return endif ! echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') endfunc func s:GotoSourcewinOrCreateIt() --- 1206,1213 ---- let s:evalFromBalloonExpr = 0 return endif ! let msgVal = substitute(a:msg, '.*msg="\(.*\)"', '\1', '') ! echoerr substitute(msgVal, '\\"', '"', 'g') endfunc func s:GotoSourcewinOrCreateIt() *************** *** 903,908 **** --- 1218,1284 ---- endif endfunc + func s:GetDisasmWindow() + if exists('g:termdebug_config') + return get(g:termdebug_config, 'disasm_window', 0) + endif + if exists('g:termdebug_disasm_window') + return g:termdebug_disasm_window + endif + return 0 + endfunc + + func s:GetDisasmWindowHeight() + if exists('g:termdebug_config') + return get(g:termdebug_config, 'disasm_window_height', 0) + endif + if exists('g:termdebug_disasm_window') && g:termdebug_disasm_window > 1 + return g:termdebug_disasm_window + endif + return 0 + endfunc + + func s:GotoAsmwinOrCreateIt() + if !win_gotoid(s:asmwin) + if win_gotoid(s:sourcewin) + exe 'rightbelow new' + else + exe 'new' + endif + + let s:asmwin = win_getid(winnr()) + + setlocal nowrap + setlocal number + setlocal noswapfile + setlocal buftype=nofile + setlocal modifiable + + let asmbuf = bufnr('Termdebug-asm-listing') + if asmbuf > 0 + exe 'buffer' . asmbuf + else + exe 'file Termdebug-asm-listing' + endif + + if s:GetDisasmWindowHeight() > 0 + exe 'resize ' .. s:GetDisasmWindowHeight() + endif + endif + + if s:asm_addr != '' + let lnum = search('^' . s:asm_addr) + if lnum == 0 + if s:stopped + call s:SendCommand('disassemble $pc') + endif + else + call sign_unplace('TermDebug', #{id: s:asm_id}) + call sign_place(s:asm_id, 'TermDebug', 'debugPC', '%', #{lnum: lnum}) + endif + endif + endfunc + " Handle stopping and running message from gdb. " Will update the sign that shows the current position. func s:HandleCursor(msg) *************** *** 921,947 **** else let fname = '' endif if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') if lnum =~ '^[0-9]*$' ! call s:GotoSourcewinOrCreateIt() if expand('%:p') != fnamemodify(fname, ':p') ! if &modified ! " TODO: find existing window ! exe 'split ' . fnameescape(fname) ! let s:sourcewin = win_getid(winnr()) ! call s:InstallWinbar() ! else ! exe 'edit ' . fnameescape(fname) ! endif endif exe lnum ! exe 'sign unplace ' . s:pc_id ! exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC priority=110 file=' . fname setlocal signcolumn=yes endif elseif !s:stopped || fname != '' ! exe 'sign unplace ' . s:pc_id endif call win_gotoid(wid) --- 1297,1363 ---- else let fname = '' endif + + if a:msg =~ 'addr=' + let asm_addr = s:GetAsmAddr(a:msg) + if asm_addr != '' + let s:asm_addr = asm_addr + + let curwinid = win_getid(winnr()) + if win_gotoid(s:asmwin) + let lnum = search('^' . s:asm_addr) + if lnum == 0 + call s:SendCommand('disassemble $pc') + else + call sign_unplace('TermDebug', #{id: s:asm_id}) + call sign_place(s:asm_id, 'TermDebug', 'debugPC', '%', #{lnum: lnum}) + endif + + call win_gotoid(curwinid) + endif + endif + endif + if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') if lnum =~ '^[0-9]*$' ! call s:GotoSourcewinOrCreateIt() if expand('%:p') != fnamemodify(fname, ':p') ! echomsg 'different fname: "' .. expand('%:p') .. '" vs "' .. fnamemodify(fname, ':p') .. '"' ! augroup Termdebug ! " Always open a file read-only instead of showing the ATTENTION ! " prompt, since we are unlikely to want to edit the file. ! " The file may be changed but not saved, warn for that. ! au SwapExists * echohl WarningMsg ! \ | echo 'Warning: file is being edited elsewhere' ! \ | echohl None ! \ | let v:swapchoice = '0' ! augroup END ! if &modified ! " TODO: find existing window ! exe 'split ' . fnameescape(fname) ! let s:sourcewin = win_getid(winnr()) ! call s:InstallWinbar() ! else ! exe 'edit ' . fnameescape(fname) ! endif ! augroup Termdebug ! au! SwapExists ! augroup END endif exe lnum ! normal! zv ! call sign_unplace('TermDebug', #{id: s:pc_id}) ! call sign_place(s:pc_id, 'TermDebug', 'debugPC', fname, ! \ #{lnum: lnum, priority: 110}) ! if !exists('b:save_signcolumn') ! let b:save_signcolumn = &signcolumn ! call add(s:signcolumn_buflist, bufnr()) ! endif setlocal signcolumn=yes endif elseif !s:stopped || fname != '' ! call sign_unplace('TermDebug', #{id: s:pc_id}) endif call win_gotoid(wid) *************** *** 949,959 **** let s:BreakpointSigns = [] ! func s:CreateBreakpoint(id, subid) let nr = printf('%d.%d', a:id, a:subid) if index(s:BreakpointSigns, nr) == -1 call add(s:BreakpointSigns, nr) ! exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" endif endfunc --- 1365,1382 ---- let s:BreakpointSigns = [] ! func s:CreateBreakpoint(id, subid, enabled) let nr = printf('%d.%d', a:id, a:subid) if index(s:BreakpointSigns, nr) == -1 call add(s:BreakpointSigns, nr) ! if a:enabled == "n" ! let hiName = "debugBreakpointDisabled" ! else ! let hiName = "debugBreakpoint" ! endif ! call sign_define('debugBreakpoint' .. nr, ! \ #{text: substitute(nr, '\..*', '', ''), ! \ texthl: hiName}) endif endfunc *************** *** 963,971 **** " Handle setting a breakpoint " Will update the sign that shows the breakpoint ! func s:HandleNewBreakpoint(msg) if a:msg !~ 'fullname=' ! " a watch does not have a file name return endif for msg in s:SplitMsg(a:msg) --- 1386,1399 ---- " Handle setting a breakpoint " Will update the sign that shows the breakpoint ! func s:HandleNewBreakpoint(msg, modifiedFlag) if a:msg !~ 'fullname=' ! " a watch or a pending breakpoint does not have a file name ! if a:msg =~ 'pending=' ! let nr = substitute(a:msg, '.*number=\"\([0-9.]*\)\".*', '\1', '') ! let target = substitute(a:msg, '.*pending=\"\([^"]*\)\".*', '\1', '') ! echomsg 'Breakpoint ' . nr . ' (' . target . ') pending.' ! endif return endif for msg in s:SplitMsg(a:msg) *************** *** 981,987 **** " If "nr" is 123 it becomes "123.0" and subid is "0". " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') ! call s:CreateBreakpoint(id, subid) if has_key(s:breakpoints, id) let entries = s:breakpoints[id] --- 1409,1416 ---- " If "nr" is 123 it becomes "123.0" and subid is "0". " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') ! let enabled = substitute(msg, '.*enabled="\([yn]\)".*', '\1', '') ! call s:CreateBreakpoint(id, subid, enabled) if has_key(s:breakpoints, id) let entries = s:breakpoints[id] *************** *** 1008,1020 **** if bufloaded(fname) call s:PlaceSign(id, subid, entry) endif endfor endfunc func s:PlaceSign(id, subid, entry) let nr = printf('%d.%d', a:id, a:subid) ! exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname'] let a:entry['placed'] = 1 endfunc --- 1437,1462 ---- if bufloaded(fname) call s:PlaceSign(id, subid, entry) + let posMsg = ' at line ' . lnum . '.' + else + let posMsg = ' in ' . fname . ' at line ' . lnum . '.' + endif + if !a:modifiedFlag + let actionTaken = 'created' + elseif enabled == 'n' + let actionTaken = 'disabled' + else + let actionTaken = 'enabled' endif + echomsg 'Breakpoint ' . nr . ' ' . actionTaken . posMsg endfor endfunc func s:PlaceSign(id, subid, entry) let nr = printf('%d.%d', a:id, a:subid) ! call sign_place(s:Breakpoint2SignNumber(a:id, a:subid), 'TermDebug', ! \ 'debugBreakpoint' .. nr, a:entry['fname'], ! \ #{lnum: a:entry['lnum'], priority: 110}) let a:entry['placed'] = 1 endfunc *************** *** 1028,1038 **** if has_key(s:breakpoints, id) for [subid, entry] in items(s:breakpoints[id]) if has_key(entry, 'placed') ! exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) ! unlet entry['placed'] endif endfor unlet s:breakpoints[id] endif endfunc --- 1470,1482 ---- if has_key(s:breakpoints, id) for [subid, entry] in items(s:breakpoints[id]) if has_key(entry, 'placed') ! call sign_unplace('TermDebug', ! \ #{id: s:Breakpoint2SignNumber(id, subid)}) ! unlet entry['placed'] endif endfor unlet s:breakpoints[id] + echomsg 'Breakpoint ' . id . ' cleared.' endif endfunc *************** *** 1053,1059 **** for [id, entries] in items(s:breakpoints) for [subid, entry] in items(entries) if entry['fname'] == fname ! call s:PlaceSign(id, subid, entry) endif endfor endfor --- 1497,1503 ---- for [id, entries] in items(s:breakpoints) for [subid, entry] in items(entries) if entry['fname'] == fname ! call s:PlaceSign(id, subid, entry) endif endfor endfor *************** *** 1065,1071 **** for [id, entries] in items(s:breakpoints) for [subid, entry] in items(entries) if entry['fname'] == fname ! let entry['placed'] = 0 endif endfor endfor --- 1509,1515 ---- for [id, entries] in items(s:breakpoints) for [subid, entry] in items(entries) if entry['fname'] == fname ! let entry['placed'] = 0 endif endfor endfor *** ../vim-8.2.5009/src/version.c 2022-05-23 15:33:03.077095124 +0100 --- src/version.c 2022-05-23 21:44:10.675925972 +0100 *************** *** 736,737 **** --- 736,739 ---- { /* Add new patch number below this line */ + /**/ + 5010, /**/ -- Life would be so much easier if we could just look at the source code. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///