2024-06-05 21:27:38 +02:00
vim9script
# Debugger plugin using gdb .
# Author : Bram Moolenaar
# Copyright : Vim license applies , see ":help license"
2024-07-04 17:14:03 +02:00
# Last Change : 2024 Jul 04
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
2024-06-13 19:23:07 +02:00
def Echoerr ( msg : string )
echohl ErrorMsg | echom $'[termdebug] {msg}' | echohl None
enddef
2024-07-04 17:14:03 +02:00
def Echowarn ( msg : string )
echohl WarningMsg | echom $'[termdebug] {msg}' | echohl None
enddef
2024-06-13 19:23:07 +02:00
# Variables to keep their status among multiple instances of Termdebug
# Avoid to source the script twice .
2024-06-05 21:27:38 +02:00
if exists ( 'g:termdebug_loaded' )
2024-06-13 19:23:07 +02:00
Echoerr ( 'Termdebug already loaded.' )
finish
2017-09-19 22:06:03 +02:00
endif
2024-06-05 21:27:38 +02:00
g :termdebug_loaded = true
2024-06-20 22:17:34 +02:00
g :termdebug_is_running = false
# The command that starts debugging , e .g . ":Termdebug vim" .
# To end type "quit" in the gdb window .
command - nargs = * - complete = file - bang Termdebug StartDebug ( < bang > 0 , < f - args > )
command - nargs = + - complete = file - bang TermdebugCommand StartDebugCommand ( < bang > 0 , < f - args > )
2024-06-05 21:27:38 +02:00
2024-06-22 16:28:19 +02:00
enum Way
Prompt ,
Terminal
endenum
2024-06-13 19:23:07 +02:00
# Script variables declaration . These variables are re - initialized at every
# Termdebug instance
2024-06-22 16:28:19 +02:00
var way : Way
2024-06-13 19:23:07 +02:00
var err : string
2024-06-05 21:27:38 +02:00
2024-06-13 19:23:07 +02:00
var pc_id : number
var asm_id : number
var break_id : number
var stopped : bool
var running : bool
2024-06-05 21:27:38 +02:00
2024-06-13 19:23:07 +02:00
var parsing_disasm_msg : number
var asm_lines : list < string >
var asm_addr : string
2024-06-05 21:27:38 +02:00
# These shall be constants but cannot be initialized here
# They indicate the buffer numbers of the main buffers used
2024-06-13 19:23:07 +02:00
var gdbbufnr : number
var gdbbufname : string
var varbufnr : number
var varbufname : string
var asmbufnr : number
var asmbufname : string
2024-06-23 17:25:05 +02:00
var promptbufnr : number
2024-06-18 20:18:20 +02:00
# This is for the "debugged-program" thing
2024-06-13 19:23:07 +02:00
var ptybufnr : number
2024-06-18 20:18:20 +02:00
var ptybufname : string
2024-06-13 19:23:07 +02:00
var commbufnr : number
2024-06-18 20:18:20 +02:00
var commbufname : string
2024-06-05 21:27:38 +02:00
2024-06-13 19:23:07 +02:00
var gdbjob : job
var gdb_channel : channel
2024-06-05 21:27:38 +02:00
# These changes because they relate to windows
2024-06-13 19:23:07 +02:00
var pid : number
var gdbwin : number
var varwin : number
var asmwin : number
var ptywin : number
var sourcewin : number
2024-06-05 21:27:38 +02:00
# 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 }}
2024-06-13 19:23:07 +02:00
var breakpoints : dict < any >
2024-06-05 21:27:38 +02:00
# Contains breakpoints by file /lnum . The key is "fname:lnum" .
# Each entry is a list of breakpoint IDs at that position .
2024-06-13 19:23:07 +02:00
var breakpoint_locations : dict < any >
var BreakpointSigns : list < string >
2024-06-05 21:27:38 +02:00
2024-06-13 19:23:07 +02:00
var evalFromBalloonExpr : bool
var evalFromBalloonExprResult : string
var ignoreEvalError : bool
var evalexpr : string
2024-06-05 21:27:38 +02:00
# Remember the old value of 'signcolumn' for each buffer that it 's set in , so
# that we can restore the value for all buffers .
2024-06-13 19:23:07 +02:00
var signcolumn_buflist : list < number >
2024-06-18 20:18:20 +02:00
var saved_columns : number
2024-06-05 21:27:38 +02:00
2024-06-13 19:23:07 +02:00
var allleft : bool
2024-06-05 21:27:38 +02:00
# This was s :vertical but I cannot use vertical as variable name
2024-06-13 19:23:07 +02:00
var vvertical : bool
var winbar_winids : list < number >
var saved_mousemodel : string
2024-06-18 20:18:20 +02:00
var saved_K_map : dict < any >
var saved_plus_map : dict < any >
var saved_minus_map : dict < any >
2024-06-13 19:23:07 +02:00
def InitScriptVariables ( )
if exists ( 'g:termdebug_config' ) && has_key ( g :termdebug_config , 'use_prompt' )
2024-06-22 16:28:19 +02:00
way = g :termdebug_config ['use_prompt' ] ? Way .Prompt : Way .Terminal
2024-06-13 19:23:07 +02:00
elseif exists ( 'g:termdebug_use_prompt' )
2024-06-22 16:28:19 +02:00
way = g :termdebug_use_prompt ? Way .Prompt : Way .Terminal
2024-06-13 19:23:07 +02:00
elseif has ( 'terminal' ) && ! has ( 'win32' )
2024-06-22 16:28:19 +02:00
way = Way .Terminal
2018-06-17 21:34:11 +02:00
else
2024-06-22 16:28:19 +02:00
way = Way .Prompt
2018-06-17 21:34:11 +02:00
endif
2024-06-13 19:23:07 +02:00
err = ''
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'
2024-06-23 17:25:05 +02:00
promptbufnr = 0
2024-06-18 20:18:20 +02:00
# This is for the "debugged-program" thing
ptybufname = "debugged-program"
2024-06-13 19:23:07 +02:00
ptybufnr = 0
2024-06-18 20:18:20 +02:00
commbufname = "gdb-communication"
2024-06-13 19:23:07 +02:00
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 ( ) ]
2024-06-18 20:18:20 +02:00
saved_columns = &columns
2017-11-12 18:02:06 +01:00
2024-06-13 19:23:07 +02:00
winbar_winids = []
2024-06-05 21:27:38 +02:00
2024-06-20 22:17:34 +02:00
saved_K_map = maparg ( 'K' , 'n' , false , true )
saved_plus_map = maparg ( '+' , 'n' , false , true )
saved_minus_map = maparg ( '-' , 'n' , false , true )
2024-06-13 19:23:07 +02:00
if has ( 'menu' )
2024-06-18 20:18:20 +02:00
saved_mousemodel = &mousemodel
2024-06-13 19:23:07 +02:00
endif
enddef
2024-06-20 22:17:34 +02:00
def SanityCheck ( ) : bool
var gdb_cmd = GetCommand ( ) [0 ]
2024-07-17 20:16:02 +02:00
var cwd = $'{getcwd()}/'
if exists ( '+shellslash' ) && ! &shellslash
# on windows , need to handle backslash
cwd - > substitute ( '\\' , '/' , 'g' )
endif
2024-06-20 22:17:34 +02:00
var is_check_ok = true
# Need either the + terminal feature or + channel and the prompt buffer .
# The terminal feature does not work with gdb on win32 .
2024-06-22 16:28:19 +02:00
if ( way is Way .Prompt ) && ! has ( 'channel' )
2024-06-20 22:17:34 +02:00
err = 'Cannot debug, +channel feature is not supported'
2024-06-22 16:28:19 +02:00
elseif ( way is Way .Prompt ) && ! exists ( '*prompt_setprompt' )
2024-06-20 22:17:34 +02:00
err = 'Cannot debug, missing prompt buffer support'
2024-07-17 20:16:02 +02:00
elseif ( way is Way .Prompt ) && ! empty ( glob ( $'{cwd}{gdb_cmd}' ) )
2024-06-20 22:17:34 +02:00
err = $"You have a file/folder named '{gdb_cmd}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder."
2024-07-17 20:16:02 +02:00
elseif ! empty ( glob ( $'{cwd}{asmbufname}' ) )
2024-06-20 22:17:34 +02:00
err = $"You have a file/folder named '{asmbufname}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder."
2024-07-17 20:16:02 +02:00
elseif ! empty ( glob ( $'{cwd}{varbufname}' ) )
2024-06-20 22:17:34 +02:00
err = $"You have a file/folder named '{varbufname}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder."
elseif ! executable ( gdb_cmd )
err = $"Cannot execute debugger program '{gdb_cmd}'"
endif
if ! empty ( err )
Echoerr ( err )
is_check_ok = false
endif
return is_check_ok
enddef
2024-06-05 21:27:38 +02:00
2024-07-04 17:14:03 +02:00
def DeprecationWarnings ( )
# TODO Remove the deprecated features after 1 Jan 2025 .
var config_param = ''
if exists ( 'g:termdebug_wide' )
config_param = 'g:termdebug_wide'
elseif exists ( 'g:termdebug_popup' )
config_param = 'g:termdebug_popup'
elseif exists ( 'g:termdebugger' )
config_param = 'g:termdebugger'
elseif exists ( 'g:termdebug_variables_window' )
config_param = 'g:termdebug_variables_window'
elseif exists ( 'g:termdebug_disasm_window' )
config_param = 'g:termdebug_disasm_window'
elseif exists ( 'g:termdebug_map_K' )
config_param = 'g:termdebug_map_K'
elseif exists ( 'g:termdebug_use_prompt' )
config_param = 'g:termdebug_use_prompt'
endif
if ! empty ( config_param )
Echowarn ( $"Deprecation Warning : '{config_param}' parameter
\ is deprecated and will be removed in the future . See ':h g:termdebug_config' for alternatives .")
endif
enddef
2024-06-05 21:27:38 +02:00
# 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'
2024-06-12 20:37:05 +02:00
exe $"hi {default}debugPC term=reverse ctermbg=lightblue guibg=lightblue"
2024-06-05 21:27:38 +02:00
elseif new = = # 'dark' && old ! = # 'dark'
2024-06-12 20:37:05 +02:00
exe $"hi {default}debugPC term=reverse ctermbg=darkblue guibg=darkblue"
2024-06-05 21:27:38 +02:00
endif
enddef
# Define the default highlighting , using the current 'background' value .
def InitHighlight ( )
2024-06-23 17:25:05 +02:00
Highlight ( true , '' , &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 >
2024-06-20 22:17:34 +02:00
var cmd : any
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
2024-06-20 22:17:34 +02:00
else
cmd = 'gdb'
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 StartDebug ( bang : bool , ...gdb_args : list < string > )
# First argument is the command to debug , second core file or process ID .
2024-06-12 20:37:05 +02:00
StartDebug_internal ( {gdb_args : gdb_args , bang : bang })
2024-06-05 21:27:38 +02:00
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-12 20:37:05 +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 > )
2024-06-20 22:17:34 +02:00
if g :termdebug_is_running
2024-06-05 21:27:38 +02:00
Echoerr ( 'Terminal debugger already running, cannot run two' )
2018-04-14 18:59:50 +02:00
return
endif
2024-06-20 22:17:34 +02:00
InitScriptVariables ( )
if ! SanityCheck ( )
2019-09-30 20:47:54 +02:00
return
endif
2024-07-04 17:14:03 +02:00
DeprecationWarnings ( )
2019-09-30 20:47:54 +02:00
2021-08-14 21:25:52 +02:00
if exists ( '#User#TermdebugStartPre' )
doauto < nomodeline > User TermdebugStartPre
endif
2024-06-23 17:25:05 +02:00
# Uncomment this line to write logging in "debuglog" .
# call ch_logfile ( 'debuglog' , 'w' )
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
&columns = wide
# If we make the Vim window wider , use the whole left half for the debug
# windows .
2024-06-13 19:23:07 +02:00
allleft = true
2017-09-27 22:23:55 +02:00
endif
2024-06-13 19:23:07 +02:00
vvertical = true
2017-09-14 16:10:38 +02:00
else
2024-06-13 19:23:07 +02:00
vvertical = false
2017-09-14 16:10:38 +02:00
endif
2024-06-22 16:28:19 +02:00
if way is Way .Prompt
2024-06-05 21:27:38 +02:00
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-20 22:17:34 +02:00
g :termdebug_is_running = true
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 ( )
2024-06-23 17:25:05 +02:00
var buf_numbers = [promptbufnr , ptybufnr , commbufnr , asmbufnr , varbufnr ]
2024-06-18 20:18:20 +02:00
for buf_nr in buf_numbers
2024-06-14 12:15:11 -04:00
if buf_nr > 0 && bufexists ( buf_nr )
2024-06-18 20:18:20 +02:00
exe $'bwipe! {buf_nr}'
2024-06-14 12:15:11 -04:00
endif
endfor
2024-06-05 21:27:38 +02:00
enddef
2018-12-22 15:14:49 +01:00
2024-06-13 19:23:07 +02:00
def IsGdbStarted ( ) : bool
var gdbproc_status = job_status ( term_getjob ( gdbbufnr ) )
if gdbproc_status ! = # 'run'
return false
2021-05-30 20:54:13 +02:00
endif
2024-06-13 19:23:07 +02:00
return true
2024-06-05 21:27:38 +02:00
enddef
2024-06-23 17:25:05 +02:00
def CreateProgramPty ( ) : string
2024-06-13 19:23:07 +02:00
ptybufnr = term_start ( 'NONE' , {
2024-06-18 20:18:20 +02:00
term_name : ptybufname ,
2024-06-11 19:10:32 +02:00
vertical : vvertical })
2024-06-13 19:23:07 +02:00
if ptybufnr = = 0
2024-06-23 17:25:05 +02:00
return null_string
2017-09-08 21:10:04 +02:00
endif
2024-06-05 21:27:38 +02:00
ptywin = win_getid ( )
2024-06-13 19:23:07 +02:00
2024-06-05 21:27:38 +02:00
if vvertical
# Assuming the source code window will get a signcolumn , use two more
# columns for that , thus one less for the terminal window .
2024-06-12 20:37:05 +02:00
exe $":{(&columns / 2 - 1)}wincmd |"
2024-06-05 21:27:38 +02:00
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-23 17:25:05 +02:00
return job_info ( term_getjob ( ptybufnr ) ) ['tty_out' ]
enddef
def CreateCommunicationPty ( ) : string
2024-06-05 21:27:38 +02:00
# Create a hidden terminal window to communicate with gdb
2024-06-13 19:23:07 +02:00
commbufnr = term_start ( 'NONE' , {
2024-06-18 20:18:20 +02:00
term_name : commbufname ,
2024-06-11 19:10:32 +02:00
out_cb : function ( 'CommOutput' ) ,
hidden : 1
})
2024-06-13 19:23:07 +02:00
if commbufnr = = 0
2024-06-23 17:25:05 +02:00
return null_string
2017-09-08 21:10:04 +02:00
endif
2024-06-23 17:25:05 +02:00
return job_info ( term_getjob ( commbufnr ) ) ['tty_out' ]
enddef
2017-08-27 16:52:01 +02:00
2024-06-23 17:25:05 +02:00
def CreateGdbConsole ( dict : dict < any > , pty : string , commpty : string ) : string
2024-06-13 19:23:07 +02:00
# Start the gdb buffer
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
2024-06-13 19:23:07 +02:00
gdbbufname = gdb_cmd [0 ]
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-12 20:37:05 +02:00
ch_log ( $'executing "{join(gdb_cmd)}"' )
2024-06-13 19:23:07 +02:00
gdbbufnr = term_start ( gdb_cmd , {
term_name : gdbbufname ,
term_finish : 'close' ,
})
if gdbbufnr = = 0
2024-06-23 17:25:05 +02:00
return 'Failed to open the gdb terminal window'
2017-09-08 21:10:04 +02:00
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
2024-06-20 22:17:34 +02:00
if exists ( 'g:termdebug_config' ) && has_key ( g :termdebug_config , 'timeout' )
counter_max = g :termdebug_config ['timeout' ]
endif
2024-06-05 21:27:38 +02:00
var success = false
2024-06-13 19:23:07 +02:00
while ! success && counter < counter_max
if ! IsGdbStarted ( )
2024-06-23 17:25:05 +02:00
return $'{gdbbufname} exited unexpectedly'
2021-05-30 20:54:13 +02:00
endif
for lnum in range ( 1 , 200 )
2024-06-13 19:23:07 +02:00
if term_getline ( gdbbufnr , lnum ) = ~ 'startupdone'
2024-06-05 21:27:38 +02:00
success = true
2021-05-30 20:54:13 +02:00
endif
endfor
2024-06-05 21:27:38 +02:00
# Each count is 10 ms
counter + = 1
2021-05-30 20:54:13 +02:00
sleep 10 m
endwhile
2024-06-13 19:23:07 +02:00
if ! success
2024-06-23 17:25:05 +02:00
return 'Failed to startup the gdb program.'
2024-06-05 21:27:38 +02:00
endif
# - - - - gdb started . Next , let 's set the MI interface . - - -
# Set arguments to be run .
2024-07-14 16:58:32 +02:00
if ! empty ( proc_args )
2024-06-13 19:23:07 +02:00
term_sendkeys ( gdbbufnr , $"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 .
2024-06-13 19:23:07 +02:00
term_sendkeys ( gdbbufnr , $"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
2024-06-13 19:23:07 +02:00
while ! success && counter < counter_max
if ! IsGdbStarted ( )
2024-06-23 17:25:05 +02:00
return $'{gdbbufname} exited unexpectedly'
2018-12-22 15:14:49 +01:00
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-13 19:23:07 +02:00
var line1 = term_getline ( gdbbufnr , lnum )
var line2 = term_getline ( gdbbufnr , 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
2024-06-13 19:23:07 +02:00
response = $"{line1}{line2}"
2021-11-27 10:57:26 +00:00
if response = ~ 'Undefined command'
2024-06-05 21:27:38 +02:00
# CHECKME : possibly send a "server show version" here
2024-06-23 17:25:05 +02:00
return 'Sorry, your gdb is too old, gdb 7.12 is required'
2021-11-27 10:57:26 +00:00
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 10 m
endwhile
2024-06-13 19:23:07 +02:00
if ! success
2024-06-23 17:25:05 +02:00
return 'Cannot check if your gdb works, continuing anyway'
endif
return ''
enddef
# Open a terminal window without a job , to run the debugged program in .
def StartDebug_term ( dict : dict < any > )
var programpty = CreateProgramPty ( )
if programpty is null_string
Echoerr ( 'Failed to open the program terminal window' )
CloseBuffers ( )
return
endif
var commpty = CreateCommunicationPty ( )
if commpty is null_string
Echoerr ( 'Failed to open the communication terminal window' )
CloseBuffers ( )
return
endif
var err_message = CreateGdbConsole ( dict , programpty , commpty )
if ! empty ( err_message )
Echoerr ( err_message )
CloseBuffers ( )
2024-06-05 21:27:38 +02:00
return
endif
2024-06-23 17:25:05 +02:00
job_setoptions ( term_getjob ( gdbbufnr ) , {exit_cb : function ( 'EndDebug' ) })
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 > )
2024-06-20 22:17:34 +02:00
var gdb_cmd = GetCommand ( )
gdbbufname = gdb_cmd [0 ]
2024-06-05 21:27:38 +02:00
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 ( )
2024-06-23 17:25:05 +02:00
promptbufnr = bufnr ( '' )
prompt_setprompt ( promptbufnr , 'gdb> ' )
2018-06-17 21:34:11 +02:00
set buftype = prompt
2024-06-20 22:17:34 +02:00
exe $"file {gdbbufname}"
2024-05-21 23:33:03 +02:00
2024-06-23 17:25:05 +02:00
prompt_setcallback ( promptbufnr , function ( 'PromptCallback' ) )
prompt_setinterrupt ( promptbufnr , function ( 'PromptInterrupt' ) )
2024-06-05 21:27:38 +02:00
if vvertical
# Assuming the source code window will get a signcolumn , use two more
# columns for that , thus one less for the terminal window .
2024-06-12 20:37:05 +02:00
exe $":{(&columns / 2 - 1)}wincmd |"
2024-06-05 21:27:38 +02:00
endif
var gdb_args = get ( dict , 'gdb_args' , [])
var proc_args = get ( dict , 'proc_args' , [])
# 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
2024-06-12 20:37:05 +02:00
ch_log ( $'executing "{join(gdb_cmd)}"' )
2024-06-05 21:27:38 +02:00
gdbjob = job_start ( gdb_cmd , {
2024-06-23 17:25:05 +02:00
exit_cb : function ( 'EndDebug' ) ,
2024-06-11 19:10:32 +02:00
out_cb : function ( 'GdbOutCallback' ) ,
})
2024-06-05 21:27:38 +02:00
if job_status ( gdbjob ) ! = "run"
Echoerr ( 'Failed to start gdb' )
2024-06-23 17:25:05 +02:00
exe $'bwipe! {promptbufnr}'
2018-06-17 21:34:11 +02:00
return
endif
2024-06-23 17:25:05 +02:00
exe $'au BufUnload <buffer={promptbufnr}> ++once ' ..
2024-06-11 19:10:32 +02:00
'call job_stop(gdbjob, ''kill'')'
2024-06-05 21:27:38 +02:00
# 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-13 19:23:07 +02:00
ptybufnr = 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 .
2024-06-13 19:23:07 +02:00
belowright ptybufnr = term_start ( 'NONE' , {
2024-06-11 19:10:32 +02:00
term_name : 'debugged program' ,
2024-06-13 19:23:07 +02:00
vertical : vvertical
2024-06-11 19:10:32 +02:00
})
2024-06-13 19:23:07 +02:00
if ptybufnr = = 0
2024-06-05 21:27:38 +02:00
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 ( )
2024-06-13 19:23:07 +02:00
var pty = job_info ( term_getjob ( ptybufnr ) ) ['tty_out' ]
2024-06-12 20:37:05 +02:00
SendCommand ( $'tty {pty}' )
2024-06-05 21:27:38 +02:00
# 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' )
2024-06-12 20:37:05 +02:00
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-12 20:37:05 +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 .
2024-06-12 20:37:05 +02:00
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 )
2024-06-12 20:37:05 +02:00
ch_log ( $'sending to gdb: {cmd}' )
2024-06-22 16:28:19 +02:00
if way is Way .Prompt
2024-06-12 20:37:05 +02:00
ch_sendraw ( gdb_channel , $"{cmd}\n" )
2018-06-17 21:34:11 +02:00
else
2024-06-13 19:23:07 +02:00
term_sendkeys ( commbufnr , $"{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 ( )
2024-06-22 16:28:19 +02:00
if way is Way .Prompt
2024-06-09 16:42:45 +02:00
PromptInterrupt ( )
else
SendCommand ( '-exec-interrupt' )
endif
enddef
# Continue the program
def ContinueCommand ( )
2024-06-22 16:28:19 +02:00
if way is Way .Prompt
2024-06-09 16:42:45 +02:00
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 .
2024-06-14 12:33:17 -04:00
def g :TermDebugSendCommand ( cmd : string )
2024-06-22 16:28:19 +02:00
if way is Way .Prompt
2024-06-12 20:37:05 +02:00
ch_sendraw ( gdb_channel , $"{cmd}\n" )
2018-06-17 21:34:11 +02:00
else
2024-06-20 22:17:34 +02:00
var do_continue = false
2024-06-05 21:27:38 +02:00
if ! stopped
2024-06-20 22:17:34 +02:00
do_continue = true
2024-06-09 16:42:45 +02:00
StopCommand ( )
2018-06-17 21:34:11 +02:00
sleep 10 m
endif
2024-06-05 21:27:38 +02:00
# TODO : should we prepend CTRL - U to clear the command ?
2024-06-13 19:23:07 +02:00
term_sendkeys ( gdbbufnr , $"{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
2024-06-13 19:23:07 +02:00
stopped = false
2024-06-05 21:27:38 +02:00
ch_log ( 'assume that program is running after this command' )
SendCommand ( cmd )
2021-11-27 10:57:26 +00:00
else
2024-06-12 20:37:05 +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 )
2024-06-12 20:37:05 +02:00
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' | |
2024-06-11 19:10:32 +02:00
( 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 )
2024-06-13 19:23:07 +02:00
if ! empty ( evalexpr ) && decoded_text = ~ 'A syntax error in expression, near\|No symbol .* in current context'
2024-06-05 21:27:38 +02:00
# Silently drop evaluation errors .
2024-06-13 19:23:07 +02:00
evalexpr = ''
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:
2024-06-13 19:23:07 +02:00
# - remove line breaks ( unless "literal" is true )
2024-06-05 21:27:38 +02:00
# - change \" to "
2024-06-13 19:23:07 +02:00
# - change \\t to \t ( unless "literal" is true )
2024-06-05 21:27:38 +02:00
# - change \0 xhh to \xhh ( disabled for now )
# - change \ooo to octal
# - change \\ to \
def DecodeMessage ( quotedText : string , literal : bool ) : string
if quotedText [0 ] ! = '"'
2024-06-12 20:37:05 +02:00
Echoerr ( $'DecodeMessage(): missing quote in {quotedText}' )
2024-06-05 21:27:38 +02:00
return ''
2018-06-17 21:34:11 +02:00
endif
2024-06-05 21:27:38 +02:00
var msg = quotedText
2024-06-13 19:23:07 +02: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
2024-06-13 19:23:07 +02:00
- > substitute ( '\\000' , NullRepl , 'g' )
- > substitute ( '\\\(\o\o\o\)' , ( m ) = > nr2char ( str2nr ( m [1 ], 8 ) ) , 'g' )
# You could also use - > substitute ( '\\\\\(\o\o\o\)' , '\=nr2char(str2nr(submatch(1), 8))' , "g" )
2024-06-05 21:27:38 +02:00
#\ 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
2024-06-13 19:23:07 +02:00
- > substitute ( '\\\\' , '\' , 'g' )
- > substitute ( NullRepl , '\\000' , 'g' )
2024-06-05 21:27:38 +02:00
if ! literal
2023-08-22 19:30:29 +01:00
return msg
2024-06-11 19:10:32 +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
2024-06-23 17:25:05 +02:00
def EndDebug ( job : any , status : any )
2021-08-14 21:25:52 +02:00
if exists ( '#User#TermdebugStopPre' )
doauto < nomodeline > User TermdebugStopPre
endif
2024-06-23 17:25:05 +02:00
if way is Way .Prompt
ch_log ( "Returning from EndDebug()" )
2024-06-05 21:27:38 +02:00
endif
2018-06-17 21:34:11 +02:00
2024-06-05 21:27:38 +02:00
var curwinid = win_getid ( )
2024-06-23 17:25:05 +02:00
CloseBuffers ( )
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-12 20:37:05 +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-12 20:37:05 +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-18 20:18:20 +02:00
&columns = saved_columns
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-20 22:17:34 +02:00
g :termdebug_is_running = false
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
# 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-12 20:37:05 +02:00
var lnum = search ( $'^{asm_addr}' )
2021-01-11 19:40:15 +01:00
if lnum ! = 0
2024-06-12 20:37:05 +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 , '^\~\"[ ]*' , '' , '' )
2024-06-13 19:23:07 +02:00
- > substitute ( '^=>[ ]*' , '' , '' )
- > substitute ( '\\n\"\r$' , '' , '' )
- > substitute ( '\\n\"$' , '' , '' )
- > substitute ( '\r' , '' , '' )
- > substitute ( '\\t' , ' ' , 'g' )
2024-06-05 21:27:38 +02:00
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
2024-06-12 20:37:05 +02:00
var spaces = repeat ( ' ' , 16 )
setline ( 1 , $'Type{spaces}Name{spaces}Value' )
2024-06-05 21:27:38 +02:00
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' ] ..
2024-06-11 19:10:32 +02:00
repeat ( ' ' , max ( [20 - len ( vardict ['type' ]) , 1 ]) ) ..
vardict ['name' ] ..
repeat ( ' ' , max ( [20 - len ( vardict ['name' ]) , 1 ]) ) ..
vardict ['value' ])
2024-06-05 21:27:38 +02:00
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 ( )
2024-06-20 22:17:34 +02:00
command Winbar InstallWinbar ( true )
2024-06-05 21:27:38 +02:00
2024-06-20 22:17:34 +02:00
var map = true
2022-05-23 21:49:41 +01:00
if exists ( 'g:termdebug_config' )
2024-06-20 22:17:34 +02:00
map = get ( g :termdebug_config , 'map_K' , true )
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-18 20:18:20 +02:00
if ! empty ( saved_K_map ) && ! saved_K_map .buffer | | empty ( saved_K_map )
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-20 22:17:34 +02:00
map = true
2023-08-22 22:19:14 +02:00
if exists ( 'g:termdebug_config' )
2024-06-20 22:17:34 +02:00
map = get ( g :termdebug_config , 'map_plus' , true )
2023-08-22 22:19:14 +02:00
endif
if map
2024-06-18 20:18:20 +02:00
if ! empty ( saved_plus_map ) && ! saved_plus_map .buffer | | empty ( saved_plus_map )
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-20 22:17:34 +02:00
map = true
2023-08-22 22:19:14 +02:00
if exists ( 'g:termdebug_config' )
2024-06-20 22:17:34 +02:00
map = get ( g :termdebug_config , 'map_minus' , true )
2023-08-22 22:19:14 +02:00
endif
if map
2024-06-18 20:18:20 +02:00
if ! empty ( saved_minus_map ) && ! saved_minus_map .buffer | | empty ( saved_minus_map )
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-20 22:17:34 +02:00
InstallWinbar ( false )
2018-03-03 20:47:21 +01:00
2024-06-20 22:17:34 +02:00
var pup = true
2022-05-23 21:49:41 +01:00
if exists ( 'g:termdebug_config' )
2024-06-20 22:17:34 +02:00
pup = get ( g :termdebug_config , 'popup' , true )
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
&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 .
2024-06-20 22:17:34 +02:00
def InstallWinbar ( force : bool )
2024-06-05 21:27:38 +02:00
# install the window toolbar by default , can be disabled in the config
2024-06-20 22:17:34 +02:00
var winbar = true
2023-04-22 22:40:14 +01:00
if exists ( 'g:termdebug_config' )
2024-06-20 22:17:34 +02:00
winbar = get ( g :termdebug_config , 'winbar' , true )
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-18 20:18:20 +02:00
2024-06-19 19:50:32 +02:00
if ! empty ( saved_K_map ) && ! saved_K_map .buffer
2024-06-18 20:18:20 +02:00
mapset ( saved_K_map )
elseif empty ( saved_K_map )
silent ! nunmap K
endif
2024-06-19 19:50:32 +02:00
if ! empty ( saved_plus_map ) && ! saved_plus_map .buffer
2024-06-18 20:18:20 +02:00
mapset ( saved_plus_map )
elseif empty ( saved_plus_map )
silent ! nunmap +
endif
2024-06-19 19:50:32 +02:00
if ! empty ( saved_minus_map ) && ! saved_minus_map .buffer
2024-06-18 20:18:20 +02:00
mapset ( saved_minus_map )
elseif empty ( saved_minus_map )
silent ! nunmap -
2023-08-22 22:19:14 +02:00
endif
2017-09-17 23:03:31 +02:00
2024-06-19 19:50:32 +02:00
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 )
2024-06-18 20:18:20 +02:00
# winbar_winids = []
&mousemodel = saved_mousemodel
try
aunmenu PopUp .- SEP3 -
aunmenu PopUp .Set \ breakpoint
aunmenu PopUp .Clear \ breakpoint
aunmenu PopUp .Run \ until
aunmenu PopUp .Evaluate
catch
# ignore any errors in removing the PopUp menu
endtry
2017-09-17 23:03:31 +02:00
endif
2024-06-05 21:27:38 +02:00
sign_unplace ( 'TermDebug' )
sign_undefine ( 'debugPC' )
sign_undefine ( BreakpointSigns - > map ( "'debugBreakpoint' .. v:val" ) )
enddef
2024-07-17 20:12:29 +02:00
def QuoteArg ( x : string ) : string
# Find all the occurrences of " and \ and escape them and double quote
# the resulting string .
return printf ( '"%s"' , x - > substitute ( '[\\"]' , '\\&' , 'g' ) )
enddef
2024-06-05 21:27:38 +02:00
# :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
2024-06-13 19:23:07 +02:00
stopped = false
2024-06-05 21:27:38 +02:00
ch_log ( 'assume that program is running after this command' )
# Use the fname :lnum format
2024-07-17 20:12:29 +02:00
var AT = empty ( at ) ? QuoteArg ( $"{expand('%:p')}:{line('.')}" ) : at
2024-06-12 20:37:05 +02:00
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 .
2024-06-20 22:17:34 +02:00
var do_continue = false
2024-06-05 21:27:38 +02:00
if ! stopped
2024-06-20 22:17:34 +02:00
do_continue = true
2024-06-09 16:42:45 +02:00
StopCommand ( )
2017-11-12 18:02:06 +01:00
sleep 10 m
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 .
2024-07-17 20:12:29 +02:00
var AT = empty ( at ) ? QuoteArg ( $"{expand('%:p')}:{line('.')}" ) : at
2024-06-05 21:27:38 +02:00
var cmd = ''
if tbreak
2024-06-12 20:37:05 +02:00
cmd = $'-break-insert -t {AT}'
2023-12-14 20:30:26 +01:00
else
2024-06-12 20:37:05 +02:00
cmd = $'-break-insert {AT}'
2023-12-14 20:30:26 +01:00
endif
2024-06-05 21:27:38 +02:00
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" .
2024-06-12 20:37:05 +02:00
SendCommand ( $'-break-delete {id}' )
2024-06-05 21:27:38 +02:00
for subid in keys ( breakpoints [id ])
sign_unplace ( 'TermDebug' ,
2024-06-11 19:10:32 +02:00
{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-12 20:37:05 +02:00
echomsg $'Breakpoint {nr} cleared from line {lnum}.'
2021-11-21 21:13:36 +00:00
else
2024-06-12 20:37:05 +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-12 20:37:05 +02:00
echomsg $'No breakpoint to remove at line {lnum}.'
2024-06-05 21:27:38 +02:00
endif
enddef
def Run ( args : string )
if args ! = ''
2024-06-12 20:37:05 +02:00
SendResumingCommand ( $'-exec-arguments {args}' )
2024-06-05 21:27:38 +02:00
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-12 20:37:05 +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-12 20:37:05 +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-12 20:37:05 +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
2024-06-11 19:10:32 +02:00
- > substitute ( '\\' , '\\\\' , 'g' )
- > substitute ( '"' , '\\"' , 'g' )
2024-06-12 20:37:05 +02:00
SendCommand ( $'-data-evaluate-expression "{expr_escaped}"' )
2024-06-05 21:27:38 +02:00
evalexpr = exprLHS
enddef
# :Evaluate - evaluate what is specified / under the cursor
def Evaluate ( range : number , arg : string )
var expr = GetEvaluationExpression ( range , arg )
2024-06-13 19:23:07 +02:00
echom $"expr: {expr}"
ignoreEvalError = false
2024-06-05 21:27:38 +02:00
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
2024-06-13 19:23:07 +02:00
- > substitute ( '.*value="\(.*\)"' , '\1' , '' )
- > substitute ( '\\"' , '"' , 'g' )
- > substitute ( '\\\\' , '\\' , 'g' )
2024-06-05 21:27:38 +02:00
#\ multi - byte characters arrive in octal form , replace everything but NULL values
2024-06-13 19:23:07 +02:00
- > substitute ( '\\000' , NullRepl , 'g' )
- > substitute ( '\\\(\o\o\o\)' , ( m ) = > nr2char ( str2nr ( m [1 ], 8 ) ) , 'g' )
2024-06-05 21:27:38 +02:00
#\ 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' )
2024-06-13 19:23:07 +02:00
- > substitute ( NullRepl , '\\000' , 'g' )
2024-06-05 21:27:38 +02:00
if evalFromBalloonExpr
2024-06-13 19:23:07 +02:00
if empty ( evalFromBalloonExprResult )
2024-06-12 20:37:05 +02:00
evalFromBalloonExprResult = $'{evalexpr}: {value}'
2017-11-18 18:52:04 +01:00
else
2024-06-12 20:37:05 +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-12 20:37:05 +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 .
2024-06-13 19:23:07 +02:00
ignoreEvalError = true
2024-06-12 20:37:05 +02:00
SendEval ( $'*{evalexpr}' )
2017-11-18 18:52:04 +01:00
else
2024-06-13 19:23:07 +02:00
evalFromBalloonExpr = false
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-13 19:23:07 +02:00
evalFromBalloonExpr = true
2024-06-05 21:27:38 +02:00
evalFromBalloonExprResult = ''
2024-06-13 19:23:07 +02:00
ignoreEvalError = true
2024-06-05 21:27:38 +02:00
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 .
2024-06-13 19:23:07 +02:00
ignoreEvalError = false
evalFromBalloonExpr = true
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 ( )
2024-06-20 22:17:34 +02:00
InstallWinbar ( false )
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'
2024-06-12 20:37:05 +02:00
exe $'{mdf} :60new'
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-13 19:23:07 +02:00
if asmbufnr > 0 && bufexists ( asmbufnr )
exe $'buffer {asmbufnr}'
2024-05-21 23:33:03 +02:00
else
2024-06-20 22:17:34 +02:00
exe $"silent file {asmbufname}"
asmbufnr = bufnr ( asmbufname )
2021-01-11 19:40:15 +01:00
endif
2024-06-05 21:27:38 +02:00
if mdf ! = 'vert' && GetDisasmWindowHeight ( ) > 0
2024-06-12 20:37:05 +02:00
exe $'resize {GetDisasmWindowHeight()}'
2021-01-11 19:40:15 +01:00
endif
endif
2024-06-05 21:27:38 +02:00
if asm_addr ! = ''
2024-06-12 20:37:05 +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
if stopped
SendCommand ( 'disassemble $pc' )
2021-01-11 19:40:15 +01:00
endif
else
2024-06-12 20:37:05 +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'
2024-06-12 20:37:05 +02:00
exe $'{mdf} :60new'
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-20 22:17:34 +02:00
# If exists , then open , otherwise create
2024-06-13 19:23:07 +02:00
if varbufnr > 0 && bufexists ( varbufnr )
exe $'buffer {varbufnr}'
2024-05-21 23:33:03 +02:00
else
2024-06-20 22:17:34 +02:00
exe $"silent file {varbufname}"
varbufnr = bufnr ( varbufname )
2023-05-13 16:29:15 +02:00
endif
2024-06-05 21:27:38 +02:00
if mdf ! = 'vert' && GetVariablesWindowHeight ( ) > 0
2024-06-12 20:37:05 +02:00
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' )
2024-06-20 22:17:34 +02:00
stopped = true
2024-06-05 21:27:38 +02:00
if msg = ~ '^\*stopped,reason="exited-normally"'
2024-06-13 19:23:07 +02:00
running = false
2023-05-13 16:29:15 +02:00
endif
2024-06-05 21:27:38 +02:00
elseif msg = ~ '^\*running'
ch_log ( 'program running' )
2024-06-13 19:23:07 +02:00
stopped = false
running = true
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 )
2024-06-12 20:37:05 +02:00
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-12 20:37:05 +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-13 19:23:07 +02:00
if running && stopped && bufwinnr ( varbufname ) ! = -1
2024-06-05 21:27:38 +02:00
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' )
2024-06-12 20:37:05 +02:00
echomsg $"different fname: '{expand('%:p')}' vs '{fnamemodify(fname, ':p')}'"
2023-08-21 02:06:49 +08:00
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-11 19:10:32 +02:00
| echo 'Warning: file is being edited elsewhere'
| echohl None
2024-06-13 19:23:07 +02:00
| 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
2024-06-12 20:37:05 +02:00
exe $'split {fnameescape(fname)}'
2024-06-05 21:27:38 +02:00
sourcewin = win_getid ( )
2024-06-20 22:17:34 +02:00
InstallWinbar ( false )
2022-03-19 15:18:53 +00:00
else
2024-06-12 20:37:05 +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-12 20:37:05 +02:00
exe $":{lnum}"
2021-11-16 19:18:26 +00:00
normal ! zv
2024-06-12 20:37:05 +02:00
sign_unplace ( 'TermDebug' , {id : pc_id })
2024-06-05 21:27:38 +02:00
sign_place ( pc_id , 'TermDebug' , 'debugPC' , fname ,
2024-06-11 19:10:32 +02:00
{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 ! = ''
2024-06-12 20:37:05 +02:00
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-12 20:37:05 +02:00
sign_define ( $'debugBreakpoint{nr}' ,
2024-06-11 19:10:32 +02:00
{text : slice ( label , 0 , 2 ) ,
texthl : hiName })
2024-06-05 21:27:38 +02:00
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' , '' )
2024-06-12 20:37:05 +02:00
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 )
2024-06-12 20:37:05 +02:00
posMsg = $' at line {lnum}.'
2021-11-27 10:57:26 +00:00
else
2024-06-12 20:37:05 +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-12 20:37:05 +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' ,
2024-06-12 20:37:05 +02:00
$'debugBreakpoint{nr}' , entry ['fname' ],
2024-06-11 19:10:32 +02:00
{lnum : entry ['lnum' ], priority : 110 })
2024-06-05 21:27:38 +02:00
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' ,
2024-06-12 20:37:05 +02:00
{id : Breakpoint2SignNumber ( str2nr ( id ) , str2nr ( subid ) ) })
2024-06-05 21:27:38 +02:00
remove ( entry , 'placed' )
2018-12-09 15:53:01 +01:00
endif
endfor
2024-06-05 21:27:38 +02:00
remove ( breakpoints , id )
2024-06-12 20:37:05 +02:00
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
2024-06-12 20:37:05 +02:00
ch_log ( $'Detected process ID: {pid}' )
2024-06-05 21:27:38 +02:00
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-16 08:35:57 +02:00
PlaceSign ( str2nr ( id ) , str2nr ( 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