2024-06-05 21:27:38 +02:00
|
|
|
vim9script
|
|
|
|
|
|
|
|
# Debugger plugin using gdb.
|
|
|
|
|
|
|
|
# Author: Bram Moolenaar
|
|
|
|
# Copyright: Vim license applies, see ":help license"
|
|
|
|
# Last Change: 2024 Jun 03
|
2024-06-05 22:24:24 +02:00
|
|
|
# Converted to Vim9: Ubaldo Tiberi <ubaldo.tiberi@gmail.com>
|
2024-06-05 21:27:38 +02:00
|
|
|
|
|
|
|
# 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
|
|
|
|
# Not used when g:termdebug_use_prompt is set to 1.
|
|
|
|
# - 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
|
|
|
|
# current statement from gdb.
|
|
|
|
|
|
|
|
# USING A TERMINAL WINDOW
|
|
|
|
|
|
|
|
# Opens two visible terminal windows:
|
|
|
|
# 1. runs a pty for the debugged program, as with ":term NONE"
|
|
|
|
# 2. runs gdb, passing the pty of the debugged program
|
|
|
|
# A third terminal window is hidden, it is used for communication with gdb.
|
|
|
|
|
|
|
|
# USING A PROMPT BUFFER
|
|
|
|
|
|
|
|
# Opens a window with a prompt buffer to communicate with gdb.
|
|
|
|
# Gdb is run as a job with callbacks for I/O.
|
|
|
|
# On Unix another terminal window is opened to run the debugged program
|
|
|
|
# On MS-Windows a separate console is opened to run the debugged program
|
|
|
|
|
|
|
|
# The communication with gdb uses GDB/MI. See:
|
|
|
|
# https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html
|
|
|
|
|
|
|
|
# In case this gets sourced twice.
|
|
|
|
if exists('g:termdebug_loaded')
|
2017-09-19 22:06:03 +02:00
|
|
|
finish
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
g:termdebug_loaded = true
|
|
|
|
|
|
|
|
var way = 'terminal'
|
|
|
|
var err = 'no errors'
|
|
|
|
|
|
|
|
var pc_id = 12
|
|
|
|
var asm_id = 13
|
|
|
|
var break_id = 14 # breakpoint number is added to this
|
|
|
|
var stopped = 1
|
|
|
|
var running = 0
|
|
|
|
|
|
|
|
var parsing_disasm_msg = 0
|
|
|
|
var asm_lines = []
|
|
|
|
var asm_addr = ''
|
|
|
|
|
|
|
|
# These shall be constants but cannot be initialized here
|
|
|
|
# They indicate the buffer numbers of the main buffers used
|
|
|
|
var gdbbuf = 0
|
|
|
|
var varbuf = 0
|
|
|
|
var asmbuf = 0
|
|
|
|
var promptbuf = 0
|
|
|
|
# This is for the "debugged program" thing
|
|
|
|
var ptybuf = 0
|
|
|
|
var commbuf = 0
|
|
|
|
|
|
|
|
var gdbjob = null_job
|
|
|
|
var gdb_channel = null_channel
|
|
|
|
# These changes because they relate to windows
|
|
|
|
var pid = 0
|
|
|
|
var gdbwin = 0
|
|
|
|
var varwin = 0
|
|
|
|
var asmwin = 0
|
|
|
|
var ptywin = 0
|
|
|
|
var 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}}
|
|
|
|
var breakpoints = {}
|
|
|
|
|
|
|
|
# Contains breakpoints by file/lnum. The key is "fname:lnum".
|
|
|
|
# Each entry is a list of breakpoint IDs at that position.
|
|
|
|
var breakpoint_locations = {}
|
|
|
|
var BreakpointSigns: list<string> = []
|
|
|
|
|
|
|
|
|
|
|
|
var evalFromBalloonExpr = 0
|
|
|
|
var evalFromBalloonExprResult = ''
|
|
|
|
var ignoreEvalError = 0
|
|
|
|
var 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.
|
|
|
|
var signcolumn_buflist = [bufnr()]
|
|
|
|
var save_columns = 0
|
|
|
|
|
|
|
|
var allleft = 0
|
|
|
|
# This was s:vertical but I cannot use vertical as variable name
|
|
|
|
var vvertical = 0
|
|
|
|
|
|
|
|
var winbar_winids = []
|
|
|
|
var plus_map_saved = {}
|
|
|
|
var minus_map_saved = {}
|
|
|
|
var k_map_saved = {}
|
|
|
|
var saved_mousemodel = ''
|
|
|
|
|
|
|
|
|
|
|
|
# Need either the +terminal feature or +channel and the prompt buffer.
|
|
|
|
# The terminal feature does not work with gdb on win32.
|
2018-06-17 21:34:11 +02:00
|
|
|
if has('terminal') && !has('win32')
|
2024-06-05 21:27:38 +02:00
|
|
|
way = 'terminal'
|
2018-06-17 21:34:11 +02:00
|
|
|
elseif has('channel') && exists('*prompt_setprompt')
|
2024-06-05 21:27:38 +02:00
|
|
|
way = 'prompt'
|
2018-06-17 21:34:11 +02:00
|
|
|
else
|
|
|
|
if has('terminal')
|
2024-06-05 21:27:38 +02:00
|
|
|
err = 'Cannot debug, missing prompt buffer support'
|
2018-06-17 21:34:11 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
err = 'Cannot debug, +channel feature is not supported'
|
2018-06-17 21:34:11 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
command -nargs=* -complete=file -bang Termdebug echoerr err
|
|
|
|
command -nargs=+ -complete=file -bang TermdebugCommand echoerr err
|
2018-06-17 21:34:11 +02:00
|
|
|
finish
|
|
|
|
endif
|
2017-11-12 18:02:06 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
|
|
|
|
# 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>)
|
|
|
|
|
|
|
|
|
|
|
|
# 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.
|
|
|
|
def Breakpoint2SignNumber(id: number, subid: number): number
|
|
|
|
return break_id + id * 1000 + subid
|
|
|
|
enddef
|
|
|
|
|
|
|
|
# Define or adjust the default highlighting, using background "new".
|
|
|
|
# When the 'background' option is set then "old" has the old value.
|
|
|
|
def Highlight(init: bool, old: string, new: string)
|
|
|
|
var default = init ? 'default ' : ''
|
|
|
|
if new ==# 'light' && old !=# 'light'
|
|
|
|
exe "hi " .. default .. "debugPC term=reverse ctermbg=lightblue guibg=lightblue"
|
|
|
|
elseif new ==# 'dark' && old !=# 'dark'
|
|
|
|
exe "hi " .. default .. "debugPC term=reverse ctermbg=darkblue guibg=darkblue"
|
|
|
|
endif
|
|
|
|
enddef
|
|
|
|
|
|
|
|
# Define the default highlighting, using the current 'background' value.
|
|
|
|
def InitHighlight()
|
|
|
|
Highlight(1, '', &background)
|
2023-06-24 14:20:36 +01:00
|
|
|
hi default debugBreakpoint term=reverse ctermbg=red guibg=red
|
|
|
|
hi default debugBreakpointDisabled term=reverse ctermbg=gray guibg=gray
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2023-06-24 14:20:36 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Setup an autocommand to redefine the default highlight when the colorscheme
|
|
|
|
# is changed.
|
|
|
|
def InitAutocmd()
|
2023-06-24 14:20:36 +01:00
|
|
|
augroup TermDebug
|
|
|
|
autocmd!
|
2024-06-05 21:27:38 +02:00
|
|
|
autocmd ColorScheme * InitHighlight()
|
2023-06-24 14:20:36 +01:00
|
|
|
augroup END
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2017-09-08 21:10:04 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Get the command to execute the debugger as a list, defaults to ["gdb"].
|
|
|
|
def GetCommand(): list<string>
|
|
|
|
var cmd = 'gdb'
|
2022-05-23 21:49:41 +01:00
|
|
|
if exists('g:termdebug_config')
|
2024-06-05 21:27:38 +02:00
|
|
|
cmd = get(g:termdebug_config, 'command', 'gdb')
|
2022-05-23 21:49:41 +01:00
|
|
|
elseif exists('g:termdebugger')
|
2024-06-05 21:27:38 +02:00
|
|
|
cmd = g:termdebugger
|
2022-05-23 21:49:41 +01:00
|
|
|
endif
|
|
|
|
|
|
|
|
return type(cmd) == v:t_list ? copy(cmd) : [cmd]
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
|
|
|
|
def Echoerr(msg: string)
|
|
|
|
echohl ErrorMsg | echom '[termdebug] ' .. msg | echohl None
|
|
|
|
enddef
|
2021-12-24 13:18:38 +00:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def StartDebug(bang: bool, ...gdb_args: list<string>)
|
|
|
|
# First argument is the command to debug, second core file or process ID.
|
|
|
|
StartDebug_internal({'gdb_args': gdb_args, 'bang': bang})
|
|
|
|
enddef
|
2023-08-21 02:07:49 +08:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def StartDebugCommand(bang: bool, ...args: list<string>)
|
|
|
|
# First argument is the command to debug, rest are run arguments.
|
2024-06-09 16:42:45 +02:00
|
|
|
StartDebug_internal({'gdb_args': [args[0]], 'proc_args': args[1 : ], 'bang': bang})
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2018-04-16 16:21:49 +02:00
|
|
|
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def StartDebug_internal(dict: dict<any>)
|
|
|
|
if gdbwin > 0
|
|
|
|
Echoerr('Terminal debugger already running, cannot run two')
|
2018-04-14 18:59:50 +02:00
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
var gdbcmd = GetCommand()
|
2021-12-24 13:18:38 +00:00
|
|
|
if !executable(gdbcmd[0])
|
2024-06-05 21:27:38 +02:00
|
|
|
Echoerr('Cannot execute debugger program "' .. gdbcmd[0] .. '"')
|
2019-09-30 20:47:54 +02:00
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
2021-08-14 21:25:52 +02:00
|
|
|
if exists('#User#TermdebugStartPre')
|
|
|
|
doauto <nomodeline> User TermdebugStartPre
|
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Assume current window is the source code window
|
|
|
|
sourcewin = win_getid()
|
|
|
|
var wide = 0
|
2020-10-26 21:12:46 +01:00
|
|
|
|
2022-05-23 21:49:41 +01:00
|
|
|
if exists('g:termdebug_config')
|
2024-06-05 21:27:38 +02:00
|
|
|
wide = get(g:termdebug_config, 'wide', 0)
|
2022-05-23 21:49:41 +01:00
|
|
|
elseif exists('g:termdebug_wide')
|
2024-06-05 21:27:38 +02:00
|
|
|
wide = g:termdebug_wide
|
2022-05-23 21:49:41 +01:00
|
|
|
endif
|
|
|
|
if wide > 0
|
|
|
|
if &columns < wide
|
2024-06-05 21:27:38 +02:00
|
|
|
save_columns = &columns
|
|
|
|
&columns = wide
|
|
|
|
# If we make the Vim window wider, use the whole left half for the debug
|
|
|
|
# windows.
|
|
|
|
allleft = 1
|
2017-09-27 22:23:55 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
vvertical = 1
|
2018-06-17 21:34:11 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
vvertical = 0
|
2018-06-17 21:34:11 +02:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Override using a terminal window by setting g:termdebug_use_prompt to 1.
|
|
|
|
var use_prompt = 0
|
2022-05-23 21:49:41 +01:00
|
|
|
if exists('g:termdebug_config')
|
2024-06-05 21:27:38 +02:00
|
|
|
use_prompt = get(g:termdebug_config, 'use_prompt', 0)
|
2022-05-23 21:49:41 +01:00
|
|
|
elseif exists('g:termdebug_use_prompt')
|
2024-06-05 21:27:38 +02:00
|
|
|
use_prompt = g:termdebug_use_prompt
|
2022-05-23 21:49:41 +01:00
|
|
|
endif
|
2018-06-17 21:34:11 +02:00
|
|
|
if has('terminal') && !has('win32') && !use_prompt
|
2024-06-05 21:27:38 +02:00
|
|
|
way = 'terminal'
|
2017-09-14 16:10:38 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
way = 'prompt'
|
2017-09-14 16:10:38 +02:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if way == 'prompt'
|
|
|
|
StartDebug_prompt(dict)
|
2018-06-17 21:34:11 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
StartDebug_term(dict)
|
2018-06-17 21:34:11 +02:00
|
|
|
endif
|
2021-01-11 19:40:15 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if GetDisasmWindow()
|
|
|
|
var curwinid = win_getid()
|
|
|
|
GotoAsmwinOrCreateIt()
|
|
|
|
win_gotoid(curwinid)
|
2021-01-11 19:40:15 +01:00
|
|
|
endif
|
2021-08-14 21:25:52 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if GetVariablesWindow()
|
|
|
|
var curwinid = win_getid()
|
|
|
|
GotoVariableswinOrCreateIt()
|
|
|
|
win_gotoid(curwinid)
|
2023-05-13 16:29:15 +02:00
|
|
|
endif
|
|
|
|
|
2021-08-14 21:25:52 +02:00
|
|
|
if exists('#User#TermdebugStartPost')
|
|
|
|
doauto <nomodeline> User TermdebugStartPost
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Use when debugger didn't start or ended.
|
|
|
|
def CloseBuffers()
|
|
|
|
exe 'bwipe! ' .. ptybuf
|
|
|
|
exe 'bwipe! ' .. commbuf
|
|
|
|
if asmbuf > 0 && bufexists(asmbuf)
|
|
|
|
exe 'bwipe! ' .. asmbuf
|
2023-05-13 16:29:15 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
if varbuf > 0 && bufexists(varbuf)
|
|
|
|
exe 'bwipe! ' .. varbuf
|
2023-05-13 16:29:15 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
running = 0
|
|
|
|
gdbwin = 0
|
|
|
|
enddef
|
2018-12-22 15:14:49 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# IsGdbRunning(): bool may be a better name?
|
|
|
|
def CheckGdbRunning(): string
|
|
|
|
var gdbproc = term_getjob(gdbbuf)
|
|
|
|
var gdbproc_status = 'unknown'
|
|
|
|
if type(gdbproc) == v:t_job
|
|
|
|
gdbproc_status = job_status(gdbproc)
|
|
|
|
endif
|
|
|
|
if gdbproc == v:null || gdbproc_status !=# 'run'
|
|
|
|
Echoerr(string(GetCommand()[0]) .. ' exited unexpectedly')
|
|
|
|
CloseBuffers()
|
2021-05-30 20:54:13 +02:00
|
|
|
return ''
|
|
|
|
endif
|
|
|
|
return 'ok'
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
|
|
|
|
# Open a terminal window without a job, to run the debugged program in.
|
|
|
|
def StartDebug_term(dict: dict<any>)
|
|
|
|
ptybuf = term_start('NONE', {
|
|
|
|
\ 'term_name': 'debugged program',
|
|
|
|
\ 'vertical': vvertical,
|
|
|
|
\ })
|
|
|
|
if ptybuf == 0
|
|
|
|
Echoerr('Failed to open the program terminal window')
|
2017-09-08 21:10:04 +02:00
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
var pty = job_info(term_getjob(ptybuf))['tty_out']
|
|
|
|
ptywin = win_getid()
|
|
|
|
if vvertical
|
|
|
|
# Assuming the source code window will get a signcolumn, use two more
|
|
|
|
# columns for that, thus one less for the terminal window.
|
|
|
|
exe ":" .. (&columns / 2 - 1) .. "wincmd |"
|
|
|
|
if allleft
|
|
|
|
# use the whole left column
|
2019-05-26 21:33:31 +02:00
|
|
|
wincmd H
|
|
|
|
endif
|
2017-11-18 18:52:04 +01:00
|
|
|
endif
|
2017-09-08 21:10:04 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Create a hidden terminal window to communicate with gdb
|
|
|
|
commbuf = term_start('NONE', {
|
|
|
|
\ 'term_name': 'gdb communication',
|
|
|
|
\ 'out_cb': function('CommOutput'),
|
|
|
|
\ 'hidden': 1,
|
|
|
|
\ })
|
|
|
|
if commbuf == 0
|
|
|
|
Echoerr('Failed to open the communication terminal window')
|
|
|
|
exe 'bwipe! ' .. ptybuf
|
2017-09-08 21:10:04 +02:00
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
var commpty = job_info(term_getjob(commbuf))['tty_out']
|
2017-08-27 16:52:01 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var gdb_args = get(dict, 'gdb_args', [])
|
|
|
|
var proc_args = get(dict, 'proc_args', [])
|
2018-04-16 16:21:49 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var gdb_cmd = GetCommand()
|
2022-05-23 21:49:41 +01:00
|
|
|
|
|
|
|
if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_add_args')
|
2024-06-05 21:27:38 +02:00
|
|
|
gdb_cmd = g:termdebug_config.command_add_args(gdb_cmd, pty)
|
2022-05-23 21:49:41 +01:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
# 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
|
|
|
|
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).
|
|
|
|
gdb_cmd += ['-iex', 'set mi-async on']
|
|
|
|
# Open a terminal window to run the debugger.
|
|
|
|
gdb_cmd += ['-tty', pty]
|
|
|
|
# Command executed _after_ startup is done, provides us with the necessary
|
|
|
|
# feedback
|
|
|
|
gdb_cmd += ['-ex', 'echo startupdone\n']
|
2022-05-23 21:49:41 +01:00
|
|
|
endif
|
|
|
|
|
|
|
|
if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_filter')
|
2024-06-05 21:27:38 +02:00
|
|
|
gdb_cmd = g:termdebug_config.command_filter(gdb_cmd)
|
2022-05-23 21:49:41 +01:00
|
|
|
endif
|
2021-11-27 10:57:26 +00:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Adding arguments requested by the user
|
|
|
|
gdb_cmd += gdb_args
|
2021-11-27 10:57:26 +00:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
ch_log('executing "' .. join(gdb_cmd) .. '"')
|
|
|
|
gdbbuf = term_start(gdb_cmd, {
|
|
|
|
\ 'term_name': 'gdb',
|
|
|
|
\ 'term_finish': 'close',
|
|
|
|
\ })
|
|
|
|
if gdbbuf == 0
|
|
|
|
Echoerr('Failed to open the gdb terminal window')
|
|
|
|
CloseBuffers()
|
2017-09-08 21:10:04 +02:00
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
gdbwin = win_getid()
|
2017-09-08 21:10:04 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Wait for the "startupdone" message before sending any commands.
|
|
|
|
var counter = 0
|
|
|
|
var counter_max = 300
|
|
|
|
var success = false
|
|
|
|
while success == false && counter < counter_max
|
|
|
|
if CheckGdbRunning() != 'ok'
|
|
|
|
# Failure. If NOK just return.
|
2021-05-30 20:54:13 +02:00
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
for lnum in range(1, 200)
|
2024-06-05 21:27:38 +02:00
|
|
|
if term_getline(gdbbuf, lnum) =~ 'startupdone'
|
|
|
|
success = true
|
2021-05-30 20:54:13 +02:00
|
|
|
endif
|
|
|
|
endfor
|
2024-06-05 21:27:38 +02:00
|
|
|
|
|
|
|
# Each count is 10ms
|
|
|
|
counter += 1
|
2021-05-30 20:54:13 +02:00
|
|
|
sleep 10m
|
|
|
|
endwhile
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if success == false
|
|
|
|
Echoerr('Failed to startup the gdb program.')
|
|
|
|
CloseBuffers()
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
# ---- gdb started. Next, let's set the MI interface. ---
|
|
|
|
# Set arguments to be run.
|
2018-04-16 16:21:49 +02:00
|
|
|
if len(proc_args)
|
2024-06-05 21:27:38 +02:00
|
|
|
term_sendkeys(gdbbuf, 'server set args ' .. join(proc_args) .. "\r")
|
2018-04-16 16:21:49 +02:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Connect gdb to the communication pty, using the GDB/MI interface.
|
|
|
|
# Prefix "server" to avoid adding this to the history.
|
|
|
|
term_sendkeys(gdbbuf, 'server new-ui mi ' .. commpty .. "\r")
|
2017-11-12 18:02:06 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Wait for the response to show up, users may not notice the error and wonder
|
|
|
|
# why the debugger doesn't work.
|
|
|
|
counter = 0
|
|
|
|
counter_max = 300
|
|
|
|
success = false
|
|
|
|
while success == false && counter < counter_max
|
|
|
|
if CheckGdbRunning() != 'ok'
|
2018-12-22 15:14:49 +01:00
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var response = ''
|
2019-09-04 14:24:24 +02:00
|
|
|
for lnum in range(1, 200)
|
2024-06-05 21:27:38 +02:00
|
|
|
var line1 = term_getline(gdbbuf, lnum)
|
|
|
|
var line2 = term_getline(gdbbuf, lnum + 1)
|
2019-09-04 14:24:24 +02:00
|
|
|
if line1 =~ 'new-ui mi '
|
2024-06-05 21:27:38 +02:00
|
|
|
# response can be in the same line or the next line
|
|
|
|
response = line1 .. line2
|
2021-11-27 10:57:26 +00:00
|
|
|
if response =~ 'Undefined command'
|
2024-06-05 21:27:38 +02:00
|
|
|
Echoerr('Sorry, your gdb is too old, gdb 7.12 is required')
|
|
|
|
# CHECKME: possibly send a "server show version" here
|
|
|
|
CloseBuffers()
|
2021-11-27 10:57:26 +00:00
|
|
|
return
|
|
|
|
endif
|
|
|
|
if response =~ 'New UI allocated'
|
2024-06-05 21:27:38 +02:00
|
|
|
# Success!
|
|
|
|
success = true
|
2021-11-27 10:57:26 +00:00
|
|
|
endif
|
2019-09-04 14:24:24 +02:00
|
|
|
elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi '
|
2024-06-05 21:27:38 +02:00
|
|
|
# Reading symbols might take a while, try more times
|
|
|
|
counter -= 1
|
2018-03-11 20:51:52 +01:00
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
if response =~ 'New UI allocated'
|
|
|
|
break
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
counter += 1
|
2018-03-11 20:51:52 +01:00
|
|
|
sleep 10m
|
|
|
|
endwhile
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if success == false
|
|
|
|
Echoerr('Cannot check if your gdb works, continuing anyway')
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
job_setoptions(term_getjob(gdbbuf), {'exit_cb': function('EndTermDebug')})
|
2021-08-29 21:55:35 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Set the filetype, this can be used to add mappings.
|
2021-08-29 21:55:35 +02:00
|
|
|
set filetype=termdebug
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
StartDebugCommon(dict)
|
|
|
|
enddef
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Open a window with a prompt buffer to run gdb in.
|
|
|
|
def StartDebug_prompt(dict: dict<any>)
|
|
|
|
if vvertical
|
2018-06-17 21:34:11 +02:00
|
|
|
vertical new
|
|
|
|
else
|
|
|
|
new
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
gdbwin = win_getid()
|
|
|
|
promptbuf = bufnr('')
|
|
|
|
prompt_setprompt(promptbuf, 'gdb> ')
|
2018-06-17 21:34:11 +02:00
|
|
|
set buftype=prompt
|
2024-05-21 23:33:03 +02:00
|
|
|
|
|
|
|
if empty(glob('gdb'))
|
|
|
|
file gdb
|
|
|
|
elseif empty(glob('Termdebug-gdb-console'))
|
|
|
|
file Termdebug-gdb-console
|
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
Echoerr("You have a file/folder named 'gdb'
|
2024-05-21 23:33:03 +02:00
|
|
|
\ or 'Termdebug-gdb-console'.
|
|
|
|
\ Please exit and rename them because Termdebug may not work as expected.")
|
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
prompt_setcallback(promptbuf, function('PromptCallback'))
|
|
|
|
prompt_setinterrupt(promptbuf, function('PromptInterrupt'))
|
|
|
|
|
|
|
|
if vvertical
|
|
|
|
# Assuming the source code window will get a signcolumn, use two more
|
|
|
|
# columns for that, thus one less for the terminal window.
|
|
|
|
exe ":" .. (&columns / 2 - 1) .. "wincmd |"
|
|
|
|
endif
|
|
|
|
|
|
|
|
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
|
|
|
|
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).
|
|
|
|
gdb_cmd += ['-iex', 'set mi-async on']
|
|
|
|
# directly communicate via mi2
|
|
|
|
gdb_cmd += ['--interpreter=mi2']
|
|
|
|
|
|
|
|
# Adding arguments requested by the user
|
|
|
|
gdb_cmd += gdb_args
|
|
|
|
|
|
|
|
ch_log('executing "' .. join(gdb_cmd) .. '"')
|
|
|
|
gdbjob = job_start(gdb_cmd, {
|
|
|
|
\ 'exit_cb': function('EndPromptDebug'),
|
|
|
|
\ 'out_cb': function('GdbOutCallback'),
|
|
|
|
\ })
|
|
|
|
if job_status(gdbjob) != "run"
|
|
|
|
Echoerr('Failed to start gdb')
|
|
|
|
exe 'bwipe! ' .. promptbuf
|
2018-06-17 21:34:11 +02:00
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
exe $'au BufUnload <buffer={promptbuf}> ++once ' ..
|
|
|
|
\ 'call job_stop(gdbjob, ''kill'')'
|
|
|
|
# Mark the buffer modified so that it's not easy to close.
|
2018-06-20 22:38:21 +02:00
|
|
|
set modified
|
2024-06-05 21:27:38 +02:00
|
|
|
gdb_channel = job_getchannel(gdbjob)
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
ptybuf = 0
|
2018-06-17 21:34:11 +02:00
|
|
|
if has('win32')
|
2024-06-05 21:27:38 +02:00
|
|
|
# MS-Windows: run in a new console window for maximum compatibility
|
|
|
|
SendCommand('set new-console on')
|
2018-06-17 21:34:11 +02:00
|
|
|
elseif has('terminal')
|
2024-06-05 21:27:38 +02:00
|
|
|
# Unix: Run the debugged program in a terminal window. Open it below the
|
|
|
|
# gdb window.
|
|
|
|
belowright ptybuf = term_start('NONE', {
|
|
|
|
\ 'term_name': 'debugged program',
|
|
|
|
\ })
|
|
|
|
if ptybuf == 0
|
|
|
|
Echoerr('Failed to open the program terminal window')
|
|
|
|
job_stop(gdbjob)
|
2018-06-17 21:34:11 +02:00
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
ptywin = win_getid()
|
|
|
|
var pty = job_info(term_getjob(ptybuf))['tty_out']
|
|
|
|
SendCommand('tty ' .. pty)
|
|
|
|
|
|
|
|
# Since GDB runs in a prompt window, the environment has not been set to
|
|
|
|
# match a terminal window, need to do that now.
|
|
|
|
SendCommand('set env TERM = xterm-color')
|
|
|
|
SendCommand('set env ROWS = ' .. winheight(ptywin))
|
|
|
|
SendCommand('set env LINES = ' .. winheight(ptywin))
|
|
|
|
SendCommand('set env COLUMNS = ' .. winwidth(ptywin))
|
|
|
|
SendCommand('set env COLORS = ' .. &t_Co)
|
|
|
|
SendCommand('set env VIM_TERMINAL = ' .. v:version)
|
2018-06-17 21:34:11 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
# TODO: open a new terminal, get the tty name, pass on to gdb
|
|
|
|
SendCommand('show inferior-tty')
|
2018-06-17 21:34:11 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
SendCommand('set print pretty on')
|
|
|
|
SendCommand('set breakpoint pending on')
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Set arguments to be run
|
2018-06-17 21:34:11 +02:00
|
|
|
if len(proc_args)
|
2024-06-05 21:27:38 +02:00
|
|
|
SendCommand('set args ' .. join(proc_args))
|
2018-06-17 21:34:11 +02:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
StartDebugCommon(dict)
|
2018-06-17 21:34:11 +02:00
|
|
|
startinsert
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def StartDebugCommon(dict: dict<any>)
|
|
|
|
# Sign used to highlight the line where the program has stopped.
|
|
|
|
# There can be only one.
|
|
|
|
sign_define('debugPC', {'linehl': 'debugPC'})
|
2017-09-14 16:10:38 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Install debugger commands in the text window.
|
|
|
|
win_gotoid(sourcewin)
|
|
|
|
InstallCommands()
|
|
|
|
win_gotoid(gdbwin)
|
2017-09-09 22:19:47 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Enable showing a balloon with eval info
|
2017-11-19 19:56:27 +01:00
|
|
|
if has("balloon_eval") || has("balloon_eval_term")
|
2017-11-18 18:52:04 +01:00
|
|
|
set balloonexpr=TermDebugBalloonExpr()
|
2017-11-19 19:56:27 +01:00
|
|
|
if has("balloon_eval")
|
|
|
|
set ballooneval
|
|
|
|
endif
|
2017-11-18 18:52:04 +01:00
|
|
|
if has("balloon_eval_term")
|
|
|
|
set balloonevalterm
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
|
2017-09-17 23:03:31 +02:00
|
|
|
augroup TermDebug
|
2024-06-05 21:27:38 +02:00
|
|
|
au BufRead * BufRead()
|
|
|
|
au BufUnload * BufUnloaded()
|
|
|
|
au OptionSet background Highlight(0, v:option_old, v:option_new)
|
2017-09-17 23:03:31 +02:00
|
|
|
augroup END
|
2018-04-16 16:21:49 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Run the command if the bang attribute was given and got to the debug
|
|
|
|
# window.
|
|
|
|
if get(dict, 'bang', 0)
|
|
|
|
SendResumingCommand('-exec-run')
|
|
|
|
win_gotoid(ptywin)
|
2018-04-16 16:21:49 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2018-04-16 16:21:49 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# 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'
|
|
|
|
ch_sendraw(gdb_channel, cmd .. "\n")
|
2018-06-17 21:34:11 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
term_sendkeys(commbuf, cmd .. "\r")
|
2018-06-17 21:34:11 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2017-08-27 16:52:01 +02:00
|
|
|
|
2024-06-09 16:42:45 +02:00
|
|
|
# Interrupt or stop the program
|
|
|
|
def StopCommand()
|
|
|
|
if way == 'prompt'
|
|
|
|
PromptInterrupt()
|
|
|
|
else
|
|
|
|
SendCommand('-exec-interrupt')
|
|
|
|
endif
|
|
|
|
enddef
|
|
|
|
|
|
|
|
# Continue the program
|
|
|
|
def ContinueCommand()
|
|
|
|
if way == 'prompt'
|
|
|
|
SendCommand('continue')
|
|
|
|
else
|
|
|
|
# using -exec-continue results in CTRL-C in the gdb window not working,
|
|
|
|
# communicating via commbuf (= use of SendCommand) has the same result
|
|
|
|
SendCommand('-exec-continue')
|
|
|
|
# command Continue term_sendkeys(gdbbuf, "continue\r")
|
|
|
|
endif
|
|
|
|
enddef
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# This is global so that a user can create their mappings with this.
|
|
|
|
def TermDebugSendCommand(cmd: string)
|
|
|
|
if way == 'prompt'
|
|
|
|
ch_sendraw(gdb_channel, cmd .. "\n")
|
2018-06-17 21:34:11 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
var do_continue = 0
|
|
|
|
if !stopped
|
|
|
|
do_continue = 1
|
2024-06-09 16:42:45 +02:00
|
|
|
StopCommand()
|
2018-06-17 21:34:11 +02:00
|
|
|
sleep 10m
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
# TODO: should we prepend CTRL-U to clear the command?
|
|
|
|
term_sendkeys(gdbbuf, cmd .. "\r")
|
2018-06-17 21:34:11 +02:00
|
|
|
if do_continue
|
2024-06-09 16:42:45 +02:00
|
|
|
ContinueCommand()
|
2018-06-17 21:34:11 +02:00
|
|
|
endif
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
|
|
|
|
# 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 stopped.
|
|
|
|
def SendResumingCommand(cmd: string)
|
|
|
|
if stopped
|
|
|
|
# reset stopped here, it may take a bit of time before we get a response
|
|
|
|
stopped = 0
|
|
|
|
ch_log('assume that program is running after this command')
|
|
|
|
SendCommand(cmd)
|
2021-11-27 10:57:26 +00:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
ch_log('dropping command, program is running: ' .. cmd)
|
2021-11-27 10:57:26 +00:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2021-11-27 10:57:26 +00:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Function called when entering a line in the prompt buffer.
|
|
|
|
def PromptCallback(text: string)
|
|
|
|
SendCommand(text)
|
|
|
|
enddef
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Function called when pressing CTRL-C in the prompt buffer and when placing a
|
|
|
|
# breakpoint.
|
|
|
|
def PromptInterrupt()
|
|
|
|
ch_log('Interrupting gdb')
|
2018-06-21 20:31:14 +02:00
|
|
|
if has('win32')
|
2024-06-05 21:27:38 +02:00
|
|
|
# Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
|
|
|
|
# the debugger program so that gdb responds again.
|
|
|
|
if pid == 0
|
|
|
|
Echoerr('Cannot interrupt gdb, did not find a process ID')
|
2018-06-21 20:31:14 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
debugbreak(pid)
|
2018-06-21 20:31:14 +02:00
|
|
|
endif
|
2018-06-20 22:38:21 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
job_stop(gdbjob, 'int')
|
2018-06-20 22:38:21 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Function called when gdb outputs text.
|
|
|
|
def GdbOutCallback(channel: channel, text: string)
|
|
|
|
ch_log('received from gdb: ' .. text)
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Disassembly messages need to be forwarded as-is.
|
|
|
|
if parsing_disasm_msg > 0
|
|
|
|
CommOutput(channel, text)
|
2023-08-22 19:30:29 +01:00
|
|
|
return
|
2024-06-05 21:27:38 +02:00
|
|
|
endif
|
2023-08-22 19:30:29 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Drop the gdb prompt, we have our own.
|
|
|
|
# Drop status and echo'd commands.
|
|
|
|
if text == '(gdb) ' || text == '^done' ||
|
|
|
|
\ (text[0] == '&' && text !~ '^&"disassemble')
|
2018-06-17 21:34:11 +02:00
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
|
|
|
|
var decoded_text = ''
|
|
|
|
if text =~ '^\^error,msg='
|
|
|
|
decoded_text = DecodeMessage(text[11 : ], false)
|
|
|
|
if exists('evalexpr') && decoded_text =~ 'A syntax error in expression, near\|No symbol .* in current context'
|
|
|
|
# Silently drop evaluation errors.
|
|
|
|
evalexpr = null_string
|
2018-06-17 21:34:11 +02:00
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
elseif text[0] == '~'
|
|
|
|
decoded_text = DecodeMessage(text[1 : ], false)
|
2018-06-17 21:34:11 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
CommOutput(channel, text)
|
2018-06-17 21:34:11 +02:00
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var curwinid = win_getid()
|
|
|
|
win_gotoid(gdbwin)
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Add the output above the current prompt.
|
|
|
|
append(line('$') - 1, decoded_text)
|
2018-06-20 22:38:21 +02:00
|
|
|
set modified
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
win_gotoid(curwinid)
|
|
|
|
enddef
|
|
|
|
|
|
|
|
# Decode a message from gdb. "quotedText" starts with a ", return the text up
|
|
|
|
# to the next unescaped ", unescaping characters:
|
|
|
|
# - remove line breaks (unless "literal" is v:true)
|
|
|
|
# - change \" to "
|
|
|
|
# - change \\t to \t (unless "literal" is v:true)
|
|
|
|
# - change \0xhh to \xhh (disabled for now)
|
|
|
|
# - change \ooo to octal
|
|
|
|
# - change \\ to \
|
|
|
|
def DecodeMessage(quotedText: string, literal: bool): string
|
|
|
|
if quotedText[0] != '"'
|
|
|
|
Echoerr('DecodeMessage(): missing quote in ' .. quotedText)
|
|
|
|
return ''
|
2018-06-17 21:34:11 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
var msg = quotedText
|
2023-08-22 19:30:29 +01:00
|
|
|
\ ->substitute('^"\|[^\\]\zs".*', '', 'g')
|
|
|
|
\ ->substitute('\\"', '"', 'g')
|
2024-06-05 21:27:38 +02:00
|
|
|
#\ 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')
|
|
|
|
#\ 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')
|
2021-12-16 14:41:10 +00:00
|
|
|
\ ->substitute('\\\\', '\', 'g')
|
2024-06-05 21:27:38 +02:00
|
|
|
\ ->substitute(NullRepl, '\\000', 'g')
|
|
|
|
if !literal
|
2023-08-22 19:30:29 +01:00
|
|
|
return msg
|
2024-06-05 21:27:38 +02:00
|
|
|
\ ->substitute('\\t', "\t", 'g')
|
|
|
|
\ ->substitute('\\n', '', 'g')
|
2023-06-24 20:02:25 +01:00
|
|
|
else
|
2023-08-22 19:30:29 +01:00
|
|
|
return msg
|
2023-06-24 20:02:25 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
const NullRepl = 'XXXNULLXXX'
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Extract the "name" value from a gdb message with fullname="name".
|
|
|
|
def GetFullname(msg: string): string
|
|
|
|
if msg !~ 'fullname'
|
2018-12-02 13:47:03 +01:00
|
|
|
return ''
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
|
|
|
|
var name = DecodeMessage(substitute(msg, '.*fullname=', '', ''), true)
|
2018-06-19 22:34:46 +02:00
|
|
|
if has('win32') && name =~ ':\\\\'
|
2024-06-05 21:27:38 +02:00
|
|
|
# sometimes the name arrives double-escaped
|
|
|
|
name = substitute(name, '\\\\', '\\', 'g')
|
2018-06-19 22:34:46 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
|
2018-06-19 22:34:46 +02:00
|
|
|
return name
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2018-06-19 22:34:46 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Extract the "addr" value from a gdb message with addr="0x0001234".
|
|
|
|
def GetAsmAddr(msg: string): string
|
|
|
|
if msg !~ 'addr='
|
2021-01-11 19:40:15 +01:00
|
|
|
return ''
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
|
|
|
|
var addr = DecodeMessage(substitute(msg, '.*addr=', '', ''), false)
|
2021-01-11 19:40:15 +01:00
|
|
|
return addr
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
|
2021-08-14 21:25:52 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def EndTermDebug(job: any, status: any)
|
2021-08-14 21:25:52 +02:00
|
|
|
if exists('#User#TermdebugStopPre')
|
|
|
|
doauto <nomodeline> User TermdebugStopPre
|
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if bufexists(commbuf)
|
|
|
|
exe 'bwipe! ' .. commbuf
|
|
|
|
endif
|
|
|
|
gdbwin = 0
|
|
|
|
EndDebugCommon()
|
|
|
|
enddef
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def EndDebugCommon()
|
|
|
|
var curwinid = win_getid()
|
2017-09-09 22:19:47 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if bufexists(ptybuf)
|
|
|
|
exe 'bwipe! ' .. ptybuf
|
2018-06-17 21:34:11 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
if bufexists(asmbuf)
|
|
|
|
exe 'bwipe! ' .. asmbuf
|
2023-08-22 19:30:29 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
if bufexists(varbuf)
|
|
|
|
exe 'bwipe! ' .. varbuf
|
2023-08-22 19:30:29 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
running = 0
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Restore 'signcolumn' in all buffers for which it was set.
|
|
|
|
win_gotoid(sourcewin)
|
|
|
|
var was_buf = bufnr()
|
|
|
|
for bufnr in signcolumn_buflist
|
2020-10-26 21:12:46 +01:00
|
|
|
if bufexists(bufnr)
|
2024-06-05 21:27:38 +02:00
|
|
|
exe ":" .. bufnr .. "buf"
|
2020-10-26 21:12:46 +01:00
|
|
|
if exists('b:save_signcolumn')
|
2024-06-05 21:27:38 +02:00
|
|
|
&signcolumn = b:save_signcolumn
|
2022-03-19 15:18:53 +00:00
|
|
|
unlet b:save_signcolumn
|
2020-10-26 21:12:46 +01:00
|
|
|
endif
|
|
|
|
endif
|
|
|
|
endfor
|
2022-04-18 15:36:40 +01:00
|
|
|
if bufexists(was_buf)
|
2024-06-05 21:27:38 +02:00
|
|
|
exe ":" .. was_buf .. "buf"
|
2022-04-18 15:36:40 +01:00
|
|
|
endif
|
2020-10-26 21:12:46 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
DeleteCommands()
|
2017-09-09 22:19:47 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
win_gotoid(curwinid)
|
2018-06-17 21:34:11 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if save_columns > 0
|
|
|
|
&columns = save_columns
|
2017-09-14 16:10:38 +02:00
|
|
|
endif
|
2017-09-17 23:03:31 +02:00
|
|
|
|
2017-11-19 19:56:27 +01:00
|
|
|
if has("balloon_eval") || has("balloon_eval_term")
|
2017-11-18 18:52:04 +01:00
|
|
|
set balloonexpr=
|
2017-11-19 19:56:27 +01:00
|
|
|
if has("balloon_eval")
|
|
|
|
set noballooneval
|
|
|
|
endif
|
2017-11-18 18:52:04 +01:00
|
|
|
if has("balloon_eval_term")
|
|
|
|
set noballoonevalterm
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
|
2021-08-14 21:25:52 +02:00
|
|
|
if exists('#User#TermdebugStopPost')
|
|
|
|
doauto <nomodeline> User TermdebugStopPost
|
|
|
|
endif
|
|
|
|
|
2017-09-17 23:03:31 +02:00
|
|
|
au! TermDebug
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2017-09-08 21:10:04 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def EndPromptDebug(job: any, status: any)
|
2021-08-14 21:25:52 +02:00
|
|
|
if exists('#User#TermdebugStopPre')
|
|
|
|
doauto <nomodeline> User TermdebugStopPre
|
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
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"
|
|
|
|
# - 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\n"
|
|
|
|
# - CommOutput: &"No function contains specified address.\n"
|
|
|
|
# - CommOutput: ^error,msg="No function contains specified address."
|
|
|
|
def HandleDisasmMsg(msg: string)
|
|
|
|
if msg =~ '^\^done'
|
|
|
|
var curwinid = win_getid()
|
|
|
|
if win_gotoid(asmwin)
|
|
|
|
silent! :%delete _
|
|
|
|
setline(1, asm_lines)
|
2021-01-11 19:40:15 +01:00
|
|
|
set nomodified
|
|
|
|
set filetype=asm
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var lnum = search('^' .. asm_addr)
|
2021-01-11 19:40:15 +01:00
|
|
|
if lnum != 0
|
2024-06-05 21:27:38 +02:00
|
|
|
sign_unplace('TermDebug', {'id': asm_id})
|
|
|
|
sign_place(asm_id, 'TermDebug', 'debugPC', '%', {'lnum': lnum})
|
2021-01-11 19:40:15 +01:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
win_gotoid(curwinid)
|
2021-01-11 19:40:15 +01:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
parsing_disasm_msg = 0
|
|
|
|
asm_lines = []
|
|
|
|
|
|
|
|
elseif msg =~ '^\^error,msg='
|
|
|
|
if 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
|
|
|
|
SendCommand('disassemble $pc,+100')
|
2021-01-11 19:40:15 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
parsing_disasm_msg = 0
|
|
|
|
elseif msg =~ '^&"disassemble \$pc'
|
|
|
|
if msg =~ '+100'
|
|
|
|
# This is our second disasm attempt
|
|
|
|
parsing_disasm_msg = 2
|
2021-01-11 19:40:15 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
elseif msg !~ '^&"disassemble'
|
|
|
|
var value = substitute(msg, '^\~\"[ ]*', '', '')
|
|
|
|
value = substitute(value, '^=>[ ]*', '', '')
|
|
|
|
value = substitute(value, '\\n\"\r$', '', '')
|
|
|
|
value = substitute(value, '\\n\"$', '', '')
|
|
|
|
value = substitute(value, '\r', '', '')
|
|
|
|
value = substitute(value, '\\t', ' ', 'g')
|
|
|
|
|
|
|
|
if value != '' || !empty(asm_lines)
|
|
|
|
add(asm_lines, value)
|
2021-01-11 19:40:15 +01:00
|
|
|
endif
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
|
|
|
|
|
|
|
|
def ParseVarinfo(varinfo: string): dict<any>
|
|
|
|
var dict = {}
|
|
|
|
var nameIdx = matchstrpos(varinfo, '{name="\([^"]*\)"')
|
|
|
|
dict['name'] = varinfo[nameIdx[1] + 7 : nameIdx[2] - 2]
|
|
|
|
var typeIdx = matchstrpos(varinfo, ',type="\([^"]*\)"')
|
|
|
|
# 'type' maybe is a url-like string,
|
|
|
|
# try to shorten it and show only the /tail
|
|
|
|
dict['type'] = (varinfo[typeIdx[1] + 7 : typeIdx[2] - 2])->fnamemodify(':t')
|
|
|
|
var valueIdx = matchstrpos(varinfo, ',value="\(.*\)"}')
|
2023-05-13 16:29:15 +02:00
|
|
|
if valueIdx[1] == -1
|
2024-06-05 21:27:38 +02:00
|
|
|
dict['value'] = 'Complex value'
|
2023-05-13 16:29:15 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
dict['value'] = varinfo[valueIdx[1] + 8 : valueIdx[2] - 3]
|
2023-05-13 16:29:15 +02:00
|
|
|
endif
|
|
|
|
return dict
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
|
|
|
|
def HandleVariablesMsg(msg: string)
|
|
|
|
var curwinid = win_getid()
|
|
|
|
if win_gotoid(varwin)
|
|
|
|
silent! :%delete _
|
|
|
|
var spaceBuffer = 20
|
|
|
|
setline(1, 'Type' ..
|
|
|
|
\ repeat(' ', 16) ..
|
|
|
|
\ 'Name' ..
|
|
|
|
\ repeat(' ', 16) ..
|
|
|
|
\ 'Value')
|
|
|
|
var cnt = 1
|
|
|
|
var capture = '{name=".\{-}",\%(arg=".\{-}",\)\{0,1\}type=".\{-}"\%(,value=".\{-}"\)\{0,1\}}'
|
|
|
|
var varinfo = matchstr(msg, capture, 0, cnt)
|
|
|
|
|
2023-05-13 16:29:15 +02:00
|
|
|
while varinfo != ''
|
2024-06-05 21:27:38 +02:00
|
|
|
var vardict = ParseVarinfo(varinfo)
|
|
|
|
setline(cnt + 1, vardict['type'] ..
|
|
|
|
\ repeat(' ', max([20 - len(vardict['type']), 1])) ..
|
|
|
|
\ vardict['name'] ..
|
|
|
|
\ repeat(' ', max([20 - len(vardict['name']), 1])) ..
|
|
|
|
\ vardict['value'])
|
|
|
|
cnt += 1
|
|
|
|
varinfo = matchstr(msg, capture, 0, cnt)
|
2023-05-13 16:29:15 +02:00
|
|
|
endwhile
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
win_gotoid(curwinid)
|
|
|
|
enddef
|
|
|
|
|
2023-05-13 16:29:15 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Handle a message received from gdb on the GDB/MI interface.
|
|
|
|
def CommOutput(chan: channel, message: string)
|
|
|
|
# We may use the standard MI message formats? See #10300 on github that mentions
|
|
|
|
# the following links:
|
|
|
|
# https://sourceware.org/gdb/current/onlinedocs/gdb.html/GDB_002fMI-Input-Syntax.html#GDB_002fMI-Input-Syntax
|
|
|
|
# https://sourceware.org/gdb/current/onlinedocs/gdb.html/GDB_002fMI-Output-Syntax.html#GDB_002fMI-Output-Syntax
|
2017-09-08 21:10:04 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var msgs = split(message, "\r")
|
|
|
|
|
|
|
|
var msg = ''
|
|
|
|
for received_msg in msgs
|
|
|
|
# remove prefixed NL
|
|
|
|
if received_msg[0] == "\n"
|
|
|
|
msg = received_msg[1 : ]
|
|
|
|
else
|
|
|
|
msg = received_msg
|
2017-09-08 21:10:04 +02:00
|
|
|
endif
|
2021-01-11 19:40:15 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if parsing_disasm_msg > 0
|
|
|
|
HandleDisasmMsg(msg)
|
2021-01-11 19:40:15 +01:00
|
|
|
elseif msg != ''
|
2017-09-17 23:03:31 +02:00
|
|
|
if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
|
2024-06-05 21:27:38 +02:00
|
|
|
HandleCursor(msg)
|
2021-11-27 10:57:26 +00:00
|
|
|
elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
|
2024-06-05 21:27:38 +02:00
|
|
|
HandleNewBreakpoint(msg, 0)
|
2021-11-27 10:57:26 +00:00
|
|
|
elseif msg =~ '^=breakpoint-modified,'
|
2024-06-05 21:27:38 +02:00
|
|
|
HandleNewBreakpoint(msg, 1)
|
2017-09-09 22:19:47 +02:00
|
|
|
elseif msg =~ '^=breakpoint-deleted,'
|
2024-06-05 21:27:38 +02:00
|
|
|
HandleBreakpointDelete(msg)
|
2018-06-20 22:38:21 +02:00
|
|
|
elseif msg =~ '^=thread-group-started'
|
2024-06-05 21:27:38 +02:00
|
|
|
HandleProgramRun(msg)
|
2017-09-10 19:14:31 +02:00
|
|
|
elseif msg =~ '^\^done,value='
|
2024-06-05 21:27:38 +02:00
|
|
|
HandleEvaluate(msg)
|
2017-09-10 19:14:31 +02:00
|
|
|
elseif msg =~ '^\^error,msg='
|
2024-06-05 21:27:38 +02:00
|
|
|
HandleError(msg)
|
2023-08-22 19:30:29 +01:00
|
|
|
elseif msg =~ '^&"disassemble'
|
2024-06-05 21:27:38 +02:00
|
|
|
parsing_disasm_msg = 1
|
|
|
|
asm_lines = []
|
|
|
|
HandleDisasmMsg(msg)
|
2023-05-13 16:29:15 +02:00
|
|
|
elseif msg =~ '^\^done,variables='
|
2024-06-05 21:27:38 +02:00
|
|
|
HandleVariablesMsg(msg)
|
2017-09-09 22:19:47 +02:00
|
|
|
endif
|
|
|
|
endif
|
|
|
|
endfor
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2017-09-09 22:19:47 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def GotoProgram()
|
2019-09-20 14:38:13 +02:00
|
|
|
if has('win32')
|
|
|
|
if executable('powershell')
|
2024-06-05 21:27:38 +02:00
|
|
|
system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', pid))
|
2019-09-20 14:38:13 +02:00
|
|
|
endif
|
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
win_gotoid(ptywin)
|
|
|
|
endif
|
|
|
|
enddef
|
|
|
|
|
|
|
|
# Install commands in the current window to control the debugger.
|
|
|
|
def InstallCommands()
|
|
|
|
|
|
|
|
command -nargs=? Break SetBreakpoint(<q-args>)
|
|
|
|
command -nargs=? Tbreak SetBreakpoint(<q-args>, true)
|
|
|
|
command Clear ClearBreakpoint()
|
|
|
|
command Step SendResumingCommand('-exec-step')
|
|
|
|
command Over SendResumingCommand('-exec-next')
|
|
|
|
command -nargs=? Until Until(<q-args>)
|
|
|
|
command Finish SendResumingCommand('-exec-finish')
|
|
|
|
command -nargs=* Run Run(<q-args>)
|
|
|
|
command -nargs=* Arguments SendResumingCommand('-exec-arguments ' .. <q-args>)
|
2024-06-09 16:42:45 +02:00
|
|
|
command Stop StopCommand()
|
|
|
|
command Continue ContinueCommand()
|
2024-06-05 21:27:38 +02:00
|
|
|
|
|
|
|
command -nargs=* Frame Frame(<q-args>)
|
|
|
|
command -count=1 Up Up(<count>)
|
|
|
|
command -count=1 Down Down(<count>)
|
|
|
|
|
|
|
|
command -range -nargs=* Evaluate Evaluate(<range>, <q-args>)
|
|
|
|
command Gdb win_gotoid(gdbwin)
|
|
|
|
command Program GotoProgram()
|
|
|
|
command Source GotoSourcewinOrCreateIt()
|
|
|
|
command Asm GotoAsmwinOrCreateIt()
|
|
|
|
command Var GotoVariableswinOrCreateIt()
|
|
|
|
command Winbar InstallWinbar(1)
|
|
|
|
|
|
|
|
var map = 1
|
2022-05-23 21:49:41 +01:00
|
|
|
if exists('g:termdebug_config')
|
2024-06-05 21:27:38 +02:00
|
|
|
map = get(g:termdebug_config, 'map_K', 1)
|
2022-05-23 21:49:41 +01:00
|
|
|
elseif exists('g:termdebug_map_K')
|
2024-06-05 21:27:38 +02:00
|
|
|
map = g:termdebug_map_K
|
2022-05-23 21:49:41 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
|
2022-05-23 21:49:41 +01:00
|
|
|
if map
|
2024-06-05 21:27:38 +02:00
|
|
|
k_map_saved = maparg('K', 'n', 0, 1)
|
|
|
|
if !empty(k_map_saved) && !k_map_saved.buffer || empty(k_map_saved)
|
2023-11-08 21:44:48 +01:00
|
|
|
nnoremap K :Evaluate<CR>
|
|
|
|
endif
|
2020-05-26 21:20:45 +02:00
|
|
|
endif
|
2017-09-17 23:03:31 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
map = 1
|
2023-08-22 22:19:14 +02:00
|
|
|
if exists('g:termdebug_config')
|
2024-06-05 21:27:38 +02:00
|
|
|
map = get(g:termdebug_config, 'map_plus', 1)
|
2023-08-22 22:19:14 +02:00
|
|
|
endif
|
|
|
|
if map
|
2024-06-05 21:27:38 +02:00
|
|
|
plus_map_saved = maparg('+', 'n', 0, 1)
|
|
|
|
if !empty(plus_map_saved) && !plus_map_saved.buffer || empty(plus_map_saved)
|
2023-11-08 21:44:48 +01:00
|
|
|
nnoremap <expr> + $'<Cmd>{v:count1}Up<CR>'
|
|
|
|
endif
|
2023-08-22 22:19:14 +02:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
map = 1
|
2023-08-22 22:19:14 +02:00
|
|
|
if exists('g:termdebug_config')
|
2024-06-05 21:27:38 +02:00
|
|
|
map = get(g:termdebug_config, 'map_minus', 1)
|
2023-08-22 22:19:14 +02:00
|
|
|
endif
|
|
|
|
if map
|
2024-06-05 21:27:38 +02:00
|
|
|
minus_map_saved = maparg('-', 'n', 0, 1)
|
|
|
|
if !empty(minus_map_saved) && !minus_map_saved.buffer || empty(minus_map_saved)
|
2023-11-08 21:44:48 +01:00
|
|
|
nnoremap <expr> - $'<Cmd>{v:count1}Down<CR>'
|
|
|
|
endif
|
2023-08-22 22:19:14 +02:00
|
|
|
endif
|
|
|
|
|
|
|
|
|
2017-12-17 17:17:07 +01:00
|
|
|
if has('menu') && &mouse != ''
|
2024-06-05 21:27:38 +02:00
|
|
|
InstallWinbar(0)
|
2018-03-03 20:47:21 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var pup = 1
|
2022-05-23 21:49:41 +01:00
|
|
|
if exists('g:termdebug_config')
|
2024-06-05 21:27:38 +02:00
|
|
|
pup = get(g:termdebug_config, 'popup', 1)
|
2022-05-23 21:49:41 +01:00
|
|
|
elseif exists('g:termdebug_popup')
|
2024-06-05 21:27:38 +02:00
|
|
|
pup = g:termdebug_popup
|
2022-05-23 21:49:41 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
|
|
|
|
if pup
|
|
|
|
saved_mousemodel = &mousemodel
|
|
|
|
&mousemodel = 'popup_setpos'
|
2018-03-03 20:47:21 +01:00
|
|
|
an 1.200 PopUp.-SEP3- <Nop>
|
|
|
|
an 1.210 PopUp.Set\ breakpoint :Break<CR>
|
|
|
|
an 1.220 PopUp.Clear\ breakpoint :Clear<CR>
|
2022-04-18 15:36:40 +01:00
|
|
|
an 1.230 PopUp.Run\ until :Until<CR>
|
|
|
|
an 1.240 PopUp.Evaluate :Evaluate<CR>
|
2018-03-03 20:47:21 +01:00
|
|
|
endif
|
2017-09-17 23:03:31 +02:00
|
|
|
endif
|
2018-07-19 02:55:01 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2018-03-03 20:47:21 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Install the window toolbar in the current window.
|
|
|
|
def InstallWinbar(force: number)
|
|
|
|
# install the window toolbar by default, can be disabled in the config
|
|
|
|
var winbar = 1
|
2023-04-22 22:40:14 +01:00
|
|
|
if exists('g:termdebug_config')
|
2024-06-05 21:27:38 +02:00
|
|
|
winbar = get(g:termdebug_config, 'winbar', 1)
|
2023-04-22 22:40:14 +01:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if has('menu') && &mouse != '' && (winbar || force)
|
2018-04-06 22:26:25 +02:00
|
|
|
nnoremenu WinBar.Step :Step<CR>
|
|
|
|
nnoremenu WinBar.Next :Over<CR>
|
|
|
|
nnoremenu WinBar.Finish :Finish<CR>
|
|
|
|
nnoremenu WinBar.Cont :Continue<CR>
|
|
|
|
nnoremenu WinBar.Stop :Stop<CR>
|
|
|
|
nnoremenu WinBar.Eval :Evaluate<CR>
|
2024-06-05 21:27:38 +02:00
|
|
|
add(winbar_winids, win_getid())
|
2018-04-06 22:26:25 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2018-03-03 20:47:21 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Delete installed debugger commands in the current window.
|
|
|
|
def DeleteCommands()
|
2017-09-09 22:19:47 +02:00
|
|
|
delcommand Break
|
2023-12-14 20:30:26 +01:00
|
|
|
delcommand Tbreak
|
2018-03-03 20:47:21 +01:00
|
|
|
delcommand Clear
|
2017-09-09 22:19:47 +02:00
|
|
|
delcommand Step
|
2017-09-10 19:14:31 +02:00
|
|
|
delcommand Over
|
2022-04-18 15:36:40 +01:00
|
|
|
delcommand Until
|
2017-09-09 22:19:47 +02:00
|
|
|
delcommand Finish
|
2017-11-12 18:02:06 +01:00
|
|
|
delcommand Run
|
|
|
|
delcommand Arguments
|
|
|
|
delcommand Stop
|
2017-09-09 22:19:47 +02:00
|
|
|
delcommand Continue
|
2023-08-22 22:19:14 +02:00
|
|
|
delcommand Frame
|
|
|
|
delcommand Up
|
|
|
|
delcommand Down
|
2017-09-10 19:14:31 +02:00
|
|
|
delcommand Evaluate
|
|
|
|
delcommand Gdb
|
|
|
|
delcommand Program
|
2018-04-14 18:59:50 +02:00
|
|
|
delcommand Source
|
2021-01-11 19:40:15 +01:00
|
|
|
delcommand Asm
|
2023-05-13 16:29:15 +02:00
|
|
|
delcommand Var
|
2018-03-03 20:47:21 +01:00
|
|
|
delcommand Winbar
|
2017-09-10 19:14:31 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if exists('k_map_saved')
|
|
|
|
if !empty(k_map_saved) && !k_map_saved.buffer
|
2020-12-10 21:11:27 +01:00
|
|
|
nunmap K
|
2024-06-05 21:27:38 +02:00
|
|
|
mapset(k_map_saved)
|
|
|
|
elseif empty(k_map_saved)
|
2023-11-08 21:44:48 +01:00
|
|
|
nunmap K
|
2020-12-10 21:11:27 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
k_map_saved = {}
|
2020-05-26 21:20:45 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
if exists('plus_map_saved')
|
|
|
|
if !empty(plus_map_saved) && !plus_map_saved.buffer
|
2023-08-22 22:19:14 +02:00
|
|
|
nunmap +
|
2024-06-05 21:27:38 +02:00
|
|
|
mapset(plus_map_saved)
|
|
|
|
elseif empty(plus_map_saved)
|
2023-11-08 21:44:48 +01:00
|
|
|
nunmap +
|
2023-08-22 22:19:14 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
plus_map_saved = {}
|
2023-08-22 22:19:14 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
if exists('minus_map_saved')
|
|
|
|
if !empty(minus_map_saved) && !minus_map_saved.buffer
|
2023-08-22 22:19:14 +02:00
|
|
|
nunmap -
|
2024-06-05 21:27:38 +02:00
|
|
|
mapset(minus_map_saved)
|
|
|
|
elseif empty(minus_map_saved)
|
2023-11-08 21:44:48 +01:00
|
|
|
nunmap -
|
2023-08-22 22:19:14 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
minus_map_saved = {}
|
2023-08-22 22:19:14 +02:00
|
|
|
endif
|
2017-09-17 23:03:31 +02:00
|
|
|
|
|
|
|
if has('menu')
|
2024-06-05 21:27:38 +02:00
|
|
|
# Remove the WinBar entries from all windows where it was added.
|
|
|
|
var curwinid = win_getid()
|
|
|
|
for winid in winbar_winids
|
2018-03-03 20:47:21 +01:00
|
|
|
if win_gotoid(winid)
|
2024-06-05 21:27:38 +02:00
|
|
|
aunmenu WinBar.Step
|
|
|
|
aunmenu WinBar.Next
|
|
|
|
aunmenu WinBar.Finish
|
|
|
|
aunmenu WinBar.Cont
|
|
|
|
aunmenu WinBar.Stop
|
|
|
|
aunmenu WinBar.Eval
|
2018-03-03 20:47:21 +01:00
|
|
|
endif
|
|
|
|
endfor
|
2024-06-05 21:27:38 +02:00
|
|
|
win_gotoid(curwinid)
|
|
|
|
winbar_winids = []
|
2018-03-03 20:47:21 +01:00
|
|
|
|
2024-06-09 18:38:14 +02:00
|
|
|
if saved_mousemodel != ''
|
2024-06-05 21:27:38 +02:00
|
|
|
&mousemodel = saved_mousemodel
|
|
|
|
saved_mousemodel = null_string
|
2024-06-09 16:42:45 +02:00
|
|
|
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
|
2018-03-03 20:47:21 +01:00
|
|
|
endif
|
2017-09-17 23:03:31 +02:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
sign_unplace('TermDebug')
|
|
|
|
breakpoints = {}
|
|
|
|
breakpoint_locations = {}
|
|
|
|
|
|
|
|
sign_undefine('debugPC')
|
|
|
|
sign_undefine(BreakpointSigns->map("'debugBreakpoint' .. v:val"))
|
|
|
|
BreakpointSigns = []
|
|
|
|
enddef
|
|
|
|
|
|
|
|
|
|
|
|
# :Until - Execute until past a specified position or current line
|
|
|
|
def Until(at: string)
|
|
|
|
|
|
|
|
if stopped
|
|
|
|
# reset stopped here, it may take a bit of time before we get a response
|
|
|
|
stopped = 0
|
|
|
|
ch_log('assume that program is running after this command')
|
|
|
|
|
|
|
|
# Use the fname:lnum format
|
|
|
|
var AT = empty(at) ?
|
|
|
|
\ fnameescape(expand('%:p')) .. ':' .. line('.') : at
|
|
|
|
SendCommand('-exec-until ' .. AT)
|
2022-04-18 15:36:40 +01:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
ch_log('dropping command, program is running: exec-until')
|
2022-04-18 15:36:40 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2022-04-18 15:36:40 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# :Break - Set a breakpoint at the cursor position.
|
|
|
|
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
|
|
|
|
if !stopped
|
|
|
|
do_continue = 1
|
2024-06-09 16:42:45 +02:00
|
|
|
StopCommand()
|
2017-11-12 18:02:06 +01:00
|
|
|
sleep 10m
|
|
|
|
endif
|
2019-09-20 14:38:13 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Use the fname:lnum format, older gdb can't handle --source.
|
|
|
|
var AT = empty(at) ?
|
|
|
|
\ fnameescape(expand('%:p')) .. ':' .. line('.') : at
|
|
|
|
var cmd = ''
|
|
|
|
if tbreak
|
|
|
|
cmd = '-break-insert -t ' .. AT
|
2023-12-14 20:30:26 +01:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
cmd = '-break-insert ' .. AT
|
2023-12-14 20:30:26 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
# OK
|
|
|
|
# echom "cmsd: " .. cmd
|
|
|
|
SendCommand(cmd)
|
2017-11-12 18:02:06 +01:00
|
|
|
if do_continue
|
2024-06-09 16:42:45 +02:00
|
|
|
ContinueCommand()
|
2017-11-12 18:02:06 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
|
|
|
|
def ClearBreakpoint()
|
|
|
|
var fname = fnameescape(expand('%:p'))
|
|
|
|
var lnum = line('.')
|
|
|
|
var bploc = printf('%s:%d', fname, lnum)
|
|
|
|
var nr = 0
|
|
|
|
if has_key(breakpoint_locations, bploc)
|
|
|
|
var idx = 0
|
|
|
|
for id in breakpoint_locations[bploc]
|
|
|
|
if has_key(breakpoints, id)
|
|
|
|
# Assume this always works, the reply is simply "^done".
|
|
|
|
SendCommand('-break-delete ' .. id)
|
|
|
|
for subid in keys(breakpoints[id])
|
|
|
|
sign_unplace('TermDebug',
|
|
|
|
\ {'id': Breakpoint2SignNumber(id, str2nr(subid))})
|
2022-03-19 15:18:53 +00:00
|
|
|
endfor
|
2024-06-05 21:27:38 +02:00
|
|
|
remove(breakpoints, id)
|
|
|
|
remove(breakpoint_locations[bploc], idx)
|
|
|
|
nr = id
|
2022-03-19 15:18:53 +00:00
|
|
|
break
|
2018-12-09 15:53:01 +01:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
idx += 1
|
2018-12-09 15:53:01 +01:00
|
|
|
endif
|
|
|
|
endfor
|
2024-06-05 21:27:38 +02:00
|
|
|
|
2021-11-21 21:13:36 +00:00
|
|
|
if nr != 0
|
2024-06-05 21:27:38 +02:00
|
|
|
if empty(breakpoint_locations[bploc])
|
|
|
|
remove(breakpoint_locations, bploc)
|
2021-11-21 21:13:36 +00:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
echomsg 'Breakpoint ' .. nr .. ' cleared from line ' .. lnum .. '.'
|
2021-11-21 21:13:36 +00:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
Echoerr('Internal error trying to remove breakpoint at line ' .. lnum .. '!')
|
2017-09-09 22:19:47 +02:00
|
|
|
endif
|
2021-11-21 21:13:36 +00:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
echomsg 'No breakpoint to remove at line ' .. lnum .. '.'
|
|
|
|
endif
|
|
|
|
enddef
|
|
|
|
|
|
|
|
def Run(args: string)
|
|
|
|
if args != ''
|
|
|
|
SendResumingCommand('-exec-arguments ' .. args)
|
|
|
|
endif
|
|
|
|
SendResumingCommand('-exec-run')
|
|
|
|
enddef
|
|
|
|
|
|
|
|
# :Frame - go to a specific frame in the stack
|
|
|
|
def Frame(arg: string)
|
|
|
|
# Note: we explicit do not use mi's command
|
|
|
|
# call SendCommand('-stack-select-frame "' . arg .'"')
|
|
|
|
# as we only get a "done" mi response and would have to open the file
|
|
|
|
# 'manually' - using cli command "frame" provides us with the mi response
|
|
|
|
# already parsed and allows for more formats
|
|
|
|
if arg =~ '^\d\+$' || arg == ''
|
|
|
|
# specify frame by number
|
2024-06-09 16:42:45 +02:00
|
|
|
SendCommand('-interpreter-exec mi "frame ' .. arg .. '"')
|
2024-06-05 21:27:38 +02:00
|
|
|
elseif arg =~ '^0x[0-9a-fA-F]\+$'
|
|
|
|
# specify frame by stack address
|
2024-06-09 16:42:45 +02:00
|
|
|
SendCommand('-interpreter-exec mi "frame address ' .. arg .. '"')
|
2021-11-07 20:27:04 +00:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
# specify frame by function name
|
2024-06-09 16:42:45 +02:00
|
|
|
SendCommand('-interpreter-exec mi "frame function ' .. arg .. '"')
|
2024-06-05 21:27:38 +02:00
|
|
|
endif
|
|
|
|
enddef
|
|
|
|
|
|
|
|
# :Up - go count frames in the stack "higher"
|
|
|
|
def Up(count: number)
|
|
|
|
# the 'correct' one would be -stack-select-frame N, but we don't know N
|
|
|
|
SendCommand($'-interpreter-exec console "up {count}"')
|
|
|
|
enddef
|
|
|
|
|
|
|
|
# :Down - go count frames in the stack "below"
|
|
|
|
def Down(count: number)
|
|
|
|
# the 'correct' one would be -stack-select-frame N, but we don't know N
|
|
|
|
SendCommand($'-interpreter-exec console "down {count}"')
|
|
|
|
enddef
|
|
|
|
|
|
|
|
def SendEval(expr: string)
|
|
|
|
# check for "likely" boolean expressions, in which case we take it as lhs
|
|
|
|
var exprLHS = substitute(expr, ' *=.*', '', '')
|
|
|
|
if expr =~ "[=!<>]="
|
|
|
|
exprLHS = expr
|
|
|
|
endif
|
|
|
|
|
|
|
|
# encoding expression to prevent bad errors
|
|
|
|
var expr_escaped = expr
|
|
|
|
\ ->substitute('\\', '\\\\', 'g')
|
|
|
|
\ ->substitute('"', '\\"', 'g')
|
|
|
|
SendCommand('-data-evaluate-expression "' .. expr_escaped .. '"')
|
|
|
|
evalexpr = exprLHS
|
|
|
|
enddef
|
|
|
|
|
|
|
|
# :Evaluate - evaluate what is specified / under the cursor
|
|
|
|
def Evaluate(range: number, arg: string)
|
|
|
|
var expr = GetEvaluationExpression(range, arg)
|
|
|
|
#echom "expr:" .. expr
|
|
|
|
ignoreEvalError = 0
|
|
|
|
SendEval(expr)
|
|
|
|
enddef
|
|
|
|
|
|
|
|
|
|
|
|
# get what is specified / under the cursor
|
|
|
|
def GetEvaluationExpression(range: number, arg: string): string
|
|
|
|
var expr = ''
|
|
|
|
if arg != ''
|
|
|
|
# user supplied evaluation
|
|
|
|
expr = CleanupExpr(arg)
|
|
|
|
# DSW: replace "likely copy + paste" assignment
|
|
|
|
expr = substitute(expr, '"\([^"]*\)": *', '\1=', 'g')
|
|
|
|
elseif range == 2
|
|
|
|
# no evaluation but provided but range set
|
|
|
|
var pos = getcurpos()
|
|
|
|
var regst = getreg('v', 1, 1)
|
|
|
|
var regt = getregtype('v')
|
2017-09-10 19:14:31 +02:00
|
|
|
normal! gv"vy
|
2024-06-05 21:27:38 +02:00
|
|
|
expr = CleanupExpr(@v)
|
|
|
|
setpos('.', pos)
|
|
|
|
setreg('v', regst, regt)
|
2017-09-10 19:14:31 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
# no evaluation provided: get from C-expression under cursor
|
|
|
|
# TODO: allow filetype specific lookup #9057
|
|
|
|
expr = expand('<cexpr>')
|
2017-09-10 19:14:31 +02:00
|
|
|
endif
|
2021-11-21 21:13:36 +00:00
|
|
|
return expr
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2021-11-21 21:13:36 +00:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# 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)
|
|
|
|
def CleanupExpr(passed_expr: string): string
|
|
|
|
# replace all embedded newlines/tabs/...
|
|
|
|
var expr = substitute(passed_expr, '\_s', ' ', 'g')
|
2021-11-21 21:13:36 +00:00
|
|
|
|
|
|
|
if &filetype ==# 'cobol'
|
2024-06-05 21:27:38 +02:00
|
|
|
# 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
|
|
|
|
expr = substitute(expr, ';', ' ', 'g')
|
|
|
|
expr = substitute(expr, '[,.]\+ *$', '', '')
|
2021-11-21 21:13:36 +00:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# get rid of leading and trailing spaces
|
|
|
|
expr = substitute(expr, '^ *', '', '')
|
|
|
|
expr = substitute(expr, ' *$', '', '')
|
2021-11-21 21:13:36 +00:00
|
|
|
return expr
|
2024-06-05 21:27:38 +02:00
|
|
|
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', {-> eval('"' .. submatch(0) .. '"')}, '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 evalFromBalloonExprResult == ''
|
|
|
|
evalFromBalloonExprResult = evalexpr .. ': ' .. value
|
2017-11-18 18:52:04 +01:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
evalFromBalloonExprResult ..= ' = ' .. value
|
2017-11-18 18:52:04 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
balloon_show(evalFromBalloonExprResult)
|
2017-11-18 18:52:04 +01:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
echomsg '"' .. evalexpr .. '": ' .. value
|
2017-11-18 18:52:04 +01:00
|
|
|
endif
|
2017-09-17 23:03:31 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
|
|
|
|
# Looks like a pointer, also display what it points to.
|
|
|
|
ignoreEvalError = 1
|
|
|
|
SendEval('*' .. evalexpr)
|
2017-11-18 18:52:04 +01:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
evalFromBalloonExpr = 0
|
2017-11-18 18:52:04 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
|
2017-11-18 18:52:04 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Show a balloon with information of the variable under the mouse pointer,
|
|
|
|
# if there is any.
|
|
|
|
def TermDebugBalloonExpr(): string
|
|
|
|
if v:beval_winid != sourcewin
|
2019-07-13 23:04:31 +02:00
|
|
|
return ''
|
2018-06-17 21:34:11 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
if !stopped
|
|
|
|
# Only evaluate when stopped, otherwise setting a breakpoint using the
|
|
|
|
# mouse triggers a balloon.
|
2019-07-13 23:04:31 +02:00
|
|
|
return ''
|
2017-09-17 23:03:31 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
evalFromBalloonExpr = 1
|
|
|
|
evalFromBalloonExprResult = ''
|
|
|
|
ignoreEvalError = 1
|
|
|
|
var expr = CleanupExpr(v:beval_text)
|
|
|
|
SendEval(expr)
|
2017-11-18 18:52:04 +01:00
|
|
|
return ''
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
|
|
|
|
# Handle an error.
|
|
|
|
def HandleError(msg: string)
|
|
|
|
if ignoreEvalError
|
|
|
|
# Result of SendEval() failed, ignore.
|
|
|
|
ignoreEvalError = 0
|
|
|
|
evalFromBalloonExpr = 0
|
2017-11-18 18:52:04 +01:00
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
var msgVal = substitute(msg, '.*msg="\(.*\)"', '\1', '')
|
|
|
|
Echoerr(substitute(msgVal, '\\"', '"', 'g'))
|
|
|
|
enddef
|
2017-09-10 19:14:31 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def GotoSourcewinOrCreateIt()
|
|
|
|
if !win_gotoid(sourcewin)
|
2018-04-06 22:26:25 +02:00
|
|
|
new
|
2024-06-05 21:27:38 +02:00
|
|
|
sourcewin = win_getid()
|
|
|
|
InstallWinbar(0)
|
2018-04-06 22:26:25 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2018-04-06 22:26:25 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
|
|
|
|
def GetDisasmWindow(): number
|
2022-05-23 21:49:41 +01:00
|
|
|
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
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2022-05-23 21:49:41 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def GetDisasmWindowHeight(): number
|
2022-05-23 21:49:41 +01:00
|
|
|
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
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2022-05-23 21:49:41 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def GotoAsmwinOrCreateIt()
|
|
|
|
var mdf = ''
|
|
|
|
if !win_gotoid(asmwin)
|
|
|
|
if win_gotoid(sourcewin)
|
|
|
|
# 60 is approx spaceBuffer * 3
|
2023-11-08 21:59:15 +01:00
|
|
|
if winwidth(0) > (78 + 60)
|
2024-06-05 21:27:38 +02:00
|
|
|
mdf = 'vert'
|
|
|
|
exe mdf .. ' ' .. ':60' .. 'new'
|
2023-11-08 21:59:15 +01:00
|
|
|
else
|
|
|
|
exe 'rightbelow new'
|
|
|
|
endif
|
2021-01-11 19:40:15 +01:00
|
|
|
else
|
|
|
|
exe 'new'
|
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
asmwin = win_getid()
|
2021-01-11 19:40:15 +01:00
|
|
|
|
|
|
|
setlocal nowrap
|
|
|
|
setlocal number
|
|
|
|
setlocal noswapfile
|
|
|
|
setlocal buftype=nofile
|
2023-05-13 16:29:15 +02:00
|
|
|
setlocal bufhidden=wipe
|
|
|
|
setlocal signcolumn=no
|
2021-11-16 19:18:26 +00:00
|
|
|
setlocal modifiable
|
2021-01-11 19:40:15 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if asmbuf > 0 && bufexists(asmbuf)
|
|
|
|
exe 'buffer' .. asmbuf
|
2024-05-21 23:33:03 +02:00
|
|
|
elseif empty(glob('Termdebug-asm-listing'))
|
2023-05-13 16:29:15 +02:00
|
|
|
silent file Termdebug-asm-listing
|
2024-06-05 21:27:38 +02:00
|
|
|
asmbuf = bufnr('Termdebug-asm-listing')
|
2024-05-21 23:33:03 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
Echoerr("You have a file/folder named 'Termdebug-asm-listing'.
|
2024-05-21 23:33:03 +02:00
|
|
|
\ Please exit and rename it because Termdebug may not work as expected.")
|
2021-01-11 19:40:15 +01:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if mdf != 'vert' && GetDisasmWindowHeight() > 0
|
|
|
|
exe 'resize ' .. GetDisasmWindowHeight()
|
2021-01-11 19:40:15 +01:00
|
|
|
endif
|
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if asm_addr != ''
|
|
|
|
var lnum = search('^' .. asm_addr)
|
2021-01-11 19:40:15 +01:00
|
|
|
if lnum == 0
|
2024-06-05 21:27:38 +02:00
|
|
|
if stopped
|
|
|
|
SendCommand('disassemble $pc')
|
2021-01-11 19:40:15 +01:00
|
|
|
endif
|
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
sign_unplace('TermDebug', {'id': asm_id})
|
|
|
|
sign_place(asm_id, 'TermDebug', 'debugPC', '%', {'lnum': lnum})
|
2021-01-11 19:40:15 +01:00
|
|
|
endif
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2021-01-11 19:40:15 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def GetVariablesWindow(): number
|
2023-05-13 16:29:15 +02:00
|
|
|
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
|
|
|
|
endif
|
|
|
|
return 0
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2023-05-13 16:29:15 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def GetVariablesWindowHeight(): number
|
2023-05-13 16:29:15 +02:00
|
|
|
if exists('g:termdebug_config')
|
|
|
|
return get(g:termdebug_config, 'variables_window_height', 0)
|
|
|
|
endif
|
|
|
|
if exists('g:termdebug_variables_window') && g:termdebug_variables_window > 1
|
|
|
|
return g:termdebug_variables_window
|
|
|
|
endif
|
|
|
|
return 0
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
|
2023-05-13 16:29:15 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
def GotoVariableswinOrCreateIt()
|
|
|
|
var mdf = ''
|
|
|
|
if !win_gotoid(varwin)
|
|
|
|
if win_gotoid(sourcewin)
|
|
|
|
# 60 is approx spaceBuffer * 3
|
2023-11-08 21:59:15 +01:00
|
|
|
if winwidth(0) > (78 + 60)
|
2024-06-05 21:27:38 +02:00
|
|
|
mdf = 'vert'
|
|
|
|
exe mdf .. ' ' .. ':60' .. 'new'
|
2023-11-08 21:59:15 +01:00
|
|
|
else
|
|
|
|
exe 'rightbelow new'
|
|
|
|
endif
|
2023-05-13 16:29:15 +02:00
|
|
|
else
|
|
|
|
exe 'new'
|
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
varwin = win_getid()
|
2023-05-13 16:29:15 +02:00
|
|
|
|
|
|
|
setlocal nowrap
|
|
|
|
setlocal noswapfile
|
|
|
|
setlocal buftype=nofile
|
|
|
|
setlocal bufhidden=wipe
|
|
|
|
setlocal signcolumn=no
|
|
|
|
setlocal modifiable
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if varbuf > 0 && bufexists(varbuf)
|
|
|
|
exe 'buffer' .. varbuf
|
2024-05-21 23:33:03 +02:00
|
|
|
elseif empty(glob('Termdebug-variables-listing'))
|
2023-05-13 16:29:15 +02:00
|
|
|
silent file Termdebug-variables-listing
|
2024-06-05 21:27:38 +02:00
|
|
|
varbuf = bufnr('Termdebug-variables-listing')
|
2024-05-21 23:33:03 +02:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
Echoerr("You have a file/folder named 'Termdebug-variables-listing'.
|
2024-05-21 23:33:03 +02:00
|
|
|
\ Please exit and rename it because Termdebug may not work as expected.")
|
2023-05-13 16:29:15 +02:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if mdf != 'vert' && GetVariablesWindowHeight() > 0
|
|
|
|
exe 'resize ' .. GetVariablesWindowHeight()
|
2023-05-13 16:29:15 +02:00
|
|
|
endif
|
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if running
|
|
|
|
SendCommand('-stack-list-variables 2')
|
2023-05-13 16:29:15 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2023-05-13 16:29:15 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Handle stopping and running message from gdb.
|
|
|
|
# Will update the sign that shows the current position.
|
|
|
|
def HandleCursor(msg: string)
|
|
|
|
var wid = win_getid()
|
2017-09-09 22:19:47 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if msg =~ '^\*stopped'
|
|
|
|
ch_log('program stopped')
|
|
|
|
stopped = 1
|
|
|
|
if msg =~ '^\*stopped,reason="exited-normally"'
|
|
|
|
running = 0
|
2023-05-13 16:29:15 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
elseif msg =~ '^\*running'
|
|
|
|
ch_log('program running')
|
|
|
|
stopped = 0
|
|
|
|
running = 1
|
2017-11-12 18:02:06 +01:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var fname = ''
|
|
|
|
if msg =~ 'fullname='
|
|
|
|
fname = GetFullname(msg)
|
2018-06-19 22:34:46 +02:00
|
|
|
endif
|
2021-01-11 19:40:15 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if msg =~ 'addr='
|
|
|
|
var asm_addr_local = GetAsmAddr(msg)
|
|
|
|
if asm_addr_local != ''
|
|
|
|
asm_addr = asm_addr_local
|
2021-01-11 19:40:15 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var curwinid = win_getid()
|
|
|
|
var lnum = 0
|
|
|
|
if win_gotoid(asmwin)
|
|
|
|
lnum = search('^' .. asm_addr)
|
2022-03-19 15:18:53 +00:00
|
|
|
if lnum == 0
|
2024-06-05 21:27:38 +02:00
|
|
|
SendCommand('disassemble $pc')
|
2022-03-19 15:18:53 +00:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
sign_unplace('TermDebug', {'id': asm_id})
|
|
|
|
sign_place(asm_id, 'TermDebug', 'debugPC', '%', {'lnum': lnum})
|
2022-03-19 15:18:53 +00:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
win_gotoid(curwinid)
|
2021-01-11 19:40:15 +01:00
|
|
|
endif
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if running && stopped && bufwinnr('Termdebug-variables-listing') != -1
|
|
|
|
SendCommand('-stack-list-variables 2')
|
2023-05-13 16:29:15 +02:00
|
|
|
endif
|
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
if msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
|
|
|
|
var lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
|
2018-04-06 22:26:25 +02:00
|
|
|
if lnum =~ '^[0-9]*$'
|
2024-06-05 21:27:38 +02:00
|
|
|
GotoSourcewinOrCreateIt()
|
2018-04-06 22:26:25 +02:00
|
|
|
if expand('%:p') != fnamemodify(fname, ':p')
|
2023-08-21 02:06:49 +08:00
|
|
|
echomsg 'different fname: "' .. expand('%:p') .. '" vs "' .. fnamemodify(fname, ':p') .. '"'
|
|
|
|
augroup Termdebug
|
2024-06-05 21:27:38 +02:00
|
|
|
# Always open a file read-only instead of showing the ATTENTION
|
|
|
|
# prompt, since it is unlikely we want to edit the file.
|
|
|
|
# The file may be changed but not saved, warn for that.
|
2023-08-21 02:06:49 +08:00
|
|
|
au SwapExists * echohl WarningMsg
|
2024-06-05 21:27:38 +02:00
|
|
|
\ | echo 'Warning: file is being edited elsewhere'
|
|
|
|
\ | echohl None
|
|
|
|
\ | let v:swapchoice = 'o'
|
2022-03-19 15:18:53 +00:00
|
|
|
augroup END
|
|
|
|
if &modified
|
2024-06-05 21:27:38 +02:00
|
|
|
# TODO: find existing window
|
|
|
|
exe 'split ' .. fnameescape(fname)
|
|
|
|
sourcewin = win_getid()
|
|
|
|
call InstallWinbar(0)
|
2022-03-19 15:18:53 +00:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
exe 'edit ' .. fnameescape(fname)
|
2022-03-19 15:18:53 +00:00
|
|
|
endif
|
|
|
|
augroup Termdebug
|
|
|
|
au! SwapExists
|
|
|
|
augroup END
|
2017-09-08 21:10:04 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
exe ":" .. lnum
|
2021-11-16 19:18:26 +00:00
|
|
|
normal! zv
|
2024-06-05 21:27:38 +02:00
|
|
|
sign_unplace('TermDebug', {'id': pc_id})
|
|
|
|
sign_place(pc_id, 'TermDebug', 'debugPC', fname,
|
|
|
|
\ {'lnum': str2nr(lnum), priority: 110})
|
2020-10-26 21:12:46 +01:00
|
|
|
if !exists('b:save_signcolumn')
|
2024-06-05 21:27:38 +02:00
|
|
|
b:save_signcolumn = &signcolumn
|
|
|
|
add(signcolumn_buflist, bufnr())
|
2020-10-26 21:12:46 +01:00
|
|
|
endif
|
2018-04-06 22:26:25 +02:00
|
|
|
setlocal signcolumn=yes
|
2017-09-08 21:10:04 +02:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
elseif !stopped || fname != ''
|
|
|
|
sign_unplace('TermDebug', {'id': pc_id})
|
2017-09-09 22:19:47 +02:00
|
|
|
endif
|
2018-04-06 22:26:25 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
win_gotoid(wid)
|
|
|
|
enddef
|
2017-09-09 22:19:47 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Create breakpoint sign
|
|
|
|
def CreateBreakpoint(id: number, subid: number, enabled: string)
|
|
|
|
var nr = printf('%d.%d', id, subid)
|
|
|
|
if index(BreakpointSigns, nr) == -1
|
|
|
|
add(BreakpointSigns, nr)
|
|
|
|
var hiName = ''
|
|
|
|
if enabled == "n"
|
|
|
|
hiName = "debugBreakpointDisabled"
|
2021-11-27 10:57:26 +00:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
hiName = "debugBreakpoint"
|
2023-06-28 23:27:28 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
var label = ''
|
|
|
|
if exists('g:termdebug_config') && has_key(g:termdebug_config, 'sign')
|
|
|
|
label = g:termdebug_config['sign']
|
|
|
|
else
|
|
|
|
label = printf('%02X', id)
|
|
|
|
if id > 255
|
|
|
|
label = 'F+'
|
2023-06-28 23:27:28 +01:00
|
|
|
endif
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
sign_define('debugBreakpoint' .. nr,
|
|
|
|
\ {'text': slice(label, 0, 2),
|
|
|
|
\ 'texthl': hiName})
|
|
|
|
endif
|
|
|
|
enddef
|
|
|
|
|
|
|
|
def SplitMsg(str: string): list<string>
|
|
|
|
return split(str, '{.\{-}}\zs')
|
|
|
|
enddef
|
|
|
|
|
|
|
|
|
|
|
|
# Handle setting a breakpoint
|
|
|
|
# Will update the sign that shows the breakpoint
|
|
|
|
def HandleNewBreakpoint(msg: string, modifiedFlag: any)
|
|
|
|
var nr = ''
|
|
|
|
|
|
|
|
if msg !~ 'fullname='
|
|
|
|
# a watch or a pending breakpoint does not have a file name
|
|
|
|
if msg =~ 'pending='
|
|
|
|
nr = substitute(msg, '.*number=\"\([0-9.]*\)\".*', '\1', '')
|
|
|
|
var target = substitute(msg, '.*pending=\"\([^"]*\)\".*', '\1', '')
|
|
|
|
echomsg 'Breakpoint ' .. nr .. ' (' .. target .. ') pending.'
|
2021-11-21 21:13:36 +00:00
|
|
|
endif
|
2018-06-23 14:36:17 +02:00
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
|
|
|
|
for mm in SplitMsg(msg)
|
|
|
|
var fname = GetFullname(mm)
|
2018-12-02 13:47:03 +01:00
|
|
|
if empty(fname)
|
|
|
|
continue
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
nr = substitute(mm, '.*number="\([0-9.]*\)\".*', '\1', '')
|
2018-12-02 13:47:03 +01:00
|
|
|
if empty(nr)
|
|
|
|
return
|
|
|
|
endif
|
2018-06-23 14:36:17 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# 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.
|
|
|
|
var [id, subid; _] = map(split(nr .. '.0', '\.'), 'str2nr(v:val) + 0')
|
|
|
|
# var [id, subid; _] = map(split(nr .. '.0', '\.'), 'v:val + 0')
|
|
|
|
var enabled = substitute(mm, '.*enabled="\([yn]\)".*', '\1', '')
|
|
|
|
CreateBreakpoint(id, subid, enabled)
|
|
|
|
|
|
|
|
var entries = {}
|
|
|
|
var entry = {}
|
|
|
|
if has_key(breakpoints, id)
|
|
|
|
entries = breakpoints[id]
|
2018-12-09 15:53:01 +01:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
breakpoints[id] = entries
|
2018-12-09 15:53:01 +01:00
|
|
|
endif
|
|
|
|
if has_key(entries, subid)
|
2024-06-05 21:27:38 +02:00
|
|
|
entry = entries[subid]
|
2018-12-02 13:47:03 +01:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
entries[subid] = entry
|
2018-12-02 13:47:03 +01:00
|
|
|
endif
|
2017-09-09 22:19:47 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var lnum = str2nr(substitute(mm, '.*line="\([^"]*\)".*', '\1', ''))
|
|
|
|
entry['fname'] = fname
|
|
|
|
entry['lnum'] = lnum
|
2017-09-17 23:03:31 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var bploc = printf('%s:%d', fname, lnum)
|
|
|
|
if !has_key(breakpoint_locations, bploc)
|
|
|
|
breakpoint_locations[bploc] = []
|
2018-12-09 15:53:01 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
breakpoint_locations[bploc] += [id]
|
2018-12-09 15:53:01 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
var posMsg = ''
|
2018-12-02 13:47:03 +01:00
|
|
|
if bufloaded(fname)
|
2024-06-05 21:27:38 +02:00
|
|
|
PlaceSign(id, subid, entry)
|
|
|
|
posMsg = ' at line ' .. lnum .. '.'
|
2021-11-27 10:57:26 +00:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
posMsg = ' in ' .. fname .. ' at line ' .. lnum .. '.'
|
2021-11-27 10:57:26 +00:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
var actionTaken = ''
|
|
|
|
if !modifiedFlag
|
|
|
|
actionTaken = 'created'
|
2021-11-27 10:57:26 +00:00
|
|
|
elseif enabled == 'n'
|
2024-06-05 21:27:38 +02:00
|
|
|
actionTaken = 'disabled'
|
2021-11-27 10:57:26 +00:00
|
|
|
else
|
2024-06-05 21:27:38 +02:00
|
|
|
actionTaken = 'enabled'
|
2018-12-02 13:47:03 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
echom 'Breakpoint ' .. nr .. ' ' .. actionTaken .. posMsg
|
2018-12-02 13:47:03 +01:00
|
|
|
endfor
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
|
|
|
|
|
|
|
|
|
|
|
def PlaceSign(id: number, subid: number, entry: dict<any>)
|
|
|
|
var nr = printf('%d.%d', id, subid)
|
|
|
|
sign_place(Breakpoint2SignNumber(id, subid), 'TermDebug',
|
|
|
|
\ 'debugBreakpoint' .. nr, entry['fname'],
|
|
|
|
\ {'lnum': entry['lnum'], priority: 110})
|
|
|
|
entry['placed'] = 1
|
|
|
|
enddef
|
|
|
|
|
|
|
|
# Handle deleting a breakpoint
|
|
|
|
# Will remove the sign that shows the breakpoint
|
|
|
|
def HandleBreakpointDelete(msg: string)
|
|
|
|
var id = substitute(msg, '.*id="\([0-9]*\)\".*', '\1', '')
|
2018-12-09 15:53:01 +01:00
|
|
|
if empty(id)
|
2017-09-09 22:19:47 +02:00
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
if has_key(breakpoints, id)
|
|
|
|
for [subid, entry] in items(breakpoints[id])
|
2018-12-09 15:53:01 +01:00
|
|
|
if has_key(entry, 'placed')
|
2024-06-05 21:27:38 +02:00
|
|
|
sign_unplace('TermDebug',
|
|
|
|
\ {'id': Breakpoint2SignNumber(str2nr(id), str2nr(subid))})
|
|
|
|
remove(entry, 'placed')
|
2018-12-09 15:53:01 +01:00
|
|
|
endif
|
|
|
|
endfor
|
2024-06-05 21:27:38 +02:00
|
|
|
remove(breakpoints, id)
|
|
|
|
echomsg 'Breakpoint ' .. id .. ' cleared.'
|
2018-12-09 15:53:01 +01:00
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2017-09-17 23:03:31 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Handle the debugged program starting to run.
|
|
|
|
# Will store the process ID in pid
|
|
|
|
def HandleProgramRun(msg: string)
|
|
|
|
var nr = str2nr(substitute(msg, '.*pid="\([0-9]*\)\".*', '\1', ''))
|
2018-06-20 22:38:21 +02:00
|
|
|
if nr == 0
|
|
|
|
return
|
|
|
|
endif
|
2024-06-05 21:27:38 +02:00
|
|
|
pid = nr
|
|
|
|
ch_log('Detected process ID: ' .. pid)
|
|
|
|
enddef
|
2018-06-20 22:38:21 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Handle a BufRead autocommand event: place any signs.
|
|
|
|
def BufRead()
|
|
|
|
var fname = expand('<afile>:p')
|
|
|
|
for [id, entries] in items(breakpoints)
|
2018-12-09 15:53:01 +01:00
|
|
|
for [subid, entry] in items(entries)
|
|
|
|
if entry['fname'] == fname
|
2024-06-05 21:27:38 +02:00
|
|
|
PlaceSign(id, subid, entry)
|
2018-12-09 15:53:01 +01:00
|
|
|
endif
|
|
|
|
endfor
|
2017-09-17 23:03:31 +02:00
|
|
|
endfor
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2017-09-17 23:03:31 +02:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# Handle a BufUnloaded autocommand event: unplace any signs.
|
|
|
|
def BufUnloaded()
|
|
|
|
var fname = expand('<afile>:p')
|
|
|
|
for [id, entries] in items(breakpoints)
|
2018-12-09 15:53:01 +01:00
|
|
|
for [subid, entry] in items(entries)
|
|
|
|
if entry['fname'] == fname
|
2024-06-05 21:27:38 +02:00
|
|
|
entry['placed'] = 0
|
2018-12-09 15:53:01 +01:00
|
|
|
endif
|
|
|
|
endfor
|
2017-09-17 23:03:31 +02:00
|
|
|
endfor
|
2024-06-05 21:27:38 +02:00
|
|
|
enddef
|
2023-06-24 14:20:36 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
InitHighlight()
|
|
|
|
InitAutocmd()
|
2023-11-08 21:44:48 +01:00
|
|
|
|
2024-06-05 21:27:38 +02:00
|
|
|
# vim: sw=2 sts=2 et
|