test_debugger.vim (49507B)
1 " Tests for the Vim script debug commands 2 3 source shared.vim 4 source screendump.vim 5 source check.vim 6 7 func CheckCWD() 8 " Check that the longer lines don't wrap due to the length of the script name 9 " in cwd. Need to subtract by 1 since Vim will still wrap the message if it 10 " just fits. 11 let script_len = len( getcwd() .. '/Xtest1.vim' ) 12 let longest_line = len( 'Breakpoint in "" line 1' ) 13 if script_len > ( 75 - longest_line - 1 ) 14 throw 'Skipped: Your CWD has too many characters' 15 endif 16 endfunc 17 command! -nargs=0 -bar CheckCWD call CheckCWD() 18 19 " "options" argument can contain: 20 " 'msec' - time to wait for a match 21 " 'match' - "pattern" to use "lines" as pattern instead of text 22 func CheckDbgOutput(buf, lines, options = {}) 23 " Verify the expected output 24 let lnum = 20 - len(a:lines) 25 let msec = get(a:options, 'msec', 1000) 26 for l in a:lines 27 if get(a:options, 'match', 'equal') ==# 'pattern' 28 call WaitForAssert({-> assert_match(l, term_getline(a:buf, lnum))}, msec) 29 else 30 call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, msec) 31 endif 32 let lnum += 1 33 endfor 34 endfunc 35 36 " Run a Vim debugger command 37 " If the expected output argument is supplied, then check for it. 38 func s:RunDbgCmd(buf, cmd, ...) 39 call term_sendkeys(a:buf, a:cmd . "\r") 40 call TermWait(a:buf) 41 42 if a:0 != 0 43 let options = #{match: 'equal'} 44 if a:0 > 1 45 call extend(options, a:2) 46 endif 47 call CheckDbgOutput(a:buf, a:1, options) 48 endif 49 endfunc 50 51 " Debugger tests 52 func Test_Debugger() 53 CheckRunVimInTerminal 54 55 " Create a Vim script with some functions 56 let lines =<< trim END 57 func Foo() 58 let var1 = 1 59 let var2 = Bar(var1) + 9 60 return var2 61 endfunc 62 func Bar(var) 63 let var1 = 2 + a:var 64 let var2 = Bazz(var1) + 4 65 return var2 66 endfunc 67 func Bazz(var) 68 try 69 let var1 = 3 + a:var 70 let var3 = "another var" 71 let var3 = "value2" 72 catch 73 let var4 = "exception" 74 endtry 75 return var1 76 endfunc 77 def Vim9Func() 78 for cmd in ['confirm', 'xxxxxxx'] 79 for _ in [1, 2] 80 echo cmd 81 endfor 82 endfor 83 enddef 84 END 85 call writefile(lines, 'XtestDebug.vim', 'D') 86 87 " Start Vim in a terminal 88 let buf = RunVimInTerminal('-S XtestDebug.vim', {}) 89 90 " Start the Vim debugger 91 call s:RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()']) 92 93 " Create a few stack frames by stepping through functions 94 call s:RunDbgCmd(buf, 'step', ['line 1: let var1 = 1']) 95 call s:RunDbgCmd(buf, 'step', ['line 2: let var2 = Bar(var1) + 9']) 96 call s:RunDbgCmd(buf, 'step', ['line 1: let var1 = 2 + a:var']) 97 call s:RunDbgCmd(buf, 'step', ['line 2: let var2 = Bazz(var1) + 4']) 98 call s:RunDbgCmd(buf, 'step', ['line 1: try']) 99 call s:RunDbgCmd(buf, 'step', ['line 2: let var1 = 3 + a:var']) 100 call s:RunDbgCmd(buf, 'step', ['line 3: let var3 = "another var"']) 101 102 " check backtrace 103 call s:RunDbgCmd(buf, 'backtrace', [ 104 \ ' 2 function Foo[2]', 105 \ ' 1 Bar[2]', 106 \ '->0 Bazz', 107 \ 'line 3: let var3 = "another var"']) 108 109 " Check variables in different stack frames 110 call s:RunDbgCmd(buf, 'echo var1', ['6']) 111 112 call s:RunDbgCmd(buf, 'up') 113 call s:RunDbgCmd(buf, 'back', [ 114 \ ' 2 function Foo[2]', 115 \ '->1 Bar[2]', 116 \ ' 0 Bazz', 117 \ 'line 3: let var3 = "another var"']) 118 call s:RunDbgCmd(buf, 'echo var1', ['3']) 119 120 call s:RunDbgCmd(buf, 'u') 121 call s:RunDbgCmd(buf, 'bt', [ 122 \ '->2 function Foo[2]', 123 \ ' 1 Bar[2]', 124 \ ' 0 Bazz', 125 \ 'line 3: let var3 = "another var"']) 126 call s:RunDbgCmd(buf, 'echo var1', ['1']) 127 128 " Undefined variables 129 call s:RunDbgCmd(buf, 'step') 130 call s:RunDbgCmd(buf, 'frame 2') 131 call s:RunDbgCmd(buf, 'echo var3', [ 132 \ 'Error in function Foo[2]..Bar[2]..Bazz:', 133 \ 'line 4:', 134 \ 'E121: Undefined variable: var3']) 135 136 " var3 is defined in this level with some other value 137 call s:RunDbgCmd(buf, 'fr 0') 138 call s:RunDbgCmd(buf, 'echo var3', ['another var']) 139 140 call s:RunDbgCmd(buf, 'step') 141 call s:RunDbgCmd(buf, '') 142 call s:RunDbgCmd(buf, '') 143 call s:RunDbgCmd(buf, '') 144 call s:RunDbgCmd(buf, '') 145 call s:RunDbgCmd(buf, 'step', [ 146 \ 'function Foo[2]..Bar', 147 \ 'line 3: End of function']) 148 call s:RunDbgCmd(buf, 'up') 149 150 " Undefined var2 151 call s:RunDbgCmd(buf, 'echo var2', [ 152 \ 'Error in function Foo[2]..Bar:', 153 \ 'line 3:', 154 \ 'E121: Undefined variable: var2']) 155 156 " Var2 is defined with 10 157 call s:RunDbgCmd(buf, 'down') 158 call s:RunDbgCmd(buf, 'echo var2', ['10']) 159 160 " Backtrace movements 161 call s:RunDbgCmd(buf, 'b', [ 162 \ ' 1 function Foo[2]', 163 \ '->0 Bar', 164 \ 'line 3: End of function']) 165 166 " next command cannot go down, we are on bottom 167 call s:RunDbgCmd(buf, 'down', ['frame is zero']) 168 call s:RunDbgCmd(buf, 'up') 169 170 " next command cannot go up, we are on top 171 call s:RunDbgCmd(buf, 'up', ['frame at highest level: 1']) 172 call s:RunDbgCmd(buf, 'where', [ 173 \ '->1 function Foo[2]', 174 \ ' 0 Bar', 175 \ 'line 3: End of function']) 176 177 " fil is not frame or finish, it is file 178 call s:RunDbgCmd(buf, 'fil', ['"[No Name]" --No lines in buffer--']) 179 180 " relative backtrace movement 181 call s:RunDbgCmd(buf, 'fr -1') 182 call s:RunDbgCmd(buf, 'frame', [ 183 \ ' 1 function Foo[2]', 184 \ '->0 Bar', 185 \ 'line 3: End of function']) 186 187 call s:RunDbgCmd(buf, 'fr +1') 188 call s:RunDbgCmd(buf, 'fram', [ 189 \ '->1 function Foo[2]', 190 \ ' 0 Bar', 191 \ 'line 3: End of function']) 192 193 " go beyond limits does not crash 194 call s:RunDbgCmd(buf, 'fr 100', ['frame at highest level: 1']) 195 call s:RunDbgCmd(buf, 'fra', [ 196 \ '->1 function Foo[2]', 197 \ ' 0 Bar', 198 \ 'line 3: End of function']) 199 200 call s:RunDbgCmd(buf, 'frame -40', ['frame is zero']) 201 call s:RunDbgCmd(buf, 'fram', [ 202 \ ' 1 function Foo[2]', 203 \ '->0 Bar', 204 \ 'line 3: End of function']) 205 206 " final result 19 207 call s:RunDbgCmd(buf, 'cont', ['19']) 208 209 " breakpoints tests 210 211 " Start a debug session, so that reading the last line from the terminal 212 " works properly. 213 call s:RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()']) 214 215 " No breakpoints 216 call s:RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) 217 218 " Place some breakpoints 219 call s:RunDbgCmd(buf, 'breaka func Bar') 220 call s:RunDbgCmd(buf, 'breaklis', [' 1 func Bar line 1']) 221 call s:RunDbgCmd(buf, 'breakadd func 3 Bazz') 222 call s:RunDbgCmd(buf, 'breaklist', [' 1 func Bar line 1', 223 \ ' 2 func Bazz line 3']) 224 225 " Check whether the breakpoints are hit 226 call s:RunDbgCmd(buf, 'cont', [ 227 \ 'Breakpoint in "Bar" line 1', 228 \ 'function Foo[2]..Bar', 229 \ 'line 1: let var1 = 2 + a:var']) 230 call s:RunDbgCmd(buf, 'cont', [ 231 \ 'Breakpoint in "Bazz" line 3', 232 \ 'function Foo[2]..Bar[2]..Bazz', 233 \ 'line 3: let var3 = "another var"']) 234 235 " Delete the breakpoints 236 call s:RunDbgCmd(buf, 'breakd 1') 237 call s:RunDbgCmd(buf, 'breakli', [' 2 func Bazz line 3']) 238 call s:RunDbgCmd(buf, 'breakdel func 3 Bazz') 239 call s:RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) 240 241 call s:RunDbgCmd(buf, 'cont') 242 243 " Make sure the breakpoints are removed 244 call s:RunDbgCmd(buf, ':echo Foo()', ['19']) 245 246 " Delete a non-existing breakpoint 247 call s:RunDbgCmd(buf, ':breakdel 2', ['E161: Breakpoint not found: 2']) 248 249 " Expression breakpoint 250 call s:RunDbgCmd(buf, ':breakadd func 2 Bazz') 251 call s:RunDbgCmd(buf, ':echo Bazz(1)', [ 252 \ 'Entering Debug mode. Type "cont" to continue.', 253 \ 'function Bazz', 254 \ 'line 2: let var1 = 3 + a:var']) 255 call s:RunDbgCmd(buf, 'step') 256 call s:RunDbgCmd(buf, 'step') 257 call s:RunDbgCmd(buf, 'breaka expr var3') 258 call s:RunDbgCmd(buf, 'breakl', [' 3 func Bazz line 2', 259 \ ' 4 expr var3']) 260 call s:RunDbgCmd(buf, 'cont', ['Breakpoint in "Bazz" line 5', 261 \ 'Oldval = "''another var''"', 262 \ 'Newval = "''value2''"', 263 \ 'function Bazz', 264 \ 'line 5: catch']) 265 266 call s:RunDbgCmd(buf, 'breakdel *') 267 call s:RunDbgCmd(buf, 'breakl', ['No breakpoints defined']) 268 269 " Check for error cases 270 call s:RunDbgCmd(buf, 'breakadd abcd', [ 271 \ 'Error in function Bazz:', 272 \ 'line 5:', 273 \ 'E475: Invalid argument: abcd']) 274 call s:RunDbgCmd(buf, 'breakadd func', ['E475: Invalid argument: func']) 275 call s:RunDbgCmd(buf, 'breakadd func 2', ['E475: Invalid argument: func 2']) 276 call s:RunDbgCmd(buf, 'breaka func a()', ['E475: Invalid argument: func a()']) 277 call s:RunDbgCmd(buf, 'breakd abcd', ['E475: Invalid argument: abcd']) 278 call s:RunDbgCmd(buf, 'breakd func', ['E475: Invalid argument: func']) 279 call s:RunDbgCmd(buf, 'breakd func a()', ['E475: Invalid argument: func a()']) 280 call s:RunDbgCmd(buf, 'breakd func a', ['E161: Breakpoint not found: func a']) 281 call s:RunDbgCmd(buf, 'breakd expr', ['E475: Invalid argument: expr']) 282 call s:RunDbgCmd(buf, 'breakd expr x', ['E161: Breakpoint not found: expr x']) 283 284 " finish the current function 285 call s:RunDbgCmd(buf, 'finish', [ 286 \ 'function Bazz', 287 \ 'line 8: End of function']) 288 call s:RunDbgCmd(buf, 'cont') 289 290 " Test for :next 291 call s:RunDbgCmd(buf, ':debug echo Bar(1)') 292 call s:RunDbgCmd(buf, 'step') 293 call s:RunDbgCmd(buf, 'next') 294 call s:RunDbgCmd(buf, '', [ 295 \ 'function Bar', 296 \ 'line 3: return var2']) 297 call s:RunDbgCmd(buf, 'c') 298 299 " Test for :interrupt 300 call s:RunDbgCmd(buf, ':debug echo Bazz(1)') 301 call s:RunDbgCmd(buf, 'step') 302 call s:RunDbgCmd(buf, 'step') 303 call s:RunDbgCmd(buf, 'interrupt', [ 304 \ 'Exception thrown: Vim:Interrupt', 305 \ 'function Bazz', 306 \ 'line 5: catch']) 307 call s:RunDbgCmd(buf, 'c') 308 309 " Test showing local variable in :def function 310 call s:RunDbgCmd(buf, ':breakadd func 2 Vim9Func') 311 call s:RunDbgCmd(buf, ':call Vim9Func()', ['line 2: for _ in [1, 2]']) 312 call s:RunDbgCmd(buf, 'next', ['line 2: for _ in [1, 2]']) 313 call s:RunDbgCmd(buf, 'echo cmd', ['confirm']) 314 call s:RunDbgCmd(buf, 'breakdel *') 315 call s:RunDbgCmd(buf, 'cont') 316 317 " Test for :quit 318 call s:RunDbgCmd(buf, ':debug echo Foo()') 319 call s:RunDbgCmd(buf, 'breakdel *') 320 call s:RunDbgCmd(buf, 'breakadd func 3 Foo') 321 call s:RunDbgCmd(buf, 'breakadd func 3 Bazz') 322 call s:RunDbgCmd(buf, 'cont', [ 323 \ 'Breakpoint in "Bazz" line 3', 324 \ 'function Foo[2]..Bar[2]..Bazz', 325 \ 'line 3: let var3 = "another var"']) 326 call s:RunDbgCmd(buf, 'quit', [ 327 \ 'Breakpoint in "Foo" line 3', 328 \ 'function Foo', 329 \ 'line 3: return var2']) 330 call s:RunDbgCmd(buf, 'breakdel *') 331 call s:RunDbgCmd(buf, 'quit') 332 call s:RunDbgCmd(buf, 'enew! | only!') 333 334 call StopVimInTerminal(buf) 335 endfunc 336 337 func Test_Debugger_breakadd() 338 " Tests for :breakadd file and :breakadd here 339 " Breakpoints should be set before sourcing the file 340 CheckRunVimInTerminal 341 342 let lines =<< trim END 343 let var1 = 10 344 let var2 = 20 345 let var3 = 30 346 let var4 = 40 347 END 348 call writefile(lines, 'XdebugBreakadd.vim', 'D') 349 350 " Start Vim in a terminal 351 let buf = RunVimInTerminal('XdebugBreakadd.vim', {}) 352 call s:RunDbgCmd(buf, ':breakadd file 2 XdebugBreakadd.vim') 353 call s:RunDbgCmd(buf, ':4 | breakadd here') 354 call s:RunDbgCmd(buf, ':source XdebugBreakadd.vim', ['line 2: let var2 = 20']) 355 call s:RunDbgCmd(buf, 'cont', ['line 4: let var4 = 40']) 356 call s:RunDbgCmd(buf, 'cont') 357 358 call StopVimInTerminal(buf) 359 360 %bw! 361 362 call assert_fails('breakadd here', 'E32:') 363 call assert_fails('breakadd file Xtest.vim /\)/', 'E55:') 364 endfunc 365 366 " Test for expression breakpoint set using ":breakadd expr <expr>" 367 " FIXME: This doesn't seem to work as documented. The breakpoint is not 368 " triggered until the next function call. 369 func Test_Debugger_breakadd_expr() 370 CheckRunVimInTerminal 371 CheckCWD 372 373 let lines =<< trim END 374 func Foo() 375 eval 1 376 eval 2 377 endfunc 378 379 let g:Xtest_var += 1 380 call Foo() 381 let g:Xtest_var += 1 382 call Foo() 383 END 384 call writefile(lines, 'XbreakExpr.vim', 'D') 385 386 " Start Vim in a terminal 387 let buf = RunVimInTerminal('XbreakExpr.vim', {}) 388 call s:RunDbgCmd(buf, ':let g:Xtest_var = 10') 389 call s:RunDbgCmd(buf, ':breakadd expr g:Xtest_var') 390 let expected =<< trim eval END 391 Oldval = "10" 392 Newval = "11" 393 {fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo 394 line 1: eval 1 395 END 396 call s:RunDbgCmd(buf, ':source %', expected) 397 let expected =<< trim eval END 398 Oldval = "11" 399 Newval = "12" 400 {fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo 401 line 1: eval 1 402 END 403 call s:RunDbgCmd(buf, 'cont', expected) 404 call s:RunDbgCmd(buf, 'cont') 405 406 " Check the behavior without the g: prefix. 407 " FIXME: The Oldval and Newval don't look right here. 408 call s:RunDbgCmd(buf, ':breakdel *') 409 call s:RunDbgCmd(buf, ':breakadd expr Xtest_var') 410 let expected =<< trim eval END 411 Oldval = "13" 412 Newval = "(does not exist)" 413 {fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo 414 line 1: eval 1 415 END 416 call s:RunDbgCmd(buf, ':source %', expected) 417 let expected =<< trim eval END 418 {fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo 419 line 2: eval 2 420 END 421 call s:RunDbgCmd(buf, 'cont', expected) 422 let expected =<< trim eval END 423 Oldval = "14" 424 Newval = "(does not exist)" 425 {fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo 426 line 1: eval 1 427 END 428 call s:RunDbgCmd(buf, 'cont', expected) 429 let expected =<< trim eval END 430 {fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo 431 line 2: eval 2 432 END 433 call s:RunDbgCmd(buf, 'cont', expected) 434 call s:RunDbgCmd(buf, 'cont') 435 436 call StopVimInTerminal(buf) 437 endfunc 438 439 " def Test_Debugger_break_at_return() 440 " var lines =<< trim END 441 " vim9script 442 " def g:GetNum(): number 443 " return 1 444 " + 2 445 " + 3 446 " enddef 447 " breakadd func GetNum 448 " END 449 " writefile(lines, 'Xtest.vim') 450 " 451 " # Start Vim in a terminal 452 " var buf = RunVimInTerminal('-S Xtest.vim', {wait_for_ruler: 0}) 453 " call TermWait(buf) 454 " 455 " RunDbgCmd(buf, ':call GetNum()', 456 " ['line 1: return 1 + 2 + 3'], {match: 'pattern'}) 457 " 458 " call StopVimInTerminal(buf) 459 " call delete('Xtest.vim') 460 " enddef 461 462 func Test_Backtrace_Through_Source() 463 CheckRunVimInTerminal 464 CheckCWD 465 let file1 =<< trim END 466 func SourceAnotherFile() 467 source Xtest2.vim 468 endfunc 469 470 func CallAFunction() 471 call SourceAnotherFile() 472 call File2Function() 473 endfunc 474 475 func GlobalFunction() 476 call CallAFunction() 477 endfunc 478 END 479 call writefile(file1, 'Xtest1.vim', 'D') 480 481 let file2 =<< trim END 482 func DoAThing() 483 echo "DoAThing" 484 endfunc 485 486 func File2Function() 487 call DoAThing() 488 endfunc 489 490 call File2Function() 491 END 492 call writefile(file2, 'Xtest2.vim', 'D') 493 494 let buf = RunVimInTerminal('-S Xtest1.vim', {}) 495 496 call s:RunDbgCmd(buf, 497 \ ':debug call GlobalFunction()', 498 \ ['cmd: call GlobalFunction()']) 499 call s:RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()']) 500 501 call s:RunDbgCmd(buf, 'backtrace', ['>backtrace', 502 \ '->0 function GlobalFunction', 503 \ 'line 1: call CallAFunction()']) 504 505 call s:RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()']) 506 call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) 507 508 call s:RunDbgCmd(buf, 'backtrace', ['>backtrace', 509 \ ' 2 function GlobalFunction[1]', 510 \ ' 1 CallAFunction[1]', 511 \ '->0 SourceAnotherFile', 512 \ 'line 1: source Xtest2.vim']) 513 514 " Step into the 'source' command. Note that we print the full trace all the 515 " way though the source command. 516 call s:RunDbgCmd(buf, 'step', ['line 1: func DoAThing()']) 517 call s:RunDbgCmd(buf, 'backtrace', [ 518 \ '>backtrace', 519 \ ' 3 function GlobalFunction[1]', 520 \ ' 2 CallAFunction[1]', 521 \ ' 1 SourceAnotherFile[1]', 522 \ '->0 script ' .. getcwd() .. '/Xtest2.vim', 523 \ 'line 1: func DoAThing()']) 524 525 call s:RunDbgCmd( buf, 'up' ) 526 call s:RunDbgCmd( buf, 'backtrace', [ 527 \ '>backtrace', 528 \ ' 3 function GlobalFunction[1]', 529 \ ' 2 CallAFunction[1]', 530 \ '->1 SourceAnotherFile[1]', 531 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 532 \ 'line 1: func DoAThing()' ] ) 533 534 call s:RunDbgCmd( buf, 'up' ) 535 call s:RunDbgCmd( buf, 'backtrace', [ 536 \ '>backtrace', 537 \ ' 3 function GlobalFunction[1]', 538 \ '->2 CallAFunction[1]', 539 \ ' 1 SourceAnotherFile[1]', 540 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 541 \ 'line 1: func DoAThing()' ] ) 542 543 call s:RunDbgCmd( buf, 'up' ) 544 call s:RunDbgCmd( buf, 'backtrace', [ 545 \ '>backtrace', 546 \ '->3 function GlobalFunction[1]', 547 \ ' 2 CallAFunction[1]', 548 \ ' 1 SourceAnotherFile[1]', 549 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 550 \ 'line 1: func DoAThing()' ] ) 551 552 call s:RunDbgCmd( buf, 'up', [ 'frame at highest level: 3' ] ) 553 call s:RunDbgCmd( buf, 'backtrace', [ 554 \ '>backtrace', 555 \ '->3 function GlobalFunction[1]', 556 \ ' 2 CallAFunction[1]', 557 \ ' 1 SourceAnotherFile[1]', 558 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 559 \ 'line 1: func DoAThing()' ] ) 560 561 call s:RunDbgCmd( buf, 'down' ) 562 call s:RunDbgCmd( buf, 'backtrace', [ 563 \ '>backtrace', 564 \ ' 3 function GlobalFunction[1]', 565 \ '->2 CallAFunction[1]', 566 \ ' 1 SourceAnotherFile[1]', 567 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 568 \ 'line 1: func DoAThing()' ] ) 569 570 call s:RunDbgCmd( buf, 'down' ) 571 call s:RunDbgCmd( buf, 'backtrace', [ 572 \ '>backtrace', 573 \ ' 3 function GlobalFunction[1]', 574 \ ' 2 CallAFunction[1]', 575 \ '->1 SourceAnotherFile[1]', 576 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 577 \ 'line 1: func DoAThing()' ] ) 578 579 call s:RunDbgCmd( buf, 'down' ) 580 call s:RunDbgCmd( buf, 'backtrace', [ 581 \ '>backtrace', 582 \ ' 3 function GlobalFunction[1]', 583 \ ' 2 CallAFunction[1]', 584 \ ' 1 SourceAnotherFile[1]', 585 \ '->0 script ' .. getcwd() .. '/Xtest2.vim', 586 \ 'line 1: func DoAThing()' ] ) 587 588 call s:RunDbgCmd( buf, 'down', [ 'frame is zero' ] ) 589 590 " step until we have another meaningful trace 591 call s:RunDbgCmd(buf, 'step', ['line 5: func File2Function()']) 592 call s:RunDbgCmd(buf, 'step', ['line 9: call File2Function()']) 593 call s:RunDbgCmd(buf, 'backtrace', [ 594 \ '>backtrace', 595 \ ' 3 function GlobalFunction[1]', 596 \ ' 2 CallAFunction[1]', 597 \ ' 1 SourceAnotherFile[1]', 598 \ '->0 script ' .. getcwd() .. '/Xtest2.vim', 599 \ 'line 9: call File2Function()']) 600 601 call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()']) 602 call s:RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"']) 603 call s:RunDbgCmd(buf, 'backtrace', [ 604 \ '>backtrace', 605 \ ' 5 function GlobalFunction[1]', 606 \ ' 4 CallAFunction[1]', 607 \ ' 3 SourceAnotherFile[1]', 608 \ ' 2 script ' .. getcwd() .. '/Xtest2.vim[9]', 609 \ ' 1 function File2Function[1]', 610 \ '->0 DoAThing', 611 \ 'line 1: echo "DoAThing"']) 612 613 " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim 614 call s:RunDbgCmd(buf, 'step', ['line 1: End of function']) 615 call s:RunDbgCmd(buf, 'step', ['line 1: End of function']) 616 call s:RunDbgCmd(buf, 'step', ['line 10: End of sourced file']) 617 call s:RunDbgCmd(buf, 'step', ['line 1: End of function']) 618 call s:RunDbgCmd(buf, 'step', ['line 2: call File2Function()']) 619 call s:RunDbgCmd(buf, 'backtrace', [ 620 \ '>backtrace', 621 \ ' 1 function GlobalFunction[1]', 622 \ '->0 CallAFunction', 623 \ 'line 2: call File2Function()']) 624 625 call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()']) 626 call s:RunDbgCmd(buf, 'backtrace', [ 627 \ '>backtrace', 628 \ ' 2 function GlobalFunction[1]', 629 \ ' 1 CallAFunction[2]', 630 \ '->0 File2Function', 631 \ 'line 1: call DoAThing()']) 632 633 call StopVimInTerminal(buf) 634 endfunc 635 636 func Test_Backtrace_Autocmd() 637 CheckRunVimInTerminal 638 CheckCWD 639 let file1 =<< trim END 640 func SourceAnotherFile() 641 source Xtest2.vim 642 endfunc 643 644 func CallAFunction() 645 call SourceAnotherFile() 646 call File2Function() 647 endfunc 648 649 func GlobalFunction() 650 call CallAFunction() 651 endfunc 652 653 au User TestGlobalFunction :call GlobalFunction() | echo "Done" 654 END 655 call writefile(file1, 'Xtest1.vim', 'D') 656 657 let file2 =<< trim END 658 func DoAThing() 659 echo "DoAThing" 660 endfunc 661 662 func File2Function() 663 call DoAThing() 664 endfunc 665 666 call File2Function() 667 END 668 call writefile(file2, 'Xtest2.vim', 'D') 669 670 let buf = RunVimInTerminal('-S Xtest1.vim', {}) 671 672 call s:RunDbgCmd(buf, 673 \ ':debug doautocmd User TestGlobalFunction', 674 \ ['cmd: doautocmd User TestGlobalFunction']) 675 call s:RunDbgCmd(buf, 'step', ['cmd: call GlobalFunction() | echo "Done"']) 676 677 " At this point the only thing in the stack is the autocommand 678 call s:RunDbgCmd(buf, 'backtrace', [ 679 \ '>backtrace', 680 \ '->0 User Autocommands for "TestGlobalFunction"', 681 \ 'cmd: call GlobalFunction() | echo "Done"']) 682 683 " And now we're back into the call stack 684 call s:RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()']) 685 call s:RunDbgCmd(buf, 'backtrace', [ 686 \ '>backtrace', 687 \ ' 1 User Autocommands for "TestGlobalFunction"', 688 \ '->0 function GlobalFunction', 689 \ 'line 1: call CallAFunction()']) 690 691 call s:RunDbgCmd(buf, 'step', ['line 1: call SourceAnotherFile()']) 692 call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) 693 694 call s:RunDbgCmd(buf, 'backtrace', [ 695 \ '>backtrace', 696 \ ' 3 User Autocommands for "TestGlobalFunction"', 697 \ ' 2 function GlobalFunction[1]', 698 \ ' 1 CallAFunction[1]', 699 \ '->0 SourceAnotherFile', 700 \ 'line 1: source Xtest2.vim']) 701 702 " Step into the 'source' command. Note that we print the full trace all the 703 " way though the source command. 704 call s:RunDbgCmd(buf, 'step', ['line 1: func DoAThing()']) 705 call s:RunDbgCmd(buf, 'backtrace', [ 706 \ '>backtrace', 707 \ ' 4 User Autocommands for "TestGlobalFunction"', 708 \ ' 3 function GlobalFunction[1]', 709 \ ' 2 CallAFunction[1]', 710 \ ' 1 SourceAnotherFile[1]', 711 \ '->0 script ' .. getcwd() .. '/Xtest2.vim', 712 \ 'line 1: func DoAThing()']) 713 714 call s:RunDbgCmd( buf, 'up' ) 715 call s:RunDbgCmd( buf, 'backtrace', [ 716 \ '>backtrace', 717 \ ' 4 User Autocommands for "TestGlobalFunction"', 718 \ ' 3 function GlobalFunction[1]', 719 \ ' 2 CallAFunction[1]', 720 \ '->1 SourceAnotherFile[1]', 721 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 722 \ 'line 1: func DoAThing()' ] ) 723 724 call s:RunDbgCmd( buf, 'up' ) 725 call s:RunDbgCmd( buf, 'backtrace', [ 726 \ '>backtrace', 727 \ ' 4 User Autocommands for "TestGlobalFunction"', 728 \ ' 3 function GlobalFunction[1]', 729 \ '->2 CallAFunction[1]', 730 \ ' 1 SourceAnotherFile[1]', 731 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 732 \ 'line 1: func DoAThing()' ] ) 733 734 call s:RunDbgCmd( buf, 'up' ) 735 call s:RunDbgCmd( buf, 'backtrace', [ 736 \ '>backtrace', 737 \ ' 4 User Autocommands for "TestGlobalFunction"', 738 \ '->3 function GlobalFunction[1]', 739 \ ' 2 CallAFunction[1]', 740 \ ' 1 SourceAnotherFile[1]', 741 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 742 \ 'line 1: func DoAThing()' ] ) 743 744 call s:RunDbgCmd( buf, 'up' ) 745 call s:RunDbgCmd( buf, 'backtrace', [ 746 \ '>backtrace', 747 \ '->4 User Autocommands for "TestGlobalFunction"', 748 \ ' 3 function GlobalFunction[1]', 749 \ ' 2 CallAFunction[1]', 750 \ ' 1 SourceAnotherFile[1]', 751 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 752 \ 'line 1: func DoAThing()' ] ) 753 754 call s:RunDbgCmd( buf, 'up', [ 'frame at highest level: 4' ] ) 755 call s:RunDbgCmd( buf, 'backtrace', [ 756 \ '>backtrace', 757 \ '->4 User Autocommands for "TestGlobalFunction"', 758 \ ' 3 function GlobalFunction[1]', 759 \ ' 2 CallAFunction[1]', 760 \ ' 1 SourceAnotherFile[1]', 761 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 762 \ 'line 1: func DoAThing()' ] ) 763 764 call s:RunDbgCmd( buf, 'down' ) 765 call s:RunDbgCmd( buf, 'backtrace', [ 766 \ '>backtrace', 767 \ ' 4 User Autocommands for "TestGlobalFunction"', 768 \ '->3 function GlobalFunction[1]', 769 \ ' 2 CallAFunction[1]', 770 \ ' 1 SourceAnotherFile[1]', 771 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 772 \ 'line 1: func DoAThing()' ] ) 773 774 775 call s:RunDbgCmd( buf, 'down' ) 776 call s:RunDbgCmd( buf, 'backtrace', [ 777 \ '>backtrace', 778 \ ' 4 User Autocommands for "TestGlobalFunction"', 779 \ ' 3 function GlobalFunction[1]', 780 \ '->2 CallAFunction[1]', 781 \ ' 1 SourceAnotherFile[1]', 782 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 783 \ 'line 1: func DoAThing()' ] ) 784 785 call s:RunDbgCmd( buf, 'down' ) 786 call s:RunDbgCmd( buf, 'backtrace', [ 787 \ '>backtrace', 788 \ ' 4 User Autocommands for "TestGlobalFunction"', 789 \ ' 3 function GlobalFunction[1]', 790 \ ' 2 CallAFunction[1]', 791 \ '->1 SourceAnotherFile[1]', 792 \ ' 0 script ' .. getcwd() .. '/Xtest2.vim', 793 \ 'line 1: func DoAThing()' ] ) 794 795 call s:RunDbgCmd( buf, 'down' ) 796 call s:RunDbgCmd( buf, 'backtrace', [ 797 \ '>backtrace', 798 \ ' 4 User Autocommands for "TestGlobalFunction"', 799 \ ' 3 function GlobalFunction[1]', 800 \ ' 2 CallAFunction[1]', 801 \ ' 1 SourceAnotherFile[1]', 802 \ '->0 script ' .. getcwd() .. '/Xtest2.vim', 803 \ 'line 1: func DoAThing()' ] ) 804 805 call s:RunDbgCmd( buf, 'down', [ 'frame is zero' ] ) 806 807 " step until we have another meaningful trace 808 call s:RunDbgCmd(buf, 'step', ['line 5: func File2Function()']) 809 call s:RunDbgCmd(buf, 'step', ['line 9: call File2Function()']) 810 call s:RunDbgCmd(buf, 'backtrace', [ 811 \ '>backtrace', 812 \ ' 4 User Autocommands for "TestGlobalFunction"', 813 \ ' 3 function GlobalFunction[1]', 814 \ ' 2 CallAFunction[1]', 815 \ ' 1 SourceAnotherFile[1]', 816 \ '->0 script ' .. getcwd() .. '/Xtest2.vim', 817 \ 'line 9: call File2Function()']) 818 819 call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()']) 820 call s:RunDbgCmd(buf, 'step', ['line 1: echo "DoAThing"']) 821 call s:RunDbgCmd(buf, 'backtrace', [ 822 \ '>backtrace', 823 \ ' 6 User Autocommands for "TestGlobalFunction"', 824 \ ' 5 function GlobalFunction[1]', 825 \ ' 4 CallAFunction[1]', 826 \ ' 3 SourceAnotherFile[1]', 827 \ ' 2 script ' .. getcwd() .. '/Xtest2.vim[9]', 828 \ ' 1 function File2Function[1]', 829 \ '->0 DoAThing', 830 \ 'line 1: echo "DoAThing"']) 831 832 " Now, step (back to Xfile1.vim), and call the function _in_ Xfile2.vim 833 call s:RunDbgCmd(buf, 'step', ['line 1: End of function']) 834 call s:RunDbgCmd(buf, 'step', ['line 1: End of function']) 835 call s:RunDbgCmd(buf, 'step', ['line 10: End of sourced file']) 836 call s:RunDbgCmd(buf, 'step', ['line 1: End of function']) 837 call s:RunDbgCmd(buf, 'step', ['line 2: call File2Function()']) 838 call s:RunDbgCmd(buf, 'backtrace', [ 839 \ '>backtrace', 840 \ ' 2 User Autocommands for "TestGlobalFunction"', 841 \ ' 1 function GlobalFunction[1]', 842 \ '->0 CallAFunction', 843 \ 'line 2: call File2Function()']) 844 845 call s:RunDbgCmd(buf, 'step', ['line 1: call DoAThing()']) 846 call s:RunDbgCmd(buf, 'backtrace', [ 847 \ '>backtrace', 848 \ ' 3 User Autocommands for "TestGlobalFunction"', 849 \ ' 2 function GlobalFunction[1]', 850 \ ' 1 CallAFunction[2]', 851 \ '->0 File2Function', 852 \ 'line 1: call DoAThing()']) 853 854 855 " Now unwind so that we get back to the original autocommand (and the second 856 " cmd echo "Done") 857 call s:RunDbgCmd(buf, 'finish', ['line 1: End of function']) 858 call s:RunDbgCmd(buf, 'backtrace', [ 859 \ '>backtrace', 860 \ ' 3 User Autocommands for "TestGlobalFunction"', 861 \ ' 2 function GlobalFunction[1]', 862 \ ' 1 CallAFunction[2]', 863 \ '->0 File2Function', 864 \ 'line 1: End of function']) 865 866 call s:RunDbgCmd(buf, 'finish', ['line 2: End of function']) 867 call s:RunDbgCmd(buf, 'backtrace', [ 868 \ '>backtrace', 869 \ ' 2 User Autocommands for "TestGlobalFunction"', 870 \ ' 1 function GlobalFunction[1]', 871 \ '->0 CallAFunction', 872 \ 'line 2: End of function']) 873 874 call s:RunDbgCmd(buf, 'finish', ['line 1: End of function']) 875 call s:RunDbgCmd(buf, 'backtrace', [ 876 \ '>backtrace', 877 \ ' 1 User Autocommands for "TestGlobalFunction"', 878 \ '->0 function GlobalFunction', 879 \ 'line 1: End of function']) 880 881 call s:RunDbgCmd(buf, 'step', ['cmd: echo "Done"']) 882 call s:RunDbgCmd(buf, 'backtrace', [ 883 \ '>backtrace', 884 \ '->0 User Autocommands for "TestGlobalFunction"', 885 \ 'cmd: echo "Done"']) 886 887 call StopVimInTerminal(buf) 888 endfunc 889 890 func Test_Backtrace_CmdLine() 891 CheckRunVimInTerminal 892 CheckCWD 893 let file1 =<< trim END 894 func SourceAnotherFile() 895 source Xtest2.vim 896 endfunc 897 898 func CallAFunction() 899 call SourceAnotherFile() 900 call File2Function() 901 endfunc 902 903 func GlobalFunction() 904 call CallAFunction() 905 endfunc 906 907 au User TestGlobalFunction :call GlobalFunction() | echo "Done" 908 END 909 call writefile(file1, 'Xtest1.vim', 'D') 910 911 let file2 =<< trim END 912 func DoAThing() 913 echo "DoAThing" 914 endfunc 915 916 func File2Function() 917 call DoAThing() 918 endfunc 919 920 call File2Function() 921 END 922 call writefile(file2, 'Xtest2.vim', 'D') 923 924 let buf = RunVimInTerminal( 925 \ '-S Xtest1.vim -c "debug call GlobalFunction()"', 926 \ {'wait_for_ruler': 0}) 927 928 " Need to wait for the vim-in-terminal to be ready. 929 " With valgrind this can take quite long. 930 call CheckDbgOutput(buf, ['command line', 931 \ 'cmd: call GlobalFunction()'], #{msec: 5000}) 932 933 " At this point the only thing in the stack is the cmdline 934 call s:RunDbgCmd(buf, 'backtrace', [ 935 \ '>backtrace', 936 \ '->0 command line', 937 \ 'cmd: call GlobalFunction()']) 938 939 " And now we're back into the call stack 940 call s:RunDbgCmd(buf, 'step', ['line 1: call CallAFunction()']) 941 call s:RunDbgCmd(buf, 'backtrace', [ 942 \ '>backtrace', 943 \ ' 1 command line', 944 \ '->0 function GlobalFunction', 945 \ 'line 1: call CallAFunction()']) 946 947 call StopVimInTerminal(buf) 948 endfunc 949 950 func Test_Backtrace_DefFunction() 951 CheckRunVimInTerminal 952 CheckCWD 953 let file1 =<< trim END 954 vim9script 955 import './Xtest2.vim' as imp 956 957 def SourceAnotherFile() 958 source Xtest2.vim 959 enddef 960 961 def CallAFunction() 962 SourceAnotherFile() 963 imp.File2Function() 964 enddef 965 966 def g:GlobalFunction() 967 var some = "some var" 968 CallAFunction() 969 enddef 970 971 defcompile 972 END 973 call writefile(file1, 'Xtest1.vim', 'D') 974 975 let file2 =<< trim END 976 vim9script 977 978 def DoAThing(): number 979 var a = 100 * 2 980 a += 3 981 return a 982 enddef 983 984 export def File2Function() 985 DoAThing() 986 enddef 987 988 defcompile 989 File2Function() 990 END 991 call writefile(file2, 'Xtest2.vim', 'D') 992 993 let buf = RunVimInTerminal('-S Xtest1.vim', {}) 994 995 call s:RunDbgCmd(buf, 996 \ ':debug call GlobalFunction()', 997 \ ['cmd: call GlobalFunction()']) 998 999 call s:RunDbgCmd(buf, 'step', ['line 1: var some = "some var"']) 1000 call s:RunDbgCmd(buf, 'step', ['line 2: CallAFunction()']) 1001 call s:RunDbgCmd(buf, 'echo some', ['some var']) 1002 1003 call s:RunDbgCmd(buf, 'backtrace', [ 1004 \ '\V>backtrace', 1005 \ '\V->0 function GlobalFunction', 1006 \ '\Vline 2: CallAFunction()', 1007 \ ], 1008 \ #{match: 'pattern'}) 1009 1010 call s:RunDbgCmd(buf, 'step', ['line 1: SourceAnotherFile()']) 1011 call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) 1012 " Repeated line, because we fist are in the compiled function before the 1013 " EXEC and then in do_cmdline() before the :source command. 1014 call s:RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) 1015 call s:RunDbgCmd(buf, 'step', ['line 1: vim9script']) 1016 call s:RunDbgCmd(buf, 'step', ['line 3: def DoAThing(): number']) 1017 call s:RunDbgCmd(buf, 'step', ['line 9: export def File2Function()']) 1018 call s:RunDbgCmd(buf, 'step', ['line 13: defcompile']) 1019 call s:RunDbgCmd(buf, 'step', ['line 14: File2Function()']) 1020 call s:RunDbgCmd(buf, 'backtrace', [ 1021 \ '\V>backtrace', 1022 \ '\V 3 function GlobalFunction[2]', 1023 \ '\V 2 <SNR>\.\*_CallAFunction[1]', 1024 \ '\V 1 <SNR>\.\*_SourceAnotherFile[1]', 1025 \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim', 1026 \ '\Vline 14: File2Function()'], 1027 \ #{match: 'pattern'}) 1028 1029 " Don't step into compiled functions... 1030 call s:RunDbgCmd(buf, 'next', ['line 15: End of sourced file']) 1031 call s:RunDbgCmd(buf, 'backtrace', [ 1032 \ '\V>backtrace', 1033 \ '\V 3 function GlobalFunction[2]', 1034 \ '\V 2 <SNR>\.\*_CallAFunction[1]', 1035 \ '\V 1 <SNR>\.\*_SourceAnotherFile[1]', 1036 \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim', 1037 \ '\Vline 15: End of sourced file'], 1038 \ #{match: 'pattern'}) 1039 1040 call StopVimInTerminal(buf) 1041 endfunc 1042 1043 func Test_DefFunction_expr() 1044 CheckRunVimInTerminal 1045 CheckCWD 1046 let file3 =<< trim END 1047 vim9script 1048 g:someVar = "foo" 1049 def g:ChangeVar() 1050 g:someVar = "bar" 1051 echo "changed" 1052 enddef 1053 defcompile 1054 END 1055 call writefile(file3, 'Xtest3.vim', 'D') 1056 let buf = RunVimInTerminal('-S Xtest3.vim', {}) 1057 1058 call s:RunDbgCmd(buf, ':breakadd expr g:someVar') 1059 call s:RunDbgCmd(buf, ':call g:ChangeVar()', ['Oldval = "''foo''"', 'Newval = "''bar''"', 'function ChangeVar', 'line 2: echo "changed"']) 1060 1061 call StopVimInTerminal(buf) 1062 endfunc 1063 1064 func Test_debug_def_and_legacy_function() 1065 CheckRunVimInTerminal 1066 CheckCWD 1067 let file =<< trim END 1068 vim9script 1069 def g:SomeFunc() 1070 echo "here" 1071 echo "and" 1072 echo "there" 1073 breakadd func 2 LocalFunc 1074 LocalFunc() 1075 enddef 1076 1077 def LocalFunc() 1078 echo "first" 1079 echo "second" 1080 breakadd func LegacyFunc 1081 LegacyFunc() 1082 enddef 1083 1084 func LegacyFunc() 1085 echo "legone" 1086 echo "legtwo" 1087 endfunc 1088 1089 breakadd func 2 g:SomeFunc 1090 END 1091 call writefile(file, 'XtestDebug.vim', 'D') 1092 1093 let buf = RunVimInTerminal('-S XtestDebug.vim', {}) 1094 1095 call s:RunDbgCmd(buf,':call SomeFunc()', ['line 2: echo "and"']) 1096 call s:RunDbgCmd(buf,'next', ['line 3: echo "there"']) 1097 call s:RunDbgCmd(buf,'next', ['line 4: breakadd func 2 LocalFunc']) 1098 1099 " continue, next breakpoint is in LocalFunc() 1100 call s:RunDbgCmd(buf,'cont', ['line 2: echo "second"']) 1101 1102 " continue, next breakpoint is in LegacyFunc() 1103 call s:RunDbgCmd(buf,'cont', ['line 1: echo "legone"']) 1104 1105 call s:RunDbgCmd(buf, 'cont') 1106 1107 call StopVimInTerminal(buf) 1108 endfunc 1109 1110 func Test_debug_def_function() 1111 CheckRunVimInTerminal 1112 CheckCWD 1113 let file =<< trim END 1114 vim9script 1115 def g:Func() 1116 var n: number 1117 def Closure(): number 1118 return n + 3 1119 enddef 1120 n += Closure() 1121 echo 'result: ' .. n 1122 enddef 1123 1124 def g:FuncWithArgs(text: string, nr: number, ...items: list<number>) 1125 echo text .. nr 1126 for it in items 1127 echo it 1128 endfor 1129 echo "done" 1130 enddef 1131 1132 def g:FuncWithDict() 1133 var d = { 1134 a: 1, 1135 b: 2, 1136 } 1137 # comment 1138 def Inner() 1139 eval 1 + 2 1140 enddef 1141 enddef 1142 1143 def g:FuncComment() 1144 # comment 1145 echo "first" 1146 .. "one" 1147 # comment 1148 echo "second" 1149 enddef 1150 1151 def g:FuncForLoop() 1152 eval 1 + 2 1153 for i in [11, 22, 33] 1154 eval i + 2 1155 endfor 1156 echo "done" 1157 enddef 1158 1159 def g:FuncWithSplitLine() 1160 eval 1 + 2 1161 | eval 2 + 3 1162 enddef 1163 END 1164 call writefile(file, 'Xtest.vim', 'D') 1165 1166 let buf = RunVimInTerminal('-S Xtest.vim', {}) 1167 1168 call s:RunDbgCmd(buf, 1169 \ ':debug call Func()', 1170 \ ['cmd: call Func()']) 1171 call s:RunDbgCmd(buf, 'next', ['result: 3']) 1172 call term_sendkeys(buf, "\r") 1173 call s:RunDbgCmd(buf, 'cont') 1174 1175 call s:RunDbgCmd(buf, 1176 \ ':debug call FuncWithArgs("asdf", 42, 1, 2, 3)', 1177 \ ['cmd: call FuncWithArgs("asdf", 42, 1, 2, 3)']) 1178 call s:RunDbgCmd(buf, 'step', ['line 1: echo text .. nr']) 1179 call s:RunDbgCmd(buf, 'echo text', ['asdf']) 1180 call s:RunDbgCmd(buf, 'echo nr', ['42']) 1181 call s:RunDbgCmd(buf, 'echo items', ['[1, 2, 3]']) 1182 call s:RunDbgCmd(buf, 'step', ['asdf42', 'function FuncWithArgs', 'line 2: for it in items']) 1183 call s:RunDbgCmd(buf, 'step', ['function FuncWithArgs', 'line 2: for it in items']) 1184 call s:RunDbgCmd(buf, 'echo it', ['0']) 1185 call s:RunDbgCmd(buf, 'step', ['line 3: echo it']) 1186 call s:RunDbgCmd(buf, 'echo it', ['1']) 1187 call s:RunDbgCmd(buf, 'step', ['1', 'function FuncWithArgs', 'line 4: endfor']) 1188 call s:RunDbgCmd(buf, 'step', ['line 2: for it in items']) 1189 call s:RunDbgCmd(buf, 'echo it', ['1']) 1190 call s:RunDbgCmd(buf, 'step', ['line 3: echo it']) 1191 call s:RunDbgCmd(buf, 'step', ['2', 'function FuncWithArgs', 'line 4: endfor']) 1192 call s:RunDbgCmd(buf, 'step', ['line 2: for it in items']) 1193 call s:RunDbgCmd(buf, 'echo it', ['2']) 1194 call s:RunDbgCmd(buf, 'step', ['line 3: echo it']) 1195 call s:RunDbgCmd(buf, 'step', ['3', 'function FuncWithArgs', 'line 4: endfor']) 1196 call s:RunDbgCmd(buf, 'step', ['line 2: for it in items']) 1197 call s:RunDbgCmd(buf, 'step', ['line 5: echo "done"']) 1198 call s:RunDbgCmd(buf, 'cont') 1199 1200 call s:RunDbgCmd(buf, 1201 \ ':debug call FuncWithDict()', 1202 \ ['cmd: call FuncWithDict()']) 1203 call s:RunDbgCmd(buf, 'step', ['line 1: var d = { a: 1, b: 2, }']) 1204 call s:RunDbgCmd(buf, 'step', ['line 6: def Inner()']) 1205 call s:RunDbgCmd(buf, 'cont') 1206 1207 call s:RunDbgCmd(buf, ':breakadd func 1 FuncComment') 1208 call s:RunDbgCmd(buf, ':call FuncComment()', ['function FuncComment', 'line 2: echo "first" .. "one"']) 1209 call s:RunDbgCmd(buf, ':breakadd func 3 FuncComment') 1210 call s:RunDbgCmd(buf, 'cont', ['function FuncComment', 'line 5: echo "second"']) 1211 call s:RunDbgCmd(buf, 'cont') 1212 1213 call s:RunDbgCmd(buf, ':breakadd func 2 FuncForLoop') 1214 call s:RunDbgCmd(buf, ':call FuncForLoop()', ['function FuncForLoop', 'line 2: for i in [11, 22, 33]']) 1215 call s:RunDbgCmd(buf, 'step', ['line 2: for i in [11, 22, 33]']) 1216 call s:RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 3: eval i + 2']) 1217 call s:RunDbgCmd(buf, 'echo i', ['11']) 1218 call s:RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 4: endfor']) 1219 call s:RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 2: for i in [11, 22, 33]']) 1220 call s:RunDbgCmd(buf, 'next', ['line 3: eval i + 2']) 1221 call s:RunDbgCmd(buf, 'echo i', ['22']) 1222 1223 call s:RunDbgCmd(buf, 'breakdel *') 1224 call s:RunDbgCmd(buf, 'cont') 1225 1226 call s:RunDbgCmd(buf, ':breakadd func FuncWithSplitLine') 1227 call s:RunDbgCmd(buf, ':call FuncWithSplitLine()', ['function FuncWithSplitLine', 'line 1: eval 1 + 2 | eval 2 + 3']) 1228 1229 call s:RunDbgCmd(buf, 'cont') 1230 call StopVimInTerminal(buf) 1231 endfunc 1232 1233 func Test_debug_def_function_with_lambda() 1234 CheckRunVimInTerminal 1235 CheckCWD 1236 let lines =<< trim END 1237 vim9script 1238 def g:Func() 1239 var s = 'a' 1240 ['b']->map((_, v) => s) 1241 echo "done" 1242 enddef 1243 breakadd func 2 g:Func 1244 END 1245 call writefile(lines, 'XtestLambda.vim', 'D') 1246 1247 let buf = RunVimInTerminal('-S XtestLambda.vim', {}) 1248 1249 call s:RunDbgCmd(buf, 1250 \ ':call g:Func()', 1251 \ ['function Func', 'line 2: [''b'']->map((_, v) => s)']) 1252 call s:RunDbgCmd(buf, 1253 \ 'next', 1254 \ ['function Func', 'line 3: echo "done"']) 1255 1256 call s:RunDbgCmd(buf, 'cont') 1257 call StopVimInTerminal(buf) 1258 endfunc 1259 1260 func Test_debug_backtrace_level() 1261 CheckRunVimInTerminal 1262 CheckCWD 1263 let lines =<< trim END 1264 let s:file1_var = 'file1' 1265 let g:global_var = 'global' 1266 1267 func s:File1Func( arg ) 1268 let s:file1_var .= a:arg 1269 let local_var = s:file1_var .. ' test1' 1270 let g:global_var .= local_var 1271 source Xtest2.vim 1272 endfunc 1273 1274 call s:File1Func( 'arg1' ) 1275 END 1276 call writefile(lines, 'Xtest1.vim', 'D') 1277 1278 let lines =<< trim END 1279 let s:file2_var = 'file2' 1280 1281 func s:File2Func( arg ) 1282 let s:file2_var .= a:arg 1283 let local_var = s:file2_var .. ' test2' 1284 let g:global_var .= local_var 1285 endfunc 1286 1287 call s:File2Func( 'arg2' ) 1288 END 1289 call writefile(lines, 'Xtest2.vim', 'D') 1290 1291 let file1 = getcwd() .. '/Xtest1.vim' 1292 let file2 = getcwd() .. '/Xtest2.vim' 1293 1294 " set a breakpoint and source file1.vim 1295 let buf = RunVimInTerminal( 1296 \ '-c "breakadd file 1 Xtest1.vim" -S Xtest1.vim', 1297 \ #{wait_for_ruler: 0}) 1298 1299 call CheckDbgOutput(buf, [ 1300 \ 'Breakpoint in "' .. file1 .. '" line 1', 1301 \ 'Entering Debug mode. Type "cont" to continue.', 1302 \ 'command line..script ' .. file1, 1303 \ 'line 1: let s:file1_var = ''file1''' 1304 \ ], #{msec: 5000}) 1305 1306 " step through the initial declarations 1307 call s:RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] ) 1308 call s:RunDbgCmd(buf, 'step', [ 'line 4: func s:File1Func( arg )' ] ) 1309 call s:RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] ) 1310 call s:RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] ) 1311 call s:RunDbgCmd(buf, 'echo global_var', [ 'global' ] ) 1312 1313 " step in to the first function 1314 call s:RunDbgCmd(buf, 'step', [ 'line 11: call s:File1Func( ''arg1'' )' ] ) 1315 call s:RunDbgCmd(buf, 'step', [ 'line 1: let s:file1_var .= a:arg' ] ) 1316 call s:RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] ) 1317 call s:RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] ) 1318 call s:RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] ) 1319 call s:RunDbgCmd(buf, 1320 \'echo global_var', 1321 \[ 'E121: Undefined variable: global_var' ] ) 1322 call s:RunDbgCmd(buf, 1323 \'echo local_var', 1324 \[ 'E121: Undefined variable: local_var' ] ) 1325 call s:RunDbgCmd(buf, 1326 \'echo l:local_var', 1327 \[ 'E121: Undefined variable: l:local_var' ] ) 1328 1329 " backtrace up 1330 call s:RunDbgCmd(buf, 'backtrace', [ 1331 \ '\V>backtrace', 1332 \ '\V 2 command line', 1333 \ '\V 1 script ' .. file1 .. '[11]', 1334 \ '\V->0 function <SNR>\.\*_File1Func', 1335 \ '\Vline 1: let s:file1_var .= a:arg', 1336 \ ], 1337 \ #{ match: 'pattern' } ) 1338 call s:RunDbgCmd(buf, 'up', [ '>up' ] ) 1339 1340 call s:RunDbgCmd(buf, 'backtrace', [ 1341 \ '\V>backtrace', 1342 \ '\V 2 command line', 1343 \ '\V->1 script ' .. file1 .. '[11]', 1344 \ '\V 0 function <SNR>\.\*_File1Func', 1345 \ '\Vline 1: let s:file1_var .= a:arg', 1346 \ ], 1347 \ #{ match: 'pattern' } ) 1348 1349 " Expression evaluation in the script frame (not the function frame) 1350 " FIXME: Unexpected in this scope (a: should not be visible) 1351 call s:RunDbgCmd(buf, 'echo a:arg', [ 'arg1' ] ) 1352 call s:RunDbgCmd(buf, 'echo s:file1_var', [ 'file1' ] ) 1353 call s:RunDbgCmd(buf, 'echo g:global_var', [ 'global' ] ) 1354 " FIXME: Unexpected in this scope (global should be found) 1355 call s:RunDbgCmd(buf, 1356 \'echo global_var', 1357 \[ 'E121: Undefined variable: global_var' ] ) 1358 call s:RunDbgCmd(buf, 1359 \'echo local_var', 1360 \[ 'E121: Undefined variable: local_var' ] ) 1361 call s:RunDbgCmd(buf, 1362 \'echo l:local_var', 1363 \[ 'E121: Undefined variable: l:local_var' ] ) 1364 1365 1366 " step while backtraced jumps to the latest frame 1367 call s:RunDbgCmd(buf, 'step', [ 1368 \ 'line 2: let local_var = s:file1_var .. '' test1''' ] ) 1369 call s:RunDbgCmd(buf, 'backtrace', [ 1370 \ '\V>backtrace', 1371 \ '\V 2 command line', 1372 \ '\V 1 script ' .. file1 .. '[11]', 1373 \ '\V->0 function <SNR>\.\*_File1Func', 1374 \ '\Vline 2: let local_var = s:file1_var .. '' test1''', 1375 \ ], 1376 \ #{ match: 'pattern' } ) 1377 1378 call s:RunDbgCmd(buf, 'step', [ 'line 3: let g:global_var .= local_var' ] ) 1379 call s:RunDbgCmd(buf, 'echo local_var', [ 'file1arg1 test1' ] ) 1380 call s:RunDbgCmd(buf, 'echo l:local_var', [ 'file1arg1 test1' ] ) 1381 1382 call s:RunDbgCmd(buf, 'step', [ 'line 4: source Xtest2.vim' ] ) 1383 call s:RunDbgCmd(buf, 'step', [ 'line 1: let s:file2_var = ''file2''' ] ) 1384 call s:RunDbgCmd(buf, 'backtrace', [ 1385 \ '\V>backtrace', 1386 \ '\V 3 command line', 1387 \ '\V 2 script ' .. file1 .. '[11]', 1388 \ '\V 1 function <SNR>\.\*_File1Func[4]', 1389 \ '\V->0 script ' .. file2, 1390 \ '\Vline 1: let s:file2_var = ''file2''', 1391 \ ], 1392 \ #{ match: 'pattern' } ) 1393 1394 " Expression evaluation in the script frame file2 (not the function frame) 1395 call s:RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] ) 1396 call s:RunDbgCmd(buf, 1397 \ 'echo s:file1_var', 1398 \ [ 'E121: Undefined variable: s:file1_var' ] ) 1399 call s:RunDbgCmd(buf, 'echo g:global_var', [ 'globalfile1arg1 test1' ] ) 1400 call s:RunDbgCmd(buf, 'echo global_var', [ 'globalfile1arg1 test1' ] ) 1401 call s:RunDbgCmd(buf, 1402 \'echo local_var', 1403 \[ 'E121: Undefined variable: local_var' ] ) 1404 call s:RunDbgCmd(buf, 1405 \'echo l:local_var', 1406 \[ 'E121: Undefined variable: l:local_var' ] ) 1407 call s:RunDbgCmd(buf, 1408 \ 'echo s:file2_var', 1409 \ [ 'E121: Undefined variable: s:file2_var' ] ) 1410 1411 call s:RunDbgCmd(buf, 'step', [ 'line 3: func s:File2Func( arg )' ] ) 1412 call s:RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] ) 1413 1414 " Up the stack to the other script context 1415 call s:RunDbgCmd(buf, 'up') 1416 call s:RunDbgCmd(buf, 'backtrace', [ 1417 \ '\V>backtrace', 1418 \ '\V 3 command line', 1419 \ '\V 2 script ' .. file1 .. '[11]', 1420 \ '\V->1 function <SNR>\.\*_File1Func[4]', 1421 \ '\V 0 script ' .. file2, 1422 \ '\Vline 3: func s:File2Func( arg )', 1423 \ ], 1424 \ #{ match: 'pattern' } ) 1425 " FIXME: Unexpected. Should see the a: and l: dicts from File1Func 1426 call s:RunDbgCmd(buf, 'echo a:arg', [ 'E121: Undefined variable: a:arg' ] ) 1427 call s:RunDbgCmd(buf, 1428 \ 'echo l:local_var', 1429 \ [ 'E121: Undefined variable: l:local_var' ] ) 1430 1431 call s:RunDbgCmd(buf, 'up') 1432 call s:RunDbgCmd(buf, 'backtrace', [ 1433 \ '\V>backtrace', 1434 \ '\V 3 command line', 1435 \ '\V->2 script ' .. file1 .. '[11]', 1436 \ '\V 1 function <SNR>\.\*_File1Func[4]', 1437 \ '\V 0 script ' .. file2, 1438 \ '\Vline 3: func s:File2Func( arg )', 1439 \ ], 1440 \ #{ match: 'pattern' } ) 1441 1442 " FIXME: Unexpected (wrong script vars are used) 1443 call s:RunDbgCmd(buf, 1444 \ 'echo s:file1_var', 1445 \ [ 'E121: Undefined variable: s:file1_var' ] ) 1446 call s:RunDbgCmd(buf, 'echo s:file2_var', [ 'file2' ] ) 1447 1448 call s:RunDbgCmd(buf, 'cont') 1449 call StopVimInTerminal(buf) 1450 endfunc 1451 1452 " Test for setting a breakpoint on a :endif where the :if condition is false 1453 " and then quit the script. This should generate an interrupt. 1454 func Test_breakpt_endif_intr() 1455 func F() 1456 let g:Xpath ..= 'a' 1457 if v:false 1458 let g:Xpath ..= 'b' 1459 endif 1460 invalid_command 1461 endfunc 1462 1463 let g:Xpath = '' 1464 breakadd func 4 F 1465 try 1466 let caught_intr = 0 1467 debuggreedy 1468 call feedkeys(":call F()\<CR>quit\<CR>", "xt") 1469 catch /^Vim:Interrupt$/ 1470 call assert_match('\.F, line 4', v:throwpoint) 1471 let caught_intr = 1 1472 endtry 1473 0debuggreedy 1474 call assert_equal(1, caught_intr) 1475 call assert_equal('a', g:Xpath) 1476 breakdel * 1477 delfunc F 1478 endfunc 1479 1480 " Test for setting a breakpoint on a :else where the :if condition is false 1481 " and then quit the script. This should generate an interrupt. 1482 func Test_breakpt_else_intr() 1483 func F() 1484 let g:Xpath ..= 'a' 1485 if v:false 1486 let g:Xpath ..= 'b' 1487 else 1488 invalid_command 1489 endif 1490 invalid_command 1491 endfunc 1492 1493 let g:Xpath = '' 1494 breakadd func 4 F 1495 try 1496 let caught_intr = 0 1497 debuggreedy 1498 call feedkeys(":call F()\<CR>quit\<CR>", "xt") 1499 catch /^Vim:Interrupt$/ 1500 call assert_match('\.F, line 4', v:throwpoint) 1501 let caught_intr = 1 1502 endtry 1503 0debuggreedy 1504 call assert_equal(1, caught_intr) 1505 call assert_equal('a', g:Xpath) 1506 breakdel * 1507 delfunc F 1508 endfunc 1509 1510 " Test for setting a breakpoint on a :endwhile where the :while condition is 1511 " false and then quit the script. This should generate an interrupt. 1512 func Test_breakpt_endwhile_intr() 1513 func F() 1514 let g:Xpath ..= 'a' 1515 while v:false 1516 let g:Xpath ..= 'b' 1517 endwhile 1518 invalid_command 1519 endfunc 1520 1521 let g:Xpath = '' 1522 breakadd func 4 F 1523 try 1524 let caught_intr = 0 1525 debuggreedy 1526 call feedkeys(":call F()\<CR>quit\<CR>", "xt") 1527 catch /^Vim:Interrupt$/ 1528 call assert_match('\.F, line 4', v:throwpoint) 1529 let caught_intr = 1 1530 endtry 1531 0debuggreedy 1532 call assert_equal(1, caught_intr) 1533 call assert_equal('a', g:Xpath) 1534 breakdel * 1535 delfunc F 1536 endfunc 1537 1538 " Test for setting a breakpoint on a script local function 1539 func Test_breakpt_scriptlocal_func() 1540 let g:Xpath = '' 1541 func s:G() 1542 let g:Xpath ..= 'a' 1543 endfunc 1544 1545 let funcname = expand("<SID>") .. "G" 1546 exe "breakadd func 1 " .. funcname 1547 debuggreedy 1548 redir => output 1549 call feedkeys(":call " .. funcname .. "()\<CR>c\<CR>", "xt") 1550 redir END 1551 0debuggreedy 1552 call assert_match('Breakpoint in "' .. funcname .. '" line 1', output) 1553 call assert_equal('a', g:Xpath) 1554 breakdel * 1555 exe "delfunc " .. funcname 1556 endfunc 1557 1558 " vim: shiftwidth=2 sts=2 expandtab