Ai
12 Star 47 Fork 0

Gitee 极速下载/Vim

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/vim/vim
克隆/下载
test_debugger.vim 47.84 KB
一键复制 编辑 原始数据 按行查看 历史
zeertzjq 提交于 2025-12-02 03:43 +08:00 . patch 9.1.1943: Memory leak with :breakadd expr
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569
" Tests for the Vim script debug commands
source util/screendump.vim
CheckRunVimInTerminal
func CheckCWD()
" Check that the longer lines don't wrap due to the length of the script name
" in cwd. Need to subtract by 1 since Vim will still wrap the message if it
" just fits.
let script_len = len( getcwd() .. '/Xtest1.vim' )
let longest_line = len( 'Breakpoint in "" line 1' )
if script_len > ( 75 - longest_line - 1 )
throw 'Skipped: Your CWD has too many characters'
endif
endfunc
command! -nargs=0 -bar CheckCWD call CheckCWD()
" "options" argument can contain:
" 'msec' - time to wait for a match
" 'match' - "pattern" to use "lines" as pattern instead of text
def s:CheckDbgOutput(buf: number, lines: list<string>, options = {})
# Verify the expected output
var lnum = 20 - len(lines)
var msec = get(options, 'msec', 1000)
for l in lines
if get(options, 'match', 'equal') ==# 'pattern'
g:WaitForAssert(() => assert_match(l, term_getline(buf, lnum)), msec)
else
g:WaitForAssert(() => assert_equal(l, term_getline(buf, lnum)), msec)
endif
lnum += 1
endfor
enddef
" Run a Vim debugger command
" If the expected output argument is supplied, then check for it.
def s:RunDbgCmd(buf: number, cmd: string, ...extra: list<any>)
term_sendkeys(buf, cmd .. "\r")
g:TermWait(buf)
if len(extra) > 0
var options = {match: 'equal'}
if len(extra) > 1
extend(options, extra[1])
endif
s:CheckDbgOutput(buf, extra[0], options)
endif
enddef
" Debugger tests
def Test_Debugger()
# Create a Vim script with some functions
var lines =<< trim END
func Foo()
let var1 = 1
let var2 = Bar(var1) + 9
return var2
endfunc
func Bar(var)
let var1 = 2 + a:var
let var2 = Bazz(var1) + 4
return var2
endfunc
func Bazz(var)
try
let var1 = 3 + a:var
let var3 = "another var"
let var3 = "value2"
catch
let var4 = "exception"
endtry
return var1
endfunc
def Vim9Func()
for cmd in ['confirm', 'xxxxxxx']
for _ in [1, 2]
echo cmd
endfor
endfor
enddef
END
writefile(lines, 'XtestDebug.vim', 'D')
# Start Vim in a terminal
var buf = g:RunVimInTerminal('-S XtestDebug.vim', {})
# Start the Vim debugger
s:RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()'])
# Create a few stack frames by stepping through functions
s:RunDbgCmd(buf, 'step', ['line 1: let var1 = 1'])
s:RunDbgCmd(buf, 'step', ['line 2: let var2 = Bar(var1) + 9'])
s:RunDbgCmd(buf, 'step', ['line 1: let var1 = 2 + a:var'])
s:RunDbgCmd(buf, 'step', ['line 2: let var2 = Bazz(var1) + 4'])
s:RunDbgCmd(buf, 'step', ['line 1: try'])
s:RunDbgCmd(buf, 'step', ['line 2: let var1 = 3 + a:var'])
s:RunDbgCmd(buf, 'step', ['line 3: let var3 = "another var"'])
# check backtrace
s:RunDbgCmd(buf, 'backtrace', [
' 2 function Foo[2]',
' 1 Bar[2]',
'->0 Bazz',
'line 3: let var3 = "another var"'])
# Check variables in different stack frames
s:RunDbgCmd(buf, 'echo var1', ['6'])
s:RunDbgCmd(buf, 'up')
s:RunDbgCmd(buf, 'back', [
' 2 function Foo[2]',
'->1 Bar[2]',
' 0 Bazz',
'line 3: let var3 = "another var"'])
s:RunDbgCmd(buf, 'echo var1', ['3'])
s:RunDbgCmd(buf, 'u')
s:RunDbgCmd(buf, 'bt', [
'->2 function Foo[2]',
' 1 Bar[2]',
' 0 Bazz',
'line 3: let var3 = "another var"'])
s:RunDbgCmd(buf, 'echo var1', ['1'])
# Undefined variables
s:RunDbgCmd(buf, 'step')
s:RunDbgCmd(buf, 'frame 2')
s:RunDbgCmd(buf, 'echo var3', [
'Error detected while processing function Foo[2]..Bar[2]..Bazz:',
'line 4:',
'E121: Undefined variable: var3'])
# var3 is defined in this level with some other value
s:RunDbgCmd(buf, 'fr 0')
s:RunDbgCmd(buf, 'echo var3', ['another var'])
s:RunDbgCmd(buf, 'step')
s:RunDbgCmd(buf, '')
s:RunDbgCmd(buf, '')
s:RunDbgCmd(buf, '')
s:RunDbgCmd(buf, '')
s:RunDbgCmd(buf, 'step', [
'function Foo[2]..Bar',
'line 3: End of function'])
s:RunDbgCmd(buf, 'up')
# Undefined var2
s:RunDbgCmd(buf, 'echo var2', [
'Error detected while processing function Foo[2]..Bar:',
'line 3:',
'E121: Undefined variable: var2'])
# Var2 is defined with 10
s:RunDbgCmd(buf, 'down')
s:RunDbgCmd(buf, 'echo var2', ['10'])
# Backtrace movements
s:RunDbgCmd(buf, 'b', [
' 1 function Foo[2]',
'->0 Bar',
'line 3: End of function'])
# next command cannot go down, we are on bottom
s:RunDbgCmd(buf, 'down', ['frame is zero'])
s:RunDbgCmd(buf, 'up')
# next command cannot go up, we are on top
s:RunDbgCmd(buf, 'up', ['frame at highest level: 1'])
s:RunDbgCmd(buf, 'where', [
'->1 function Foo[2]',
' 0 Bar',
'line 3: End of function'])
# fil is not frame or finish, it is file
s:RunDbgCmd(buf, 'fil', ['"[No Name]" --No lines in buffer--'])
# relative backtrace movement
s:RunDbgCmd(buf, 'fr -1')
s:RunDbgCmd(buf, 'frame', [
' 1 function Foo[2]',
'->0 Bar',
'line 3: End of function'])
s:RunDbgCmd(buf, 'fr +1')
s:RunDbgCmd(buf, 'fram', [
'->1 function Foo[2]',
' 0 Bar',
'line 3: End of function'])
# go beyond limits does not crash
s:RunDbgCmd(buf, 'fr 100', ['frame at highest level: 1'])
s:RunDbgCmd(buf, 'fra', [
'->1 function Foo[2]',
' 0 Bar',
'line 3: End of function'])
s:RunDbgCmd(buf, 'frame -40', ['frame is zero'])
s:RunDbgCmd(buf, 'fram', [
' 1 function Foo[2]',
'->0 Bar',
'line 3: End of function'])
# final result 19
s:RunDbgCmd(buf, 'cont', ['19'])
# breakpoints tests
# Start a debug session, so that reading the last line from the terminal
# works properly.
s:RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()'])
# No breakpoints
s:RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
# Place some breakpoints
s:RunDbgCmd(buf, 'breaka func Bar')
s:RunDbgCmd(buf, 'breaklis', [' 1 func Bar line 1'])
s:RunDbgCmd(buf, 'breakadd func 3 Bazz')
s:RunDbgCmd(buf, 'breaklist', [' 1 func Bar line 1',
' 2 func Bazz line 3'])
# Check whether the breakpoints are hit
s:RunDbgCmd(buf, 'cont', [
'Breakpoint in "Bar" line 1',
'function Foo[2]..Bar',
'line 1: let var1 = 2 + a:var'])
s:RunDbgCmd(buf, 'cont', [
'Breakpoint in "Bazz" line 3',
'function Foo[2]..Bar[2]..Bazz',
'line 3: let var3 = "another var"'])
# Delete the breakpoints
s:RunDbgCmd(buf, 'breakd 1')
s:RunDbgCmd(buf, 'breakli', [' 2 func Bazz line 3'])
s:RunDbgCmd(buf, 'breakdel func 3 Bazz')
s:RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
s:RunDbgCmd(buf, 'cont')
# Make sure the breakpoints are removed
s:RunDbgCmd(buf, ':echo Foo()', ['19'])
# Delete a non-existing breakpoint
s:RunDbgCmd(buf, ':breakdel 2', ['E161: Breakpoint not found: 2'])
# Expression breakpoint
s:RunDbgCmd(buf, ':breakadd func 2 Bazz')
s:RunDbgCmd(buf, ':echo Bazz(1)', [
'Entering Debug mode. Type "cont" to continue.',
'function Bazz',
'line 2: let var1 = 3 + a:var'])
s:RunDbgCmd(buf, 'step')
s:RunDbgCmd(buf, 'step')
s:RunDbgCmd(buf, 'breaka expr var3')
s:RunDbgCmd(buf, 'breakl', [' 3 func Bazz line 2',
\ ' 4 expr var3'])
s:RunDbgCmd(buf, 'cont', ['Breakpoint in "Bazz" line 5',
'Oldval = "''another var''"',
'Newval = "''value2''"',
'function Bazz',
'line 5: catch'])
s:RunDbgCmd(buf, 'breakdel *')
s:RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
# Check for error cases
s:RunDbgCmd(buf, 'breakadd abcd', [
'Error detected while processing function Bazz:',
'line 5:',
'E475: Invalid argument: abcd'])
s:RunDbgCmd(buf, 'breakadd func', ['E475: Invalid argument: func'])
s:RunDbgCmd(buf, 'breakadd func 2', ['E475: Invalid argument: func 2'])
s:RunDbgCmd(buf, 'breaka func a()', ['E475: Invalid argument: func a()'])
s:RunDbgCmd(buf, 'breakd abcd', ['E475: Invalid argument: abcd'])
s:RunDbgCmd(buf, 'breakd func', ['E475: Invalid argument: func'])
s:RunDbgCmd(buf, 'breakd func a()', ['E475: Invalid argument: func a()'])
s:RunDbgCmd(buf, 'breakd func a', ['E161: Breakpoint not found: func a'])
s:RunDbgCmd(buf, 'breakd expr', ['E475: Invalid argument: expr'])
s:RunDbgCmd(buf, 'breakd expr x', ['E161: Breakpoint not found: expr x'])
# finish the current function
s:RunDbgCmd(buf, 'finish', [
'function Bazz',
'line 8: End of function'])
s:RunDbgCmd(buf, 'cont')
# Test for :next
s:RunDbgCmd(buf, ':debug echo Bar(1)')
s:RunDbgCmd(buf, 'step')
s:RunDbgCmd(buf, 'next')
s:RunDbgCmd(buf, '', [
'function Bar',
'line 3: return var2'])
s:RunDbgCmd(buf, 'c')
# Test for :interrupt
s:RunDbgCmd(buf, ':debug echo Bazz(1)')
s:RunDbgCmd(buf, 'step')
s:RunDbgCmd(buf, 'step')
s:RunDbgCmd(buf, 'interrupt', [
'Exception thrown: Vim:Interrupt',
'function Bazz',
'line 5: catch'])
s:RunDbgCmd(buf, 'c')
# Test showing local variable in :def function
s:RunDbgCmd(buf, ':breakadd func 2 Vim9Func')
s:RunDbgCmd(buf, ':call Vim9Func()', ['line 2: for _ in [1, 2]'])
s:RunDbgCmd(buf, 'next', ['line 2: for _ in [1, 2]'])
s:RunDbgCmd(buf, 'echo cmd', ['confirm'])
s:RunDbgCmd(buf, 'breakdel *')
s:RunDbgCmd(buf, 'cont')
# Test for :quit
s:RunDbgCmd(buf, ':debug echo Foo()')
s:RunDbgCmd(buf, 'breakdel *')
s:RunDbgCmd(buf, 'breakadd func 3 Foo')
s:RunDbgCmd(buf, 'breakadd func 3 Bazz')
s:RunDbgCmd(buf, 'cont', [
'Breakpoint in "Bazz" line 3',
'function Foo[2]..Bar[2]..Bazz',
'line 3: let var3 = "another var"'])
s:RunDbgCmd(buf, 'quit', [
'Breakpoint in "Foo" line 3',
'function Foo',
'line 3: return var2'])
s:RunDbgCmd(buf, 'breakdel *')
s:RunDbgCmd(buf, 'quit')
s:RunDbgCmd(buf, 'enew! | only!')
g:StopVimInTerminal(buf)
enddef
func Test_Debugger_breakadd()
" Tests for :breakadd file and :breakadd here
" Breakpoints should be set before sourcing the file
let lines =<< trim END
let var1 = 10
let var2 = 20
let var3 = 30
let var4 = 40
END
call writefile(lines, 'XdebugBreakadd.vim', 'D')
" Start Vim in a terminal
let buf = RunVimInTerminal('XdebugBreakadd.vim', {})
call s:RunDbgCmd(buf, ':breakadd file 2 XdebugBreakadd.vim')
call s:RunDbgCmd(buf, ':4 | breakadd here')
call s:RunDbgCmd(buf, ':source XdebugBreakadd.vim', ['line 2: let var2 = 20'])
call s:RunDbgCmd(buf, 'cont', ['line 4: let var4 = 40'])
call s:RunDbgCmd(buf, 'cont')
call StopVimInTerminal(buf)
%bw!
call assert_fails('breakadd here', 'E32:')
call assert_fails('breakadd file Xtest.vim /\)/', 'E55:')
endfunc
" Test for expression breakpoint set using ":breakadd expr <expr>"
" FIXME: This doesn't seem to work as documented. The breakpoint is not
" triggered until the next function call.
func Test_Debugger_breakadd_expr()
CheckCWD
let lines =<< trim END
func Foo()
eval 1
eval 2
endfunc
let g:Xtest_var += 1
call Foo()
let g:Xtest_var += 1
call Foo()
END
call writefile(lines, 'XbreakExpr.vim', 'D')
" Start Vim in a terminal
let buf = RunVimInTerminal('XbreakExpr.vim', {})
call s:RunDbgCmd(buf, ':let g:Xtest_var = 10')
call s:RunDbgCmd(buf, ':breakadd expr g:Xtest_var')
let expected =<< trim eval END
Oldval = "10"
Newval = "11"
{fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo
line 1: eval 1
END
call s:RunDbgCmd(buf, ':source %', expected)
let expected =<< trim eval END
Oldval = "11"
Newval = "12"
{fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo
line 1: eval 1
END
call s:RunDbgCmd(buf, 'cont', expected)
call s:RunDbgCmd(buf, 'cont')
" Check the behavior without the g: prefix.
" FIXME: The Oldval and Newval don't look right here.
call s:RunDbgCmd(buf, ':breakdel *')
call s:RunDbgCmd(buf, ':breakadd expr Xtest_var')
let expected =<< trim eval END
Oldval = "13"
Newval = "(does not exist)"
{fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo
line 1: eval 1
END
call s:RunDbgCmd(buf, ':source %', expected)
let expected =<< trim eval END
{fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo
line 2: eval 2
END
call s:RunDbgCmd(buf, 'cont', expected)
let expected =<< trim eval END
Oldval = "14"
Newval = "(does not exist)"
{fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo
line 1: eval 1
END
call s:RunDbgCmd(buf, 'cont', expected)
let expected =<< trim eval END
{fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo
line 2: eval 2
END
call s:RunDbgCmd(buf, 'cont', expected)
call s:RunDbgCmd(buf, 'cont')
call StopVimInTerminal(buf)
endfunc
def Test_Debugger_breakadd_vim9_expr()
var lines =<< trim END
vim9script
func g:EarlyFunc()
endfunc
breakadd expr DoesNotExist()
func g:LaterFunc()
endfunc
breakdel *
END
writefile(lines, 'XdebugBreak9expr.vim', 'D')
# Start Vim in a terminal
var buf = g:RunVimInTerminal('-S XdebugBreak9expr.vim', {wait_for_ruler: 0})
call g:TermWait(buf, g:RunningWithValgrind() ? 1000 : 50)
# Despite the failure the functions are defined
s:RunDbgCmd(buf, ':function g:EarlyFunc',
['function EarlyFunc()', 'endfunction'], {match: 'pattern'})
s:RunDbgCmd(buf, ':function g:LaterFunc',
['function LaterFunc()', 'endfunction'], {match: 'pattern'})
call g:StopVimInTerminal(buf)
enddef
def Test_Debugger_break_at_return()
var lines =<< trim END
vim9script
def g:GetNum(): number
return 1
+ 2
+ 3
enddef
breakadd func GetNum
END
writefile(lines, 'XdebugBreakRet.vim', 'D')
# Start Vim in a terminal
var buf = g:RunVimInTerminal('-S XdebugBreakRet.vim', {wait_for_ruler: 0})
call g:TermWait(buf, g:RunningWithValgrind() ? 1000 : 50)
s:RunDbgCmd(buf, ':call GetNum()',
['line 1: return 1 + 2 + 3'], {match: 'pattern'})
call g:StopVimInTerminal(buf)
enddef
func Test_Backtrace_Through_Source()
CheckCWD
let file1 =<< trim END
func SourceAnotherFile()
source Xtest2.vim
endfunc
func CallAFunction()
call SourceAnotherFile()
call File2Function()
endfunc
func GlobalFunction()
call CallAFunction()
endfunc
END
call writefile(file1, 'Xtest1.vim', 'D')
let file2 =<< trim END
func DoAThing()
echo "DoAThing"
endfunc
func File2Function()
call DoAThing()
endfunc
call File2Function()
END
call writefile(file2, 'Xtest2.vim', 'D')
let buf = RunVimInTerminal('-S Xtest1.vim', {})
call s:RunDbgCmd(buf,
\ ':debug call GlobalFunction()',
\ ['cmd: call GlobalFunction()'])
call s:RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
call s:RunDbgCmd(buf, 'backtrace', ['>backtrace',
\ '->0 function GlobalFunction',
\ 'line 1: call CallAFunction()'])
call s:RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
call s:RunDbgCmd(buf, 'backtrace', ['>backtrace',
\ ' 2 function GlobalFunction[1]',
\ ' 1 CallAFunction[1]',
\ '->0 SourceAnotherFile',
\ 'line 1: source Xtest2.vim'])
" Step into the 'source' command. Note that we print the full trace all the
" way though the source command.
call s:RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()'])
call s:RunDbgCmd( buf, 'up' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ '->1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'up' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 3 function GlobalFunction[1]',
\ '->2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'up' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ '->3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'up', [ 'frame at highest level: 3' ] )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ '->3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'down' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 3 function GlobalFunction[1]',
\ '->2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'down' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ '->1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'down' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'down', [ 'frame is zero' ] )
" step until we have another meaningful trace
call s:RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
call s:RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 9: call File2Function()'])
call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
call s:RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 5 function GlobalFunction[1]',
\ ' 4 CallAFunction[1]',
\ ' 3 SourceAnotherFile[1]',
\ ' 2 script ' .. getcwd() .. '/Xtest2.vim[9]',
\ ' 1 function File2Function[1]',
\ '->0 DoAThing',
\ 'line 1: echo "DoAThing"'])
" Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
call s:RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
call s:RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 1 function GlobalFunction[1]',
\ '->0 CallAFunction',
\ 'line 2: call File2Function()'])
call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 2 function GlobalFunction[1]',
\ ' 1 CallAFunction[2]',
\ '->0 File2Function',
\ 'line 1: call DoAThing()'])
call StopVimInTerminal(buf)
endfunc
func Test_Backtrace_Autocmd()
CheckCWD
let file1 =<< trim END
func SourceAnotherFile()
source Xtest2.vim
endfunc
func CallAFunction()
call SourceAnotherFile()
call File2Function()
endfunc
func GlobalFunction()
call CallAFunction()
endfunc
au User TestGlobalFunction :call GlobalFunction() | echo "Done"
END
call writefile(file1, 'Xtest1.vim', 'D')
let file2 =<< trim END
func DoAThing()
echo "DoAThing"
endfunc
func File2Function()
call DoAThing()
endfunc
call File2Function()
END
call writefile(file2, 'Xtest2.vim', 'D')
let buf = RunVimInTerminal('-S Xtest1.vim', {})
call s:RunDbgCmd(buf,
\ ':debug doautocmd User TestGlobalFunction',
\ ['cmd: doautocmd User TestGlobalFunction'])
call s:RunDbgCmd(buf, 'step', ['cmd: call GlobalFunction() | echo "Done"'])
" At this point the only thing in the stack is the autocommand
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ '->0 User Autocommands for "TestGlobalFunction"',
\ 'cmd: call GlobalFunction() | echo "Done"'])
" And now we're back into the call stack
call s:RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 1 User Autocommands for "TestGlobalFunction"',
\ '->0 function GlobalFunction',
\ 'line 1: call CallAFunction()'])
call s:RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()'])
call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 3 User Autocommands for "TestGlobalFunction"',
\ ' 2 function GlobalFunction[1]',
\ ' 1 CallAFunction[1]',
\ '->0 SourceAnotherFile',
\ 'line 1: source Xtest2.vim'])
" Step into the 'source' command. Note that we print the full trace all the
" way though the source command.
call s:RunDbgCmd(buf, 'step', ['line 1: func DoAThing()'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 4 User Autocommands for "TestGlobalFunction"',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()'])
call s:RunDbgCmd( buf, 'up' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 4 User Autocommands for "TestGlobalFunction"',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ '->1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'up' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 4 User Autocommands for "TestGlobalFunction"',
\ ' 3 function GlobalFunction[1]',
\ '->2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'up' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 4 User Autocommands for "TestGlobalFunction"',
\ '->3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'up' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ '->4 User Autocommands for "TestGlobalFunction"',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'up', [ 'frame at highest level: 4' ] )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ '->4 User Autocommands for "TestGlobalFunction"',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'down' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 4 User Autocommands for "TestGlobalFunction"',
\ '->3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'down' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 4 User Autocommands for "TestGlobalFunction"',
\ ' 3 function GlobalFunction[1]',
\ '->2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'down' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 4 User Autocommands for "TestGlobalFunction"',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ '->1 SourceAnotherFile[1]',
\ ' 0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'down' )
call s:RunDbgCmd( buf, 'backtrace', [
\ '>backtrace',
\ ' 4 User Autocommands for "TestGlobalFunction"',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 1: func DoAThing()' ] )
call s:RunDbgCmd( buf, 'down', [ 'frame is zero' ] )
" step until we have another meaningful trace
call s:RunDbgCmd(buf, 'step', ['line 5: func File2Function()'])
call s:RunDbgCmd(buf, 'step', ['line 9: call File2Function()'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 4 User Autocommands for "TestGlobalFunction"',
\ ' 3 function GlobalFunction[1]',
\ ' 2 CallAFunction[1]',
\ ' 1 SourceAnotherFile[1]',
\ '->0 script ' .. getcwd() .. '/Xtest2.vim',
\ 'line 9: call File2Function()'])
call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
call s:RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 6 User Autocommands for "TestGlobalFunction"',
\ ' 5 function GlobalFunction[1]',
\ ' 4 CallAFunction[1]',
\ ' 3 SourceAnotherFile[1]',
\ ' 2 script ' .. getcwd() .. '/Xtest2.vim[9]',
\ ' 1 function File2Function[1]',
\ '->0 DoAThing',
\ 'line 1: echo "DoAThing"'])
" Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim
call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
call s:RunDbgCmd(buf, 'step', ['line 10: End of sourced file'])
call s:RunDbgCmd(buf, 'step', ['line 1: End of function'])
call s:RunDbgCmd(buf, 'step', ['line 2: call File2Function()'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 2 User Autocommands for "TestGlobalFunction"',
\ ' 1 function GlobalFunction[1]',
\ '->0 CallAFunction',
\ 'line 2: call File2Function()'])
call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 3 User Autocommands for "TestGlobalFunction"',
\ ' 2 function GlobalFunction[1]',
\ ' 1 CallAFunction[2]',
\ '->0 File2Function',
\ 'line 1: call DoAThing()'])
" Now unwind so that we get back to the original autocommand (and the second
" cmd echo "Done")
call s:RunDbgCmd(buf, 'finish', ['line 1: End of function'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 3 User Autocommands for "TestGlobalFunction"',
\ ' 2 function GlobalFunction[1]',
\ ' 1 CallAFunction[2]',
\ '->0 File2Function',
\ 'line 1: End of function'])
call s:RunDbgCmd(buf, 'finish', ['line 2: End of function'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 2 User Autocommands for "TestGlobalFunction"',
\ ' 1 function GlobalFunction[1]',
\ '->0 CallAFunction',
\ 'line 2: End of function'])
call s:RunDbgCmd(buf, 'finish', ['line 1: End of function'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 1 User Autocommands for "TestGlobalFunction"',
\ '->0 function GlobalFunction',
\ 'line 1: End of function'])
call s:RunDbgCmd(buf, 'step', ['cmd: echo "Done"'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ '->0 User Autocommands for "TestGlobalFunction"',
\ 'cmd: echo "Done"'])
call StopVimInTerminal(buf)
endfunc
func Test_Backtrace_CmdLine()
CheckCWD
let file1 =<< trim END
func SourceAnotherFile()
source Xtest2.vim
endfunc
func CallAFunction()
call SourceAnotherFile()
call File2Function()
endfunc
func GlobalFunction()
call CallAFunction()
endfunc
au User TestGlobalFunction :call GlobalFunction() | echo "Done"
END
call writefile(file1, 'Xtest1.vim', 'D')
let file2 =<< trim END
func DoAThing()
echo "DoAThing"
endfunc
func File2Function()
call DoAThing()
endfunc
call File2Function()
END
call writefile(file2, 'Xtest2.vim', 'D')
let buf = RunVimInTerminal(
\ '-S Xtest1.vim -c "debug call GlobalFunction()"',
\ {'wait_for_ruler': 0})
" Need to wait for the vim-in-terminal to be ready.
" With valgrind this can take quite long.
call s:CheckDbgOutput(buf, ['command line',
\ 'cmd: call GlobalFunction()'], #{msec: 5000})
" At this point the only thing in the stack is the cmdline
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ '->0 command line',
\ 'cmd: call GlobalFunction()'])
" And now we're back into the call stack
call s:RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '>backtrace',
\ ' 1 command line',
\ '->0 function GlobalFunction',
\ 'line 1: call CallAFunction()'])
call StopVimInTerminal(buf)
endfunc
func Test_Backtrace_DefFunction()
CheckCWD
let file1 =<< trim END
vim9script
import './Xtest2.vim' as imp
def SourceAnotherFile()
source Xtest2.vim
enddef
def CallAFunction()
SourceAnotherFile()
imp.File2Function()
enddef
def g:GlobalFunction()
var some = "some var"
CallAFunction()
enddef
defcompile
END
call writefile(file1, 'Xtest1.vim', 'D')
let file2 =<< trim END
vim9script
def DoAThing(): number
var a = 100 * 2
a += 3
return a
enddef
export def File2Function()
DoAThing()
enddef
defcompile
File2Function()
END
call writefile(file2, 'Xtest2.vim', 'D')
let buf = RunVimInTerminal('-S Xtest1.vim', {})
call s:RunDbgCmd(buf,
\ ':debug call GlobalFunction()',
\ ['cmd: call GlobalFunction()'])
call s:RunDbgCmd(buf, 'step', ['line 1: var some = "some var"'])
call s:RunDbgCmd(buf, 'step', ['line 2: CallAFunction()'])
call s:RunDbgCmd(buf, 'echo some', ['some var'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '\V>backtrace',
\ '\V->0 function GlobalFunction',
\ '\Vline 2: CallAFunction()',
\ ],
\ #{match: 'pattern'})
call s:RunDbgCmd(buf, 'step', ['line 1: SourceAnotherFile()'])
call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
" Repeated line, because we first are in the compiled function before the
" EXEC and then in do_cmdline() before the :source command.
call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
call s:RunDbgCmd(buf, 'step', ['line 1: vim9script'])
call s:RunDbgCmd(buf, 'step', ['line 3: def DoAThing(): number'])
call s:RunDbgCmd(buf, 'step', ['line 9: export def File2Function()'])
call s:RunDbgCmd(buf, 'step', ['line 13: defcompile'])
call s:RunDbgCmd(buf, 'step', ['line 14: File2Function()'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '\V>backtrace',
\ '\V 3 function GlobalFunction[2]',
\ '\V 2 <SNR>\.\*_CallAFunction[1]',
\ '\V 1 <SNR>\.\*_SourceAnotherFile[1]',
\ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
\ '\Vline 14: File2Function()'],
\ #{match: 'pattern'})
" Don't step into compiled functions...
call s:RunDbgCmd(buf, 'next', ['line 15: End of sourced file'])
call s:RunDbgCmd(buf, 'backtrace', [
\ '\V>backtrace',
\ '\V 3 function GlobalFunction[2]',
\ '\V 2 <SNR>\.\*_CallAFunction[1]',
\ '\V 1 <SNR>\.\*_SourceAnotherFile[1]',
\ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
\ '\Vline 15: End of sourced file'],
\ #{match: 'pattern'})
call StopVimInTerminal(buf)
endfunc
func Test_DefFunction_expr()
CheckCWD
let file3 =<< trim END
vim9script
g:someVar = "foo"
def g:ChangeVar()
g:someVar = "bar"
echo "changed"
enddef
defcompile
END
call writefile(file3, 'Xtest3.vim', 'D')
let buf = RunVimInTerminal('-S Xtest3.vim', {})
call s:RunDbgCmd(buf, ':breakadd expr g:someVar')
call s:RunDbgCmd(buf, ':call g:ChangeVar()', ['Oldval = "''foo''"', 'Newval = "''bar''"', 'function ChangeVar', 'line 2: echo "changed"'])
call StopVimInTerminal(buf)
endfunc
func Test_debug_def_and_legacy_function()
CheckCWD
let file =<< trim END
vim9script
def g:SomeFunc()
echo "here"
echo "and"
echo "there"
breakadd func 2 LocalFunc
LocalFunc()
enddef
def LocalFunc()
echo "first"
echo "second"
breakadd func LegacyFunc
LegacyFunc()
enddef
func LegacyFunc()
echo "legone"
echo "legtwo"
endfunc
breakadd func 2 g:SomeFunc
END
call writefile(file, 'XtestDebug.vim', 'D')
let buf = RunVimInTerminal('-S XtestDebug.vim', {})
call s:RunDbgCmd(buf,':call SomeFunc()', ['line 2: echo "and"'])
call s:RunDbgCmd(buf,'next', ['line 3: echo "there"'])
call s:RunDbgCmd(buf,'next', ['line 4: breakadd func 2 LocalFunc'])
" continue, next breakpoint is in LocalFunc()
call s:RunDbgCmd(buf,'cont', ['line 2: echo "second"'])
" continue, next breakpoint is in LegacyFunc()
call s:RunDbgCmd(buf,'cont', ['line 1: echo "legone"'])
call s:RunDbgCmd(buf, 'cont')
call StopVimInTerminal(buf)
endfunc
func Test_debug_def_function()
CheckCWD
let file =<< trim END
vim9script
def g:Func()
var n: number
def Closure(): number
return n + 3
enddef
n += Closure()
echo 'result: ' .. n
enddef
def g:FuncWithArgs(text: string, nr: number, ...items: list<number>)
echo text .. nr
for it in items
echo it
endfor
echo "done"
enddef
def g:FuncWithDict()
var d = {
a: 1,
b: 2,
}
# comment
def Inner()
eval 1 + 2
enddef
enddef
def g:FuncComment()
# comment
echo "first"
.. "one"
# comment
echo "second"
enddef
def g:FuncForLoop()
eval 1 + 2
for i in [11, 22, 33]
eval i + 2
endfor
echo "done"
enddef
def g:FuncWithSplitLine()
eval 1 + 2
| eval 2 + 3
enddef
END
call writefile(file, 'Xtest.vim', 'D')
let buf = RunVimInTerminal('-S Xtest.vim', {})
call s:RunDbgCmd(buf,
\ ':debug call Func()',
\ ['cmd: call Func()'])
call s:RunDbgCmd(buf, 'next', ['result: 3'])
call term_sendkeys(buf, "\r")
call s:RunDbgCmd(buf, 'cont')
call s:RunDbgCmd(buf,
\ ':debug call FuncWithArgs("asdf", 42, 1, 2, 3)',
\ ['cmd: call FuncWithArgs("asdf", 42, 1, 2, 3)'])
call s:RunDbgCmd(buf, 'step', ['line 1: echo text .. nr'])
call s:RunDbgCmd(buf, 'echo text', ['asdf'])
call s:RunDbgCmd(buf, 'echo nr', ['42'])
call s:RunDbgCmd(buf, 'echo items', ['[1, 2, 3]'])
call s:RunDbgCmd(buf, 'step', ['asdf42', 'function FuncWithArgs', 'line 2: for it in items'])
call s:RunDbgCmd(buf, 'step', ['function FuncWithArgs', 'line 2: for it in items'])
call s:RunDbgCmd(buf, 'echo it', ['0'])
call s:RunDbgCmd(buf, 'step', ['line 3: echo it'])
call s:RunDbgCmd(buf, 'echo it', ['1'])
call s:RunDbgCmd(buf, 'step', ['1', 'function FuncWithArgs', 'line 4: endfor'])
call s:RunDbgCmd(buf, 'step', ['line 2: for it in items'])
call s:RunDbgCmd(buf, 'echo it', ['1'])
call s:RunDbgCmd(buf, 'step', ['line 3: echo it'])
call s:RunDbgCmd(buf, 'step', ['2', 'function FuncWithArgs', 'line 4: endfor'])
call s:RunDbgCmd(buf, 'step', ['line 2: for it in items'])
call s:RunDbgCmd(buf, 'echo it', ['2'])
call s:RunDbgCmd(buf, 'step', ['line 3: echo it'])
call s:RunDbgCmd(buf, 'step', ['3', 'function FuncWithArgs', 'line 4: endfor'])
call s:RunDbgCmd(buf, 'step', ['line 2: for it in items'])
call s:RunDbgCmd(buf, 'step', ['line 5: echo "done"'])
call s:RunDbgCmd(buf, 'cont')
call s:RunDbgCmd(buf,
\ ':debug call FuncWithDict()',
\ ['cmd: call FuncWithDict()'])
call s:RunDbgCmd(buf, 'step', ['line 1: var d = { a: 1, b: 2, }'])
call s:RunDbgCmd(buf, 'step', ['line 6: def Inner()'])
call s:RunDbgCmd(buf, 'cont')
call s:RunDbgCmd(buf, ':breakadd func 1 FuncComment')
call s:RunDbgCmd(buf, ':call FuncComment()', ['function FuncComment', 'line 2: echo "first" .. "one"'])
call s:RunDbgCmd(buf, ':breakadd func 3 FuncComment')
call s:RunDbgCmd(buf, 'cont', ['function FuncComment', 'line 5: echo "second"'])
call s:RunDbgCmd(buf, 'cont')
call s:RunDbgCmd(buf, ':breakadd func 2 FuncForLoop')
call s:RunDbgCmd(buf, ':call FuncForLoop()', ['function FuncForLoop', 'line 2: for i in [11, 22, 33]'])
call s:RunDbgCmd(buf, 'step', ['line 2: for i in [11, 22, 33]'])
call s:RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 3: eval i + 2'])
call s:RunDbgCmd(buf, 'echo i', ['11'])
call s:RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 4: endfor'])
call s:RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 2: for i in [11, 22, 33]'])
call s:RunDbgCmd(buf, 'next', ['line 3: eval i + 2'])
call s:RunDbgCmd(buf, 'echo i', ['22'])
call s:RunDbgCmd(buf, 'breakdel *')
call s:RunDbgCmd(buf, 'cont')
call s:RunDbgCmd(buf, ':breakadd func FuncWithSplitLine')
call s:RunDbgCmd(buf, ':call FuncWithSplitLine()', ['function FuncWithSplitLine', 'line 1: eval 1 + 2 | eval 2 + 3'])
call s:RunDbgCmd(buf, 'cont')
call StopVimInTerminal(buf)
endfunc
func Test_debug_def_function_with_lambda()
CheckCWD
let lines =<< trim END
vim9script
def g:Func()
var s = 'a'
['b']->map((_, v) => s)
echo "done"
enddef
breakadd func 2 g:Func
END
call writefile(lines, 'XtestLambda.vim', 'D')
let buf = RunVimInTerminal('-S XtestLambda.vim', {})
call s:RunDbgCmd(buf,
\ ':call g:Func()',
\ ['function Func', 'line 2: [''b'']->map((_, v) => s)'])
call s:RunDbgCmd(buf,
\ 'next',
\ ['function Func', 'line 3: echo "done"'])
call s:RunDbgCmd(buf, 'cont')
call StopVimInTerminal(buf)
endfunc
def Test_debug_backtrace_level()
CheckCWD
var lines =<< trim END
let s:file1_var = 'file1'
let g:global_var = 'global'
func s:File1Func( arg )
let s:file1_var .= a:arg
let local_var = s:file1_var .. ' test1'
let g:global_var .= local_var
source Xtest2.vim
endfunc
call s:File1Func( 'arg1' )
END
writefile(lines, 'Xtest1.vim', 'D')
lines =<< trim END
let s:file2_var = 'file2'
func s:File2Func( arg )
let s:file2_var .= a:arg
let local_var = s:file2_var .. ' test2'
let g:global_var .= local_var
endfunc
call s:File2Func( 'arg2' )
END
writefile(lines, 'Xtest2.vim', 'D')
var file1 = getcwd() .. '/Xtest1.vim'
var file2 = getcwd() .. '/Xtest2.vim'
# set a breakpoint and source file1.vim
var buf = g:RunVimInTerminal(
'-c "breakadd file 1 Xtest1.vim" -S Xtest1.vim',
{wait_for_ruler: 0})
s:CheckDbgOutput(buf, [
'Breakpoint in "' .. file1 .. '" line 1',
'Entering Debug mode. Type "cont" to continue.',
'command line..script ' .. file1,
'line 1: let s:file1_var = ''file1'''
], {msec: 5000})
# step through the initial declarations
s:RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] )
s:RunDbgCmd(buf, 'step', [ 'line 4: func s:File1Func( arg )' ] )
s:RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
s:RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
s:RunDbgCmd(buf, 'echo global_var', [ 'global' ] )
# step in to the first function
s:RunDbgCmd(buf, 'step', [ 'line 11: call s:File1Func( ''arg1'' )' ] )
s:RunDbgCmd(buf, 'step', [ 'line 1: let s:file1_var .= a:arg' ] )
s:RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
s:RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
s:RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
s:RunDbgCmd(buf,
'echo global_var',
[ 'E121: Undefined variable: global_var' ] )
s:RunDbgCmd(buf,
'echo local_var',
[ 'E121: Undefined variable: local_var' ] )
s:RunDbgCmd(buf,
'echo l:local_var',
[ 'E121: Undefined variable: l:local_var' ] )
# backtrace up
s:RunDbgCmd(buf, 'backtrace', [
'\V>backtrace',
'\V 2 command line',
'\V 1 script ' .. file1 .. '[11]',
'\V->0 function <SNR>\.\*_File1Func',
'\Vline 1: let s:file1_var .= a:arg',
],
{ match: 'pattern' } )
s:RunDbgCmd(buf, 'up', [ '>up' ] )
s:RunDbgCmd(buf, 'backtrace', [
'\V>backtrace',
'\V 2 command line',
'\V->1 script ' .. file1 .. '[11]',
'\V 0 function <SNR>\.\*_File1Func',
'\Vline 1: let s:file1_var .= a:arg',
],
{ match: 'pattern' } )
# Expression evaluation in the script frame (not the function frame)
# FIXME: Unexpected in this scope (a: should not be visible)
s:RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] )
s:RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] )
s:RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] )
# FIXME: Unexpected in this scope (global should be found)
s:RunDbgCmd(buf,
'echo global_var',
[ 'E121: Undefined variable: global_var' ] )
s:RunDbgCmd(buf,
'echo local_var',
[ 'E121: Undefined variable: local_var' ] )
s:RunDbgCmd(buf,
'echo l:local_var',
[ 'E121: Undefined variable: l:local_var' ] )
# step while backtraced jumps to the latest frame
s:RunDbgCmd(buf, 'step', [
'line 2: let local_var = s:file1_var .. '' test1''' ] )
s:RunDbgCmd(buf, 'backtrace', [
'\V>backtrace',
'\V 2 command line',
'\V 1 script ' .. file1 .. '[11]',
'\V->0 function <SNR>\.\*_File1Func',
'\Vline 2: let local_var = s:file1_var .. '' test1''',
],
{ match: 'pattern' } )
s:RunDbgCmd(buf, 'step', [ 'line 3: let g:global_var .= local_var' ] )
s:RunDbgCmd(buf, 'echo local_var', [ 'file1arg1 test1' ] )
s:RunDbgCmd(buf, 'echo l:local_var', [ 'file1arg1 test1' ] )
s:RunDbgCmd(buf, 'step', [ 'line 4: source Xtest2.vim' ] )
s:RunDbgCmd(buf, 'step', [ 'line 1: let s:file2_var = ''file2''' ] )
s:RunDbgCmd(buf, 'backtrace', [
'\V>backtrace',
'\V 3 command line',
'\V 2 script ' .. file1 .. '[11]',
'\V 1 function <SNR>\.\*_File1Func[4]',
'\V->0 script ' .. file2,
'\Vline 1: let s:file2_var = ''file2''',
],
{ match: 'pattern' } )
# Expression evaluation in the script frame file2 (not the function frame)
s:RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
s:RunDbgCmd(buf,
'echo s:file1_var',
[ 'E121: Undefined variable: s:file1_var' ] )
s:RunDbgCmd(buf, 'echo g:global_var', [ 'globalfile1arg1 test1' ] )
s:RunDbgCmd(buf, 'echo global_var', [ 'globalfile1arg1 test1' ] )
s:RunDbgCmd(buf,
'echo local_var',
[ 'E121: Undefined variable: local_var' ] )
s:RunDbgCmd(buf,
'echo l:local_var',
[ 'E121: Undefined variable: l:local_var' ] )
s:RunDbgCmd(buf,
'echo s:file2_var',
[ 'E121: Undefined variable: s:file2_var' ] )
s:RunDbgCmd(buf, 'step', [ 'line 3: func s:File2Func( arg )' ] )
s:RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
# Up the stack to the other script context
s:RunDbgCmd(buf, 'up')
s:RunDbgCmd(buf, 'backtrace', [
'\V>backtrace',
'\V 3 command line',
'\V 2 script ' .. file1 .. '[11]',
'\V->1 function <SNR>\.\*_File1Func[4]',
'\V 0 script ' .. file2,
'\Vline 3: func s:File2Func( arg )',
],
{ match: 'pattern' } )
# FIXME: Unexpected. Should see the a: and l: dicts from File1Func
s:RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] )
s:RunDbgCmd(buf,
'echo l:local_var',
[ 'E121: Undefined variable: l:local_var' ] )
s:RunDbgCmd(buf, 'up')
s:RunDbgCmd(buf, 'backtrace', [
'\V>backtrace',
'\V 3 command line',
'\V->2 script ' .. file1 .. '[11]',
'\V 1 function <SNR>\.\*_File1Func[4]',
'\V 0 script ' .. file2,
'\Vline 3: func s:File2Func( arg )',
],
{ match: 'pattern' } )
# FIXME: Unexpected (wrong script vars are used)
s:RunDbgCmd(buf,
'echo s:file1_var',
[ 'E121: Undefined variable: s:file1_var' ] )
s:RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] )
s:RunDbgCmd(buf, 'cont')
g:StopVimInTerminal(buf)
enddef
" Test for setting a breakpoint on a :endif where the :if condition is false
" and then quit the script. This should generate an interrupt.
func Test_breakpt_endif_intr()
func F()
let g:Xpath ..= 'a'
if v:false
let g:Xpath ..= 'b'
endif
invalid_command
endfunc
let g:Xpath = ''
breakadd func 4 F
try
let caught_intr = 0
debuggreedy
call feedkeys(":call F()\<CR>quit\<CR>", "xt")
catch /^Vim:Interrupt$/
call assert_match('\.F, line 4', v:throwpoint)
let caught_intr = 1
endtry
0debuggreedy
call assert_equal(1, caught_intr)
call assert_equal('a', g:Xpath)
breakdel *
delfunc F
endfunc
" Test for setting a breakpoint on a :else where the :if condition is false
" and then quit the script. This should generate an interrupt.
func Test_breakpt_else_intr()
func F()
let g:Xpath ..= 'a'
if v:false
let g:Xpath ..= 'b'
else
invalid_command
endif
invalid_command
endfunc
let g:Xpath = ''
breakadd func 4 F
try
let caught_intr = 0
debuggreedy
call feedkeys(":call F()\<CR>quit\<CR>", "xt")
catch /^Vim:Interrupt$/
call assert_match('\.F, line 4', v:throwpoint)
let caught_intr = 1
endtry
0debuggreedy
call assert_equal(1, caught_intr)
call assert_equal('a', g:Xpath)
breakdel *
delfunc F
endfunc
" Test for setting a breakpoint on a :endwhile where the :while condition is
" false and then quit the script. This should generate an interrupt.
func Test_breakpt_endwhile_intr()
func F()
let g:Xpath ..= 'a'
while v:false
let g:Xpath ..= 'b'
endwhile
invalid_command
endfunc
let g:Xpath = ''
breakadd func 4 F
try
let caught_intr = 0
debuggreedy
call feedkeys(":call F()\<CR>quit\<CR>", "xt")
catch /^Vim:Interrupt$/
call assert_match('\.F, line 4', v:throwpoint)
let caught_intr = 1
endtry
0debuggreedy
call assert_equal(1, caught_intr)
call assert_equal('a', g:Xpath)
breakdel *
delfunc F
endfunc
" Test for setting a breakpoint on a script local function
func Test_breakpt_scriptlocal_func()
let g:Xpath = ''
func s:G()
let g:Xpath ..= 'a'
endfunc
let funcname = expand("<SID>") .. "G"
exe "breakadd func 1 " .. funcname
debuggreedy
redir => output
call feedkeys(":call " .. funcname .. "()\<CR>c\<CR>", "xt")
redir END
0debuggreedy
call assert_match('Breakpoint in "' .. funcname .. '" line 1', output)
call assert_equal('a', g:Xpath)
breakdel *
exe "delfunc " .. funcname
endfunc
" vim: shiftwidth=2 sts=2 expandtab
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/mirrors/vim.git
git@gitee.com:mirrors/vim.git
mirrors
vim
Vim
master

搜索帮助