]> git.madduck.net Git - etc/vim.git/blob - spec/indent/indent_spec.rb

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Use Docker for easier test running (#86)
[etc/vim.git] / spec / indent / indent_spec.rb
1 require "spec_helper"
2
3 shared_examples_for "vim" do
4
5   before(:each) {
6     # clear buffer
7     vim.normal 'gg"_dG'
8
9     # Insert two blank lines.
10     # The first line is a corner case in this plugin that would shadow the
11     # correct behaviour of other tests. Thus we explicitly jump to the first
12     # line when we require so.
13     vim.feedkeys 'i\<CR>\<CR>\<ESC>'
14   }
15
16   describe "when using the indent plugin" do
17     it "sets the indentexpr and indentkeys options" do
18       vim.command("set indentexpr?").should include "GetPythonPEPIndent("
19       vim.command("set indentkeys?").should include "=elif"
20     end
21
22     it "sets autoindent and expandtab" do
23       vim.command("set autoindent?").should match(/\s*autoindent/)
24       vim.command("set expandtab?").should match(/\s*expandtab/)
25     end
26   end
27
28   describe "when entering the first line" do
29     before { vim.feedkeys '0ggipass' }
30
31     it "does not indent" do
32       indent.should == 0
33       proposed_indent.should == 0
34     end
35
36     it "does not indent when using '=='" do
37       vim.normal "=="
38       indent.should == 0
39     end
40   end
41
42   describe "when after a '(' that is at the end of its line" do
43     before { vim.feedkeys 'itest(\<CR>' }
44
45     it "indents by one level" do
46       proposed_indent.should == shiftwidth
47       vim.feedkeys 'something'
48       indent.should == shiftwidth
49       vim.normal '=='
50       indent.should == shiftwidth
51     end
52
53     it "puts the closing parenthesis at the same level" do
54       vim.feedkeys ')'
55       indent.should == (hang_closing ? shiftwidth : 0)
56     end
57   end
58
59   describe "when after an '(' that is followed by something" do
60     before { vim.feedkeys 'itest(something,\<CR>' }
61
62     it "lines up on following lines" do
63       indent.should == 5
64       vim.feedkeys 'more,\<CR>'
65       indent.should == 5
66     end
67
68     it "lines up the closing parenthesis" do
69       vim.feedkeys ')'
70       indent.should == 5
71     end
72
73     it "does not touch the closing parenthesis if it is already indented further" do
74       vim.feedkeys '  )'
75       indent.should == 7
76     end
77   end
78
79   describe "when after an '{' that is followed by a comment" do
80     before { vim.feedkeys 'imydict = {  # comment\<CR>' }
81
82     it "indent by one level" do
83       indent.should == shiftwidth
84       vim.feedkeys '1: 1,\<CR>'
85       indent.should == shiftwidth
86     end
87
88     it "lines up the closing parenthesis" do
89       vim.feedkeys '}'
90       indent.should == (hang_closing ? shiftwidth : 0)
91     end
92   end
93
94   describe "when using gq to reindent a '(' that is" do
95     before { vim.feedkeys 'itest(' }
96     it "something and has a string without spaces at the end" do
97       vim.feedkeys 'something_very_long_blaaaaaaaaa, "some_very_long_string_blaaaaaaaaaaaaaaaaaaaa"\<esc>gqq'
98       indent.should == 5
99     end
100   end
101
102   describe "when after multiple parens of different types" do
103     it "indents by one level" do
104       vim.feedkeys 'if({\<CR>'
105       indent.should == shiftwidth
106     end
107
108     it "lines up with the last paren" do
109       vim.feedkeys 'ifff({123: 456,\<CR>'
110       indent.should == 5
111     end
112   end
113
114   describe "when '#' is contained in a string that is followed by a colon" do
115     it "indents by one level" do
116         vim.feedkeys 'iif "some#thing" == "test":#test\<CR>pass'
117         indent.should == shiftwidth
118     end
119   end
120
121   describe "when '#' is not contained in a string and is followed by a colon" do
122     it "does not indent" do
123         vim.feedkeys 'iif "some#thing" == "test"#:test\<CR>'
124         indent.should == 0
125     end
126   end
127
128   describe "when inside an unfinished string" do
129     it "does not indent" do
130       vim.feedkeys 'i"test:\<ESC>'
131       vim.echo('synIDattr(synID(line("."), col("."), 0), "name")'
132               ).downcase.should include 'string'
133       vim.feedkeys 'a\<CR>'
134       proposed_indent.should == 0
135       indent.should == 0
136     end
137
138     it "does not dedent" do
139       vim.feedkeys 'iif True:\<CR>"test:\<ESC>'
140       vim.echo('synIDattr(synID(line("."), col("."), 0), "name")'
141               ).downcase.should include 'string'
142       proposed_indent.should == shiftwidth
143       indent.should == shiftwidth
144     end
145   end
146
147   describe "when the previous line has a colon in a string" do
148     before { vim.feedkeys 'itest(":".join(["1","2"]))\<CR>' }
149     it "does not indent" do
150       vim.feedkeys 'if True:'
151       indent.should == 0
152       proposed_indent.should == 0
153     end
154   end
155
156   describe "when the previous line has a list slice" do
157     it "does not indent" do
158       vim.feedkeys 'ib = a[2:]\<CR>'
159       indent.should == 0
160       proposed_indent.should == 0
161     end
162   end
163
164   describe "when line is empty inside a block" do
165     it "is indented like the previous line" do
166       vim.feedkeys 'idef a():\<CR>1\<CR>\<CR>2\<ESC>kcc'
167       indent.should == shiftwidth
168     end
169   end
170
171   describe "when an empty line is after empty line / before non-empty" do
172     it "is indented like the next line" do
173       vim.feedkeys 'idef a():\<CR>1\<CR>\<CR>\<CR>2\<ESC><<kcc'
174       indent.should == 0
175     end
176   end
177
178   describe "when an empty line is after empty line / before non-empty (nested)" do
179     it "is indented like the next line" do
180       vim.feedkeys 'idef a():\<CR>1\<CR>\<CR>\<CR>\<ESC>0i\<TAB>2\<ESC>kcc'
181       indent.should == shiftwidth
182     end
183   end
184
185   describe "when line is empty inside a block following multi-line statement" do
186     it "is indented like the previous line" do
187       vim.feedkeys 'idef a():\<CR>x = (1 +\<CR>2)\<CR>\<CR>y\<ESC>kcc'
188       indent.should == shiftwidth
189     end
190   end
191
192   describe "when line is empty inside a block following stop statement" do
193     it "is indented like the previous line minus shiftwidth" do
194       vim.feedkeys 'iif x:\<CR>if y:\<CR>pass\<CR>\<CR>z\<ESC>kcc'
195       indent.should == shiftwidth
196     end
197   end
198
199   describe "when using simple control structures" do
200       it "indents shiftwidth spaces" do
201           vim.feedkeys 'iwhile True:\<CR>pass'
202           indent.should == shiftwidth
203       end
204   end
205
206   describe "when using a function definition" do
207       it "indents shiftwidth spaces" do
208           vim.feedkeys 'idef long_function_name(\<CR>arg'
209           indent.should == shiftwidth * 2
210       end
211   end
212
213   describe "when using a class definition" do
214       it "indents shiftwidth spaces" do
215           vim.feedkeys 'iclass Foo(\<CR>'
216           indent.should == shiftwidth * 2
217       end
218   end
219
220   describe "when writing an 'else' block" do
221     it "aligns to the preceeding 'for' block" do
222       vim.feedkeys 'ifor x in "abc":\<CR>pass\<CR>else:'
223       indent.should == 0
224     end
225
226     it "aligns to the preceeding 'if' block" do
227       vim.feedkeys 'ifor x in "abc":\<CR>if True:\<CR>pass\<CR>else:'
228       indent.should == shiftwidth
229     end
230   end
231
232   describe "when using parens and control statements" do
233     it "avoids ambiguity by using extra indentation" do
234       vim.feedkeys 'iif (111 and\<CR>'
235       if shiftwidth == 4
236         indent.should == shiftwidth * 2
237       else
238         indent.should == 4
239       end
240       vim.feedkeys '222):\<CR>'
241       indent.should == shiftwidth
242       vim.feedkeys 'pass\<CR>'
243       indent.should == 0
244     end
245
246     it "still aligns parens properly if not ambiguous" do
247       vim.feedkeys 'iwhile (111 and\<CR>'
248       indent.should == 7
249       vim.feedkeys '222):\<CR>'
250       indent.should == shiftwidth
251       vim.feedkeys 'pass\<CR>'
252       indent.should == 0
253     end
254
255     it "still handles multiple parens correctly" do
256       vim.feedkeys 'iif (111 and (222 and 333\<CR>'
257       indent.should == 13
258       vim.feedkeys 'and 444\<CR>'
259       indent.should == 13
260       vim.feedkeys ')\<CR>'
261       if shiftwidth == 4
262         indent.should == shiftwidth * 2
263       else
264         indent.should == 4
265       end
266       vim.feedkeys 'and 555):\<CR>'
267       indent.should == shiftwidth
268       vim.feedkeys 'pass\<CR>'
269       indent.should == 0
270     end
271   end
272
273   describe "when a line breaks with a manual '\\'" do
274     it "indents shiftwidth spaces on normal line" do
275         vim.feedkeys 'ivalue = test + \\\\\<CR>'
276         indent.should == shiftwidth
277     end
278
279     it "indents 2x shiftwidth spaces for control structures" do
280         vim.feedkeys 'iif somevalue == xyz and \\\\\<CR>'
281         indent.should == shiftwidth * 2
282     end
283
284     it "indents relative to line above" do
285         vim.feedkeys 'i\<TAB>value = test + \\\\\<CR>'
286         indent.should == shiftwidth * 2
287     end
288   end
289
290   describe "when current line is dedented compared to previous line" do
291      before { vim.feedkeys 'i\<TAB>\<TAB>if x:\<CR>y = True\<CR>\<ESC>' }
292      it "and current line has a valid indentation (Part 1)" do
293         vim.feedkeys '0i\<TAB>if y:'
294         proposed_indent.should == -1
295      end
296
297      it "and current line has a valid indentation (Part 2)" do
298         vim.feedkeys '0i\<TAB>\<TAB>if y:'
299         proposed_indent.should == -1
300      end
301
302      it "and current line has an invalid indentation" do
303         vim.feedkeys 'i    while True:\<CR>'
304         indent.should == previous_indent + shiftwidth
305      end
306   end
307
308   describe "when current line is dedented compared to the last non-empty line" do
309      before { vim.feedkeys 'i\<TAB>\<TAB>if x:\<CR>y = True\<CR>\<CR>\<ESC>' }
310      it "and current line has a valid indentation" do
311         vim.feedkeys '0i\<TAB>if y:'
312         proposed_indent.should == -1
313      end
314   end
315
316   describe "when an 'if' is followed by" do
317      before { vim.feedkeys 'i\<TAB>\<TAB>if x:\<CR>' }
318      it "an elif, it lines up with the 'if'" do
319         vim.feedkeys 'elif y:'
320         indent.should == shiftwidth * 2
321      end
322
323      it "an 'else', it lines up with the 'if'" do
324         vim.feedkeys 'else:'
325         indent.should == shiftwidth * 2
326      end
327   end
328
329   describe "when an 'if' contains a try-except" do
330      before {
331        vim.feedkeys 'iif x:\<CR>try:\<CR>pass\<CR>except:\<CR>pass\<CR>'
332        indent.should == shiftwidth
333      }
334      it "an 'else' should be indented to the try" do
335        vim.feedkeys 'else:'
336        indent.should == shiftwidth
337        proposed_indent.should == shiftwidth
338      end
339      it "an 'else' should keep the indent of the 'if'" do
340        vim.feedkeys 'else:\<ESC><<'
341        indent.should == 0
342        proposed_indent.should == 0
343      end
344   end
345
346   describe "when a 'for' is followed by" do
347      before { vim.feedkeys 'i\<TAB>\<TAB>for x in y:\<CR>' }
348      it "an 'else', it lines up with the 'for'" do
349         vim.feedkeys 'else:'
350         indent.should == shiftwidth * 2
351      end
352   end
353
354   describe "when an 'else' is followed by" do
355      before { vim.feedkeys 'i\<TAB>\<TAB>else:\<CR>XXX\<CR>' }
356      it "a 'finally', it lines up with the 'else'" do
357         vim.feedkeys 'finally:'
358         indent.should == shiftwidth * 2
359      end
360   end
361
362
363   describe "when a 'try' is followed by" do
364      before { vim.feedkeys 'i\<TAB>\<TAB>try:\<CR>' }
365      it "an 'except', it lines up with the 'try'" do
366         vim.feedkeys 'except:'
367         indent.should == shiftwidth * 2
368      end
369
370      it "an 'else', it lines up with the 'try'" do
371         vim.feedkeys 'else:'
372         indent.should == shiftwidth * 2
373      end
374
375      it "a 'finally', it lines up with the 'try'" do
376         vim.feedkeys 'finally:'
377         indent.should == shiftwidth * 2
378      end
379   end
380
381   describe "when an 'except' is followed by" do
382      before { vim.feedkeys 'i\<TAB>\<TAB>except:\<CR>' }
383      it "an 'else', it lines up with the 'except'" do
384         vim.feedkeys 'else:'
385         indent.should == shiftwidth * 2
386      end
387
388      it "another 'except', it lines up with the previous 'except'" do
389         vim.feedkeys 'except:'
390         indent.should == shiftwidth * 2
391      end
392
393      it "a 'finally', it lines up with the 'except'" do
394         vim.feedkeys 'finally:'
395         indent.should == shiftwidth * 2
396      end
397   end
398
399   describe "when an else is used inside of a nested if" do
400     before { vim.feedkeys 'iif foo:\<CR>\<TAB>if bar:\<CR>\<TAB>\<TAB>pass\<CR>' }
401     it "indents an else to the inner if" do
402       vim.feedkeys 'else:'
403       indent.should == shiftwidth * 2
404     end
405   end
406
407   describe "when jedi-vim call signatures are used" do
408     before { vim.command 'syn match jediFunction "JEDI_CALL_SIGNATURE" keepend extend' }
409
410     it "ignores the call signature after a colon" do
411       vim.feedkeys 'iif True:  JEDI_CALL_SIGNATURE\<CR>'
412       indent.should == shiftwidth
413     end
414
415     it "ignores the call signature after a function" do
416       vim.feedkeys 'idef f(  JEDI_CALL_SIGNATURE\<CR>'
417       indent.should == shiftwidth * 2
418     end
419   end
420 end
421
422 shared_examples_for "multiline strings" do
423   before(:each) {
424     # clear buffer
425     vim.normal 'gg"_dG'
426
427     # Insert two blank lines.
428     # The first line is a corner case in this plugin that would shadow the
429     # correct behaviour of other tests. Thus we explicitly jump to the first
430     # line when we require so.
431     vim.feedkeys 'i\<CR>\<CR>\<ESC>'
432   }
433
434   describe "when after an '(' that is followed by an unfinished string" do
435     before { vim.feedkeys 'itest("""' }
436
437     it "it indents the next line" do
438       vim.feedkeys '\<CR>'
439       expected_proposed, expected_indent = multiline_indent(0, shiftwidth)
440       proposed_indent.should == expected_proposed
441       indent.should == expected_indent
442     end
443
444     it "with contents it indents the second line to the parenthesis" do
445       vim.feedkeys 'second line\<CR>'
446       expected_proposed, expected_indent = multiline_indent(0, 5)
447       proposed_indent.should == expected_proposed
448       indent.should == expected_indent
449     end
450   end
451
452   describe "when after assigning an unfinished string" do
453     before { vim.feedkeys 'itest = """' }
454
455     it "it indents the next line" do
456       vim.feedkeys '\<CR>'
457       expected_proposed, expected_indent = multiline_indent(0, shiftwidth)
458       proposed_indent.should == expected_proposed
459       indent.should == expected_indent
460     end
461   end
462
463   describe "when after assigning an unfinished string" do
464     before { vim.feedkeys 'i    test = """' }
465
466     it "it indents the next line" do
467       vim.feedkeys '\<CR>'
468       expected_proposed, expected_indent = multiline_indent(4, shiftwidth + 4)
469       proposed_indent.should == expected_proposed
470       indent.should == expected_indent
471     end
472   end
473
474   describe "when after assigning a finished string" do
475     before { vim.feedkeys 'i    test = ""' }
476
477     it "it does indent the next line" do
478       vim.feedkeys '\<CR>'
479       indent.should == 4
480     end
481
482     it "and writing a new string, it does indent the next line" do
483       vim.feedkeys '\<CR>""'
484       indent.should == 4
485     end
486   end
487
488   describe "when after a docstring" do
489     before { vim.feedkeys 'i    """' }
490     it "it does indent the next line to the docstring" do
491       vim.feedkeys '\<CR>'
492       indent.should == 4
493       proposed_indent.should == 4
494     end
495   end
496
497   describe "when after a docstring with contents" do
498     before { vim.feedkeys 'i    """First line' }
499     it "it does indent the next line to the docstring" do
500       vim.feedkeys '\<CR>'
501       indent.should == 4
502       proposed_indent.should == 4
503     end
504   end
505
506   describe "when breaking a string after opening parenthesis" do
507     before { vim.feedkeys 'i    foo("""bar<Left><Left><Left>' }
508     it "it does indent the next line as after an opening multistring" do
509       vim.feedkeys '\<CR>'
510       expected_proposed, expected_indent = multiline_indent(4, 4 + shiftwidth)
511       indent.should == expected_indent
512       proposed_indent.should == expected_proposed
513     end
514   end
515 end
516
517 SUITE_SHIFTWIDTHS = [4, 3]
518 SUITE_HANG_CLOSINGS = [false, true]
519
520 SUITE_SHIFTWIDTHS.each do |sw|
521   describe "vim when using width of #{sw}" do
522     before {
523       vim.command("set sw=#{sw} ts=#{sw} sts=#{sw} et")
524     }
525     it "sets shiftwidth to #{sw}" do
526       shiftwidth.should == sw
527     end
528
529     SUITE_HANG_CLOSINGS.each do |hc|
530       describe "vim when hang_closing is set to #{hc}" do
531         before {
532           set_hang_closing hc
533         }
534         it "sets hang_closing to #{hc}" do
535           hang_closing.should == !!hc
536         end
537
538         it_behaves_like "vim"
539       end
540     end
541   end
542 end
543
544 describe "vim when not using python_pep8_indent_multiline_string" do
545   before {
546     vim.command("set sw=4 ts=4 sts=4 et")
547     vim.command("unlet! g:python_pep8_indent_multiline_string")
548   }
549   it_behaves_like "multiline strings"
550 end
551
552 describe "vim when using python_pep8_indent_multiline_first=0" do
553   before {
554     vim.command("set sw=4 ts=4 sts=4 et")
555     vim.command("let g:python_pep8_indent_multiline_string=0")
556   }
557   it_behaves_like "multiline strings"
558 end
559
560 describe "vim when using python_pep8_indent_multiline_string=-1" do
561   before {
562     vim.command("set sw=4 ts=4 sts=4 et")
563     vim.command("let g:python_pep8_indent_multiline_string=-1")
564   }
565   it_behaves_like "multiline strings"
566 end
567
568 describe "vim when using python_pep8_indent_multiline_string=-2" do
569   before {
570     vim.command("set sw=4 ts=4 sts=4 et")
571     vim.command("let g:python_pep8_indent_multiline_string=-2")
572   }
573   it_behaves_like "multiline strings"
574 end
575
576 describe "vim for cython" do
577   before {
578     vim.command "enew"
579     vim.command "set ft=cython"
580     vim.command "runtime indent/python.vim"
581
582     # Insert two blank lines.
583     # The first line is a corner case in this plugin that would shadow the
584     # correct behaviour of other tests. Thus we explicitly jump to the first
585     # line when we require so.
586     vim.feedkeys 'i\<CR>\<CR>\<ESC>'
587   }
588
589   describe "when using a cdef function definition" do
590       it "indents shiftwidth spaces" do
591           vim.feedkeys 'icdef long_function_name(\<CR>arg'
592           indent.should == shiftwidth * 2
593       end
594   end
595
596   describe "when using a cpdef function definition" do
597       it "indents shiftwidth spaces" do
598           vim.feedkeys 'icpdef long_function_name(\<CR>arg'
599           indent.should == shiftwidth * 2
600       end
601   end
602 end
603
604 describe "Handles far away opening parens" do
605   before { vim.feedkeys '\<ESC>ggdGifrom foo import (' }
606
607   it "indents by one level" do
608     vim.feedkeys '\<CR>'
609     proposed_indent.should == shiftwidth
610   end
611
612   it "indents by one level for 10 lines" do
613     vim.command('set paste | exe "norm 9o" | set nopaste')
614     vim.feedkeys '\<Esc>o'
615     indent.should == shiftwidth
616   end
617
618   it "indents by one level for 50 lines" do
619     vim.command('set paste | exe "norm 49o" | set nopaste')
620     vim.feedkeys '\<Esc>o'
621     indent.should == shiftwidth
622   end
623 end
624
625 describe "Handles far away opening square brackets" do
626   before { vim.feedkeys '\<ESC>ggdGibar = [' }
627
628   it "indents by one level" do
629     vim.feedkeys '\<CR>'
630     proposed_indent.should == shiftwidth
631   end
632
633   it "indents by one level for 10 lines" do
634     vim.command('set paste | exe "norm 9o" | set nopaste')
635     vim.feedkeys '\<Esc>o'
636     indent.should == shiftwidth
637   end
638
639   it "indents by one level for 100 lines" do
640     vim.command('set paste | exe "norm 99o" | set nopaste')
641     vim.feedkeys '\<Esc>o'
642     indent.should == shiftwidth
643   end
644 end
645
646 describe "Handles far away opening curly brackets" do
647   before { vim.feedkeys '\<ESC>ggdGijson = {' }
648
649   it "indents by one level" do
650     vim.feedkeys '\<CR>'
651     vim.feedkeys '\<Esc>o'
652     proposed_indent.should == shiftwidth
653   end
654
655   it "indents by one level for 10 lines" do
656     vim.command('set paste | exe "norm 9o" | set nopaste')
657     vim.feedkeys '\<Esc>o'
658     indent.should == shiftwidth
659   end
660
661   it "indents by one level for 1000 lines" do
662     vim.command('set paste | exe "norm 999o" | set nopaste')
663     vim.feedkeys '\<Esc>o'
664     indent.should == shiftwidth
665   end
666 end
667
668 describe "Compact multiline dict" do
669   before { vim.feedkeys '\<ESC>ggdGid = {"one": 1,' }
670
671   it "gets indented correctly" do
672     vim.feedkeys '\<CR>'
673     proposed_indent.should == 5
674
675     vim.feedkeys '"two": 2}'
676     proposed_indent.should == 5
677
678     vim.feedkeys '\<CR>'
679     proposed_indent.should == 0
680   end
681 end