]> 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:

Coverage reporting via covimerage (#107)
[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 == 0
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 "still handles multiple parens correctly" do
255       vim.feedkeys 'iif (111 and (222 and 333\<CR>'
256       indent.should == 13
257       vim.feedkeys 'and 444\<CR>'
258       indent.should == 13
259       vim.feedkeys ')\<CR>'
260       if shiftwidth == 4
261         indent.should == shiftwidth * 2
262       else
263         indent.should == 4
264       end
265       vim.feedkeys 'and 555):\<CR>'
266       indent.should == shiftwidth
267       vim.feedkeys 'pass\<CR>'
268       indent.should == 0
269     end
270   end
271
272   describe "when a line breaks with a manual '\\'" do
273     it "indents shiftwidth spaces on normal line" do
274         vim.feedkeys 'ivalue = test + \\\\\<CR>'
275         indent.should == shiftwidth
276     end
277
278     it "indents 2x shiftwidth spaces for control structures" do
279         vim.feedkeys 'iif somevalue == xyz and \\\\\<CR>'
280         indent.should == shiftwidth * 2
281     end
282
283     it "indents relative to line above" do
284         vim.feedkeys 'i\<TAB>value = test + \\\\\<CR>'
285         indent.should == shiftwidth * 2
286     end
287   end
288
289   describe "when current line is dedented compared to previous line" do
290      before { vim.feedkeys 'i\<TAB>\<TAB>if x:\<CR>y = True\<CR>\<ESC>' }
291      it "and current line has a valid indentation (Part 1)" do
292         vim.feedkeys '0i\<TAB>if y:'
293         proposed_indent.should == -1
294      end
295
296      it "and current line has a valid indentation (Part 2)" do
297         vim.feedkeys '0i\<TAB>\<TAB>if y:'
298         proposed_indent.should == -1
299      end
300
301      it "and current line has an invalid indentation" do
302         vim.feedkeys 'i    while True:\<CR>'
303         indent.should == previous_indent + shiftwidth
304      end
305   end
306
307   describe "when current line is dedented compared to the last non-empty line" do
308      before { vim.feedkeys 'i\<TAB>\<TAB>if x:\<CR>y = True\<CR>\<CR>\<ESC>' }
309      it "and current line has a valid indentation" do
310         vim.feedkeys '0i\<TAB>if y:'
311         proposed_indent.should == -1
312      end
313   end
314
315   describe "when an 'if' is followed by" do
316      before { vim.feedkeys 'i\<TAB>\<TAB>if x:\<CR>' }
317      it "an elif, it lines up with the 'if'" do
318         vim.feedkeys 'elif y:'
319         indent.should == shiftwidth * 2
320      end
321
322      it "an 'else', it lines up with the 'if'" do
323         vim.feedkeys 'else:'
324         indent.should == shiftwidth * 2
325      end
326   end
327
328   describe "when an 'if' contains a try-except" do
329      before {
330        vim.feedkeys 'iif x:\<CR>try:\<CR>pass\<CR>except:\<CR>pass\<CR>'
331        indent.should == shiftwidth
332      }
333      it "an 'else' should be indented to the try" do
334        vim.feedkeys 'else:'
335        indent.should == shiftwidth
336        proposed_indent.should == shiftwidth
337      end
338      it "an 'else' should keep the indent of the 'if'" do
339        vim.feedkeys 'else:\<ESC><<'
340        indent.should == 0
341        proposed_indent.should == 0
342      end
343   end
344
345   describe "when a 'for' is followed by" do
346      before { vim.feedkeys 'i\<TAB>\<TAB>for x in y:\<CR>' }
347      it "an 'else', it lines up with the 'for'" do
348         vim.feedkeys 'else:'
349         indent.should == shiftwidth * 2
350      end
351   end
352
353   describe "when an 'else' is followed by" do
354      before { vim.feedkeys 'i\<TAB>\<TAB>else:\<CR>XXX\<CR>' }
355      it "a 'finally', it lines up with the 'else'" do
356         vim.feedkeys 'finally:'
357         indent.should == shiftwidth * 2
358      end
359   end
360
361
362   describe "when a 'try' is followed by" do
363      before { vim.feedkeys 'i\<TAB>\<TAB>try:\<CR>' }
364      it "an 'except', it lines up with the 'try'" do
365         vim.feedkeys 'except:'
366         indent.should == shiftwidth * 2
367      end
368
369      it "an 'else', it lines up with the 'try'" do
370         vim.feedkeys 'else:'
371         indent.should == shiftwidth * 2
372      end
373
374      it "a 'finally', it lines up with the 'try'" do
375         vim.feedkeys 'finally:'
376         indent.should == shiftwidth * 2
377      end
378   end
379
380   describe "when an 'except' is followed by" do
381      before { vim.feedkeys 'i\<TAB>\<TAB>except:\<CR>' }
382      it "an 'else', it lines up with the 'except'" do
383         vim.feedkeys 'else:'
384         indent.should == shiftwidth * 2
385      end
386
387      it "another 'except', it lines up with the previous 'except'" do
388         vim.feedkeys 'except:'
389         indent.should == shiftwidth * 2
390      end
391
392      it "a 'finally', it lines up with the 'except'" do
393         vim.feedkeys 'finally:'
394         indent.should == shiftwidth * 2
395      end
396   end
397
398   describe "when an else is used inside of a nested if" do
399     before { vim.feedkeys 'iif foo:\<CR>\<TAB>if bar:\<CR>\<TAB>\<TAB>pass\<CR>' }
400     it "indents an else to the inner if" do
401       vim.feedkeys 'else:'
402       indent.should == shiftwidth * 2
403     end
404   end
405
406   describe "when jedi-vim call signatures are used" do
407     before { vim.command 'syn match jediFunction "JEDI_CALL_SIGNATURE" keepend extend' }
408
409     it "ignores the call signature after a colon" do
410       vim.feedkeys 'iif True:  JEDI_CALL_SIGNATURE\<CR>'
411       indent.should == shiftwidth
412     end
413
414     it "ignores the call signature after a function" do
415       vim.feedkeys 'idef f(  JEDI_CALL_SIGNATURE\<CR>'
416       indent.should == shiftwidth * 2
417     end
418   end
419 end
420
421 shared_examples_for "multiline strings" do
422   before(:each) {
423     # clear buffer
424     vim.normal 'gg"_dG'
425
426     # Insert two blank lines.
427     # The first line is a corner case in this plugin that would shadow the
428     # correct behaviour of other tests. Thus we explicitly jump to the first
429     # line when we require so.
430     vim.feedkeys 'i\<CR>\<CR>\<ESC>'
431   }
432
433   describe "when after an '(' that is followed by an unfinished string" do
434     before { vim.feedkeys 'itest("""' }
435
436     it "it indents the next line" do
437       vim.feedkeys '\<CR>'
438       expected_proposed, expected_indent = multiline_indent(0, shiftwidth)
439       proposed_indent.should == expected_proposed
440       indent.should == expected_indent
441     end
442
443     it "with contents it indents the second line to the parenthesis" do
444       vim.feedkeys 'second line\<CR>'
445       expected_proposed, expected_indent = multiline_indent(0, 5)
446       proposed_indent.should == expected_proposed
447       indent.should == expected_indent
448     end
449   end
450
451   describe "when after assigning an unfinished string" do
452     before { vim.feedkeys 'itest = """' }
453
454     it "it indents the next line" do
455       vim.feedkeys '\<CR>'
456       expected_proposed, expected_indent = multiline_indent(0, shiftwidth)
457       proposed_indent.should == expected_proposed
458       indent.should == expected_indent
459     end
460   end
461
462   describe "when after assigning an unfinished string" do
463     before { vim.feedkeys 'i    test = """' }
464
465     it "it indents the next line" do
466       vim.feedkeys '\<CR>'
467       expected_proposed, expected_indent = multiline_indent(4, shiftwidth + 4)
468       proposed_indent.should == expected_proposed
469       indent.should == expected_indent
470     end
471   end
472
473   describe "when after assigning a finished string" do
474     before { vim.feedkeys 'i    test = ""' }
475
476     it "it does indent the next line" do
477       vim.feedkeys '\<CR>'
478       indent.should == 4
479     end
480
481     it "and writing a new string, it does indent the next line" do
482       vim.feedkeys '\<CR>""'
483       indent.should == 4
484     end
485   end
486
487   describe "when after a docstring" do
488     before { vim.feedkeys 'i    """' }
489     it "it does indent the next line to the docstring" do
490       vim.feedkeys '\<CR>'
491       indent.should == 4
492       proposed_indent.should == 4
493     end
494   end
495
496   describe "when after a docstring with contents" do
497     before { vim.feedkeys 'i    """First line' }
498     it "it does indent the next line to the docstring" do
499       vim.feedkeys '\<CR>'
500       indent.should == 4
501       proposed_indent.should == 4
502     end
503   end
504
505   describe "when breaking a string after opening parenthesis" do
506     before { vim.feedkeys 'i    foo("""bar<Left><Left><Left>' }
507     it "it does indent the next line as after an opening multistring" do
508       vim.feedkeys '\<CR>'
509       expected_proposed, expected_indent = multiline_indent(4, 4 + shiftwidth)
510       indent.should == expected_indent
511       proposed_indent.should == expected_proposed
512     end
513   end
514 end
515
516 SUITE_SHIFTWIDTHS = [4, 3]
517 SUITE_HANG_CLOSINGS = [false, true]
518
519 SUITE_SHIFTWIDTHS.each do |sw|
520   describe "vim when using width of #{sw}" do
521     before {
522       vim.command("set sw=#{sw} ts=#{sw} sts=#{sw} et")
523     }
524     it "sets shiftwidth to #{sw}" do
525       shiftwidth.should == sw
526     end
527
528     SUITE_HANG_CLOSINGS.each do |hc|
529       describe "vim when hang_closing is set to #{hc}" do
530         before {
531           set_hang_closing hc
532         }
533         it "sets hang_closing to #{hc}" do
534           hang_closing.should == !!hc
535         end
536
537         it_behaves_like "vim"
538       end
539     end
540   end
541 end
542
543 describe "vim when not using python_pep8_indent_multiline_string" do
544   before {
545     vim.command("set sw=4 ts=4 sts=4 et")
546     vim.command("unlet! g:python_pep8_indent_multiline_string")
547   }
548   it_behaves_like "multiline strings"
549 end
550
551 describe "vim when using python_pep8_indent_multiline_first=0" do
552   before {
553     vim.command("set sw=4 ts=4 sts=4 et")
554     vim.command("let g:python_pep8_indent_multiline_string=0")
555   }
556   it_behaves_like "multiline strings"
557 end
558
559 describe "vim when using python_pep8_indent_multiline_string=-1" do
560   before {
561     vim.command("set sw=4 ts=4 sts=4 et")
562     vim.command("let g:python_pep8_indent_multiline_string=-1")
563   }
564   it_behaves_like "multiline strings"
565 end
566
567 describe "vim when using python_pep8_indent_multiline_string=-2" do
568   before {
569     vim.command("set sw=4 ts=4 sts=4 et")
570     vim.command("let g:python_pep8_indent_multiline_string=-2")
571   }
572   it_behaves_like "multiline strings"
573 end
574
575 describe "Handles far away opening parens" do
576   before { vim.feedkeys '\<ESC>ggdGifrom foo import (' }
577
578   it "indents by one level" do
579     vim.feedkeys '\<CR>'
580     proposed_indent.should == shiftwidth
581   end
582
583   it "indents by one level for 10 lines" do
584     vim.command('set paste | exe "norm 9o" | set nopaste')
585     vim.feedkeys '\<Esc>o'
586     indent.should == shiftwidth
587   end
588
589   it "indents by one level for 50 lines" do
590     vim.command('set paste | exe "norm 49o" | set nopaste')
591     vim.feedkeys '\<Esc>o'
592     indent.should == shiftwidth
593   end
594 end
595
596 describe "Handles far away opening square brackets" do
597   before { vim.feedkeys '\<ESC>ggdGibar = [' }
598
599   it "indents by one level" do
600     vim.feedkeys '\<CR>'
601     proposed_indent.should == shiftwidth
602   end
603
604   it "indents by one level for 10 lines" do
605     vim.command('set paste | exe "norm 9o" | set nopaste')
606     vim.feedkeys '\<Esc>o'
607     indent.should == shiftwidth
608   end
609
610   it "indents by one level for 100 lines" do
611     vim.command('set paste | exe "norm 99o" | set nopaste')
612     vim.feedkeys '\<Esc>o'
613     indent.should == shiftwidth
614   end
615 end
616
617 describe "Handles far away opening curly brackets" do
618   before { vim.feedkeys '\<ESC>ggdGijson = {' }
619
620   it "indents by one level" do
621     vim.feedkeys '\<CR>'
622     vim.feedkeys '\<Esc>o'
623     proposed_indent.should == shiftwidth
624   end
625
626   it "indents by one level for 10 lines" do
627     vim.command('set paste | exe "norm 9o" | set nopaste')
628     vim.feedkeys '\<Esc>o'
629     indent.should == shiftwidth
630   end
631
632   it "indents by one level for 1000 lines" do
633     vim.command('set paste | exe "norm 999o" | set nopaste')
634     vim.feedkeys '\<Esc>o'
635     indent.should == shiftwidth
636   end
637 end
638
639 describe "Compact multiline dict" do
640   before { vim.feedkeys '\<ESC>ggdGid = {"one": 1,' }
641
642   it "gets indented correctly" do
643     vim.feedkeys '\<CR>'
644     proposed_indent.should == 5
645
646     vim.feedkeys '"two": 2}'
647     proposed_indent.should == 5
648
649     vim.feedkeys '\<CR>'
650     proposed_indent.should == 0
651   end
652 end