0
0
mirror of https://github.com/vim/vim.git synced 2025-09-26 04:04:07 -04:00

patch 9.1.0482: termdebug plugin needs more love

Problem:  termdebug plugin needs more love
Solution: start with some more Vim9 refactoring
          to improve maintenance and readability
          (Ubaldo Tiberi)

List of Changes and the Reasoning Behind Them:

1) Introduction of InitScriptVariables() Function:

Reasoning: This function has been introduced to ensure that when you open and
close Termdebug, and then open it again, there are no leftover script variable
values from the previous session. Leftover values could potentially cause
issues. The goal is for each Termdebug session to be independent of previous
sessions. At startup, all script variables are initialized. The only exception
is g:termdebug_loaded located at the very beginning of the script to prevent
sourcing the script twice. The variables are declared at script level and
defined in InitScriptVariables().

2) More Descriptive Variable Names:

Reasoning: The names of variables have been made more comprehensive. Almost
every Termdebug buffer now has a variable to indicate its name and another
variable to indicate its number, improving code readability and
maintainability. Due to the latest discussion around the &mousemodel option
save/restore mechanism, perhaps some other variables shall be prepended with
saved_.

3) Consistent Naming for GDB Terminal Buffers:

Reasoning: The name of the GDB terminal buffer now matches the name of the GDB
program being used, e.g., 'gdb', 'mygdb', 'arm-eabi-none-gdb', etc. This
ensures clarity and consistency in identifying buffers.

4) Other minor improvements:
Moved EchoErr() on top, added another test, some refactoring, mainly changed
several 0 and 1 to true and false

closes: #14980

Signed-off-by: Ubaldo Tiberi <ubaldo.tiberi@volvo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Ubaldo Tiberi
2024-06-13 19:23:07 +02:00
committed by Christian Brabandt
parent 1a946044fe
commit ef8eab86e2
3 changed files with 249 additions and 179 deletions

View File

@@ -4,7 +4,7 @@ vim9script
# Author: Bram Moolenaar # Author: Bram Moolenaar
# Copyright: Vim license applies, see ":help license" # Copyright: Vim license applies, see ":help license"
# Last Change: 2024 Jun 03 # Last Change: 2024 Jun 13
# Converted to Vim9: Ubaldo Tiberi <ubaldo.tiberi@gmail.com> # Converted to Vim9: Ubaldo Tiberi <ubaldo.tiberi@gmail.com>
# WORK IN PROGRESS - The basics works stable, more to come # WORK IN PROGRESS - The basics works stable, more to come
@@ -38,44 +38,56 @@ vim9script
# The communication with gdb uses GDB/MI. See: # The communication with gdb uses GDB/MI. See:
# https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html # https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html
# In case this gets sourced twice. def Echoerr(msg: string)
echohl ErrorMsg | 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') if exists('g:termdebug_loaded')
Echoerr('Termdebug already loaded.')
finish finish
endif endif
g:termdebug_loaded = true g:termdebug_loaded = true
var way = 'terminal' # Script variables declaration. These variables are re-initialized at every
var err = 'no errors' # Termdebug instance
var way: string
var err: string
var pc_id = 12 var pc_id: number
var asm_id = 13 var asm_id: number
var break_id = 14 # breakpoint number is added to this var break_id: number
var stopped = 1 var stopped: bool
var running = 0 var running: bool
var parsing_disasm_msg = 0 var parsing_disasm_msg: number
var asm_lines = [] var asm_lines: list<string>
var asm_addr = '' var asm_addr: string
# These shall be constants but cannot be initialized here # These shall be constants but cannot be initialized here
# They indicate the buffer numbers of the main buffers used # They indicate the buffer numbers of the main buffers used
var gdbbuf = 0 var gdbbufnr: number
var varbuf = 0 var gdbbufname: string
var asmbuf = 0 var varbufnr: number
var promptbuf = 0 var varbufname: string
var asmbufnr: number
var asmbufname: string
var promptbuf: number
# This is for the "debugged program" thing # This is for the "debugged program" thing
var ptybuf = 0 var ptybufnr: number
var commbuf = 0 var commbufnr: number
var gdbjob = null_job var gdbjob: job
var gdb_channel = null_channel var gdb_channel: channel
# These changes because they relate to windows # These changes because they relate to windows
var pid = 0 var pid: number
var gdbwin = 0 var gdbwin: number
var varwin = 0 var varwin: number
var asmwin = 0 var asmwin: number
var ptywin = 0 var ptywin: number
var sourcewin = 0 var sourcewin: number
# Contains breakpoints that have been placed, key is a string with the GDB # Contains breakpoints that have been placed, key is a string with the GDB
# breakpoint number. # breakpoint number.
@@ -84,52 +96,112 @@ var sourcewin = 0
# For a breakpoint "123.4" the id is "123" and subid is "4". # For a breakpoint "123.4" the id is "123" and subid is "4".
# Example, when breakpoint "44", "123", "123.1" and "123.2" exist: # Example, when breakpoint "44", "123", "123.1" and "123.2" exist:
# {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} # {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}}
var breakpoints = {} var breakpoints: dict<any>
# Contains breakpoints by file/lnum. The key is "fname:lnum". # Contains breakpoints by file/lnum. The key is "fname:lnum".
# Each entry is a list of breakpoint IDs at that position. # Each entry is a list of breakpoint IDs at that position.
var breakpoint_locations = {} var breakpoint_locations: dict<any>
var BreakpointSigns: list<string> = [] var BreakpointSigns: list<string>
var evalFromBalloonExpr: bool
var evalFromBalloonExpr = 0 var evalFromBalloonExprResult: string
var evalFromBalloonExprResult = '' var ignoreEvalError: bool
var ignoreEvalError = 0 var evalexpr: string
var evalexpr = ''
# Remember the old value of 'signcolumn' for each buffer that it's set in, so # Remember the old value of 'signcolumn' for each buffer that it's set in, so
# that we can restore the value for all buffers. # that we can restore the value for all buffers.
var signcolumn_buflist = [bufnr()] var signcolumn_buflist: list<number>
var save_columns = 0 var save_columns: number
var allleft = 0 var allleft: bool
# This was s:vertical but I cannot use vertical as variable name # This was s:vertical but I cannot use vertical as variable name
var vvertical = 0 var vvertical: bool
var winbar_winids = [] var winbar_winids: list<number>
var plus_map_saved = {}
var minus_map_saved = {} var saved_mousemodel: string
var k_map_saved = {}
var saved_mousemodel = null_string var k_map_saved: dict<any>
var plus_map_saved: dict<any>
var minus_map_saved: dict<any>
# Need either the +terminal feature or +channel and the prompt buffer. def InitScriptVariables()
# The terminal feature does not work with gdb on win32. if exists('g:termdebug_config') && has_key(g:termdebug_config, 'use_prompt')
if has('terminal') && !has('win32') way = g:termdebug_config['use_prompt'] ? 'prompt' : 'terminal'
elseif exists('g:termdebug_use_prompt')
way = g:termdebug_use_prompt
elseif has('terminal') && !has('win32')
way = 'terminal' way = 'terminal'
elseif has('channel') && exists('*prompt_setprompt') else
way = 'prompt' way = 'prompt'
else
if has('terminal')
err = 'Cannot debug, missing prompt buffer support'
else
err = 'Cannot debug, +channel feature is not supported'
endif endif
command -nargs=* -complete=file -bang Termdebug echoerr err err = ''
command -nargs=+ -complete=file -bang TermdebugCommand echoerr err
finish pc_id = 12
asm_id = 13
break_id = 14 # breakpoint number is added to this
stopped = true
running = false
parsing_disasm_msg = 0
asm_lines = []
asm_addr = ''
# They indicate the buffer numbers of the main buffers used
gdbbufnr = 0
gdbbufname = 'gdb'
varbufnr = 0
varbufname = 'Termdebug-variables-listing'
asmbufnr = 0
asmbufname = 'Termdebug-asm-listing'
promptbuf = 0
# This is for the "debugged program" thing
ptybufnr = 0
commbufnr = 0
gdbjob = null_job
gdb_channel = null_channel
# These changes because they relate to windows
pid = 0
gdbwin = 0
varwin = 0
asmwin = 0
ptywin = 0
sourcewin = 0
# Contains breakpoints that have been placed, key is a string with the GDB
# breakpoint number.
# Each entry is a dict, containing the sub-breakpoints. Key is the subid.
# For a breakpoint that is just a number the subid is zero.
# For a breakpoint "123.4" the id is "123" and subid is "4".
# Example, when breakpoint "44", "123", "123.1" and "123.2" exist:
# {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}}
breakpoints = {}
# Contains breakpoints by file/lnum. The key is "fname:lnum".
# Each entry is a list of breakpoint IDs at that position.
breakpoint_locations = {}
BreakpointSigns = []
evalFromBalloonExpr = false
evalFromBalloonExprResult = ''
ignoreEvalError = false
evalexpr = ''
# 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
winbar_winids = []
k_map_saved = {}
plus_map_saved = {}
minus_map_saved = {}
if has('menu')
saved_mousemodel = null_string
endif endif
enddef
# The command that starts debugging, e.g. ":Termdebug vim". # The command that starts debugging, e.g. ":Termdebug vim".
# To end type "quit" in the gdb window. # To end type "quit" in the gdb window.
command -nargs=* -complete=file -bang Termdebug StartDebug(<bang>0, <f-args>) command -nargs=* -complete=file -bang Termdebug StartDebug(<bang>0, <f-args>)
@@ -182,11 +254,8 @@ def GetCommand(): list<string>
return type(cmd) == v:t_list ? copy(cmd) : [cmd] return type(cmd) == v:t_list ? copy(cmd) : [cmd]
enddef enddef
def Echoerr(msg: string)
echohl ErrorMsg | echom $'[termdebug] {msg}' | echohl None
enddef
def StartDebug(bang: bool, ...gdb_args: list<string>) def StartDebug(bang: bool, ...gdb_args: list<string>)
InitScriptVariables()
# First argument is the command to debug, second core file or process ID. # First argument is the command to debug, second core file or process ID.
StartDebug_internal({gdb_args: gdb_args, bang: bang}) StartDebug_internal({gdb_args: gdb_args, bang: bang})
enddef enddef
@@ -227,24 +296,11 @@ def StartDebug_internal(dict: dict<any>)
&columns = wide &columns = wide
# If we make the Vim window wider, use the whole left half for the debug # If we make the Vim window wider, use the whole left half for the debug
# windows. # windows.
allleft = 1 allleft = true
endif endif
vvertical = 1 vvertical = true
else else
vvertical = 0 vvertical = false
endif
# Override using a terminal window by setting g:termdebug_use_prompt to 1.
var use_prompt = 0
if exists('g:termdebug_config')
use_prompt = get(g:termdebug_config, 'use_prompt', 0)
elseif exists('g:termdebug_use_prompt')
use_prompt = g:termdebug_use_prompt
endif
if has('terminal') && !has('win32') && !use_prompt
way = 'terminal'
else
way = 'prompt'
endif endif
if way == 'prompt' if way == 'prompt'
@@ -272,45 +328,42 @@ enddef
# Use when debugger didn't start or ended. # Use when debugger didn't start or ended.
def CloseBuffers() def CloseBuffers()
exe $'bwipe! {ptybuf}' exe $'bwipe! {ptybufnr}'
exe $'bwipe! {commbuf}' exe $'bwipe! {commbufnr}'
if asmbuf > 0 && bufexists(asmbuf) if asmbufnr > 0 && bufexists(asmbufnr)
exe $'bwipe! {asmbuf}' exe $'bwipe! {asmbufnr}'
endif endif
if varbuf > 0 && bufexists(varbuf) if varbufnr > 0 && bufexists(varbufnr)
exe $'bwipe! {varbuf}' exe $'bwipe! {varbufnr}'
endif endif
running = 0 running = 0
gdbwin = 0 gdbwin = 0
enddef enddef
# IsGdbRunning(): bool may be a better name? # IsGdbRunning(): bool may be a better name?
def CheckGdbRunning(): string def IsGdbStarted(): bool
var gdbproc = term_getjob(gdbbuf) var gdbproc_status = job_status(term_getjob(gdbbufnr))
var gdbproc_status = 'unknown' if gdbproc_status !=# 'run'
if type(gdbproc) == v:t_job
gdbproc_status = job_status(gdbproc)
endif
if gdbproc == v:null || gdbproc_status !=# 'run'
var cmd_name = string(GetCommand()[0]) var cmd_name = string(GetCommand()[0])
Echoerr($'{cmd_name} exited unexpectedly') Echoerr($'{cmd_name} exited unexpectedly')
CloseBuffers() CloseBuffers()
return '' return false
endif endif
return 'ok' return true
enddef enddef
# Open a terminal window without a job, to run the debugged program in. # Open a terminal window without a job, to run the debugged program in.
def StartDebug_term(dict: dict<any>) def StartDebug_term(dict: dict<any>)
ptybuf = term_start('NONE', { ptybufnr = term_start('NONE', {
term_name: 'debugged program', term_name: 'debugged program',
vertical: vvertical}) vertical: vvertical})
if ptybuf == 0 if ptybufnr == 0
Echoerr('Failed to open the program terminal window') Echoerr('Failed to open the program terminal window')
return return
endif endif
var pty = job_info(term_getjob(ptybuf))['tty_out'] var pty = job_info(term_getjob(ptybufnr))['tty_out']
ptywin = win_getid() ptywin = win_getid()
if vvertical if vvertical
# Assuming the source code window will get a signcolumn, use two more # Assuming the source code window will get a signcolumn, use two more
# columns for that, thus one less for the terminal window. # columns for that, thus one less for the terminal window.
@@ -322,23 +375,26 @@ def StartDebug_term(dict: dict<any>)
endif endif
# Create a hidden terminal window to communicate with gdb # Create a hidden terminal window to communicate with gdb
commbuf = term_start('NONE', { commbufnr = term_start('NONE', {
term_name: 'gdb communication', term_name: 'gdb communication',
out_cb: function('CommOutput'), out_cb: function('CommOutput'),
hidden: 1 hidden: 1
}) })
if commbuf == 0 if commbufnr == 0
Echoerr('Failed to open the communication terminal window') Echoerr('Failed to open the communication terminal window')
exe $'bwipe! {ptybuf}' exe $'bwipe! {ptybufnr}'
return return
endif endif
var commpty = job_info(term_getjob(commbuf))['tty_out'] var commpty = job_info(term_getjob(commbufnr))['tty_out']
# Start the gdb buffer
var gdb_args = get(dict, 'gdb_args', []) var gdb_args = get(dict, 'gdb_args', [])
var proc_args = get(dict, 'proc_args', []) var proc_args = get(dict, 'proc_args', [])
var gdb_cmd = GetCommand() var gdb_cmd = GetCommand()
gdbbufname = gdb_cmd[0]
if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_add_args') if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_add_args')
gdb_cmd = g:termdebug_config.command_add_args(gdb_cmd, pty) gdb_cmd = g:termdebug_config.command_add_args(gdb_cmd, pty)
else else
@@ -365,11 +421,11 @@ def StartDebug_term(dict: dict<any>)
gdb_cmd += gdb_args gdb_cmd += gdb_args
ch_log($'executing "{join(gdb_cmd)}"') ch_log($'executing "{join(gdb_cmd)}"')
gdbbuf = term_start(gdb_cmd, { gdbbufnr = term_start(gdb_cmd, {
term_name: 'gdb', term_name: gdbbufname,
term_finish: 'close', term_finish: 'close',
}) })
if gdbbuf == 0 if gdbbufnr == 0
Echoerr('Failed to open the gdb terminal window') Echoerr('Failed to open the gdb terminal window')
CloseBuffers() CloseBuffers()
return return
@@ -380,14 +436,14 @@ def StartDebug_term(dict: dict<any>)
var counter = 0 var counter = 0
var counter_max = 300 var counter_max = 300
var success = false var success = false
while success == false && counter < counter_max while !success && counter < counter_max
if CheckGdbRunning() != 'ok' if !IsGdbStarted()
# Failure. If NOK just return. CloseBuffers()
return return
endif endif
for lnum in range(1, 200) for lnum in range(1, 200)
if term_getline(gdbbuf, lnum) =~ 'startupdone' if term_getline(gdbbufnr, lnum) =~ 'startupdone'
success = true success = true
endif endif
endfor endfor
@@ -397,7 +453,7 @@ def StartDebug_term(dict: dict<any>)
sleep 10m sleep 10m
endwhile endwhile
if success == false if !success
Echoerr('Failed to startup the gdb program.') Echoerr('Failed to startup the gdb program.')
CloseBuffers() CloseBuffers()
return return
@@ -406,30 +462,30 @@ def StartDebug_term(dict: dict<any>)
# ---- gdb started. Next, let's set the MI interface. --- # ---- gdb started. Next, let's set the MI interface. ---
# Set arguments to be run. # Set arguments to be run.
if len(proc_args) if len(proc_args)
term_sendkeys(gdbbuf, $"server set args {join(proc_args)}\r") term_sendkeys(gdbbufnr, $"server set args {join(proc_args)}\r")
endif endif
# Connect gdb to the communication pty, using the GDB/MI interface. # Connect gdb to the communication pty, using the GDB/MI interface.
# Prefix "server" to avoid adding this to the history. # Prefix "server" to avoid adding this to the history.
term_sendkeys(gdbbuf, $"server new-ui mi {commpty}\r") term_sendkeys(gdbbufnr, $"server new-ui mi {commpty}\r")
# Wait for the response to show up, users may not notice the error and wonder # Wait for the response to show up, users may not notice the error and wonder
# why the debugger doesn't work. # why the debugger doesn't work.
counter = 0 counter = 0
counter_max = 300 counter_max = 300
success = false success = false
while success == false && counter < counter_max while !success && counter < counter_max
if CheckGdbRunning() != 'ok' if !IsGdbStarted()
return return
endif endif
var response = '' var response = ''
for lnum in range(1, 200) for lnum in range(1, 200)
var line1 = term_getline(gdbbuf, lnum) var line1 = term_getline(gdbbufnr, lnum)
var line2 = term_getline(gdbbuf, lnum + 1) var line2 = term_getline(gdbbufnr, lnum + 1)
if line1 =~ 'new-ui mi ' if line1 =~ 'new-ui mi '
# response can be in the same line or the next line # response can be in the same line or the next line
response = line1 .. line2 response = $"{line1}{line2}"
if response =~ 'Undefined command' if response =~ 'Undefined command'
Echoerr('Sorry, your gdb is too old, gdb 7.12 is required') Echoerr('Sorry, your gdb is too old, gdb 7.12 is required')
# CHECKME: possibly send a "server show version" here # CHECKME: possibly send a "server show version" here
@@ -452,12 +508,12 @@ def StartDebug_term(dict: dict<any>)
sleep 10m sleep 10m
endwhile endwhile
if success == false if !success
Echoerr('Cannot check if your gdb works, continuing anyway') Echoerr('Cannot check if your gdb works, continuing anyway')
return return
endif endif
job_setoptions(term_getjob(gdbbuf), {exit_cb: function('EndTermDebug')}) job_setoptions(term_getjob(gdbbufnr), {exit_cb: function('EndTermDebug')})
# Set the filetype, this can be used to add mappings. # Set the filetype, this can be used to add mappings.
set filetype=termdebug set filetype=termdebug
@@ -531,23 +587,24 @@ def StartDebug_prompt(dict: dict<any>)
set modified set modified
gdb_channel = job_getchannel(gdbjob) gdb_channel = job_getchannel(gdbjob)
ptybuf = 0 ptybufnr = 0
if has('win32') if has('win32')
# MS-Windows: run in a new console window for maximum compatibility # MS-Windows: run in a new console window for maximum compatibility
SendCommand('set new-console on') SendCommand('set new-console on')
elseif has('terminal') elseif has('terminal')
# Unix: Run the debugged program in a terminal window. Open it below the # Unix: Run the debugged program in a terminal window. Open it below the
# gdb window. # gdb window.
belowright ptybuf = term_start('NONE', { belowright ptybufnr = term_start('NONE', {
term_name: 'debugged program', term_name: 'debugged program',
vertical: vvertical
}) })
if ptybuf == 0 if ptybufnr == 0
Echoerr('Failed to open the program terminal window') Echoerr('Failed to open the program terminal window')
job_stop(gdbjob) job_stop(gdbjob)
return return
endif endif
ptywin = win_getid() ptywin = win_getid()
var pty = job_info(term_getjob(ptybuf))['tty_out'] var pty = job_info(term_getjob(ptybufnr))['tty_out']
SendCommand($'tty {pty}') SendCommand($'tty {pty}')
# Since GDB runs in a prompt window, the environment has not been set to # Since GDB runs in a prompt window, the environment has not been set to
@@ -615,7 +672,7 @@ def SendCommand(cmd: string)
if way == 'prompt' if way == 'prompt'
ch_sendraw(gdb_channel, $"{cmd}\n") ch_sendraw(gdb_channel, $"{cmd}\n")
else else
term_sendkeys(commbuf, $"{cmd}\r") term_sendkeys(commbufnr, $"{cmd}\r")
endif endif
enddef enddef
@@ -652,7 +709,7 @@ def TermDebugSendCommand(cmd: string)
sleep 10m sleep 10m
endif endif
# TODO: should we prepend CTRL-U to clear the command? # TODO: should we prepend CTRL-U to clear the command?
term_sendkeys(gdbbuf, $"{cmd}\r") term_sendkeys(gdbbufnr, $"{cmd}\r")
if do_continue if do_continue
ContinueCommand() ContinueCommand()
endif endif
@@ -665,7 +722,7 @@ enddef
def SendResumingCommand(cmd: string) def SendResumingCommand(cmd: string)
if stopped if stopped
# reset stopped here, it may take a bit of time before we get a response # reset stopped here, it may take a bit of time before we get a response
stopped = 0 stopped = false
ch_log('assume that program is running after this command') ch_log('assume that program is running after this command')
SendCommand(cmd) SendCommand(cmd)
else else
@@ -715,9 +772,9 @@ def GdbOutCallback(channel: channel, text: string)
var decoded_text = '' var decoded_text = ''
if text =~ '^\^error,msg=' if text =~ '^\^error,msg='
decoded_text = DecodeMessage(text[11 : ], false) decoded_text = DecodeMessage(text[11 : ], false)
if exists('evalexpr') && decoded_text =~ 'A syntax error in expression, near\|No symbol .* in current context' if !empty(evalexpr) && decoded_text =~ 'A syntax error in expression, near\|No symbol .* in current context'
# Silently drop evaluation errors. # Silently drop evaluation errors.
evalexpr = null_string evalexpr = ''
return return
endif endif
elseif text[0] == '~' elseif text[0] == '~'
@@ -739,9 +796,9 @@ enddef
# Decode a message from gdb. "quotedText" starts with a ", return the text up # Decode a message from gdb. "quotedText" starts with a ", return the text up
# to the next unescaped ", unescaping characters: # to the next unescaped ", unescaping characters:
# - remove line breaks (unless "literal" is v:true) # - remove line breaks (unless "literal" is true)
# - change \" to " # - change \" to "
# - change \\t to \t (unless "literal" is v:true) # - change \\t to \t (unless "literal" is true)
# - change \0xhh to \xhh (disabled for now) # - change \0xhh to \xhh (disabled for now)
# - change \ooo to octal # - change \ooo to octal
# - change \\ to \ # - change \\ to \
@@ -751,19 +808,20 @@ def DecodeMessage(quotedText: string, literal: bool): string
return '' return ''
endif endif
var msg = quotedText var msg = quotedText
\ ->substitute('^"\|[^\\]\zs".*', '', 'g') ->substitute('^"\|[^\\]\zs".*', '', 'g')
\ ->substitute('\\"', '"', 'g') ->substitute('\\"', '"', 'g')
#\ multi-byte characters arrive in octal form #\ multi-byte characters arrive in octal form
#\ NULL-values must be kept encoded as those break the string otherwise #\ NULL-values must be kept encoded as those break the string otherwise
\ ->substitute('\\000', NullRepl, 'g') ->substitute('\\000', NullRepl, 'g')
\ ->substitute('\\\(\o\o\o\)', (m) => nr2char(str2nr(m[1], 8)), '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 #\ Note: GDB docs also mention hex encodings - the translations below work
#\ but we keep them out for performance-reasons until we actually see #\ but we keep them out for performance-reasons until we actually see
#\ those in mi-returns #\ those in mi-returns
#\ \ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') #\ \ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g')
#\ \ ->substitute('\\0x00', NullRepl, 'g') #\ \ ->substitute('\\0x00', NullRepl, 'g')
\ ->substitute('\\\\', '\', 'g') ->substitute('\\\\', '\', 'g')
\ ->substitute(NullRepl, '\\000', 'g') ->substitute(NullRepl, '\\000', 'g')
if !literal if !literal
return msg return msg
->substitute('\\t', "\t", 'g') ->substitute('\\t', "\t", 'g')
@@ -805,8 +863,8 @@ def EndTermDebug(job: any, status: any)
doauto <nomodeline> User TermdebugStopPre doauto <nomodeline> User TermdebugStopPre
endif endif
if bufexists(commbuf) if commbufnr > 0 && bufexists(commbufnr)
exe $'bwipe! {commbuf}' exe $'bwipe! {commbufnr}'
endif endif
gdbwin = 0 gdbwin = 0
EndDebugCommon() EndDebugCommon()
@@ -815,16 +873,16 @@ enddef
def EndDebugCommon() def EndDebugCommon()
var curwinid = win_getid() var curwinid = win_getid()
if bufexists(ptybuf) if ptybufnr > 0 && bufexists(ptybufnr)
exe $'bwipe! {ptybuf}' exe $'bwipe! {ptybufnr}'
endif endif
if bufexists(asmbuf) if asmbufnr > 0 && bufexists(asmbufnr)
exe $'bwipe! {asmbuf}' exe $'bwipe! {asmbufnr}'
endif endif
if bufexists(varbuf) if varbufnr > 0 && bufexists(varbufnr)
exe $'bwipe! {varbuf}' exe $'bwipe! {varbufnr}'
endif endif
running = 0 running = false
# Restore 'signcolumn' in all buffers for which it was set. # Restore 'signcolumn' in all buffers for which it was set.
win_gotoid(sourcewin) win_gotoid(sourcewin)
@@ -932,11 +990,11 @@ def HandleDisasmMsg(msg: string)
endif endif
elseif msg !~ '^&"disassemble' elseif msg !~ '^&"disassemble'
var value = substitute(msg, '^\~\"[ ]*', '', '') var value = substitute(msg, '^\~\"[ ]*', '', '')
value = substitute(value, '^=>[ ]*', '', '') ->substitute('^=>[ ]*', '', '')
value = substitute(value, '\\n\"\r$', '', '') ->substitute('\\n\"\r$', '', '')
value = substitute(value, '\\n\"$', '', '') ->substitute('\\n\"$', '', '')
value = substitute(value, '\r', '', '') ->substitute('\r', '', '')
value = substitute(value, '\\t', ' ', 'g') ->substitute('\\t', ' ', 'g')
if value != '' || !empty(asm_lines) if value != '' || !empty(asm_lines)
add(asm_lines, value) add(asm_lines, value)
@@ -1248,7 +1306,7 @@ def Until(at: string)
if stopped if stopped
# reset stopped here, it may take a bit of time before we get a response # reset stopped here, it may take a bit of time before we get a response
stopped = 0 stopped = false
ch_log('assume that program is running after this command') ch_log('assume that program is running after this command')
# Use the fname:lnum format # Use the fname:lnum format
@@ -1379,8 +1437,8 @@ enddef
# :Evaluate - evaluate what is specified / under the cursor # :Evaluate - evaluate what is specified / under the cursor
def Evaluate(range: number, arg: string) def Evaluate(range: number, arg: string)
var expr = GetEvaluationExpression(range, arg) var expr = GetEvaluationExpression(range, arg)
#echom $"expr: {expr}" echom $"expr: {expr}"
ignoreEvalError = 0 ignoreEvalError = false
SendEval(expr) SendEval(expr)
enddef enddef
@@ -1435,21 +1493,20 @@ enddef
def HandleEvaluate(msg: string) def HandleEvaluate(msg: string)
var value = msg var value = msg
\ ->substitute('.*value="\(.*\)"', '\1', '') ->substitute('.*value="\(.*\)"', '\1', '')
\ ->substitute('\\"', '"', 'g') ->substitute('\\"', '"', 'g')
\ ->substitute('\\\\', '\\', 'g') ->substitute('\\\\', '\\', 'g')
#\ multi-byte characters arrive in octal form, replace everything but NULL values #\ multi-byte characters arrive in octal form, replace everything but NULL values
\ ->substitute('\\000', NullRepl, 'g') ->substitute('\\000', NullRepl, 'g')
# \ ->substitute('\\\o\o\o', {-> eval('"' .. submatch(0) .. '"')}, 'g') ->substitute('\\\(\o\o\o\)', (m) => nr2char(str2nr(m[1], 8)), 'g')
\ ->substitute('\\\(\o\o\o\)', (m) => nr2char(str2nr(m[1], 8)), 'g')
#\ Note: GDB docs also mention hex encodings - the translations below work #\ Note: GDB docs also mention hex encodings - the translations below work
#\ but we keep them out for performance-reasons until we actually see #\ but we keep them out for performance-reasons until we actually see
#\ those in mi-returns #\ those in mi-returns
#\ ->substitute('\\0x00', NullRep, 'g') #\ ->substitute('\\0x00', NullRep, 'g')
#\ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') #\ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g')
\ ->substitute(NullRepl, '\\000', 'g') ->substitute(NullRepl, '\\000', 'g')
if evalFromBalloonExpr if evalFromBalloonExpr
if evalFromBalloonExprResult == '' if empty(evalFromBalloonExprResult)
evalFromBalloonExprResult = $'{evalexpr}: {value}' evalFromBalloonExprResult = $'{evalexpr}: {value}'
else else
evalFromBalloonExprResult ..= $' = {value}' evalFromBalloonExprResult ..= $' = {value}'
@@ -1461,10 +1518,10 @@ def HandleEvaluate(msg: string)
if evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' if evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
# Looks like a pointer, also display what it points to. # Looks like a pointer, also display what it points to.
ignoreEvalError = 1 ignoreEvalError = true
SendEval($'*{evalexpr}') SendEval($'*{evalexpr}')
else else
evalFromBalloonExpr = 0 evalFromBalloonExpr = false
endif endif
enddef enddef
@@ -1480,9 +1537,9 @@ def TermDebugBalloonExpr(): string
# mouse triggers a balloon. # mouse triggers a balloon.
return '' return ''
endif endif
evalFromBalloonExpr = 1 evalFromBalloonExpr = true
evalFromBalloonExprResult = '' evalFromBalloonExprResult = ''
ignoreEvalError = 1 ignoreEvalError = true
var expr = CleanupExpr(v:beval_text) var expr = CleanupExpr(v:beval_text)
SendEval(expr) SendEval(expr)
return '' return ''
@@ -1492,8 +1549,8 @@ enddef
def HandleError(msg: string) def HandleError(msg: string)
if ignoreEvalError if ignoreEvalError
# Result of SendEval() failed, ignore. # Result of SendEval() failed, ignore.
ignoreEvalError = 0 ignoreEvalError = false
evalFromBalloonExpr = 0 evalFromBalloonExpr = true
return return
endif endif
var msgVal = substitute(msg, '.*msg="\(.*\)"', '\1', '') var msgVal = substitute(msg, '.*msg="\(.*\)"', '\1', '')
@@ -1554,11 +1611,11 @@ def GotoAsmwinOrCreateIt()
setlocal signcolumn=no setlocal signcolumn=no
setlocal modifiable setlocal modifiable
if asmbuf > 0 && bufexists(asmbuf) if asmbufnr > 0 && bufexists(asmbufnr)
exe $'buffer {asmbuf}' exe $'buffer {asmbufnr}'
elseif empty(glob('Termdebug-asm-listing')) elseif empty(glob('Termdebug-asm-listing'))
silent file Termdebug-asm-listing silent file Termdebug-asm-listing
asmbuf = bufnr('Termdebug-asm-listing') asmbufnr = bufnr('Termdebug-asm-listing')
else else
Echoerr("You have a file/folder named 'Termdebug-asm-listing'. " .. Echoerr("You have a file/folder named 'Termdebug-asm-listing'. " ..
"Please exit and rename it because Termdebug may not work " .. "Please exit and rename it because Termdebug may not work " ..
@@ -1628,11 +1685,11 @@ def GotoVariableswinOrCreateIt()
setlocal signcolumn=no setlocal signcolumn=no
setlocal modifiable setlocal modifiable
if varbuf > 0 && bufexists(varbuf) if varbufnr > 0 && bufexists(varbufnr)
exe $'buffer {varbuf}' exe $'buffer {varbufnr}'
elseif empty(glob('Termdebug-variables-listing')) elseif empty(glob('Termdebug-variables-listing'))
silent file Termdebug-variables-listing silent file Termdebug-variables-listing
varbuf = bufnr('Termdebug-variables-listing') varbufnr = bufnr('Termdebug-variables-listing')
else else
Echoerr("You have a file/folder named 'Termdebug-variables-listing'. " .. Echoerr("You have a file/folder named 'Termdebug-variables-listing'. " ..
"Please exit and rename it because Termdebug may not work " .. "Please exit and rename it because Termdebug may not work " ..
@@ -1658,12 +1715,12 @@ def HandleCursor(msg: string)
ch_log('program stopped') ch_log('program stopped')
stopped = 1 stopped = 1
if msg =~ '^\*stopped,reason="exited-normally"' if msg =~ '^\*stopped,reason="exited-normally"'
running = 0 running = false
endif endif
elseif msg =~ '^\*running' elseif msg =~ '^\*running'
ch_log('program running') ch_log('program running')
stopped = 0 stopped = false
running = 1 running = true
endif endif
var fname = '' var fname = ''
@@ -1692,7 +1749,7 @@ def HandleCursor(msg: string)
endif endif
endif endif
if running && stopped && bufwinnr('Termdebug-variables-listing') != -1 if running && stopped && bufwinnr(varbufname) != -1
SendCommand('-stack-list-variables 2') SendCommand('-stack-list-variables 2')
endif endif
@@ -1709,13 +1766,13 @@ def HandleCursor(msg: string)
au SwapExists * echohl WarningMsg au SwapExists * echohl WarningMsg
| echo 'Warning: file is being edited elsewhere' | echo 'Warning: file is being edited elsewhere'
| echohl None | echohl None
| let v:swapchoice = 'o' | v:swapchoice = 'o'
augroup END augroup END
if &modified if &modified
# TODO: find existing window # TODO: find existing window
exe $'split {fnameescape(fname)}' exe $'split {fnameescape(fname)}'
sourcewin = win_getid() sourcewin = win_getid()
call InstallWinbar(0) InstallWinbar(0)
else else
exe $'edit {fnameescape(fname)}' exe $'edit {fnameescape(fname)}'
endif endif

View File

@@ -338,5 +338,16 @@ func Test_termdebug_bufnames()
unlet g:termdebug_config unlet g:termdebug_config
endfunc endfunc
function Test_termdebug_save_restore_variables()
let &mousemodel=''
Termdebug
call WaitForAssert({-> assert_equal(3, winnr('$'))})
call WaitForAssert({-> assert_match(&mousemodel, 'popup_setpos')})
wincmd t
quit!
call WaitForAssert({-> assert_equal(1, winnr('$'))})
call WaitForAssert({-> assert_true(empty(&mousemodel))})
endfunction
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
482,
/**/ /**/
481, 481,
/**/ /**/