/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ide.eclipse.adt.internal.editors; import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest; import org.eclipse.core.resources.IFile; import org.eclipse.jface.text.DocumentCommand; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.swt.graphics.Point; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.IDE; @SuppressWarnings("javadoc") public class AndroidXmlAutoEditStrategyTest extends AdtProjectTest { public void checkInsertNewline(String before, String after) throws Exception { AndroidXmlAutoEditStrategy s = new AndroidXmlAutoEditStrategy(); // All tests just operate on the "edithandling" document; the contents are // ignored and replaced with the before-document passed in IFile file = getLayoutFile(getProject(), "edithandling.xml"); IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); assertNotNull(page); IEditorPart editor = IDE.openEditor(page, file); assertTrue(editor instanceof AndroidXmlEditor); AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor; ISourceViewer viewer = layoutEditor.getStructuredSourceViewer(); String newDocumentContent = stripCaret(before); IDocument document = viewer.getDocument(); document.replace(0, document.getLength(), newDocumentContent); // Determine the offset, and possibly make text range selections as well int offset = updateCaret(viewer, before); DocumentCommand c = new TestDocumentCommand(); c.doit = true; c.offset = offset; c.caretOffset = -1; c.length = 0; c.text = "\n"; s.customizeDocumentCommand(document, c); if (c.doit) { if (c.length == 0 && c.text == null) { return; } document.replace(c.offset, c.length, c.text); int caretOffset = c.offset + c.text.length(); // The shiftsCaret flag doesn't behave the way it's documented to //if (c.shiftsCaret && c.caretOffset != -1) { if (c.caretOffset != -1) { caretOffset = c.caretOffset; } viewer.setSelectedRange(caretOffset, 0); } String text = document.get(); Point selectedRange = viewer.getSelectedRange(); assert selectedRange.y == 0; String textWithCaret = text; if (selectedRange.x >= 0) { textWithCaret = text.substring(0, selectedRange.x) + "^" + text.substring(selectedRange.x); } assertEquals(after, textWithCaret); } private static String stripCaret(String s) { int index = s.indexOf('^'); assertTrue(index != -1); return s.substring(0, index) + s.substring(index + 1); } public void testCornerCase1() throws Exception { checkInsertNewline("^", "\n^"); } public void testCornerCase2() throws Exception { checkInsertNewline( "\n^", "\n\n^"); } public void testCornerCase3() throws Exception { checkInsertNewline( " ^", " \n" + " ^"); } public void testSimpleIndentation1() throws Exception { checkInsertNewline( " ^ ", " \n" + " ^ "); } public void testSimpleIndentation2() throws Exception { checkInsertNewline( "\n" + " foo^\n", "\n" + " foo\n" + " ^\n"); } public void testSimpleIndentation3() throws Exception { checkInsertNewline( "\n" + " <newtag>^\n", "\n" + " <newtag>\n" + " ^\n"); } public void testSimpleIndentation4() throws Exception { checkInsertNewline( "\n" + " <newtag/>^\n", "\n" + " <newtag/>\n" + " ^\n"); } public void testSimpleIndentation5() throws Exception { checkInsertNewline( "\n" + " <newtag^\n", "\n" + " <newtag\n" + " ^\n"); } public void testSplitAttribute() throws Exception { checkInsertNewline( "\n" + " <newtag ^attribute='value'/>\n", "\n" + " <newtag \n" + " ^attribute='value'/>\n"); } public void testIndentationInComments1() throws Exception { // Make sure that inside a comment we ignore tags etc checkInsertNewline( "<!--\n foo^\n--->\n", "<!--\n foo\n ^\n--->\n"); } public void testIndentationInComments2() throws Exception { // Make sure that inside a comment we ignore tags etc checkInsertNewline( "\n" + "<!--\n" + "<foo><^\n" + "-->\n", "\n" + "<!--\n" + "<foo><\n" + "^\n" + "-->\n"); } public void testSurroundCaret() throws Exception { checkInsertNewline( "\n" + " <item>^</item>\n", "\n" + " <item>\n" + " ^\n" + " </item>\n"); } public void testSurroundCaret2() throws Exception { // This test combines both surround with and continuing earlier lines (where // it searches for a matching tag) checkInsertNewline( "\n" + " <foo\n" + " name='value'>^</foo>\n", "\n" + " <foo\n" + " name='value'>\n" + " ^\n" + " </foo>\n"); } public void testContinueEarlierLine1() throws Exception { // Here we need to indent to the exact location of an earlier line checkInsertNewline( "\n" + " <foo\n" + " name='value'/>^\n", "\n" + " <foo\n" + " name='value'/>\n" + " ^\n"); } public void testContinueEarlierLine2() throws Exception { checkInsertNewline( "\n" + " <foo\n" + " name='value'></foo>^\n", "\n" + " <foo\n" + " name='value'></foo>\n" + " ^\n"); // Note that // <foo // > // </foo> // does not require special handling, this only happens with the closing tag is sharing // a line. } public void testContinueEarlierLine3() throws Exception { // Make sure the code to look up the corresponding opening tag works properly checkInsertNewline( "\n" + " <foo\n" + " name='value'><bar></bar><baz/></foo>^\n", "\n" + " <foo\n" + " name='value'><bar></bar><baz/></foo>\n" + " ^\n"); } public void testContinueEarlierLine4() throws Exception { checkInsertNewline( " <Button\n" + " android:id=\"@+id/button1\"\n" + " android:layout_width=\"wrap_content\"\n" + " android:layout_height=\"wrap_content\"\n" + " android:text=\"Button\" >^\n" + " </Button>\n", " <Button\n" + " android:id=\"@+id/button1\"\n" + " android:layout_width=\"wrap_content\"\n" + " android:layout_height=\"wrap_content\"\n" + " android:text=\"Button\" >\n" + " ^\n" + " </Button>\n"); } public void testIndent() throws Exception { checkInsertNewline( " <Button\n" + " attr=\"value\"></Button>^\n", " <Button\n" + " attr=\"value\"></Button>\n" + " ^\n" + ""); } public void testLineBeginning1() throws Exception { // Test that if you insert on a blank line, we just add a newline and indent checkInsertNewline( "<foo>\n" + "^\n" + "</foo>", "<foo>\n" + "\n" + " ^\n" + "</foo>"); } public void testLineBeginning2() throws Exception { // Test that if you insert with the caret on the beginning of a line that has // content, we insert an indent correctly checkInsertNewline( "<foo>\n" + "^ <bar/>\n" + "</foo>", "<foo>\n" + "\n" + " ^<bar/>\n" + "</foo>"); } public void testLineBeginning3() throws Exception { checkInsertNewline( "<foo>\n" + " <bar>\n" + "^\n" + " <baz/>\n" + " </bar>\n" + "</foo>", "<foo>\n" + " <bar>\n" + "\n" + " ^\n" + " <baz/>\n" + " </bar>\n" + "</foo>"); } public void testLineBeginning4() throws Exception { // Test that if you insert with the caret on the beginning of a line that has // content, we insert an indent correctly checkInsertNewline( "<foo>\n" + " <bar>\n" + "\n" + "^ <baz/>\n" + " </bar>\n" + "</foo>", "<foo>\n" + " <bar>\n" + "\n" + "\n" + " ^<baz/>\n" + " </bar>\n" + "</foo>"); } public void testLineBeginning5() throws Exception { // Test that if you insert with the caret on the beginning of a line that has // content, we insert an indent correctly checkInsertNewline( "<foo>\n" + " <bar>\n" + "\n" + " ^ <baz/>\n" + " </bar>\n" + "</foo>", "<foo>\n" + " <bar>\n" + "\n" + " \n" + " ^<baz/>\n" + " </bar>\n" + "</foo>"); } public void testLineBeginning6() throws Exception { checkInsertNewline( " <foo>\n" + " <bar>\n" + " \n" + " \n" + "^ </bar>\n" + " </foo>\n", " <foo>\n" + " <bar>\n" + " \n" + " \n" + "\n" + " ^</bar>\n" + " </foo>\n"); } public void testBlankContinuation() throws Exception { checkInsertNewline( " <foo>\n" + " <bar>\n" + " ^\n" + " </bar>\n" + " </foo>\n" + "", " <foo>\n" + " <bar>\n" + " \n" + " ^\n" + " </bar>\n" + " </foo>\n" + ""); } public void testIssue22332a() throws Exception { checkInsertNewline( "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<resources>\n" + "\n" + " <string name=\"hello\">Hello World, MainActivity!</string>^\n" + "\n" + "</resources>", "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<resources>\n" + "\n" + " <string name=\"hello\">Hello World, MainActivity!</string>\n" + " ^\n" + "\n" + "</resources>"); } public void testIssue22332b() throws Exception { checkInsertNewline( "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<resources>\n" + "\n" + " <string name=\"hello\">Hello World, MainActivity!</string>\n" + " ^\n" + "\n" + "</resources>", "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<resources>\n" + "\n" + " <string name=\"hello\">Hello World, MainActivity!</string>\n" + " \n" + " ^\n" + "\n" + "</resources>"); } /** * To test * When you press / after < I should reindent the current line. For example, * if you have * <foo> * <bar> * </ the moment you've typed this we should dedent it back out * When you press newline we need to reindent */ /** Subclassed for test usage since constructor is protected */ private class TestDocumentCommand extends DocumentCommand { public TestDocumentCommand() { } } }