diff options
author | Tor Norbye <tnorbye@google.com> | 2011-08-02 14:13:47 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2011-08-03 11:51:10 -0700 |
commit | 83d04640bf245bb9ce108cef01e188761bc905f8 (patch) | |
tree | c48105226804977c718d78bc190060b48e9f350a /eclipse/plugins/com.android.ide.eclipse.tests | |
parent | e6836b9ded9013725500aa2c9cd113545628a693 (diff) | |
download | sdk-83d04640bf245bb9ce108cef01e188761bc905f8.zip sdk-83d04640bf245bb9ce108cef01e188761bc905f8.tar.gz sdk-83d04640bf245bb9ce108cef01e188761bc905f8.tar.bz2 |
Improve smart-indent handling in XML files
This changeset adds a new "auto edit strategy" for Android XML files,
which replaces the default XML auto-indenter (which just copied the
indentation of the previous line.)
The new indenter instead uses the lexical information to indent based
on the tag and bracket balance.
For example, if the | represents the caret position, then the
following shows how the edits are transformed:
<foo name="value"/>| => <foo name="value"/>
|
<foo name="value">| => <foo name="value">
|
<foo <foo
attr1 = "value1"|> => attr1 = "value1"
|>
<foo <foo
attr1 = "value1"/>| => attr1 = "value1"/>
|
It can also modify the text after the caret, as in the following
transformation:
<foo>|</foo> => <foo>
|
</foo>
See the unit test for more cases.
Change-Id: Ia99ecf6573ff4a9473970aa2fd481d2228ddf45d
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.tests')
2 files changed, 297 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategyTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategyTest.java new file mode 100644 index 0000000..ec0e73f --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategyTest.java @@ -0,0 +1,294 @@ +/* + * 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; + +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" + + ""); + } + + /** + * 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() { + } + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/edithandling.xml b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/edithandling.xml new file mode 100644 index 0000000..0488fd6 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/testdata/edithandling.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"> +</LinearLayout> |