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