diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-09-19 04:05:15 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-09-19 04:05:15 +0000 |
commit | 6e9cd6b491267e6dff3e3f3f37d8af5f28e40672 (patch) | |
tree | 35661af16c4a0ef2a9a8e225d2d5cc82605ea289 /runtime/pack/dist/opt/termdebug | |
parent | Adding upstream version 2:9.1.0496. (diff) | |
download | vim-6e9cd6b491267e6dff3e3f3f37d8af5f28e40672.tar.xz vim-6e9cd6b491267e6dff3e3f3f37d8af5f28e40672.zip |
Adding upstream version 2:9.1.0698.upstream/2%9.1.0698
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'runtime/pack/dist/opt/termdebug')
-rw-r--r-- | runtime/pack/dist/opt/termdebug/plugin/termdebug.vim | 625 |
1 files changed, 334 insertions, 291 deletions
diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index d24ae57..377827e 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -4,7 +4,7 @@ vim9script # Author: Bram Moolenaar # Copyright: Vim license applies, see ":help license" -# Last Change: 2024 Jun 16 +# Last Change: 2024 Jul 04 # Converted to Vim9: Ubaldo Tiberi <ubaldo.tiberi@gmail.com> # WORK IN PROGRESS - The basics works stable, more to come @@ -15,7 +15,7 @@ vim9script # There are two ways to run gdb: # - In a terminal window; used if possible, does not work on MS-Windows -# Not used when g:termdebug_use_prompt is set to 1. +# Not used when g:termdebug_use_prompt is set to true. # - Using a "prompt" buffer; may use a terminal window for the program # For both the current window is used to view source code and shows the @@ -42,18 +42,33 @@ def Echoerr(msg: string) echohl ErrorMsg | echom $'[termdebug] {msg}' | echohl None enddef +def Echowarn(msg: string) + echohl WarningMsg | echom $'[termdebug] {msg}' | echohl None +enddef # Variables to keep their status among multiple instances of Termdebug # Avoid to source the script twice. if exists('g:termdebug_loaded') - Echoerr('Termdebug already loaded.') - finish + Echoerr('Termdebug already loaded.') + finish endif g:termdebug_loaded = true +g:termdebug_is_running = false + + +# The command that starts debugging, e.g. ":Termdebug vim". +# To end type "quit" in the gdb window. +command -nargs=* -complete=file -bang Termdebug StartDebug(<bang>0, <f-args>) +command -nargs=+ -complete=file -bang TermdebugCommand StartDebugCommand(<bang>0, <f-args>) + +enum Way + Prompt, + Terminal +endenum # Script variables declaration. These variables are re-initialized at every # Termdebug instance -var way: string +var way: Way var err: string var pc_id: number @@ -74,10 +89,12 @@ var varbufnr: number var varbufname: string var asmbufnr: number var asmbufname: string -var promptbuf: number -# This is for the "debugged program" thing +var promptbufnr: number +# 'pty' refers to the "debugged-program" pty var ptybufnr: number +var ptybufname: string var commbufnr: number +var commbufname: string var gdbjob: job var gdb_channel: channel @@ -110,7 +127,7 @@ var evalexpr: string # Remember the old value of 'signcolumn' for each buffer that it's set in, so # that we can restore the value for all buffers. var signcolumn_buflist: list<number> -var save_columns: number +var saved_columns: number var allleft: bool # This was s:vertical but I cannot use vertical as variable name @@ -120,20 +137,19 @@ var winbar_winids: list<number> var saved_mousemodel: string -var k_map_saved: dict<any> -var plus_map_saved: dict<any> -var minus_map_saved: dict<any> - +var saved_K_map: dict<any> +var saved_plus_map: dict<any> +var saved_minus_map: dict<any> def InitScriptVariables() if exists('g:termdebug_config') && has_key(g:termdebug_config, 'use_prompt') - way = g:termdebug_config['use_prompt'] ? 'prompt' : 'terminal' + way = g:termdebug_config['use_prompt'] ? Way.Prompt : Way.Terminal elseif exists('g:termdebug_use_prompt') - way = g:termdebug_use_prompt + way = g:termdebug_use_prompt ? Way.Prompt : Way.Terminal elseif has('terminal') && !has('win32') - way = 'terminal' + way = Way.Terminal else - way = 'prompt' + way = Way.Prompt endif err = '' @@ -154,9 +170,11 @@ def InitScriptVariables() varbufname = 'Termdebug-variables-listing' asmbufnr = 0 asmbufname = 'Termdebug-asm-listing' - promptbuf = 0 - # This is for the "debugged program" thing + promptbufnr = 0 + # This is for the "debugged-program" thing + ptybufname = "debugged-program" ptybufnr = 0 + commbufname = "gdb-communication" commbufnr = 0 gdbjob = null_job @@ -190,23 +208,87 @@ def InitScriptVariables() # Remember the old value of 'signcolumn' for each buffer that it's set in, so # that we can restore the value for all buffers. signcolumn_buflist = [bufnr()] - save_columns = 0 + saved_columns = &columns winbar_winids = [] - k_map_saved = null_dict - plus_map_saved = null_dict - minus_map_saved = null_dict + saved_K_map = maparg('K', 'n', false, true) + saved_plus_map = maparg('+', 'n', false, true) + saved_minus_map = maparg('-', 'n', false, true) if has('menu') - saved_mousemodel = null_string + saved_mousemodel = &mousemodel + endif +enddef + +def SanityCheck(): bool + var gdb_cmd = GetCommand()[0] + var cwd = $'{getcwd()}/' + if exists('+shellslash') && !&shellslash + # on windows, need to handle backslash + cwd->substitute('\\', '/', 'g') + endif + var is_check_ok = true + # Need either the +terminal feature or +channel and the prompt buffer. + # The terminal feature does not work with gdb on win32. + if (way is Way.Prompt) && !has('channel') + err = 'Cannot debug, +channel feature is not supported' + elseif (way is Way.Prompt) && !exists('*prompt_setprompt') + err = 'Cannot debug, missing prompt buffer support' + elseif (way is Way.Prompt) && !empty(glob($'{cwd}{gdb_cmd}')) + err = $"You have a file/folder named '{gdb_cmd}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder." + elseif !empty(glob($'{cwd}{asmbufname}')) + err = $"You have a file/folder named '{asmbufname}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder." + elseif !empty(glob($'{cwd}{varbufname}')) + err = $"You have a file/folder named '{varbufname}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder." + elseif !executable(gdb_cmd) + err = $"Cannot execute debugger program '{gdb_cmd}'" + endif + + if !empty(err) + Echoerr(err) + is_check_ok = false + endif + return is_check_ok +enddef + +def DeprecationWarnings() + # TODO Remove the deprecated features after 1 Jan 2025. + var config_param = '' + if exists('g:termdebug_wide') + config_param = 'g:termdebug_wide' + elseif exists('g:termdebug_popup') + config_param = 'g:termdebug_popup' + elseif exists('g:termdebugger') + config_param = 'g:termdebugger' + elseif exists('g:termdebug_variables_window') + config_param = 'g:termdebug_variables_window' + elseif exists('g:termdebug_disasm_window') + config_param = 'g:termdebug_disasm_window' + elseif exists('g:termdebug_map_K') + config_param = 'g:termdebug_map_K' + elseif exists('g:termdebug_use_prompt') + config_param = 'g:termdebug_use_prompt' + endif + + if !empty(config_param) + Echowarn($"Deprecation Warning: '{config_param}' parameter + \ is deprecated and will be removed in the future. See ':h g:termdebug_config' for alternatives.") endif -enddef -# The command that starts debugging, e.g. ":Termdebug vim". -# To end type "quit" in the gdb window. -command -nargs=* -complete=file -bang Termdebug StartDebug(<bang>0, <f-args>) -command -nargs=+ -complete=file -bang TermdebugCommand StartDebugCommand(<bang>0, <f-args>) + # termdebug config types + if exists('g:termdebug_config') && !empty(g:termdebug_config) + for key in keys(g:termdebug_config) + if index(['disasm_window', 'variables_window', 'use_prompt', 'map_K', 'map_minus', 'map_plus'], key) != -1 + if typename(g:termdebug_config[key]) == 'number' + var val = g:termdebug_config[key] + Echowarn($"Deprecation Warning: 'g:termdebug_config[\"{key}\"] = {val}' will be deprecated. + \ Please use 'g:termdebug_config[\"{key}\"] = {val != 0}'" ) + endif + endif + endfor + endif +enddef # Take a breakpoint number as used by GDB and turn it into an integer. # The breakpoint may contain a dot: 123.4 -> 123004 @@ -228,7 +310,7 @@ enddef # Define the default highlighting, using the current 'background' value. def InitHighlight() - Highlight(1, '', &background) + Highlight(true, '', &background) hi default debugBreakpoint term=reverse ctermbg=red guibg=red hi default debugBreakpointDisabled term=reverse ctermbg=gray guibg=gray enddef @@ -244,18 +326,19 @@ enddef # Get the command to execute the debugger as a list, defaults to ["gdb"]. def GetCommand(): list<string> - var cmd = 'gdb' + var cmd: any if exists('g:termdebug_config') cmd = get(g:termdebug_config, 'command', 'gdb') elseif exists('g:termdebugger') cmd = g:termdebugger + else + cmd = 'gdb' endif return type(cmd) == v:t_list ? copy(cmd) : [cmd] enddef def StartDebug(bang: bool, ...gdb_args: list<string>) - InitScriptVariables() # First argument is the command to debug, second core file or process ID. StartDebug_internal({gdb_args: gdb_args, bang: bang}) enddef @@ -265,22 +348,25 @@ def StartDebugCommand(bang: bool, ...args: list<string>) StartDebug_internal({gdb_args: [args[0]], proc_args: args[1 : ], bang: bang}) enddef - def StartDebug_internal(dict: dict<any>) - if gdbwin > 0 + if g:termdebug_is_running Echoerr('Terminal debugger already running, cannot run two') return endif - var gdbcmd = GetCommand() - if !executable(gdbcmd[0]) - Echoerr($'Cannot execute debugger program "{gdbcmd[0]}"') + + InitScriptVariables() + if !SanityCheck() return endif + DeprecationWarnings() if exists('#User#TermdebugStartPre') doauto <nomodeline> User TermdebugStartPre endif + # Uncomment this line to write logging in "debuglog". + # ch_logfile('debuglog', 'w') + # Assume current window is the source code window sourcewin = win_getid() var wide = 0 @@ -292,7 +378,6 @@ def StartDebug_internal(dict: dict<any>) endif if wide > 0 if &columns < wide - save_columns = &columns &columns = wide # If we make the Vim window wider, use the whole left half for the debug # windows. @@ -303,7 +388,7 @@ def StartDebug_internal(dict: dict<any>) vvertical = false endif - if way == 'prompt' + if way is Way.Prompt StartDebug_prompt(dict) else StartDebug_term(dict) @@ -324,44 +409,34 @@ def StartDebug_internal(dict: dict<any>) if exists('#User#TermdebugStartPost') doauto <nomodeline> User TermdebugStartPost endif + g:termdebug_is_running = true enddef # Use when debugger didn't start or ended. def CloseBuffers() - var bufnames = ['debugged\ program', 'gdb\ communication', asmbufname, varbufname] - for bufname in bufnames - var buf_nr = bufnr(bufname) + var buf_numbers = [promptbufnr, ptybufnr, commbufnr, asmbufnr, varbufnr] + for buf_nr in buf_numbers if buf_nr > 0 && bufexists(buf_nr) - exe $'bwipe! {bufname}' + exe $'bwipe! {buf_nr}' endif endfor - - running = 0 - gdbwin = 0 enddef -# IsGdbRunning(): bool may be a better name? def IsGdbStarted(): bool var gdbproc_status = job_status(term_getjob(gdbbufnr)) if gdbproc_status !=# 'run' - var cmd_name = string(GetCommand()[0]) - Echoerr($'{cmd_name} exited unexpectedly') - CloseBuffers() return false endif return true enddef -# Open a terminal window without a job, to run the debugged program in. -def StartDebug_term(dict: dict<any>) +def CreateProgramPty(): string ptybufnr = term_start('NONE', { - term_name: 'debugged program', + term_name: ptybufname, vertical: vvertical}) if ptybufnr == 0 - Echoerr('Failed to open the program terminal window') - return + return null_string endif - var pty = job_info(term_getjob(ptybufnr))['tty_out'] ptywin = win_getid() if vvertical @@ -374,19 +449,23 @@ def StartDebug_term(dict: dict<any>) endif endif + return job_info(term_getjob(ptybufnr))['tty_out'] +enddef + +def CreateCommunicationPty(): string # Create a hidden terminal window to communicate with gdb commbufnr = term_start('NONE', { - term_name: 'gdb communication', - out_cb: function('CommOutput'), + term_name: commbufname, + out_cb: CommOutput, hidden: 1 }) if commbufnr == 0 - Echoerr('Failed to open the communication terminal window') - exe $'bwipe! {ptybufnr}' - return + return null_string endif - var commpty = job_info(term_getjob(commbufnr))['tty_out'] + return job_info(term_getjob(commbufnr))['tty_out'] +enddef +def CreateGdbConsole(dict: dict<any>, pty: string, commpty: string): string # Start the gdb buffer var gdb_args = get(dict, 'gdb_args', []) var proc_args = get(dict, 'proc_args', []) @@ -422,24 +501,25 @@ def StartDebug_term(dict: dict<any>) ch_log($'executing "{join(gdb_cmd)}"') gdbbufnr = term_start(gdb_cmd, { - term_name: gdbbufname, - term_finish: 'close', - }) + term_name: gdbbufname, + term_finish: 'close', + }) if gdbbufnr == 0 - Echoerr('Failed to open the gdb terminal window') - CloseBuffers() - return + return 'Failed to open the gdb terminal window' endif gdbwin = win_getid() # Wait for the "startupdone" message before sending any commands. var counter = 0 var counter_max = 300 + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'timeout') + counter_max = g:termdebug_config['timeout'] + endif + var success = false while !success && counter < counter_max if !IsGdbStarted() - CloseBuffers() - return + return $'{gdbbufname} exited unexpectedly' endif for lnum in range(1, 200) @@ -454,14 +534,12 @@ def StartDebug_term(dict: dict<any>) endwhile if !success - Echoerr('Failed to startup the gdb program.') - CloseBuffers() - return + return 'Failed to startup the gdb program.' endif # ---- gdb started. Next, let's set the MI interface. --- # Set arguments to be run. - if len(proc_args) + if !empty(proc_args) term_sendkeys(gdbbufnr, $"server set args {join(proc_args)}\r") endif @@ -476,7 +554,7 @@ def StartDebug_term(dict: dict<any>) success = false while !success && counter < counter_max if !IsGdbStarted() - return + return $'{gdbbufname} exited unexpectedly' endif var response = '' @@ -487,10 +565,8 @@ def StartDebug_term(dict: dict<any>) # response can be in the same line or the next line 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 - CloseBuffers() - return + return 'Sorry, your gdb is too old, gdb 7.12 is required' endif if response =~ 'New UI allocated' # Success! @@ -509,11 +585,37 @@ def StartDebug_term(dict: dict<any>) endwhile if !success - Echoerr('Cannot check if your gdb works, continuing anyway') + return 'Cannot check if your gdb works, continuing anyway' + endif + return '' +enddef + + +# Open a terminal window without a job, to run the debugged program in. +def StartDebug_term(dict: dict<any>) + + var programpty = CreateProgramPty() + if programpty is null_string + Echoerr('Failed to open the program terminal window') + CloseBuffers() + return + endif + + var commpty = CreateCommunicationPty() + if commpty is null_string + Echoerr('Failed to open the communication terminal window') + CloseBuffers() + return + endif + + var err_message = CreateGdbConsole(dict, programpty, commpty) + if !empty(err_message) + Echoerr(err_message) + CloseBuffers() return endif - job_setoptions(term_getjob(gdbbufnr), {exit_cb: function('EndTermDebug')}) + job_setoptions(term_getjob(gdbbufnr), {exit_cb: EndDebug}) # Set the filetype, this can be used to add mappings. set filetype=termdebug @@ -523,29 +625,22 @@ enddef # Open a window with a prompt buffer to run gdb in. def StartDebug_prompt(dict: dict<any>) + var gdb_cmd = GetCommand() + gdbbufname = gdb_cmd[0] + if vvertical vertical new else new endif gdbwin = win_getid() - promptbuf = bufnr('') - prompt_setprompt(promptbuf, 'gdb> ') + promptbufnr = bufnr('') + prompt_setprompt(promptbufnr, 'gdb> ') set buftype=prompt + exe $"file {gdbbufname}" - if empty(glob('gdb')) - file gdb - elseif empty(glob('Termdebug-gdb-console')) - file Termdebug-gdb-console - else - Echoerr("You have a file/folder named 'gdb' " .. - "or 'Termdebug-gdb-console'. " .. - "Please exit and rename them because Termdebug may not work " .. - "as expected.") - endif - - prompt_setcallback(promptbuf, function('PromptCallback')) - prompt_setinterrupt(promptbuf, function('PromptInterrupt')) + prompt_setcallback(promptbufnr, PromptCallback) + prompt_setinterrupt(promptbufnr, PromptInterrupt) if vvertical # Assuming the source code window will get a signcolumn, use two more @@ -556,7 +651,6 @@ def StartDebug_prompt(dict: dict<any>) var gdb_args = get(dict, 'gdb_args', []) var proc_args = get(dict, 'proc_args', []) - var gdb_cmd = GetCommand() # Add -quiet to avoid the intro message causing a hit-enter prompt. gdb_cmd += ['-quiet'] # Disable pagination, it causes everything to stop at the gdb, needs to be run early @@ -573,16 +667,16 @@ def StartDebug_prompt(dict: dict<any>) ch_log($'executing "{join(gdb_cmd)}"') gdbjob = job_start(gdb_cmd, { - exit_cb: function('EndPromptDebug'), - out_cb: function('GdbOutCallback'), + exit_cb: EndDebug, + out_cb: GdbOutCallback }) if job_status(gdbjob) != "run" Echoerr('Failed to start gdb') - exe $'bwipe! {promptbuf}' + exe $'bwipe! {promptbufnr}' return endif - exe $'au BufUnload <buffer={promptbuf}> ++once ' .. - 'call job_stop(gdbjob, ''kill'')' + exe $'au BufUnload <buffer={promptbufnr}> ++once ' .. + 'call job_stop(gdbjob, ''kill'')' # Mark the buffer modified so that it's not easy to close. set modified gdb_channel = job_getchannel(gdbjob) @@ -623,7 +717,7 @@ def StartDebug_prompt(dict: dict<any>) SendCommand('set breakpoint pending on') # Set arguments to be run - if len(proc_args) + if !empty(proc_args) SendCommand($'set args {join(proc_args)}') endif @@ -669,7 +763,7 @@ enddef # Send a command to gdb. "cmd" is the string without line terminator. def SendCommand(cmd: string) ch_log($'sending to gdb: {cmd}') - if way == 'prompt' + if way is Way.Prompt ch_sendraw(gdb_channel, $"{cmd}\n") else term_sendkeys(commbufnr, $"{cmd}\r") @@ -678,7 +772,7 @@ enddef # Interrupt or stop the program def StopCommand() - if way == 'prompt' + if way is Way.Prompt PromptInterrupt() else SendCommand('-exec-interrupt') @@ -687,7 +781,7 @@ enddef # Continue the program def ContinueCommand() - if way == 'prompt' + if way is Way.Prompt SendCommand('continue') else # using -exec-continue results in CTRL-C in the gdb window not working, @@ -699,12 +793,12 @@ enddef # This is global so that a user can create their mappings with this. def g:TermDebugSendCommand(cmd: string) - if way == 'prompt' + if way is Way.Prompt ch_sendraw(gdb_channel, $"{cmd}\n") else - var do_continue = 0 + var do_continue = false if !stopped - do_continue = 1 + do_continue = true StopCommand() sleep 10m endif @@ -765,7 +859,7 @@ def GdbOutCallback(channel: channel, text: string) # Drop the gdb prompt, we have our own. # Drop status and echo'd commands. if text == '(gdb) ' || text == '^done' || - (text[0] == '&' && text !~ '^&"disassemble') + (text[0] == '&' && text !~ '^&"disassemble') return endif @@ -808,20 +902,18 @@ def DecodeMessage(quotedText: string, literal: bool): string return '' endif var msg = quotedText - ->substitute('^"\|[^\\]\zs".*', '', 'g') - ->substitute('\\"', '"', 'g') - #\ multi-byte characters arrive in octal form - #\ NULL-values must be kept encoded as those break the string otherwise - ->substitute('\\000', NullRepl, 'g') - ->substitute('\\\(\o\o\o\)', (m) => nr2char(str2nr(m[1], 8)), 'g') - # You could also use ->substitute('\\\\\(\o\o\o\)', '\=nr2char(str2nr(submatch(1), 8))', "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', NullRepl, 'g') - ->substitute('\\\\', '\', 'g') - ->substitute(NullRepl, '\\000', 'g') + ->substitute('^"\|[^\\]\zs".*', '', 'g') + ->substitute('\\"', '"', 'g') + #\ multi-byte characters arrive in octal form + #\ NULL-values must be kept encoded as those break the string otherwise + ->substitute('\\000', NullRepl, 'g') + ->substitute('\\\(\o\o\o\)', (m) => nr2char(str2nr(m[1], 8)), 'g') + # You could also use ->substitute('\\\\\(\o\o\o\)', '\=nr2char(str2nr(submatch(1), 8))', "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('\\\\', '\', 'g') + ->substitute(NullRepl, '\\000', 'g') if !literal return msg ->substitute('\\t', "\t", 'g') @@ -857,32 +949,17 @@ def GetAsmAddr(msg: string): string return addr enddef - -def EndTermDebug(job: any, status: any) +def EndDebug(job: any, status: any) if exists('#User#TermdebugStopPre') doauto <nomodeline> User TermdebugStopPre endif - if commbufnr > 0 && bufexists(commbufnr) - exe $'bwipe! {commbufnr}' + if way is Way.Prompt + ch_log("Returning from EndDebug()") endif - gdbwin = 0 - EndDebugCommon() -enddef -def EndDebugCommon() var curwinid = win_getid() - - if ptybufnr > 0 && bufexists(ptybufnr) - exe $'bwipe! {ptybufnr}' - endif - if asmbufnr > 0 && bufexists(asmbufnr) - exe $'bwipe! {asmbufnr}' - endif - if varbufnr > 0 && bufexists(varbufnr) - exe $'bwipe! {varbufnr}' - endif - running = false + CloseBuffers() # Restore 'signcolumn' in all buffers for which it was set. win_gotoid(sourcewin) @@ -904,9 +981,7 @@ def EndDebugCommon() win_gotoid(curwinid) - if save_columns > 0 - &columns = save_columns - endif + &columns = saved_columns if has("balloon_eval") || has("balloon_eval_term") set balloonexpr= @@ -923,23 +998,9 @@ def EndDebugCommon() endif au! TermDebug + g:termdebug_is_running = false enddef -def EndPromptDebug(job: any, status: any) - if exists('#User#TermdebugStopPre') - doauto <nomodeline> User TermdebugStopPre - endif - - if bufexists(promptbuf) - exe $'bwipe! {promptbuf}' - endif - - EndDebugCommon() - gdbwin = 0 - ch_log("Returning from EndPromptDebug()") -enddef - - # Disassembly window - added by Michael Sartain # # - CommOutput: &"disassemble $pc\n" @@ -990,11 +1051,11 @@ def HandleDisasmMsg(msg: string) endif elseif msg !~ '^&"disassemble' var value = substitute(msg, '^\~\"[ ]*', '', '') - ->substitute('^=>[ ]*', '', '') - ->substitute('\\n\"\r$', '', '') - ->substitute('\\n\"$', '', '') - ->substitute('\r', '', '') - ->substitute('\\t', ' ', 'g') + ->substitute('^=>[ ]*', '', '') + ->substitute('\\n\"\r$', '', '') + ->substitute('\\n\"$', '', '') + ->substitute('\r', '', '') + ->substitute('\\t', ' ', 'g') if value != '' || !empty(asm_lines) add(asm_lines, value) @@ -1070,9 +1131,9 @@ def CommOutput(chan: channel, message: string) if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' HandleCursor(msg) elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' - HandleNewBreakpoint(msg, 0) + HandleNewBreakpoint(msg, false) elseif msg =~ '^=breakpoint-modified,' - HandleNewBreakpoint(msg, 1) + HandleNewBreakpoint(msg, true) elseif msg =~ '^=breakpoint-deleted,' HandleBreakpointDelete(msg) elseif msg =~ '^=thread-group-started' @@ -1127,74 +1188,70 @@ def InstallCommands() command Source GotoSourcewinOrCreateIt() command Asm GotoAsmwinOrCreateIt() command Var GotoVariableswinOrCreateIt() - command Winbar InstallWinbar(1) + command Winbar InstallWinbar(true) - var map = 1 + var map = true if exists('g:termdebug_config') - map = get(g:termdebug_config, 'map_K', 1) + map = get(g:termdebug_config, 'map_K', true) elseif exists('g:termdebug_map_K') map = g:termdebug_map_K endif if map - k_map_saved = maparg('K', 'n', 0, 1) - if !empty(k_map_saved) && !k_map_saved.buffer || empty(k_map_saved) + if !empty(saved_K_map) && !saved_K_map.buffer || empty(saved_K_map) nnoremap K :Evaluate<CR> endif endif - map = 1 + map = true if exists('g:termdebug_config') - map = get(g:termdebug_config, 'map_plus', 1) + map = get(g:termdebug_config, 'map_plus', true) endif if map - plus_map_saved = maparg('+', 'n', 0, 1) - if !empty(plus_map_saved) && !plus_map_saved.buffer || empty(plus_map_saved) + if !empty(saved_plus_map) && !saved_plus_map.buffer || empty(saved_plus_map) nnoremap <expr> + $'<Cmd>{v:count1}Up<CR>' endif endif - map = 1 + map = true if exists('g:termdebug_config') - map = get(g:termdebug_config, 'map_minus', 1) + map = get(g:termdebug_config, 'map_minus', true) endif if map - minus_map_saved = maparg('-', 'n', 0, 1) - if !empty(minus_map_saved) && !minus_map_saved.buffer || empty(minus_map_saved) + if !empty(saved_minus_map) && !saved_minus_map.buffer || empty(saved_minus_map) nnoremap <expr> - $'<Cmd>{v:count1}Down<CR>' endif endif if has('menu') && &mouse != '' - InstallWinbar(0) + InstallWinbar(false) - var pup = 1 + var pup = true if exists('g:termdebug_config') - pup = get(g:termdebug_config, 'popup', 1) + pup = get(g:termdebug_config, 'popup', true) elseif exists('g:termdebug_popup') pup = g:termdebug_popup endif if pup - saved_mousemodel = &mousemodel &mousemodel = 'popup_setpos' an 1.200 PopUp.-SEP3- <Nop> - an 1.210 PopUp.Set\ breakpoint :Break<CR> - an 1.220 PopUp.Clear\ breakpoint :Clear<CR> - an 1.230 PopUp.Run\ until :Until<CR> - an 1.240 PopUp.Evaluate :Evaluate<CR> + an 1.210 PopUp.Set\ breakpoint <cmd>Break<CR> + an 1.220 PopUp.Clear\ breakpoint <cmd>Clear<CR> + an 1.230 PopUp.Run\ until <cmd>Until<CR> + an 1.240 PopUp.Evaluate <cmd>Evaluate<CR> endif endif enddef # Install the window toolbar in the current window. -def InstallWinbar(force: number) +def InstallWinbar(force: bool) # install the window toolbar by default, can be disabled in the config - var winbar = 1 + var winbar = true if exists('g:termdebug_config') - winbar = get(g:termdebug_config, 'winbar', 1) + winbar = get(g:termdebug_config, 'winbar', true) endif if has('menu') && &mouse != '' && (winbar || force) @@ -1232,40 +1289,26 @@ def DeleteCommands() delcommand Var delcommand Winbar - if k_map_saved isnot null_dict - if !empty(k_map_saved) && k_map_saved.buffer - # pass - elseif !empty(k_map_saved) && !k_map_saved.buffer - nunmap K - mapset(k_map_saved) - elseif empty(k_map_saved) - silent! nunmap K - endif - k_map_saved = null_dict - endif - if plus_map_saved isnot null_dict - if !empty(plus_map_saved) && plus_map_saved.buffer - # pass - elseif !empty(plus_map_saved) && !plus_map_saved.buffer - nunmap + - mapset(plus_map_saved) - elseif empty(plus_map_saved) - silent! nunmap + - endif - plus_map_saved = null_dict - endif - if minus_map_saved isnot null_dict - if !empty(minus_map_saved) && minus_map_saved.buffer - # pass - elseif !empty(minus_map_saved) && !minus_map_saved.buffer - nunmap - - mapset(minus_map_saved) - elseif empty(minus_map_saved) - silent! nunmap - - endif - minus_map_saved = null_dict + + if !empty(saved_K_map) && !saved_K_map.buffer + mapset(saved_K_map) + elseif empty(saved_K_map) + silent! nunmap K + endif + + if !empty(saved_plus_map) && !saved_plus_map.buffer + mapset(saved_plus_map) + elseif empty(saved_plus_map) + silent! nunmap + + endif + + if !empty(saved_minus_map) && !saved_minus_map.buffer + mapset(saved_minus_map) + elseif empty(saved_minus_map) + silent! nunmap - endif + if has('menu') # Remove the WinBar entries from all windows where it was added. var curwinid = win_getid() @@ -1280,32 +1323,29 @@ def DeleteCommands() endif endfor win_gotoid(curwinid) - winbar_winids = [] - - if saved_mousemodel isnot null_string - &mousemodel = saved_mousemodel - saved_mousemodel = null_string - try - aunmenu PopUp.-SEP3- - aunmenu PopUp.Set\ breakpoint - aunmenu PopUp.Clear\ breakpoint - aunmenu PopUp.Run\ until - aunmenu PopUp.Evaluate - catch - # ignore any errors in removing the PopUp menu - endtry - endif + + &mousemodel = saved_mousemodel + try + aunmenu PopUp.-SEP3- + aunmenu PopUp.Set\ breakpoint + aunmenu PopUp.Clear\ breakpoint + aunmenu PopUp.Run\ until + aunmenu PopUp.Evaluate + catch + # ignore any errors in removing the PopUp menu + endtry endif sign_unplace('TermDebug') - breakpoints = {} - breakpoint_locations = {} - sign_undefine('debugPC') sign_undefine(BreakpointSigns->map("'debugBreakpoint' .. v:val")) - BreakpointSigns = [] enddef +def QuoteArg(x: string): string + # Find all the occurrences of " and \ and escape them and double quote + # the resulting string. + return printf('"%s"', x ->substitute('[\\"]', '\\&', 'g')) +enddef # :Until - Execute until past a specified position or current line def Until(at: string) @@ -1316,7 +1356,7 @@ def Until(at: string) ch_log('assume that program is running after this command') # Use the fname:lnum format - var AT = empty(at) ? $"{fnameescape(expand('%:p'))}:{line('.')}" : at + var AT = empty(at) ? QuoteArg($"{expand('%:p')}:{line('.')}") : at SendCommand($'-exec-until {AT}') else ch_log('dropping command, program is running: exec-until') @@ -1327,23 +1367,21 @@ enddef def SetBreakpoint(at: string, tbreak=false) # Setting a breakpoint may not work while the program is running. # Interrupt to make it work. - var do_continue = 0 + var do_continue = false if !stopped - do_continue = 1 + do_continue = true StopCommand() sleep 10m endif # Use the fname:lnum format, older gdb can't handle --source. - var AT = empty(at) ? $"{fnameescape(expand('%:p'))}:{line('.')}" : at + var AT = empty(at) ? QuoteArg($"{expand('%:p')}:{line('.')}") : at var cmd = '' if tbreak cmd = $'-break-insert -t {AT}' else cmd = $'-break-insert {AT}' endif - # OK - # echom $"cmsd: {cmd}" SendCommand(cmd) if do_continue ContinueCommand() @@ -1497,27 +1535,33 @@ def CleanupExpr(passed_expr: string): string return expr enddef +def Balloon_show(expr: string) + if has("balloon_eval") || has("balloon_eval_term") + balloon_show(expr) + endif +enddef + def HandleEvaluate(msg: string) var value = msg - ->substitute('.*value="\(.*\)"', '\1', '') - ->substitute('\\"', '"', 'g') - ->substitute('\\\\', '\\', 'g') - #\ multi-byte characters arrive in octal form, replace everything but NULL values - ->substitute('\\000', NullRepl, 'g') - ->substitute('\\\(\o\o\o\)', (m) => nr2char(str2nr(m[1], 8)), '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', NullRep, 'g') - #\ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') - ->substitute(NullRepl, '\\000', 'g') + ->substitute('.*value="\(.*\)"', '\1', '') + ->substitute('\\"', '"', 'g') + ->substitute('\\\\', '\\', 'g') + #\ multi-byte characters arrive in octal form, replace everything but NULL values + ->substitute('\\000', NullRepl, 'g') + ->substitute('\\\(\o\o\o\)', (m) => nr2char(str2nr(m[1], 8)), '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', NullRep, 'g') + #\ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') + ->substitute(NullRepl, '\\000', 'g') if evalFromBalloonExpr if empty(evalFromBalloonExprResult) evalFromBalloonExprResult = $'{evalexpr}: {value}' else evalFromBalloonExprResult ..= $' = {value}' endif - balloon_show(evalFromBalloonExprResult) + Balloon_show(evalFromBalloonExprResult) else echomsg $'"{evalexpr}": {value}' endif @@ -1567,19 +1611,22 @@ def GotoSourcewinOrCreateIt() if !win_gotoid(sourcewin) new sourcewin = win_getid() - InstallWinbar(0) + InstallWinbar(false) endif enddef -def GetDisasmWindow(): number - 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 +def GetDisasmWindow(): bool + # TODO Remove the deprecated features after 1 Jan 2025. + var val: any + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'disasm_window') + val = g:termdebug_config['disasm_window'] + elseif exists('g:termdebug_disasm_window') + val = g:termdebug_disasm_window + else + val = false endif - return 0 + return typename(val) == 'number' ? val != 0 : val enddef def GetDisasmWindowHeight(): number @@ -1619,13 +1666,9 @@ def GotoAsmwinOrCreateIt() if asmbufnr > 0 && bufexists(asmbufnr) exe $'buffer {asmbufnr}' - elseif empty(glob('Termdebug-asm-listing')) - silent file Termdebug-asm-listing - asmbufnr = bufnr('Termdebug-asm-listing') else - Echoerr("You have a file/folder named 'Termdebug-asm-listing'. " .. - "Please exit and rename it because Termdebug may not work " .. - "as expected.") + exe $"silent file {asmbufname}" + asmbufnr = bufnr(asmbufname) endif if mdf != 'vert' && GetDisasmWindowHeight() > 0 @@ -1646,14 +1689,17 @@ def GotoAsmwinOrCreateIt() endif enddef -def GetVariablesWindow(): number - if exists('g:termdebug_config') - return get(g:termdebug_config, 'variables_window', 0) - endif - if exists('g:termdebug_disasm_window') - return g:termdebug_variables_window +def GetVariablesWindow(): bool + # TODO Remove the deprecated features after 1 Jan 2025. + var val: any + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'variables_window') + val = g:termdebug_config['variables_window'] + elseif exists('g:termdebug_variables_window') + val = g:termdebug_variables_window + else + val = false endif - return 0 + return typename(val) == 'number' ? val != 0 : val enddef def GetVariablesWindowHeight(): number @@ -1691,15 +1737,12 @@ def GotoVariableswinOrCreateIt() setlocal signcolumn=no setlocal modifiable + # If exists, then open, otherwise create if varbufnr > 0 && bufexists(varbufnr) exe $'buffer {varbufnr}' - elseif empty(glob('Termdebug-variables-listing')) - silent file Termdebug-variables-listing - varbufnr = bufnr('Termdebug-variables-listing') else - Echoerr("You have a file/folder named 'Termdebug-variables-listing'. " .. - "Please exit and rename it because Termdebug may not work " .. - "as expected.") + exe $"silent file {varbufname}" + varbufnr = bufnr(varbufname) endif if mdf != 'vert' && GetVariablesWindowHeight() > 0 @@ -1719,7 +1762,7 @@ def HandleCursor(msg: string) if msg =~ '^\*stopped' ch_log('program stopped') - stopped = 1 + stopped = true if msg =~ '^\*stopped,reason="exited-normally"' running = false endif @@ -1778,7 +1821,7 @@ def HandleCursor(msg: string) # TODO: find existing window exe $'split {fnameescape(fname)}' sourcewin = win_getid() - InstallWinbar(0) + InstallWinbar(false) else exe $'edit {fnameescape(fname)}' endif @@ -1790,7 +1833,7 @@ def HandleCursor(msg: string) normal! zv sign_unplace('TermDebug', {id: pc_id}) sign_place(pc_id, 'TermDebug', 'debugPC', fname, - {lnum: str2nr(lnum), priority: 110}) + {lnum: str2nr(lnum), priority: 110}) if !exists('b:save_signcolumn') b:save_signcolumn = &signcolumn add(signcolumn_buflist, bufnr()) @@ -1837,7 +1880,7 @@ enddef # Handle setting a breakpoint # Will update the sign that shows the breakpoint -def HandleNewBreakpoint(msg: string, modifiedFlag: any) +def HandleNewBreakpoint(msg: string, modifiedFlag: bool) var nr = '' if msg !~ 'fullname=' |