X-Git-Url: https://git.madduck.net/etc/vim.git/blobdiff_plain/6e60ef05e7335ae4e5dd6a6a973c62f2a29198e4..60ba5e11a61618c0344e2db190210145083c91f8:/spec/indent/indent_spec.rb diff --git a/spec/indent/indent_spec.rb b/spec/indent/indent_spec.rb index c3488f3..745e939 100644 --- a/spec/indent/indent_spec.rb +++ b/spec/indent/indent_spec.rb @@ -1,8 +1,16 @@ require "spec_helper" -describe "vim" do +shared_examples_for "vim" do + before(:each) { + # clear buffer + vim.normal 'gg"_dG' - before(:each) { vim.normal 'gg"_dG' } # clear buffer + # Insert two blank lines. + # The first line is a corner case in this plugin that would shadow the + # correct behaviour of other tests. Thus we explicitly jump to the first + # line when we require so. + vim.feedkeys 'i\\\' + } describe "when using the indent plugin" do it "sets the indentexpr and indentkeys options" do @@ -17,11 +25,11 @@ describe "vim" do end describe "when entering the first line" do - before { vim.feedkeys 'ipass' } + before { vim.feedkeys '0ggipass' } it "does not indent" do - proposed_indent.should == 0 indent.should == 0 + proposed_indent.should == 0 end it "does not indent when using '=='" do @@ -30,22 +38,22 @@ describe "vim" do end end - # describe "when after a '(' that is at the end of its line" do - # before { vim.feedkeys 'itest(\' } + describe "when after a '(' that is at the end of its line" do + before { vim.feedkeys 'itest(\' } - # it "indents by one level" do - # proposed_indent.should == shiftwidth - # vim.feedkeys 'something' - # indent.should == shiftwidth - # vim.normal '==' - # indent.should == shiftwidth - # end + it "indents by one level" do + proposed_indent.should == shiftwidth + vim.feedkeys 'something' + indent.should == shiftwidth + vim.normal '==' + indent.should == shiftwidth + end - # it "puts the closing parenthesis at the same level" do - # vim.feedkeys ')' - # indent.should == 0 - # end - # end + it "puts the closing parenthesis at the same level" do + vim.feedkeys ')' + indent.should == (hang_closing ? shiftwidth : 0) + end + end describe "when after an '(' that is followed by something" do before { vim.feedkeys 'itest(something,\' } @@ -67,17 +75,722 @@ describe "vim" do end end - def shiftwidth - @shiftwidth ||= vim.echo("exists('*shiftwidth') ? shiftwidth() : &sw").to_i + describe "when after an '{' that is followed by a comment" do + before { vim.feedkeys 'imydict = { # comment\' } + + it "indent by one level" do + indent.should == shiftwidth + vim.feedkeys '1: 1,\' + indent.should == shiftwidth + end + + it "lines up the closing parenthesis" do + vim.feedkeys '}' + indent.should == (hang_closing ? shiftwidth : 0) + end + end + + describe "when using gq to reindent a '(' that is" do + before { vim.feedkeys 'itest(' } + it "something and has a string without spaces at the end" do + vim.feedkeys 'something_very_long_blaaaaaaaaa, "some_very_long_string_blaaaaaaaaaaaaaaaaaaaa"\gqq' + indent.should == 5 + end + end + + describe "when after multiple parens of different types" do + it "indents by one level" do + vim.feedkeys 'if({\' + indent.should == shiftwidth + end + + it "lines up with the last paren" do + vim.feedkeys 'ifff({123: 456,\' + indent.should == 5 + end + end + + describe "when '#' is contained in a string that is followed by a colon" do + it "indents by one level" do + vim.feedkeys 'iif "some#thing" == "test":#test\pass' + indent.should == shiftwidth + end + end + + describe "when '#' is not contained in a string and is followed by a colon" do + it "does not indent" do + vim.feedkeys 'iif "some#thing" == "test"#:test\' + indent.should == 0 + end + end + + describe "when inside an unfinished string" do + it "does not indent" do + vim.feedkeys 'i"test:\' + vim.echo('synIDattr(synID(line("."), col("."), 0), "name")' + ).downcase.should include 'string' + vim.feedkeys 'a\' + proposed_indent.should == -1 + indent.should == 0 + end + + it "does not dedent" do + vim.feedkeys 'iif True:\"test:\' + vim.echo('synIDattr(synID(line("."), col("."), 0), "name")' + ).downcase.should include 'string' + proposed_indent.should == shiftwidth + indent.should == shiftwidth + end + end + + describe "when the previous line has a colon in a string" do + before { vim.feedkeys 'itest(":".join(["1","2"]))\' } + it "does not indent" do + vim.feedkeys 'if True:' + indent.should == 0 + proposed_indent.should == 0 + end + end + + describe "when the previous line has a list slice" do + it "does not indent" do + vim.feedkeys 'ib = a[2:]\' + indent.should == 0 + proposed_indent.should == 0 + end + end + + describe "when line is empty inside a block" do + it "is indented like the previous line" do + vim.feedkeys 'idef a():\1\\2\kcc' + indent.should == shiftwidth + end + end + + describe "when an empty line is after empty line / before non-empty" do + it "is indented like the next line" do + vim.feedkeys 'idef a():\1\\\2\<1\\\\0i\2\kcc' + indent.should == shiftwidth + end + end + + describe "when line is empty inside a block following multi-line statement" do + it "is indented like the previous line" do + vim.feedkeys 'idef a():\x = (1 +\2)\\y\kcc' + indent.should == shiftwidth + end + end + + describe "when line is empty inside a block following stop statement" do + it "is indented like the previous line minus shiftwidth" do + vim.feedkeys 'iif x:\if y:\pass\\z\kcc' + indent.should == shiftwidth + end + end + + describe "when using simple control structures" do + it "indents shiftwidth spaces" do + vim.feedkeys 'iwhile True:\pass' + indent.should == shiftwidth + end + end + + describe "when using a function definition" do + it "handles indent with closing parenthesis on same line" do + vim.feedkeys 'idef long_function_name(\arg' + indent.should == shiftwidth + vim.feedkeys '):' + indent.should == shiftwidth * 2 + end + + it "handles indent with closing parenthesis on new line" do + vim.feedkeys 'idef long_function_name(\arg' + indent.should == shiftwidth + vim.feedkeys '\' + indent.should == shiftwidth + vim.feedkeys ')' + indent.should == (hang_closing ? shiftwidth * 2 : 0) + vim.feedkeys ':' + indent.should == (hang_closing ? shiftwidth * 2 : 0) + vim.feedkeys '\k' + indent.should == shiftwidth + end + end + + describe "when using a class definition" do + it "indents shiftwidth spaces" do + vim.feedkeys 'iclass Foo(\' + indent.should == shiftwidth + end + end + + describe "when writing an 'else' block" do + it "aligns to the preceeding 'for' block" do + vim.feedkeys 'ifor x in "abc":\pass\else:' + indent.should == 0 + end + + it "aligns to the preceeding 'if' block" do + vim.feedkeys 'ifor x in "abc":\if True:\pass\else:' + indent.should == shiftwidth + end + end + + describe "when using parens and control statements" do + it "avoids ambiguity by using extra indentation" do + vim.feedkeys 'iif (111 and\' + if shiftwidth == 4 + indent.should == shiftwidth * 2 + else + indent.should == 4 + end + vim.feedkeys '222):\' + indent.should == shiftwidth + vim.feedkeys 'pass\' + indent.should == 0 + end + + it "still aligns parens properly if not ambiguous" do + vim.feedkeys 'iwhile (111 and\' + indent.should == 7 + vim.feedkeys '222):\' + indent.should == shiftwidth + vim.feedkeys 'pass\' + indent.should == 0 + end + + it "handles nested expressions (Flake8's E127)" do + vim.feedkeys 'i[\x for x in foo\if (\' + indent.should == shiftwidth * 2 + end + + it "still handles multiple parens correctly" do + vim.feedkeys 'iif (111 and (222 and 333\' + indent.should == 13 + vim.feedkeys 'and 444\' + indent.should == 13 + vim.feedkeys ')\' + if shiftwidth == 4 + indent.should == shiftwidth * 2 + else + indent.should == 4 + end + vim.feedkeys 'and 555):\' + indent.should == shiftwidth + vim.feedkeys 'pass\' + indent.should == 0 + end + end + + describe "when a line breaks with a manual '\\'" do + it "indents shiftwidth spaces on normal line" do + vim.feedkeys 'ivalue = test + \\\\\' + indent.should == shiftwidth + end + + it "indents 2x shiftwidth spaces for control structures" do + vim.feedkeys 'iif somevalue == xyz and \\\\\' + indent.should == shiftwidth * 2 + end + + it "indents relative to line above" do + vim.feedkeys 'i\value = test + \\\\\' + indent.should == shiftwidth * 2 + end + end + + describe "when current line is dedented compared to previous line" do + before { vim.feedkeys 'i\\if x:\y = True\\' } + it "and current line has a valid indentation (Part 1)" do + vim.feedkeys '0i\if y:' + proposed_indent.should == -1 + end + + it "and current line has a valid indentation (Part 2)" do + vim.feedkeys '0i\\if y:' + proposed_indent.should == -1 + end + + it "and current line has an invalid indentation" do + vim.feedkeys 'i while True:\' + indent.should == previous_indent + shiftwidth + end + end + + describe "when current line is dedented compared to the last non-empty line" do + before { vim.feedkeys 'i\\if x:\y = True\\\' } + it "and current line has a valid indentation" do + vim.feedkeys '0i\if y:' + proposed_indent.should == -1 + end + end + + describe "when an 'if' is followed by" do + before { vim.feedkeys 'i\\if x:\' } + it "an elif, it lines up with the 'if'" do + vim.feedkeys 'elif y:' + indent.should == shiftwidth * 2 + end + + it "an 'else', it lines up with the 'if'" do + vim.feedkeys 'else:' + indent.should == shiftwidth * 2 + end + end + + describe "when an 'if' contains a try-except" do + before { + vim.feedkeys 'iif x:\try:\pass\except:\pass\' + indent.should == shiftwidth + } + it "an 'else' should be indented to the try" do + vim.feedkeys 'else:' + indent.should == shiftwidth + proposed_indent.should == shiftwidth + end + it "an 'else' should keep the indent of the 'if'" do + vim.feedkeys 'else:\<<' + indent.should == 0 + proposed_indent.should == 0 + end + end + + describe "when a 'for' is followed by" do + before { vim.feedkeys 'i\\for x in y:\' } + it "an 'else', it lines up with the 'for'" do + vim.feedkeys 'else:' + indent.should == shiftwidth * 2 + end + end + + describe "when an 'else' is followed by" do + before { vim.feedkeys 'i\\else:\XXX\' } + it "a 'finally', it lines up with the 'else'" do + vim.feedkeys 'finally:' + indent.should == shiftwidth * 2 + end + end + + + describe "when a 'try' is followed by" do + before { vim.feedkeys 'i\\try:\' } + it "an 'except', it lines up with the 'try'" do + vim.feedkeys 'except:' + indent.should == shiftwidth * 2 + end + + it "an 'else', it lines up with the 'try'" do + vim.feedkeys 'else:' + indent.should == shiftwidth * 2 + end + + it "a 'finally', it lines up with the 'try'" do + vim.feedkeys 'finally:' + indent.should == shiftwidth * 2 + end + end + + describe "when an 'except' is followed by" do + before { vim.feedkeys 'i\\except:\' } + it "an 'else', it lines up with the 'except'" do + vim.feedkeys 'else:' + indent.should == shiftwidth * 2 + end + + it "another 'except', it lines up with the previous 'except'" do + vim.feedkeys 'except:' + indent.should == shiftwidth * 2 + end + + it "a 'finally', it lines up with the 'except'" do + vim.feedkeys 'finally:' + indent.should == shiftwidth * 2 + end + end + + describe "when an else is used inside of a nested if" do + before { vim.feedkeys 'iif foo:\if bar:\pass\' } + it "indents the else to the inner if" do + vim.feedkeys 'else:' + indent.should == shiftwidth + end + end + + describe "when an else is used outside of a nested if" do + before { vim.feedkeys 'iif True:\if True:\pass\\0' } + it "indents the else to the outer if" do + indent.should == 0 + proposed_indent.should == shiftwidth + + vim.feedkeys 'ielse:' + indent.should == 0 + proposed_indent.should == 0 + end + end + + describe "when jedi-vim call signatures are used" do + before { vim.command 'syn match jediFunction "JEDI_CALL_SIGNATURE" keepend extend' } + + it "ignores the call signature after a colon" do + vim.feedkeys 'iif True: JEDI_CALL_SIGNATURE\' + indent.should == shiftwidth + end + + it "ignores the call signature after a function" do + vim.feedkeys 'idef f( JEDI_CALL_SIGNATURE\' + indent.should == shiftwidth + end + end +end + +shared_examples_for "multiline strings" do + before(:each) { + # clear buffer + vim.normal 'gg"_dG' + + # Insert two blank lines. + # The first line is a corner case in this plugin that would shadow the + # correct behaviour of other tests. Thus we explicitly jump to the first + # line when we require so. + vim.feedkeys 'i\\\' + } + + describe "when after an '(' that is followed by an unfinished string" do + before { vim.feedkeys 'itest("""' } + + it "it indents the next line" do + vim.feedkeys '\' + expected_proposed, expected_indent = multiline_indent(0, shiftwidth) + proposed_indent.should == expected_proposed + indent.should == expected_indent + end + + it "with contents it indents the second line to the parenthesis" do + vim.feedkeys 'second line\' + expected_proposed, expected_indent = multiline_indent(0, 5) + proposed_indent.should == expected_proposed + indent.should == expected_indent + end + end + + describe "when after assigning an unfinished string" do + before { vim.feedkeys 'itest = """' } + + it "it indents the next line" do + vim.feedkeys '\' + expected_proposed, expected_indent = multiline_indent(0, shiftwidth) + proposed_indent.should == expected_proposed + indent.should == expected_indent + end + end + + describe "when after assigning an indented unfinished string" do + before { vim.feedkeys 'i test = """' } + + it "it indents the next line" do + vim.feedkeys '\' + expected_proposed, expected_indent = multiline_indent(4, shiftwidth + 4) + proposed_indent.should == expected_proposed + indent.should == expected_indent + end end - def tabstop - @tabstop ||= vim.echo("&tabstop").to_i + + describe "when after assigning an indented finished string" do + before { vim.feedkeys 'i test = ""' } + + it "it does indent the next line" do + vim.feedkeys '\' + indent.should == 4 + end + + it "and writing a new string, it does indent the next line" do + vim.feedkeys '\""' + indent.should == 4 + end + end + + describe "when after a docstring" do + it "it does indent the next line to the docstring" do + vim.feedkeys 'i """\' + indent.should == 4 + proposed_indent.should == 4 + end + + it "indents the closing docstring quotes" do + vim.feedkeys 'i """\\"""' + indent.should == 4 + proposed_indent.should == 4 + vim.echo('getline(3)').should == ' """' + end + + it "indents non-matching docstring quotes" do + vim.feedkeys 'i """\\' + vim.feedkeys "0C'''" + vim.echo('line(".")').should == "4" + vim.echo('getline(".")').should == "'''" + indent.should == 0 + proposed_indent.should == -1 + end end - def indent - vim.echo("indent('.')").to_i + + describe "when after a docstring with contents" do + before { vim.feedkeys 'i """First line' } + it "it does indent the next line to the docstring" do + vim.feedkeys '\' + indent.should == 4 + proposed_indent.should == 4 + end end - def proposed_indent - vim.echo("GetPythonPEPIndent(line('.'))").to_i + + describe "when breaking a string after opening parenthesis" do + before { vim.feedkeys 'i foo("""bar\\\' } + it "it does indent the next line as after an opening multistring" do + vim.feedkeys '\' + _, expected_indent = multiline_indent(4, 4 + shiftwidth) + indent.should == expected_indent + proposed_indent.should == -1 + + # it keeps the indent after an empty line + vim.feedkeys '\' + proposed_indent, expected_indent = multiline_indent(4, 4 + shiftwidth) + indent.should == expected_indent + proposed_indent.should == proposed_indent + + # it keeps the indent of nonblank above + vim.feedkeys '\\' + proposed_indent, expected_indent = multiline_indent(4, 4 + shiftwidth) + indent.should == expected_indent + proposed_indent.should == proposed_indent + + # it keeps the indent of nonblank above before an empty line + vim.feedkeys '\' + proposed_indent, expected_indent = multiline_indent(4, 4 + shiftwidth) + indent.should == expected_indent + proposed_indent.should == proposed_indent + end end end +SUITE_SHIFTWIDTHS = [4, 3] +SUITE_HANG_CLOSINGS = [false, true] + +SUITE_SHIFTWIDTHS.each do |sw| + describe "vim when using width of #{sw}" do + before { + vim.command("set sw=#{sw} ts=#{sw} sts=#{sw} et") + } + it "sets shiftwidth to #{sw}" do + shiftwidth.should == sw + end + + SUITE_HANG_CLOSINGS.each do |hc| + describe "vim when hang_closing is set to #{hc}" do + before { + set_hang_closing hc + } + it "sets hang_closing to #{hc}" do + hang_closing.should == !!hc + end + + it_behaves_like "vim" + end + end + end +end + +describe "vim when not using python_pep8_indent_multiline_string" do + before { + vim.command("set sw=4 ts=4 sts=4 et") + vim.command("unlet! g:python_pep8_indent_multiline_string") + } + it_behaves_like "multiline strings" +end + +describe "vim when using python_pep8_indent_multiline_first=0" do + before { + vim.command("set sw=4 ts=4 sts=4 et") + vim.command("let g:python_pep8_indent_multiline_string=0") + } + it_behaves_like "multiline strings" +end + +describe "vim when using python_pep8_indent_multiline_string=-1" do + before { + vim.command("set sw=4 ts=4 sts=4 et") + vim.command("let g:python_pep8_indent_multiline_string=-1") + } + it_behaves_like "multiline strings" +end + +describe "vim when using python_pep8_indent_multiline_string=-2" do + before { + vim.command("set sw=4 ts=4 sts=4 et") + vim.command("let g:python_pep8_indent_multiline_string=-2") + } + it_behaves_like "multiline strings" +end + +describe "Handles far away opening parens" do + before { vim.feedkeys '\ggdGifrom foo import (' } + + it "indents by one level" do + vim.feedkeys '\' + proposed_indent.should == shiftwidth + end + + it "indents by one level for 10 lines" do + vim.command('set paste | exe "norm 9o" | set nopaste') + vim.feedkeys '\o' + indent.should == shiftwidth + end + + it "indents by one level for 50 lines" do + vim.command('set paste | exe "norm 49o" | set nopaste') + vim.feedkeys '\o' + indent.should == shiftwidth + end +end + +describe "Handles far away opening square brackets" do + before { vim.feedkeys '\ggdGibar = [' } + + it "indents by one level" do + vim.feedkeys '\' + proposed_indent.should == shiftwidth + end + + it "indents by one level for 10 lines" do + vim.command('set paste | exe "norm 9o" | set nopaste') + vim.feedkeys '\o' + indent.should == shiftwidth + end + + it "indents by one level for 100 lines" do + vim.command('set paste | exe "norm 99o" | set nopaste') + vim.feedkeys '\o' + indent.should == shiftwidth + end +end + +describe "Handles far away opening curly brackets" do + before { vim.feedkeys '\ggdGijson = {' } + + it "indents by one level" do + vim.feedkeys '\' + vim.feedkeys '\o' + proposed_indent.should == shiftwidth + end + + it "indents by one level for 10 lines" do + vim.command('set paste | exe "norm 9o" | set nopaste') + vim.feedkeys '\o' + indent.should == shiftwidth + end + + it "indents by one level for 1000 lines" do + vim.command('set paste | exe "norm 999o" | set nopaste') + vim.feedkeys '\o' + indent.should == shiftwidth + end +end + +describe "Compact multiline dict" do + before { vim.feedkeys '\ggdGid = {"one": 1,' } + + it "gets indented correctly" do + vim.feedkeys '\' + proposed_indent.should == 5 + + vim.feedkeys '"two": 2}' + proposed_indent.should == 5 + + vim.feedkeys '\' + proposed_indent.should == 0 + end +end + +describe "Using O" do + before { + vim.feedkeys '\ggdG' + vim.feedkeys 'iif foo:\' + } + + it "respects autoindent" do + vim.feedkeys '1\\' + indent.should == shiftwidth + vim.feedkeys '\ko' + indent.should == shiftwidth + vim.feedkeys '\kO' + indent.should == shiftwidth + # Uses/keeps indent from line above + vim.feedkeys '\i2\O' + indent.should == shiftwidth + # Uses/keeps indent from line above + vim.feedkeys '\j\O' + indent.should == 0 + end +end + +describe "searchpairpos" do + before { vim.feedkeys '\ggdG' } + it "handles nested parenthesis" do + vim.feedkeys 'iif foo.startswith("("):\' + indent.should == shiftwidth + end +end + +describe "o within TODO" do + before { + vim.feedkeys '\ggdG' + vim.feedkeys 'iif 1: # TODO\' + # Assertion that we have a pythonTodo here. + vim.echo('synIDattr(synID(line("."), col("."), 0), "name")').should match 'pythonTodo' + } + + it "respects autoindent" do + vim.feedkeys 'o' + indent.should == shiftwidth + end +end + +describe "elif after else" do + before { + vim.feedkeys '\ggdG' + } + + it "is indented to the outer if" do + vim.feedkeys 'iif 1:\if 2:\pass\else:\pass\elif 3:\' + indent.should == 0 + + vim.feedkeys '\ggdG' + vim.feedkeys 'i if 1:\if 2:\pass\else:\pass\elif 3:\' + indent.should == 4 + end +end + +describe "elif after two ifs" do + before { + vim.feedkeys '\ggdG' + } + + it "keeps its indent to the outer if" do + vim.feedkeys 'iif 1:\if 2:\pass\elif 3:\pass\' + indent.should == 4 + vim.feedkeys '\' + indent.should == 0 + proposed_indent.should == shiftwidth + vim.feedkeys 'ielif 4:' + indent.should == 0 + proposed_indent.should == 0 + vim.feedkeys '\' + indent.should == 4 + proposed_indent.should == 4 + end +end