-
Notifications
You must be signed in to change notification settings - Fork 398
/
test49.vim
9802 lines (8661 loc) · 219 KB
/
test49.vim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
" Vim script language tests
" Author: Servatius Brandt <Servatius.Brandt@fujitsu-siemens.com>
" Last Change: 2010 Sep 29
"-------------------------------------------------------------------------------
" Test environment {{{1
"-------------------------------------------------------------------------------
" Adding new tests easily. {{{2
"
" Writing new tests is eased considerably with the following functions and
" abbreviations (see "Commands for recording the execution path", "Automatic
" argument generation").
"
" To get the abbreviations, execute the command
"
" :let test49_set_env = 1 | source test49.vim
"
" To get them always (from src/testdir), put a line
"
" au! BufRead test49.vim let test49_set_env = 1 | source test49.vim
"
" into the local .vimrc file in the src/testdir directory.
"
if exists("test49_set_env") && test49_set_env
" Automatic argument generation for the test environment commands.
function! Xsum()
let addend = substitute(getline("."), '^.*"\s*X:\s*\|^.*', '', "")
" Evaluate arithmetic expression.
if addend != ""
exec "let g:Xsum = g:Xsum + " . addend
endif
endfunction
function! Xcheck()
let g:Xsum=0
?XpathINIT?,.call Xsum()
exec "norm A "
return g:Xsum
endfunction
iab Xcheck Xcheck<Space><C-R>=Xcheck()<CR><C-O>x
function! Xcomment(num)
let str = ""
let tabwidth = &sts ? &sts : &ts
let tabs = (48+tabwidth - a:num - virtcol(".")) / tabwidth
while tabs > 0
let str = str . "\t"
let tabs = tabs - 1
endwhile
let str = str . '" X:'
return str
endfunction
function! Xloop()
let back = line(".") . "|norm" . virtcol(".") . "|"
norm 0
let last = search('X\(loop\|path\)INIT\|Xloop\>', "bW")
exec back
let theline = getline(last)
if theline =~ 'X\(loop\|path\)INIT'
let num = 1
else
let num = 2 * substitute(theline, '.*Xloop\s*\(\d\+\).*', '\1', "")
endif
?X\(loop\|path\)INIT?
\s/\(XloopINIT!\=\s*\d\+\s\+\)\@<=\(\d\+\)/\=2*submatch(2)/
exec back
exec "norm a "
return num . Xcomment(strlen(num))
endfunction
iab Xloop Xloop<Space><C-R>=Xloop()<CR><C-O>x
function! Xpath(loopinit)
let back = line(".") . "|norm" . virtcol(".") . "|"
norm 0
let last = search('XpathINIT\|Xpath\>\|XloopINIT', "bW")
exec back
let theline = getline(last)
if theline =~ 'XpathINIT'
let num = 1
elseif theline =~ 'Xpath\>'
let num = 2 * substitute(theline, '.*Xpath\s*\(\d\+\).*', '\1', "")
else
let pattern = '.*XloopINIT!\=\s*\(\d\+\)\s*\(\d\+\).*'
let num = substitute(theline, pattern, '\1', "")
let factor = substitute(theline, pattern, '\2', "")
" The "<C-O>x" from the "Xpath" iab and the character triggering its
" expansion are in the input buffer. Save and clear typeahead so
" that it is not read away by the call to "input()" below. Restore
" afterwards.
call inputsave()
let loops = input("Number of iterations in previous loop? ")
call inputrestore()
while (loops > 0)
let num = num * factor
let loops = loops - 1
endwhile
endif
exec "norm a "
if a:loopinit
return num . " 1"
endif
return num . Xcomment(strlen(num))
endfunction
iab Xpath Xpath<Space><C-R>=Xpath(0)<CR><C-O>x
iab XloopINIT XloopINIT<Space><C-R>=Xpath(1)<CR><C-O>x
" Also useful (see ExtraVim below):
aug ExtraVim
au!
au BufEnter <sfile> syn region ExtraVim
\ start=+^if\s\+ExtraVim(.*)+ end=+^endif+
\ transparent keepend
au BufEnter <sfile> syn match ExtraComment /^"/
\ contained containedin=ExtraVim
au BufEnter <sfile> hi link ExtraComment vimComment
aug END
aug Xpath
au BufEnter <sfile> syn keyword Xpath
\ XpathINIT Xpath XloopINIT Xloop XloopNEXT Xcheck Xout
au BufEnter <sfile> hi link Xpath Special
aug END
do BufEnter <sfile>
" Do not execute the tests when sourcing this file for getting the functions
" and abbreviations above, which are intended for easily adding new test
" cases; they are not needed for test execution. Unlet the variable
" controlling this so that an explicit ":source" command for this file will
" execute the tests.
unlet test49_set_env
finish
endif
" Commands for recording the execution path. {{{2
"
" The Xpath/Xloop commands can be used for computing the eXecution path by
" adding (different) powers of 2 from those script lines, for which the
" execution should be checked. Xloop provides different addends for each
" execution of a loop. Permitted values are 2^0 to 2^30, so that 31 execution
" points (multiply counted inside loops) can be tested.
"
" Note that the arguments of the following commands can be generated
" automatically, see below.
"
" Usage: {{{3
"
" - Use XpathINIT at the beginning of the test.
"
" - Use Xpath to check if a line is executed.
" Argument: power of 2 (decimal).
"
" - To check multiple execution of loops use Xloop for automatically
" computing Xpath values:
"
" - Use XloopINIT before the loop.
" Two arguments:
" - the first Xpath value (power of 2) to be used (Xnext),
" - factor for computing a new Xnext value when reexecuting a loop
" (by a ":continue" or ":endwhile"); this should be 2^n where
" n is the number of Xloop commands inside the loop.
" If XloopINIT! is used, the first execution of XloopNEXT is
" a no-operation.
"
" - Use Xloop inside the loop:
" One argument:
" The argument and the Xnext value are multiplied to build the
" next Xpath value. No new Xnext value is prepared. The argument
" should be 2^(n-1) for the nth Xloop command inside the loop.
" If the loop has only one Xloop command, the argument can be
" ommitted (default: 1).
"
" - Use XloopNEXT before ":continue" and ":endwhile". This computes a new
" Xnext value for the next execution of the loop by multiplying the old
" one with the factor specified in the XloopINIT command. No Argument.
" Alternatively, when XloopINIT! is used, a single XloopNEXT at the
" beginning of the loop can be used.
"
" Nested loops are not supported.
"
" - Use Xcheck at end of each test. It prints the test number, the expected
" execution path value, the test result ("OK" or "FAIL"), and, if the tests
" fails, the actual execution path.
" One argument:
" Expected Xpath/Xloop sum for the correct execution path.
" In order that this value can be computed automatically, do the
" following: For each line in the test with an Xpath and Xloop
" command, add a comment starting with "X:" and specifying an
" expression that evaluates to the value contributed by this line to
" the correct execution path. (For copying an Xpath argument of at
" least two digits into the comment, press <C-P>.) At the end of the
" test, just type "Xcheck" and press <Esc>.
"
" - In order to add additional information to the test output file, use the
" Xout command. Argument(s) like ":echo".
"
" Automatic argument generation: {{{3
"
" The arguments of the Xpath, XloopINIT, Xloop, and Xcheck commands can be
" generated automatically, so that new tests can easily be written without
" mental arithmetic. The Xcheck argument is computed from the "X:" comments
" of the preceding Xpath and Xloop commands. See the commands and
" abbreviations at the beginning of this file.
"
" Implementation: {{{3
" XpathINIT, Xpath, XloopINIT, Xloop, XloopNEXT, Xcheck, Xout.
"
" The variants for existing g:ExtraVimResult are needed when executing a script
" in an extra Vim process, see ExtraVim below.
" EXTRA_VIM_START - do not change or remove this line.
com! XpathINIT let g:Xpath = 0
if exists("g:ExtraVimResult")
com! -count -bar Xpath exec "!echo <count> >>" . g:ExtraVimResult
else
com! -count -bar Xpath let g:Xpath = g:Xpath + <count>
endif
com! -count -nargs=1 -bang
\ XloopINIT let g:Xnext = <count> |
\ let g:Xfactor = <args> |
\ let g:Xskip = strlen("<bang>")
if exists("g:ExtraVimResult")
com! -count=1 -bar Xloop exec "!echo " . (g:Xnext * <count>) . " >>" .
\ g:ExtraVimResult
else
com! -count=1 -bar Xloop let g:Xpath = g:Xpath + g:Xnext * <count>
endif
com! XloopNEXT let g:Xnext = g:Xnext *
\ (g:Xskip ? 1 : g:Xfactor) |
\ let g:Xskip = 0
let @r = ""
let Xtest = 1
com! -count Xcheck let Xresult = "*** Test " .
\ (Xtest<10?" ":Xtest<100?" ":"") .
\ Xtest . ": " . (
\ (Xpath==<count>) ? "OK (".Xpath.")" :
\ "FAIL (".Xpath." instead of <count>)"
\ ) |
\ let @R = Xresult . "\n" |
\ echo Xresult |
\ let Xtest = Xtest + 1
if exists("g:ExtraVimResult")
com! -nargs=+ Xoutq exec "!echo @R:'" .
\ substitute(substitute(<q-args>,
\ "'", '&\\&&', "g"), "\n", "@NL@", "g")
\ . "' >>" . g:ExtraVimResult
else
com! -nargs=+ Xoutq let @R = "--- Test " .
\ (g:Xtest<10?" ":g:Xtest<100?" ":"") .
\ g:Xtest . ": " . substitute(<q-args>,
\ "\n", "&\t ", "g") . "\n"
endif
com! -nargs=+ Xout exec 'Xoutq' <args>
" Switch off storing of lines for undoing changes. Speeds things up a little.
set undolevels=-1
" EXTRA_VIM_STOP - do not change or remove this line.
" ExtraVim() - Run a script file in an extra Vim process. {{{2
"
" This is useful for testing immediate abortion of the script processing due to
" an error in a command dynamically enclosed by a :try/:tryend region or when an
" exception is thrown but not caught or when an interrupt occurs. It can also
" be used for testing :finish.
"
" An interrupt location can be specified by an "INTERRUPT" comment. A number
" telling how often this location is reached (in a loop or in several function
" calls) should be specified as argument. When missing, once per script
" invocation or function call is assumed. INTERRUPT locations are tested by
" setting a breakpoint in that line and using the ">quit" debug command when
" the breakpoint is reached. A function for which an INTERRUPT location is
" specified must be defined before calling it (or executing it as a script by
" using ExecAsScript below).
"
" This function is only called in normal modus ("g:ExtraVimResult" undefined).
"
" Tests to be executed as an extra script should be written as follows:
"
" column 1 column 1
" | |
" v v
"
" XpathINIT XpathINIT
" if ExtraVim() if ExtraVim()
" ... " ...
" ... " ...
" endif endif
" Xcheck <number> Xcheck <number>
"
" Double quotes in column 1 are removed before the script is executed.
" They should be used if the test has unbalanced conditionals (:if/:endif,
" :while:/endwhile, :try/:endtry) or for a line with a syntax error. The
" extra script may use Xpath, XloopINIT, Xloop, XloopNEXT, and Xout as usual.
"
" A file name may be specified as argument. All messages of the extra Vim
" process are then redirected to the file. An existing file is overwritten.
"
let ExtraVimCount = 0
let ExtraVimBase = expand("<sfile>")
let ExtraVimTestEnv = ""
"
function! ExtraVim(...)
" Count how often this function is called.
let g:ExtraVimCount = g:ExtraVimCount + 1
" Disable folds to prevent that the ranges in the ":write" commands below
" are extended up to the end of a closed fold. This also speeds things up
" considerably.
set nofoldenable
" Open a buffer for this test script and copy the test environment to
" a temporary file. Take account of parts relevant for the extra script
" execution only.
let current_buffnr = bufnr("%")
execute "view +1" g:ExtraVimBase
if g:ExtraVimCount == 1
let g:ExtraVimTestEnv = tempname()
execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w"
\ g:ExtraVimTestEnv "|']+"
execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>"
\ g:ExtraVimTestEnv "|']+"
execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>"
\ g:ExtraVimTestEnv "|']+"
execute "/E" . "XTRA_VIM_START/+,/E" . "XTRA_VIM_STOP/-w >>"
\ g:ExtraVimTestEnv "|']+"
endif
" Start the extra Vim script with a ":source" command for the test
" environment. The source line number where the extra script will be
" appended, needs to be passed as variable "ExtraVimBegin" to the script.
let extra_script = tempname()
exec "!echo 'source " . g:ExtraVimTestEnv . "' >" . extra_script
let extra_begin = 1
" Starting behind the test environment, skip over the first g:ExtraVimCount
" occurrences of "if ExtraVim()" and copy the following lines up to the
" matching "endif" to the extra Vim script.
execute "/E" . "ND_OF_TEST_ENVIRONMENT/"
exec 'norm ' . g:ExtraVimCount . '/^\s*if\s\+ExtraVim(.*)/+' . "\n"
execute ".,/^endif/-write >>" . extra_script
" Open a buffer for the extra Vim script, delete all ^", and write the
" script if was actually modified.
execute "edit +" . (extra_begin + 1) extra_script
,$s/^"//e
update
" Count the INTERRUPTs and build the breakpoint and quit commands.
let breakpoints = ""
let debug_quits = ""
let in_func = 0
exec extra_begin
while search(
\ '"\s*INTERRUPT\h\@!\|^\s*fu\%[nction]\>!\=\s*\%(\u\|s:\)\w*\s*(\|'
\ . '^\s*\\\|^\s*endf\%[unction]\>\|'
\ . '\%(^\s*fu\%[nction]!\=\s*\)\@<!\%(\u\|s:\)\w*\s*(\|'
\ . 'ExecAsScript\s\+\%(\u\|s:\)\w*',
\ "W") > 0
let theline = getline(".")
if theline =~ '^\s*fu'
" Function definition.
let in_func = 1
let func_start = line(".")
let func_name = substitute(theline,
\ '^\s*fu\%[nction]!\=\s*\(\%(\u\|s:\)\w*\).*', '\1', "")
elseif theline =~ '^\s*endf'
" End of function definition.
let in_func = 0
else
let finding = substitute(theline, '.*\(\%' . col(".") . 'c.*\)',
\ '\1', "")
if finding =~ '^"\s*INTERRUPT\h\@!'
" Interrupt comment. Compose as many quit commands as
" specified.
let cnt = substitute(finding,
\ '^"\s*INTERRUPT\s*\(\d*\).*$', '\1', "")
let quits = ""
while cnt > 0
" Use "\r" rather than "\n" to separate the quit commands.
" "\r" is not interpreted as command separator by the ":!"
" command below but works to separate commands in the
" external vim.
let quits = quits . "q\r"
let cnt = cnt - 1
endwhile
if in_func
" Add the function breakpoint and note the number of quits
" to be used, if specified, or one for every call else.
let breakpoints = breakpoints . " -c 'breakadd func " .
\ (line(".") - func_start) . " " .
\ func_name . "'"
if quits != ""
let debug_quits = debug_quits . quits
elseif !exists("quits{func_name}")
let quits{func_name} = "q\r"
else
let quits{func_name} = quits{func_name} . "q\r"
endif
else
" Add the file breakpoint and the quits to be used for it.
let breakpoints = breakpoints . " -c 'breakadd file " .
\ line(".") . " " . extra_script . "'"
if quits == ""
let quits = "q\r"
endif
let debug_quits = debug_quits . quits
endif
else
" Add the quits to be used for calling the function or executing
" it as script file.
if finding =~ '^ExecAsScript'
" Sourcing function as script.
let finding = substitute(finding,
\ '^ExecAsScript\s\+\(\%(\u\|s:\)\w*\).*', '\1', "")
else
" Function call.
let finding = substitute(finding,
\ '^\(\%(\u\|s:\)\w*\).*', '\1', "")
endif
if exists("quits{finding}")
let debug_quits = debug_quits . quits{finding}
endif
endif
endif
endwhile
" Close the buffer for the script and create an (empty) resultfile.
bwipeout
let resultfile = tempname()
exec "!>" . resultfile
" Run the script in an extra vim. Switch to extra modus by passing the
" resultfile in ExtraVimResult. Redirect messages to the file specified as
" argument if any. Use ":debuggreedy" so that the commands provided on the
" pipe are consumed at the debug prompt. Use "-N" to enable command-line
" continuation ("C" in 'cpo'). Add "nviminfo" to 'viminfo' to avoid
" messing up the user's viminfo file.
let redirect = a:0 ?
\ " -c 'au VimLeave * redir END' -c 'redir\\! >" . a:1 . "'" : ""
exec "!echo '" . debug_quits . "q' | ../vim -u NONE -N -Xes" . redirect .
\ " -c 'debuggreedy|set viminfo+=nviminfo'" .
\ " -c 'let ExtraVimBegin = " . extra_begin . "'" .
\ " -c 'let ExtraVimResult = \"" . resultfile . "\"'" . breakpoints .
\ " -S " . extra_script
" Build the resulting sum for resultfile and add it to g:Xpath. Add Xout
" information provided by the extra Vim process to the test output.
let sum = 0
exec "edit" resultfile
let line = 1
while line <= line("$")
let theline = getline(line)
if theline =~ '^@R:'
exec 'Xout "' . substitute(substitute(
\ escape(escape(theline, '"'), '\"'),
\ '^@R:', '', ""), '@NL@', "\n", "g") . '"'
else
let sum = sum + getline(line)
endif
let line = line + 1
endwhile
bwipeout
let g:Xpath = g:Xpath + sum
" Delete the extra script and the resultfile.
call delete(extra_script)
call delete(resultfile)
" Switch back to the buffer that was active when this function was entered.
exec "buffer" current_buffnr
" Return 0. This protects extra scripts from being run in the main Vim
" process.
return 0
endfunction
" ExtraVimThrowpoint() - Relative throwpoint in ExtraVim script {{{2
"
" Evaluates v:throwpoint and returns the throwpoint relative to the beginning of
" an ExtraVim script as passed by ExtraVim() in ExtraVimBegin.
"
" EXTRA_VIM_START - do not change or remove this line.
function! ExtraVimThrowpoint()
if !exists("g:ExtraVimBegin")
Xout "ExtraVimThrowpoint() used outside ExtraVim() script."
return v:throwpoint
endif
if v:throwpoint =~ '^function\>'
return v:throwpoint
endif
return "line " .
\ (substitute(v:throwpoint, '.*, line ', '', "") - g:ExtraVimBegin) .
\ " of ExtraVim() script"
endfunction
" EXTRA_VIM_STOP - do not change or remove this line.
" MakeScript() - Make a script file from a function. {{{2
"
" Create a script that consists of the body of the function a:funcname.
" Replace any ":return" by a ":finish", any argument variable by a global
" variable, and and every ":call" by a ":source" for the next following argument
" in the variable argument list. This function is useful if similar tests are
" to be made for a ":return" from a function call or a ":finish" in a script
" file.
"
" In order to execute a function specifying an INTERRUPT location (see ExtraVim)
" as a script file, use ExecAsScript below.
"
" EXTRA_VIM_START - do not change or remove this line.
function! MakeScript(funcname, ...)
let script = tempname()
execute "redir! >" . script
execute "function" a:funcname
redir END
execute "edit" script
" Delete the "function" and the "endfunction" lines. Do not include the
" word "function" in the pattern since it might be translated if LANG is
" set. When MakeScript() is being debugged, this deletes also the debugging
" output of its line 3 and 4.
exec '1,/.*' . a:funcname . '(.*)/d'
/^\d*\s*endfunction\>/,$d
%s/^\d*//e
%s/return/finish/e
%s/\<a:\(\h\w*\)/g:\1/ge
normal gg0
let cnt = 0
while search('\<call\s*\%(\u\|s:\)\w*\s*(.*)', 'W') > 0
let cnt = cnt + 1
s/\<call\s*\%(\u\|s:\)\w*\s*(.*)/\='source ' . a:{cnt}/
endwhile
g/^\s*$/d
write
bwipeout
return script
endfunction
" EXTRA_VIM_STOP - do not change or remove this line.
" ExecAsScript - Source a temporary script made from a function. {{{2
"
" Make a temporary script file from the function a:funcname, ":source" it, and
" delete it afterwards.
"
" When inside ":if ExtraVim()", add a file breakpoint for each INTERRUPT
" location specified in the function.
"
" EXTRA_VIM_START - do not change or remove this line.
function! ExecAsScript(funcname)
" Make a script from the function passed as argument.
let script = MakeScript(a:funcname)
" When running in an extra Vim process, add a file breakpoint for each
" function breakpoint set when the extra Vim process was invoked by
" ExtraVim().
if exists("g:ExtraVimResult")
let bplist = tempname()
execute "redir! >" . bplist
breaklist
redir END
execute "edit" bplist
" Get the line number from the function breakpoint. Works also when
" LANG is set.
execute 'v/^\s*\d\+\s\+func\s\+' . a:funcname . '\s.*/d'
%s/^\s*\d\+\s\+func\s\+\%(\u\|s:\)\w*\s\D*\(\d*\).*/\1/e
let cnt = 0
while cnt < line("$")
let cnt = cnt + 1
if getline(cnt) != ""
execute "breakadd file" getline(cnt) script
endif
endwhile
bwipeout!
call delete(bplist)
endif
" Source and delete the script.
exec "source" script
call delete(script)
endfunction
com! -nargs=1 -bar ExecAsScript call ExecAsScript(<f-args>)
" EXTRA_VIM_STOP - do not change or remove this line.
" END_OF_TEST_ENVIRONMENT - do not change or remove this line.
"-------------------------------------------------------------------------------
" Test 1: :endwhile in function {{{1
"
" Detect if a broken loop is (incorrectly) reactivated by the
" :endwhile. Use a :return to prevent an endless loop, and make
" this test first to get a meaningful result on an error before other
" tests will hang.
"-------------------------------------------------------------------------------
XpathINIT
function! F()
Xpath 1 " X: 1
let first = 1
XloopINIT 2 8
while 1
Xloop 1 " X: 2 + 0 * 16
if first
Xloop 2 " X: 4 + 0 * 32
let first = 0
XloopNEXT
break
else
Xloop 4 " X: 0 + 0 * 64
return
endif
endwhile
endfunction
call F()
Xpath 128 " X: 128
function! G()
Xpath 256 " X: 256 + 0 * 2048
let first = 1
XloopINIT 512 8
while 1
Xloop 1 " X: 512 + 0 * 4096
if first
Xloop 2 " X: 1024 + 0 * 8192
let first = 0
XloopNEXT
break
else
Xloop 4 " X: 0 + 0 * 16384
return
endif
if 1 " unmatched :if
endwhile
endfunction
call G()
Xpath 32768 " X: 32768
Xcheck 34695
" Leave F and G for execution as scripts in the next test.
"-------------------------------------------------------------------------------
" Test 2: :endwhile in script {{{1
"
" Detect if a broken loop is (incorrectly) reactivated by the
" :endwhile. Use a :finish to prevent an endless loop, and place
" this test before others that might hang to get a meaningful result
" on an error.
"
" This test executes the bodies of the functions F and G from the
" previous test as script files (:return replaced by :finish).
"-------------------------------------------------------------------------------
XpathINIT
ExecAsScript F " X: 1 + 2 + 4
Xpath 128 " X: 128
ExecAsScript G " X: 256 + 512 + 1024
Xpath 32768 " X: 32768
unlet first
delfunction F
delfunction G
Xcheck 34695
"-------------------------------------------------------------------------------
" Test 3: :if, :elseif, :while, :continue, :break {{{1
"-------------------------------------------------------------------------------
XpathINIT
if 1
Xpath 1 " X: 1
let loops = 3
XloopINIT 2 512
while loops > -1 " main loop: loops == 3, 2, 1 (which breaks)
if loops <= 0
let break_err = 1
let loops = -1
else " 3: 2: 1:
Xloop 1 " X: 2 + 2*512 + 2*512*512
endif
if (loops == 2)
while loops == 2 " dummy loop
Xloop 2 " X: 4*512
let loops = loops - 1
continue " stop dummy loop
Xloop 4 " X: 0
endwhile
XloopNEXT
continue " continue main loop
Xloop 8 " X: 0
elseif (loops == 1)
let p = 1
while p " dummy loop
Xloop 16 " X: 32*512*512
let p = 0
break " break dummy loop
Xloop 32 " X: 0
endwhile
Xloop 64 " X: 128*512*512
unlet p
break " break main loop
Xloop 128 " X: 0
endif
if (loops > 0)
Xloop 256 " X: 512
endif
while loops == 3 " dummy loop
let loops = loops - 1
endwhile " end dummy loop
XloopNEXT
endwhile " end main loop
Xpath 268435456 " X: 1024*512*512
else
Xpath 536870912 " X: 0
endif
Xpath 1073741824 " X: 4096*512*512
if exists("break_err")
" The Xpath command does not accept 2^31 (negative); add explicitly:
let Xpath = Xpath + 2147483648 " X: 0
unlet break_err
endif
unlet loops
Xcheck 1384648195
"-------------------------------------------------------------------------------
" Test 4: :return {{{1
"-------------------------------------------------------------------------------
XpathINIT
function! F()
if 1
Xpath 1 " X: 1
let loops = 3
XloopINIT 2 16
while loops > 0 " 3: 2: 1:
Xloop 1 " X: 2 + 2*16 + 0*16*16
if (loops == 2)
Xloop 2 " X: 4*16
return
Xloop 4 " X: 0
endif
Xloop 8 " X: 16
let loops = loops - 1
XloopNEXT
endwhile
Xpath 8192 " X: 0
else
Xpath 16384 " X: 0
endif
endfunction
call F()
Xpath 32768 " X: 8*16*16*16
Xcheck 32883
" Leave F for execution as a script in the next test.
"-------------------------------------------------------------------------------
" Test 5: :finish {{{1
"
" This test executes the body of the function F from the previous test
" as a script file (:return replaced by :finish).
"-------------------------------------------------------------------------------
XpathINIT
ExecAsScript F " X: 1 + 2 + 2*16 + 4*16 + 16
Xpath 32768 " X: 32768
unlet loops
delfunction F
Xcheck 32883
"-------------------------------------------------------------------------------
" Test 6: Defining functions in :while loops {{{1
"
" Functions can be defined inside other functions. An inner function
" gets defined when the outer function is executed. Functions may
" also be defined inside while loops. Expressions in braces for
" defining the function name are allowed.
"-------------------------------------------------------------------------------
XpathINIT
if ExtraVim()
" The command CALL collects the argument of all its invocations in "calls"
" when used from a function (that is, when the global variable "calls" needs
" the "g:" prefix). This is to check that the function code is skipped when
" the function is defined. For inner functions, do so only if the outer
" function is not being executed.
"
let calls = ""
com! -nargs=1 CALL
\ if !exists("calls") && !exists("outer") |
\ let g:calls = g:calls . <args> |
\ endif
XloopINIT! 1 16
let i = 0
while i < 3
XloopNEXT
let i = i + 1
if i == 1
Xloop 1 " X: 1
function! F1(arg)
CALL a:arg
let outer = 1
XloopINIT! 4096 4
let j = 0
while j < 1
XloopNEXT
Xloop 1 " X: 4096
let j = j + 1
function! G1(arg)
CALL a:arg
endfunction
Xloop 2 " X: 8192
endwhile
endfunction
Xloop 2 " X: 2
continue
endif
Xloop 4 " X: 4 * (16 + 256)
function! F{i}(i, arg)
CALL a:arg
let outer = 1
XloopINIT! 16384 4
if a:i == 3
XloopNEXT
XloopNEXT
XloopNEXT
endif
let k = 0
while k < 3
XloopNEXT
Xloop 1 " X: 16384*(1+4+16+64+256+1024)
let k = k + 1
function! G{a:i}{k}(arg)
CALL a:arg
endfunction
Xloop 2 " X: 32768*(1+4+16+64+256+1024)
endwhile
endfunction
Xloop 8 " X: 8 * (16 + 256)
endwhile
if exists("*G1")
Xpath 67108864 " X: 0
endif
if exists("*F1")
call F1("F1")
if exists("*G1")
call G1("G1")
endif
endif
if exists("G21") || exists("G21") || exists("G21")
Xpath 134217728 " X: 0
endif
if exists("*F2")
call F2(2, "F2")
if exists("*G21")
call G21("G21")
endif
if exists("*G22")
call G22("G22")
endif
if exists("*G23")
call G23("G23")
endif
endif
if exists("G31") || exists("G31") || exists("G31")
Xpath 268435456 " X: 0
endif
if exists("*F3")
call F3(3, "F3")
if exists("*G31")
call G31("G31")
endif
if exists("*G32")
call G32("G32")
endif
if exists("*G33")
call G33("G33")
endif
endif
Xpath 536870912 " X: 536870912
if calls != "F1G1F2G21G22G23F3G31G32G33"
Xpath 1073741824 " X: 0
Xout "calls is" calls
endif
delfunction F1
delfunction G1
delfunction F2
delfunction G21
delfunction G22
delfunction G23
delfunction G31
delfunction G32
delfunction G33
endif
Xcheck 603978947
"-------------------------------------------------------------------------------
" Test 7: Continuing on errors outside functions {{{1
"
" On an error outside a function, the script processing continues
" at the line following the outermost :endif or :endwhile. When not
" inside an :if or :while, the script processing continues at the next
" line.
"-------------------------------------------------------------------------------
XpathINIT
if 1
Xpath 1 " X: 1
while 1
Xpath 2 " X: 2
asdf
Xpath 4 " X: 0
break
endwhile | Xpath 8 " X: 0
Xpath 16 " X: 0
endif | Xpath 32 " X: 0
Xpath 64 " X: 64
while 1
Xpath 128 " X: 128
if 1
Xpath 256 " X: 256
asdf
Xpath 512 " X: 0
endif | Xpath 1024 " X: 0
Xpath 2048 " X: 0
break
endwhile | Xpath 4096 " X: 0
Xpath 8192 " X: 8192
asdf
Xpath 16384 " X: 16384
asdf | Xpath 32768 " X: 0
Xpath 65536 " X: 65536