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

README: update badges: CircleCI/Codecov
[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 "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>\<TAB>if bar:\<CR>\<TAB>\<TAB>pass\<CR>' }
405     it "indents an else to the inner if" do
406       vim.feedkeys 'else:'
407       indent.should == shiftwidth * 2
408     end
409   end
410
411   describe "when jedi-vim call signatures are used" do
412     before { vim.command 'syn match jediFunction "JEDI_CALL_SIGNATURE" keepend extend' }
413
414     it "ignores the call signature after a colon" do
415       vim.feedkeys 'iif True:  JEDI_CALL_SIGNATURE\<CR>'
416       indent.should == shiftwidth
417     end
418
419     it "ignores the call signature after a function" do
420       vim.feedkeys 'idef f(  JEDI_CALL_SIGNATURE\<CR>'
421       indent.should == shiftwidth * 2
422     end
423   end
424 end
425
426 shared_examples_for "multiline strings" do
427   before(:each) {
428     # clear buffer
429     vim.normal 'gg"_dG'
430
431     # Insert two blank lines.
432     # The first line is a corner case in this plugin that would shadow the
433     # correct behaviour of other tests. Thus we explicitly jump to the first
434     # line when we require so.
435     vim.feedkeys 'i\<CR>\<CR>\<ESC>'
436   }
437
438   describe "when after an '(' that is followed by an unfinished string" do
439     before { vim.feedkeys 'itest("""' }
440
441     it "it indents the next line" do
442       vim.feedkeys '\<CR>'
443       expected_proposed, expected_indent = multiline_indent(0, shiftwidth)
444       proposed_indent.should == expected_proposed
445       indent.should == expected_indent
446     end
447
448     it "with contents it indents the second line to the parenthesis" do
449       vim.feedkeys 'second line\<CR>'
450       expected_proposed, expected_indent = multiline_indent(0, 5)
451       proposed_indent.should == expected_proposed
452       indent.should == expected_indent
453     end
454   end
455
456   describe "when after assigning an unfinished string" do
457     before { vim.feedkeys 'itest = """' }
458
459     it "it indents the next line" do
460       vim.feedkeys '\<CR>'
461       expected_proposed, expected_indent = multiline_indent(0, shiftwidth)
462       proposed_indent.should == expected_proposed
463       indent.should == expected_indent
464     end
465   end
466
467   describe "when after assigning an unfinished string" do
468     before { vim.feedkeys 'i    test = """' }
469
470     it "it indents the next line" do
471       vim.feedkeys '\<CR>'
472       expected_proposed, expected_indent = multiline_indent(4, shiftwidth + 4)
473       proposed_indent.should == expected_proposed
474       indent.should == expected_indent
475     end
476   end
477
478   describe "when after assigning a finished string" do
479     before { vim.feedkeys 'i    test = ""' }
480
481     it "it does indent the next line" do
482       vim.feedkeys '\<CR>'
483       indent.should == 4
484     end
485
486     it "and writing a new string, it does indent the next line" do
487       vim.feedkeys '\<CR>""'
488       indent.should == 4
489     end
490   end
491
492   describe "when after a docstring" do
493     before { vim.feedkeys 'i    """' }
494     it "it does indent the next line to the docstring" do
495       vim.feedkeys '\<CR>'
496       indent.should == 4
497       proposed_indent.should == 4
498     end
499   end
500
501   describe "when after a docstring with contents" do
502     before { vim.feedkeys 'i    """First line' }
503     it "it does indent the next line to the docstring" do
504       vim.feedkeys '\<CR>'
505       indent.should == 4
506       proposed_indent.should == 4
507     end
508   end
509
510   describe "when breaking a string after opening parenthesis" do
511     before { vim.feedkeys 'i    foo("""bar<Left><Left><Left>' }
512     it "it does indent the next line as after an opening multistring" do
513       vim.feedkeys '\<CR>'
514       expected_proposed, expected_indent = multiline_indent(4, 4 + shiftwidth)
515       indent.should == expected_indent
516       proposed_indent.should == expected_proposed
517     end
518   end
519 end
520
521 SUITE_SHIFTWIDTHS = [4, 3]
522 SUITE_HANG_CLOSINGS = [false, true]
523
524 SUITE_SHIFTWIDTHS.each do |sw|
525   describe "vim when using width of #{sw}" do
526     before {
527       vim.command("set sw=#{sw} ts=#{sw} sts=#{sw} et")
528     }
529     it "sets shiftwidth to #{sw}" do
530       shiftwidth.should == sw
531     end
532
533     SUITE_HANG_CLOSINGS.each do |hc|
534       describe "vim when hang_closing is set to #{hc}" do
535         before {
536           set_hang_closing hc
537         }
538         it "sets hang_closing to #{hc}" do
539           hang_closing.should == !!hc
540         end
541
542         it_behaves_like "vim"
543       end
544     end
545   end
546 end
547
548 describe "vim when not using python_pep8_indent_multiline_string" do
549   before {
550     vim.command("set sw=4 ts=4 sts=4 et")
551     vim.command("unlet! g:python_pep8_indent_multiline_string")
552   }
553   it_behaves_like "multiline strings"
554 end
555
556 describe "vim when using python_pep8_indent_multiline_first=0" do
557   before {
558     vim.command("set sw=4 ts=4 sts=4 et")
559     vim.command("let g:python_pep8_indent_multiline_string=0")
560   }
561   it_behaves_like "multiline strings"
562 end
563
564 describe "vim when using python_pep8_indent_multiline_string=-1" do
565   before {
566     vim.command("set sw=4 ts=4 sts=4 et")
567     vim.command("let g:python_pep8_indent_multiline_string=-1")
568   }
569   it_behaves_like "multiline strings"
570 end
571
572 describe "vim when using python_pep8_indent_multiline_string=-2" do
573   before {
574     vim.command("set sw=4 ts=4 sts=4 et")
575     vim.command("let g:python_pep8_indent_multiline_string=-2")
576   }
577   it_behaves_like "multiline strings"
578 end
579
580 describe "Handles far away opening parens" do
581   before { vim.feedkeys '\<ESC>ggdGifrom foo import (' }
582
583   it "indents by one level" do
584     vim.feedkeys '\<CR>'
585     proposed_indent.should == shiftwidth
586   end
587
588   it "indents by one level for 10 lines" do
589     vim.command('set paste | exe "norm 9o" | set nopaste')
590     vim.feedkeys '\<Esc>o'
591     indent.should == shiftwidth
592   end
593
594   it "indents by one level for 50 lines" do
595     vim.command('set paste | exe "norm 49o" | set nopaste')
596     vim.feedkeys '\<Esc>o'
597     indent.should == shiftwidth
598   end
599 end
600
601 describe "Handles far away opening square brackets" do
602   before { vim.feedkeys '\<ESC>ggdGibar = [' }
603
604   it "indents by one level" do
605     vim.feedkeys '\<CR>'
606     proposed_indent.should == shiftwidth
607   end
608
609   it "indents by one level for 10 lines" do
610     vim.command('set paste | exe "norm 9o" | set nopaste')
611     vim.feedkeys '\<Esc>o'
612     indent.should == shiftwidth
613   end
614
615   it "indents by one level for 100 lines" do
616     vim.command('set paste | exe "norm 99o" | set nopaste')
617     vim.feedkeys '\<Esc>o'
618     indent.should == shiftwidth
619   end
620 end
621
622 describe "Handles far away opening curly brackets" do
623   before { vim.feedkeys '\<ESC>ggdGijson = {' }
624
625   it "indents by one level" do
626     vim.feedkeys '\<CR>'
627     vim.feedkeys '\<Esc>o'
628     proposed_indent.should == shiftwidth
629   end
630
631   it "indents by one level for 10 lines" do
632     vim.command('set paste | exe "norm 9o" | set nopaste')
633     vim.feedkeys '\<Esc>o'
634     indent.should == shiftwidth
635   end
636
637   it "indents by one level for 1000 lines" do
638     vim.command('set paste | exe "norm 999o" | set nopaste')
639     vim.feedkeys '\<Esc>o'
640     indent.should == shiftwidth
641   end
642 end
643
644 describe "Compact multiline dict" do
645   before { vim.feedkeys '\<ESC>ggdGid = {"one": 1,' }
646
647   it "gets indented correctly" do
648     vim.feedkeys '\<CR>'
649     proposed_indent.should == 5
650
651     vim.feedkeys '"two": 2}'
652     proposed_indent.should == 5
653
654     vim.feedkeys '\<CR>'
655     proposed_indent.should == 0
656   end
657 end