/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. * * Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common * Development and Distribution License("CDDL") (collectively, the * "License"). You may not use this file except in compliance with the * License. You can obtain a copy of the License at * http://www.netbeans.org/cddl-gplv2.html * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the * specific language governing permissions and limitations under the * License. When distributing the software, include this License Header * Notice in each file and include the License file at * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the * License Header, with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * Contributor(s): * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun * Microsystems, Inc. All Rights Reserved. * * If you wish your version of this file to be governed by only the CDDL * or only the GPL Version 2, indicate your decision by adding * "[Contributor] elects to include this software in this distribution * under the [CDDL or GPL Version 2] license." If you do not indicate a * single choice of license, a recipient has the option to distribute * your version of this file under either the CDDL, the GPL Version 2 or * to extend the choice of license to its licensees as provided above. * However, if you add GPL Version 2 code and therefore, elected the GPL * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. */ package org.netbeans.modules.ruby; import javax.swing.JTextArea; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; import org.netbeans.editor.BaseDocument; import org.netbeans.editor.Utilities; /** * @todo Test that if you insert x="" and then DELETE the ", it wipes out BOTH of them! * @todo Try typing in whole source files and other than tracking missing end and } closure * statements the buffer should be identical - both in terms of quotes to the rhs not having * accumulated as well as indentation being correct. * @todo * // TODO: Test * // - backspace deletion * // - entering incomplete output * // automatic reindentation of "end", "else" etc. * * * * @author Tor Norbye */ public class RubyKeystrokeHandlerTest extends RubyTestBase { public RubyKeystrokeHandlerTest(String testName) { super(testName); } private void insertChar(String original, char insertText, String expected) throws Exception { insertChar(original, insertText, expected, null); } private void insertChar(String original, char insertText, String expected, String selection) throws Exception { insertChar(original, insertText, expected, selection, false); } @Override public void deleteWord(String original, String expected) throws Exception { // Try deleting the word not just using the testcase but also surrounded by strings // to make sure there's no problem with lexer token directions super.deleteWord(original, expected); super.deleteWord(original+"foo", expected+"foo"); super.deleteWord("foo"+original, "foo"+expected); super.deleteWord(original+"::", expected+"::"); super.deleteWord(original+"::", expected+"::"); } public void testInsertX() throws Exception { insertChar("c^ass", 'l', "cl^ass"); } public void testInsertX2() throws Exception { insertChar("clas^", 's', "class^"); } public void testNoMatchInComments() throws Exception { insertChar("# Hello^", '\'', "# Hello'^"); insertChar("# Hello^", '"', "# Hello\"^"); insertChar("# Hello^", '[', "# Hello[^"); insertChar("# Hello^", '(', "# Hello(^"); } public void testNoMatchInStrings() throws Exception { insertChar("x = \"^\"", '\'', "x = \"'^\""); insertChar("x = \"^\"", '[', "x = \"[^\""); insertChar("x = \"^\"", '(', "x = \"(^\""); insertChar("x = \"^)\"", ')', "x = \")^)\""); insertChar("x = '^'", '"', "x = '\"^'"); insertChar("x = \"\nf^\n\"", '\'', "x = \"\nf'^\n\""); insertChar("x = \"\nf^\n\"", '[', "x = \"\nf[^\n\""); insertChar("x = \"\nf^\n\"", '(', "x = \"\nf(^\n\""); insertChar("x = '\nf^\n'", '"', "x = '\nf\"^\n'"); } public void testSingleQuotes1() throws Exception { insertChar("x = ^", '\'', "x = '^'"); } public void testSingleQuotes2() throws Exception { insertChar("x = '^'", '\'', "x = ''^"); } public void testSingleQuotes3() throws Exception { insertChar("x = '^'", 'a', "x = 'a^'"); } public void testSingleQuotes4() throws Exception { insertChar("x = '\\^'", '\'', "x = '\\'^'"); } public void testDoubleQuotes1() throws Exception { insertChar("x = ^", '"', "x = \"^\""); } public void testDoubleQuotes2() throws Exception { insertChar("x = \"^\"", '"', "x = \"\"^"); } public void testDoubleQuotes3() throws Exception { insertChar("x = \"^\"", 'a', "x = \"a^\""); } public void testDobuleQuotes4() throws Exception { insertChar("x = \"\\^\"", '"', "x = \"\\\"^\""); } public void testDocs() throws Exception { insertBreak("=begin^\n", "=begin\n^\n=end\n"); } public void testDocsEnd() throws Exception { insertBreak("=begin^", "=begin\n^\n=end"); } public void testDocsEnd2() throws Exception { insertBreak("def foo\nend\n=begin^", "def foo\nend\n=begin\n^\n=end"); } public void testInsertEnd1() throws Exception { insertBreak("x^", "x\n^"); } public void testInsertEnd2() throws Exception { insertBreak("class Foo^", "class Foo\n ^\nend"); } public void testInsertEnd3() throws Exception { insertBreak("class Foo^\nend", "class Foo\n ^\nend"); } public void testInsertEnd4() throws Exception { insertBreak("foo bar, {:x => :x,\n :y => :y, ^:z => :z} do\nend\n", "foo bar, {:x => :x,\n :y => :y, \n ^:z => :z} do\nend\n"); } public void testInsertEnd5() throws Exception { insertBreak("if a_condition ^thing()", "if a_condition \n ^thing()\nend"); } public void testInsertIf1() throws Exception { insertBreak(" if true^", " if true\n ^\n end"); } // This doesn't work // public void testInsertIf2() throws Exception { // insertBreak(" if true\n else", 20, " if true\n else\n end", 27); // } public void testBrackets1() throws Exception { insertChar("x = ^", '[', "x = [^]"); } public void testBrackets2() throws Exception { insertChar("x = [^]", ']', "x = []^"); } public void testBracketsSpecialName() throws Exception { // "[]" and "[]=" are valid method names! insertChar("def ^", '[', "def [^]"); } public void testBracketsSpecialName2() throws Exception { // "[]" and "[]=" are valid method names! insertChar("def [^]", ']', "def []^"); } public void testBrackets3() throws Exception { insertChar("x = [^]", 'a', "x = [a^]"); } public void testBrackets4() throws Exception { insertChar("x = [^]", '[', "x = [[^]]"); } public void testBrackets5() throws Exception { insertChar("x = [[^]]", ']', "x = [[]^]"); } public void testBrackets6() throws Exception { insertChar("x = [[]^]", ']', "x = [[]]^"); } public void testParens1() throws Exception { insertChar("x = ^", '(', "x = (^)"); } public void testParens2() throws Exception { insertChar("x = (^)", ')', "x = ()^"); } public void testParens3() throws Exception { insertChar("x = (^)", 'a', "x = (a^)"); } public void testParens4() throws Exception { insertChar("x = (^)", '(', "x = ((^))"); } public void testParens5() throws Exception { insertChar("x = ((^))", ')', "x = (()^)"); } public void testParens6() throws Exception { insertChar("x = (()^)", ')', "x = (())^"); } public void testRegexp1() throws Exception { insertChar("x = ^", '/', "x = /^/"); } public void testRegexp2() throws Exception { insertChar("x = /^/", '/', "x = //^"); } public void testRegexp3() throws Exception { insertChar("x = /^/", 'a', "x = /a^/"); } public void testRegexp4() throws Exception { insertChar("x = /\\^/", '/', "x = /\\/^/"); } public void testRegexp5() throws Exception { insertChar(" regexp = /fofo^\n # Subsequently, you can make calls to it by name with <tt>yield</tt> in", '/', " regexp = /fofo/^\n # Subsequently, you can make calls to it by name with <tt>yield</tt> in"); } public void testRegexp6() throws Exception { insertChar(" regexp = /fofo^\n", '/', " regexp = /fofo/^\n"); } public void testRegexp7() throws Exception { insertChar("x = ^\n", '/', "x = /^/\n"); } public void testRegexp8() throws Exception { insertChar("x = /^/\n", '/', "x = //^\n"); } public void testRegexp9() throws Exception { insertChar("x = /^/\n", 'a', "x = /a^/\n"); } public void testRegexp10() throws Exception { insertChar("x = /\\^/\n", '/', "x = /\\/^/\n"); } public void testRegexp11() throws Exception { insertChar("/foo^", '/', "/foo/^"); } public void testNotRegexp1() throws Exception { insertChar("x = 10 ^", '/', "x = 10 /^"); } public void testNotRegexp2() throws Exception { insertChar("x = 3.14 ^", '/', "x = 3.14 /^"); } // This test doesn't work; the lexer identifies x = y / as the // beginning of a regular expression. Without the space it DOES // work (see regexp4) //public void testNotRegexp3() throws Exception { // insertChar("x = y ^", '/', "x = y /^"); //} public void testNotRegexp4() throws Exception { insertChar("x = y^", '/', "x = y/^"); } public void testRegexpPercent1() throws Exception { insertChar("x = %r^", '(', "x = %r(^)"); } public void testRegexpPercent2() throws Exception { insertChar("x = %r(^)", ')', "x = %r()^"); } public void testSinglePercent1() throws Exception { insertChar("x = %q^", '(', "x = %q(^)"); } public void testSinglePercent2() throws Exception { insertChar("x = %q(^)", ')', "x = %q()^"); } // Broken!! // I've gotta handle proper parenthesis nesting here... e.g. // %q(()) // public void testSinglePercent3() throws Exception { // insertChar("x = %q(^)", '(', "x = %q((^))"); // } // Broken!! // public void testSinglePercent4() throws Exception { // insertChar("x = %q((^))", ')', "x = %q(()^)"); // } public void testSinglePercent5() throws Exception { insertChar("x = %q((^))", 'a', "x = %q((a^))"); } public void testSinglePercent6() throws Exception { insertChar("x = %q^", '-', "x = %q-^-"); } public void testSinglePercent7() throws Exception { insertChar("x = %q-^-", '-', "x = %q--^"); } public void testSinglePercent8() throws Exception { insertChar("x = %q^", ' ', "x = %q ^ "); } // Broken! // public void testSinglePercent9() throws Exception { // insertChar("x = %q ^ ", ' ', "x = %q ^"); // } public void testSinglePercent10() throws Exception { insertChar("x = %q ^ ", 'x', "x = %q x^ "); } public void testSinglePercent11() throws Exception { insertChar("x = %q-\\^-", '-', "x = %q-\\-^-"); } public void testHeredoc1() throws Exception { insertBreak("x=<<FOO^\n", "x=<<FOO\n^\nFOO\n"); } public void testHeredoc2() throws Exception { insertBreak("x=f(<<FOO,^\n", "x=f(<<FOO,\n^\nFOO\n"); } public void testBackspace1() throws Exception { deleteChar("x^", "^"); } public void testBackspace2() throws Exception { deleteChar("x^y", "^y"); } public void testBackspace3() throws Exception { deleteChar("xy^z", "x^z"); } public void testBackspace4() throws Exception { deleteChar("xy^z", "x^z"); } public void testBackspace5() throws Exception { deleteChar("x=\"^\"", "x=^"); } public void testBackspace6() throws Exception { deleteChar("x='^'", "x=^"); } public void testBackspace7() throws Exception { deleteChar("x=(^)", "x=^"); } public void testBackspace7b() throws Exception { deleteChar("x=[^]", "x=^"); } public void testBackspace8() throws Exception { // See bug 111534 deleteChar("x={^}", "x=^"); } public void testBackspace9() throws Exception { deleteChar("x=/^/", "x=^"); } public void testPercentBackspace() throws Exception { deleteChar("x=\"#{^}\"", "x=\"#^\""); } public void testPercentBackspace2() throws Exception { deleteChar("x=\"#{a^}\"", "x=\"#{^}\""); } public void testPercentBackspace3() throws Exception { deleteChar("x=\"a#{^}b\"", "x=\"a#^b\""); } public void testPercentBackspace4() throws Exception { deleteChar("x=/#{^}/", "x=/#^/"); } public void testPercentBackspace5() throws Exception { deleteChar("x=/#{a^}/", "x=/#{^}/"); } public void testPercentBackspace6() throws Exception { deleteChar("x=/a#{^}b/", "x=/a#^b/"); } public void testContComment() throws Exception { if (RubyKeystrokeHandler.CONTINUE_COMMENTS) { insertBreak("# ^", "# \n# ^"); } else { insertBreak("# ^", "# \n^"); } } public void testContComment2() throws Exception { // No auto-# on new lines if (RubyKeystrokeHandler.CONTINUE_COMMENTS) { insertBreak(" # ^", " # \n # ^"); } else { insertBreak(" # ^", " # \n ^"); } } public void testContComment3() throws Exception { // No auto-# on new lines if (RubyKeystrokeHandler.CONTINUE_COMMENTS) { insertBreak(" #\t^", " #\t\n #\t^"); } else { insertBreak(" #\t^", " #\t\n ^"); } } public void testContComment4() throws Exception { insertBreak("# foo\n^", "# foo\n\n^"); } public void testContComment5() throws Exception { // No auto-# on new lines if (RubyKeystrokeHandler.CONTINUE_COMMENTS) { insertBreak(" # ^", " # \n # ^"); } else { insertBreak(" # ^", " # \n ^"); } } public void testContComment6() throws Exception { insertBreak(" # foo^bar", " # foo\n # ^bar"); } public void testContComment7() throws Exception { insertBreak(" # foo^\n # bar", " # foo\n # ^\n # bar"); } public void testContComment8() throws Exception { insertBreak(" # foo^bar", " # foo\n # ^bar"); } public void testContComment9() throws Exception { insertBreak("^# foobar", "\n^# foobar"); } public void testContComment10() throws Exception { insertBreak("#foo\n^# foobar", "#foo\n# ^\n# foobar"); } public void testContComment11() throws Exception { insertBreak("code #foo\n^# foobar", "code #foo\n\n^# foobar"); } public void testContComment12() throws Exception { insertBreak(" code\n^# foobar", " code\n\n ^# foobar"); } public void testContComment14() throws Exception { insertBreak("def foo\n code\n^# foobar\nend\n", "def foo\n code\n\n ^# foobar\nend\n"); } public void testContComment15() throws Exception { insertBreak("\n\n^# foobar", "\n\n\n^# foobar"); } public void testContComment16() throws Exception { insertBreak("\n \n^# foobar", "\n \n\n^# foobar"); } public void testContComment17() throws Exception { insertBreak("def foo\n # cmnt1\n^ # cmnt2\nend\n", "def foo\n # cmnt1\n # ^\n # cmnt2\nend\n"); } public void testNoContComment() throws Exception { // No auto-# on new lines insertBreak("foo # ^", "foo # \n^"); } public void testDeleteContComment() throws Exception { deleteChar("# ^", "^"); deleteChar("\n# ^", "\n^"); } public void testDeleteContComment2() throws Exception { deleteChar("# ^ ", "^ "); deleteChar("\n# ^ ", "\n^ "); } public void testNoDeleteContComment() throws Exception { deleteChar("# ^", "# ^"); deleteChar("#^", "^"); deleteChar("puts '# ^'", "puts '#^'"); } // public void testFreakOutEditor1() throws Exception { // String before = "x = method_call(50, <<TOKEN1, \"arg3\", <<TOKEN2, /startofregexp/^\nThis is part of the string\nTOKEN1\nrestofregexp/)"; // String after = "x = method_call(50, <<TOKEN1, \"arg3\", <<TOKEN2, /startofregexp^\nThis is part of the string\nTOKEN1\nrestofregexp/)"; // deleteChar(before, after); // } // public void testInsertPercentInString() throws Exception { insertChar("x = \"foo ^\"", '#', "x = \"foo #{^}\""); } public void testInsertPercentInString2() throws Exception { // Make sure type-through works insertChar("x = \"foo #{^}\"", '}', "x = \"foo #{}^\""); } public void testInsertPercentInString3() throws Exception { insertChar("x = \"foo #{^}\"", '{', "x = \"foo #{^}\""); } public void testInsertPercentInString4() throws Exception { insertChar("x = \"foo #{^a}\"", '}', "x = \"foo #{}^a}\""); } public void testInsertPercentInString5() throws Exception { insertChar("x = \"foo {^}\"", '}', "x = \"foo {}^}\""); } public void testInsertPercentInString6() throws Exception { insertChar("x = \"foo {^}\"", '{', "x = \"foo {{^}\""); } public void testNoInsertPercentInString() throws Exception { insertChar("x = 'foo ^'", '#', "x = 'foo #^'"); } public void testNoInsertPercentElsewhere() throws Exception { insertChar("x = ^", '#', "x = #^"); } public void testInsertPercentInRegexp() throws Exception { insertChar("x = /foo ^/", '#', "x = /foo #{^}/"); } public void testInsertPercentInRegexp2() throws Exception { // Make sure type-through works insertChar("x = /foo #{^}/", '}', "x = /foo #{}^/"); } public void testInsertPercentInRegexp3() throws Exception { insertChar("x = /foo #{^}/", '{', "x = /foo #{^}/"); } public void testInsertPercentInRegexp4() throws Exception { insertChar("x = /foo #{^a}/", '}', "x = /foo #{}^a}/"); } public void testInsertPercentInRegexp5() throws Exception { insertChar("x = /foo {^}/", '}', "x = /foo {}^}/"); } public void testInsertPercentInRegexp6() throws Exception { insertChar("x = /foo {^}/", '{', "x = /foo {{^}/"); } public void testReplaceSelection1() throws Exception { insertChar("x = foo^", 'y', "x = y^", "foo"); } public void testReplaceSelection2() throws Exception { insertChar("x = foo^", '"', "x = \"foo\"^", "foo"); } public void testReplaceSelection3() throws Exception { insertChar("x = \"foo^bar\"", '#', "x = \"#{foo}^bar\"", "foo"); } public void testReplaceSelection4() throws Exception { insertChar("x = 'foo^bar'", '#', "x = '#^bar'", "foo"); } public void testReplaceSelection5() throws Exception { insertChar("'(^position:absolute;'", '{', "'{^position:absolute;'", "("); } public void testReplaceSelection6() throws Exception { insertChar("'position^:absolute;'", '{', "'pos{^:absolute;'", "ition"); } public void testReplaceSelectionChangeType1() throws Exception { insertChar("x = \"foo\"^", '\'', "x = 'foo'^", "\"foo\""); } public void testReplaceSelectionChangeType2() throws Exception { insertChar("x = \"foo\"^", '{', "x = {foo}^", "\"foo\""); } public void testReplaceSelectionNotInTemplateMode1() throws Exception { insertChar("x = foo^", '"', "x = \"^\"", "foo", true); } public void testReplaceSelectionNotInTemplateMode2() throws Exception { insertChar("x = \"foo^bar\"", '#', "x = \"#{^}bar\"", "foo", true); } public void testReplaceCommentSelectionBold() throws Exception { insertChar("# foo^", '*', "# *foo*^", "foo"); } public void testReplaceCommentSelectionTerminal() throws Exception { insertChar("# foo^", '+', "# +foo+^", "foo"); } public void testReplaceCommentSelectionItalic() throws Exception { insertChar("# foo^", '_', "# _foo_^", "foo"); } public void testReplaceCommentSelectionWords() throws Exception { // No replacement if it contains multiple lines insertChar("# foo bar^", '*', "# *^", "foo bar"); } public void testReplaceCommentOther() throws Exception { // No replacement if it's not one of the three chars insertChar("# foo^", 'x', "# x^", "foo"); } public void testdeleteWord() throws Exception { deleteWord("foo_bar_baz^", "foo_bar_^"); } public void testdeleteWord111303() throws Exception { deleteWord("foo::bar^", "foo::^"); deleteWord("Foo::Bar^", "Foo::^"); deleteWord("Foo::Bar_Baz^", "Foo::Bar_^"); } public void testdeleteWordx111305() throws Exception { deleteWord("foo_bar^", "foo_^"); deleteWord("x.foo_bar^.y", "x.foo_^.y"); } public void testdeleteWord2() throws Exception { deleteWord("foo_bar_baz ^", "foo_bar_baz^"); deleteWord("foo_bar_^", "foo_^"); } public void testdeleteWord3() throws Exception { deleteWord("FooBarBaz^", "FooBar^"); } public void testDeleteWord4_110998() throws Exception { deleteWord("Blah::Set^Foo", "Blah::^Foo"); } public void testdeleteWord5() throws Exception { deleteWord("foo_bar_^", "foo_^"); } public void testdeleteWords() throws Exception { deleteWord("foo bar^", "foo ^"); } public void testDeleteWord4_110998c() throws Exception { String before = " snark^\n"; String after = " ^\n"; deleteWord(before, after); } public void testDeleteWord4_110998b() throws Exception { String before = "" + " snark(%w(a b c))\n" + " snark(%W(a b c))\n" + " snark^\n" + " snark(%Q(a b c))\n" + " snark(%w(a b c))\n"; String after = "" + " snark(%w(a b c))\n" + " snark(%W(a b c))\n" + " ^\n" + " snark(%Q(a b c))\n" + " snark(%w(a b c))\n"; deleteWord(before, after); } public void testBackwardsDeletion() throws Exception { String s = "Foo::Bar = whatever('hello') \n nextline"; RubyKeystrokeHandler bc = new RubyKeystrokeHandler(); for (int i = s.length(); i >= 1; i--) { String shortened = s.substring(0, i); BaseDocument doc = getDocument(shortened); JTextArea ta = new JTextArea(doc); Caret caret = ta.getCaret(); int dot = i; caret.setDot(dot); int begin = bc.getNextWordOffset(doc, dot, true); if (begin == -1) { begin = Utilities.getPreviousWord(ta, dot); } assert begin != -1 && begin < i; } } public void test108889() throws Exception { // Reproduce 108889: AIOOBE and AE during editing // NOTE: While the test currently throws an exception, when the // exception is fixed the test won't actually pass; that's an expected // fail I will deal with later insertChar("x = %q((^))", 'a', "x = %q((a^))"); } public void test110332() throws Exception { String before = "args = {\n" + " :name => args[:name],\n" + " :status => :missing,\n" + " :s2_test_comments => comments, \n" + " :metric => '', \n" + " :duration => '', \n" + " :setback? => true,\n" + " :progress? => false, :compare_metric => 0, ^:compare_duration => 0}\n" + " OpenStruct.new\n" + ""; String after = "args = {\n" + " :name => args[:name],\n" + " :status => :missing,\n" + " :s2_test_comments => comments, \n" + " :metric => '', \n" + " :duration => '', \n" + " :setback? => true,\n" + " :progress? => false, :compare_metric => 0, \n ^:compare_duration => 0}\n" + " OpenStruct.new\n" + ""; insertBreak(before, after); } public void test110332b() throws Exception { String before = "args = {\n" + " :name => args[:name],\n" + " :status => :missing,\n" + " :s2_test_comments => comments, \n" + " :metric => '', \n" + " :duration => '', \n" + " :setback? => true,\n" + " :progress? => false, :compare_metric => 0,^ :compare_duration => 0}\n" + " OpenStruct.new\n" + ""; String after = "args = {\n" + " :name => args[:name],\n" + " :status => :missing,\n" + " :s2_test_comments => comments, \n" + " :metric => '', \n" + " :duration => '', \n" + " :setback? => true,\n" + " :progress? => false, :compare_metric => 0,\n ^:compare_duration => 0}\n" + " OpenStruct.new\n" + ""; insertBreak(before, after); } public void testLogicalRange1() throws Exception { String code = "if (true)\n fo^o\nend"; String next = "if (true)\n %<%fo^o%>%\nend"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRange2() throws Exception { String code = "if (true)\n %<%fo^o%>%\nend"; String next = "%<%if (true)\n fo^o\nend%>%"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRange3() throws Exception { String code = "def foo\nif (true)\n %<%fo^o%>%\nend\nend"; String next = "def foo\n%<%if (true)\n fo^o\nend%>%\nend"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRange4() throws Exception { String code = "class Foo\ndef foo\nif (true)\n %<%fo^o%>%\nend\nend\nend"; String next = "class Foo\ndef foo\n%<%if (true)\n fo^o\nend%>%\nend\nend"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRange5() throws Exception { String code = "class Foo\ndef foo\n%<%if (true)\n fo^o\nend%>%\nend\nend"; String next = "class Foo\n%<%def foo\nif (true)\n fo^o\nend\nend%>%\nend"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRange6() throws Exception { String code = "class Foo\n%<%def fo^o\nif (true)\n foo\nend\nend%>%\nend"; String next = "%<%class Foo\ndef fo^o\nif (true)\n foo\nend\nend\nend%>%"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRangeComment1() throws Exception { String code = "foo\n # Foo Bar\n # Foo^y Baary\n # Bye\ndef foo\nend\n"; String next = "foo\n # Foo Bar\n %<%# Foo^y Baary%>%\n # Bye\ndef foo\nend\n"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRangeComment2() throws Exception { String code = "foo\n # Foo Bar\n %<%# Foo^y Baary%>%\n # Bye\ndef foo\nend\n"; String next = "foo\n %<%# Foo Bar\n # Foo^y Baary\n # Bye%>%\ndef foo\nend\n"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRangeComment3() throws Exception { String code = "foo\n # Foo Bar\n\n %<%# Foo^y Baary%>%\n # Bye\ndef foo\nend\n"; String next = "foo\n # Foo Bar\n\n %<%# Foo^y Baary\n # Bye%>%\ndef foo\nend\n"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRangeComment5() throws Exception { String code = "foo\n foo # Foo Bar\n %<%# Foo^y Baary%>%\n # Bye\ndef foo\nend\n"; String next = "foo\n foo # Foo Bar\n %<%# Foo^y Baary\n # Bye%>%\ndef foo\nend\n"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRangeStrings1() throws Exception { String code = "x = 'foo b^ar baz', y = \"whatever\""; String next = "x = %<%'foo b^ar baz'%>%, y = \"whatever\""; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRangeStrings2() throws Exception { String code = "x = %q-foo b^ar baz-, y = \"whatever\""; String next = "x = %<%%q-foo b^ar baz-%>%, y = \"whatever\""; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRangeStrings3() throws Exception { String code = "def foo\nif (true)\nx = %<%'foo b^ar baz'%>%\nend\nend"; String next = "def foo\nif (true)\n%<%x = 'foo b^ar baz'%>%\nend\nend"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRangeStrings4() throws Exception { String code = "def foo\nif (true)\n%<%x = 'foo b^ar baz'%>%\nend\nend"; String next = "def foo\n%<%if (true)\nx = 'foo b^ar baz'\nend%>%\nend"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRangeStrings5() throws Exception { String code = "def test\n 'return^ me'\nend"; String next = "def test\n %<%'return^ me'%>%\nend"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRangeStrings6() throws Exception { String code = "def test\n %<%'return^ me'%>%\nend"; String next = "%<%def test\n 'return^ me'\nend%>%"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRangeRegexps1() throws Exception { String code = "x = /foo b^ar baz/, y = \"whatever\""; // Uhm - is it good that we're selecting the -inside- of the regexp? String next = "x = /%<%foo b^ar baz%>%/, y = \"whatever\""; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRange111941a() throws Exception { String code = "foo.ba^r.snark"; String next = "foo.%<%ba^r%>%.snark"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); code = "foo.%<%ba^r%>%.snark"; next = "%<%foo.ba^r%>%.snark"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); code = "%<%foo.ba^r%>%.snark"; next = "%<%foo.ba^r.snark%>%"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRange111941b() throws Exception { String code = "foo.bar.sn^ark"; String next = "foo.bar.%<%sn^ark%>%"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); code = "foo.bar.%<%sn^ark%>%"; next = "%<%foo.bar.sn^ark%>%"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testLogicalRange111941c() throws Exception { String code = "foo().ba^r().snark()"; String next = "foo().%<%ba^r()%>%.snark()"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); code = "foo().%<%ba^r()%>%.snark()"; next = "%<%foo().ba^r()%>%.snark()"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); code = "%<%foo().ba^r()%>%.snark()"; next = "%<%foo().ba^r().snark()%>%"; assertLogicalRange(code, true, next); assertLogicalRange(next, false, code); } public void testPipes1() throws Exception { insertChar("5.each { ^", '|', "5.each { |^|"); } public void testPipes2() throws Exception { insertChar("5.each { ^}", '|', "5.each { |^|}"); } public void testPipes3() throws Exception { insertChar("5.each { |^|}", '|', "5.each { ||^}"); } public void testPipes4() throws Exception { insertChar("5.each { |foo^|}", '|', "5.each { |foo|^}"); } public void testNegativePipes1() throws Exception { insertChar("'^'", '|', "'|^'"); } public void testNegativePipes2() throws Exception { insertChar("/^/", '|', "/|^/"); } public void testNegativePipes3() throws Exception { insertChar("#^", '|', "#|^"); } public void testNegativePipes4() throws Exception { insertChar("\"^\"", '|', "\"|^\""); } public void testNegativePipes5() throws Exception { insertChar("5.each { |f^oo|}", '|', "5.each { |f|^oo|}"); } public void testNegativePipes6() throws Exception { insertChar("5.each { |^|foo|}", '|', "5.each { ||^foo|}"); } public void testNegativePipes7() throws Exception { insertChar("x = true ^", '|', "x = true |^"); } public void testNegativePipes8() throws Exception { insertChar("x = true |^", '|', "x = true ||^"); } public void testBackspacePipes() throws Exception { deleteChar("x=|^|", "x=^"); } public void testBackspacePipes2() throws Exception { deleteChar("x=|^x", "x=^x"); } public void testBackspacePipes3() throws Exception { deleteChar("x=|^", "x=^"); } }