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 | |
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')
6 files changed, 727 insertions, 2 deletions
diff --git a/eclipse/dictionary.txt b/eclipse/dictionary.txt index 002c62b..e966f98 100644 --- a/eclipse/dictionary.txt +++ b/eclipse/dictionary.txt @@ -191,11 +191,13 @@ regexp regexps registry reindent +reindenting remap reparse reparses rescales residual +resilient resizability resizable risky @@ -226,8 +228,10 @@ stretchiness struct styleable styleables +subclassed subclassing submenu +subregion supertype syncs synthetically diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java index 657c186..1db8fa0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java @@ -23,6 +23,7 @@ import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.texteditor.ITextEditor; /** Utility methods for ADT */ @@ -132,6 +133,25 @@ public class AdtUtils { } /** + * Returns the current text editor (the currently visible and active editor), or null + * if not found. + * + * @return the current text editor, or null + */ + public static ITextEditor getActiveTextEditor() { + IEditorPart editor = getActiveEditor(); + if (editor != null) { + if (editor instanceof ITextEditor) { + return (ITextEditor) editor; + } else { + return (ITextEditor) editor.getAdapter(ITextEditor.class); + } + } + + return null; + } + + /** * Returns the file for the current editor, if any. * * @return the file for the current editor, or null if none diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidSourceViewerConfig.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidSourceViewerConfig.java index bfe7a41..6537be1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidSourceViewerConfig.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidSourceViewerConfig.java @@ -20,6 +20,7 @@ package com.android.ide.eclipse.adt.internal.editors; import com.android.ide.eclipse.adt.internal.editors.formatting.AndroidXmlFormatter; import com.android.ide.eclipse.adt.internal.editors.formatting.AndroidXmlFormattingStrategy; +import org.eclipse.jface.text.DefaultAutoIndentStrategy; import org.eclipse.jface.text.IAutoEditStrategy; import org.eclipse.jface.text.ITextHover; import org.eclipse.jface.text.ITextViewer; @@ -69,6 +70,7 @@ public class AndroidSourceViewerConfig extends StructuredTextViewerConfiguration * processors are applicable * @return IContentAssistProcessors or null if should not be supported */ + @SuppressWarnings("deprecation") // XMLContentAssistProcessor @Override protected IContentAssistProcessor[] getContentAssistProcessors( ISourceViewer sourceViewer, String partitionType) { @@ -106,11 +108,24 @@ public class AndroidSourceViewerConfig extends StructuredTextViewerConfiguration return super.getTextHover(sourceViewer, contentType); } + @SuppressWarnings("deprecation") @Override public IAutoEditStrategy[] getAutoEditStrategies( ISourceViewer sourceViewer, String contentType) { - // TODO auto edit strategies for android xml - return super.getAutoEditStrategies(sourceViewer, contentType); + IAutoEditStrategy[] strategies = super.getAutoEditStrategies(sourceViewer, contentType); + List<IAutoEditStrategy> s = new ArrayList<IAutoEditStrategy>(strategies.length + 1); + s.add(new AndroidXmlAutoEditStrategy()); + + // Add other registered strategies, except the builtin indentation strategy which is + // now handled by the above AndroidXmlAutoEditStrategy + for (IAutoEditStrategy strategy : strategies) { + if (strategy instanceof DefaultAutoIndentStrategy) { + continue; + } + s.add(strategy); + } + + return s.toArray(new IAutoEditStrategy[s.size()]); } @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java new file mode 100644 index 0000000..914a5f4 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java @@ -0,0 +1,389 @@ +/* + * 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 static org.eclipse.wst.xml.core.internal.regions.DOMRegionContext.XML_EMPTY_TAG_CLOSE; +import static org.eclipse.wst.xml.core.internal.regions.DOMRegionContext.XML_END_TAG_OPEN; +import static org.eclipse.wst.xml.core.internal.regions.DOMRegionContext.XML_TAG_CLOSE; +import static org.eclipse.wst.xml.core.internal.regions.DOMRegionContext.XML_TAG_NAME; +import static org.eclipse.wst.xml.core.internal.regions.DOMRegionContext.XML_TAG_OPEN; + +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.AdtUtils; +import com.android.ide.eclipse.adt.internal.editors.formatting.XmlFormatPreferences; +import com.android.util.Pair; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.DocumentCommand; +import org.eclipse.jface.text.IAutoEditStrategy; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.TextUtilities; +import org.eclipse.ui.texteditor.ITextEditor; +import org.eclipse.ui.texteditor.ITextEditorExtension3; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.provisional.IModelManager; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; + +/** + * Edit strategy for Android XML files. It attempts a number of edit + * enhancements: + * <ul> + * <li> Auto indentation. The default XML indentation scheme is to just copy the + * indentation of the previous line. This edit strategy improves on that situation + * by considering the tag and bracket balance on the current line and using it + * to determine whether the next line should be indented or use the same + * indentation as the parent, or even the indentation of an earlier line + * (when for example the current line closes an element which was started on an + * earlier line.) + * <li> Newline handling. In addition to indenting, it can also adjust the following text + * appropriately when a newline is inserted. For example, it will reformat + * the following (where | represents the caret position): + * <pre> + * {@code <item name="a">|</item>} + * </pre> + * into + * <pre> + * {@code <item name="a">} + * | + * {@code </item>} + * </pre> + * </ul> + * In the future we might consider other editing enhancements here as well, such as + * refining the comment handling, or reindenting when you type the / of a closing tag, + * or even making the bracket matcher more resilient. + */ +@SuppressWarnings("restriction") // XML model +public class AndroidXmlAutoEditStrategy implements IAutoEditStrategy { + + public void customizeDocumentCommand(IDocument document, DocumentCommand c) { + if (!isSmartInsertMode()) { + return; + } + + if (!(document instanceof IStructuredDocument)) { + // This shouldn't happen unless this strategy is used on an invalid document + return; + } + IStructuredDocument doc = (IStructuredDocument) document; + + // Handle newlines/indentation + if (c.length == 0 && c.text != null + && TextUtilities.endsWith(doc.getLegalLineDelimiters(), c.text) != -1) { + + IModelManager modelManager = StructuredModelManager.getModelManager(); + IStructuredModel model = modelManager.getModelForRead(doc); + if (model != null) { + try { + final int offset = c.offset; + int lineInfoOffset = (offset == doc.getLength() ? offset - 1 : offset); + IRegion info = doc.getLineInformationOfOffset(lineInfoOffset); + int lineStart = info.getOffset(); + int textStart = findTextStart(doc, lineStart, offset); + + IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(textStart); + if (region != null && region.getType() == XML_TAG_NAME) { + Pair<Integer,Integer> balance = getBalance(doc, textStart, offset); + int tagBalance = balance.getFirst(); + int bracketBalance = balance.getSecond(); + + String lineIndent = ""; //$NON-NLS-1$ + if (textStart > lineStart) { + lineIndent = doc.get(lineStart, textStart - lineStart); + } + + // We only care if tag or bracket balance is greater than 0; + // we never *dedent* on negative balances + boolean addIndent = false; + if (bracketBalance < 0) { + // Handle + // <foo + // ></foo>^ + // and + // <foo + // />^ + ITextRegion left = getRegionAt(doc, offset, true /*biasLeft*/); + if (left != null + && (left.getType().equals(XML_TAG_CLOSE) + || left.getType().equals(XML_EMPTY_TAG_CLOSE))) { + + // Find the corresponding open tag... + // The org.eclipse.wst.xml.ui.gotoMatchingTag frequently + // doesn't work, it just says "No matching brace found" + // (or I would use that here). + + int targetBalance = 0; + ITextRegion right = getRegionAt(doc, offset, false /*biasLeft*/); + if (right != null && right.getType().equals(XML_END_TAG_OPEN)) { + targetBalance = -1; + } + int openTag = findTagBackwards(doc, offset, targetBalance); + if (openTag != -1) { + // Look up the indentation of the given line + lineIndent = AndroidXmlEditor.getIndentAtOffset(doc, openTag); + } + } + } else if (tagBalance > 0 || bracketBalance > 0) { + // Add indentation + addIndent = true; + } + + StringBuilder sb = new StringBuilder(c.text); + sb.append(lineIndent); + String oneIndentUnit = XmlFormatPreferences.create().getOneIndentUnit(); + if (addIndent) { + sb.append(oneIndentUnit); + } + + // Handle + // <foo>^</foo> + // turning into + // <foo> + // ^ + // </foo> + ITextRegion left = getRegionAt(doc, offset, true /*biasLeft*/); + ITextRegion right = getRegionAt(doc, offset, false /*biasLeft*/); + if (left != null && right != null + && left.getType().equals(XML_TAG_CLOSE) + && right.getType().equals(XML_END_TAG_OPEN)) { + // Move end tag + if (tagBalance > 0 && bracketBalance < 0) { + sb.append(oneIndentUnit); + } + c.caretOffset = offset + sb.length(); + c.shiftsCaret = false; + sb.append(TextUtilities.getDefaultLineDelimiter(doc)); + sb.append(lineIndent); + } + c.text = sb.toString(); + } else { + copyPreviousLineIndentation(doc, c); + } + } catch (BadLocationException e) { + AdtPlugin.log(e, null); + } finally { + model.releaseFromRead(); + } + } + } + } + + /** + * Finds the first non-whitespace character on the given line + * + * @param document the document to search + * @param lineStart the offset of the beginning of the line + * @param lineEnd the offset of the end of the line, or the maximum position on the + * line to search + * @return the offset of the first non whitespace character, or the maximum position, + * whichever is smallest + * @throws BadLocationException if the offsets are invalid + */ + protected int findTextStart(IDocument document, int lineStart, int lineEnd) + throws BadLocationException { + for (int offset = lineStart; offset < lineEnd; offset++) { + char c = document.getChar(offset); + if (c != ' ' && c != '\t') { + return offset; + } + } + + return lineEnd; + } + + /** + * Indent the new line the same way as the current line. + * + * @param doc the document to indent in + * @param command the document command to customize + * @throws BadLocationException if the offsets are invalid + */ + private void copyPreviousLineIndentation(IDocument doc, DocumentCommand command) + throws BadLocationException { + + if (command.offset == -1 || doc.getLength() == 0) { + return; + } + + int offset = Math.min(command.offset, doc.getLength() - 1); + IRegion info = doc.getLineInformationOfOffset(offset); + int lineStart = info.getOffset(); + int textStart = findTextStart(doc, lineStart, command.offset); + + StringBuilder sb = new StringBuilder(command.text); + if (textStart > lineStart) { + sb.append(doc.get(lineStart, textStart - lineStart)); + } + + command.text = sb.toString(); + } + + + /** + * Returns the subregion at the given offset, with a bias to the left or a bias to the + * right. In other words, if | represents the caret position, in the XML + * {@code <foo>|</bar>} then the subregion with bias left is the closing {@code >} and + * the subregion with bias right is the opening {@code </}. + * + * @param doc the document + * @param offset the offset in the document + * @param biasLeft whether we should look at the token on the left or on the right + * @return the subregion at the given offset, or null if not found + */ + private ITextRegion getRegionAt(IStructuredDocument doc, int offset, + boolean biasLeft) { + if (biasLeft) { + offset--; + } + IStructuredDocumentRegion region = + doc.getRegionAtCharacterOffset(offset); + if (region != null) { + return region.getRegionAtCharacterOffset(offset); + } + + return null; + } + + /** + * Returns a pair of (tag-balance,bracket-balance) for the range textStart to offset. + * + * @param doc the document + * @param start the offset of the starting character (inclusive) + * @param end the offset of the ending character (exclusive) + * @return the balance of tags and brackets + */ + private Pair<Integer, Integer> getBalance(IStructuredDocument doc, + int start, int end) { + // Balance of open and closing tags + // <foo></foo> has tagBalance = 0, <foo> has tagBalance = 1 + int tagBalance = 0; + // Balance of open and closing brackets + // <foo attr1="value1"> has bracketBalance = 1, <foo has bracketBalance = 1 + int bracketBalance = 0; + IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(start); + + if (region != null) { + boolean inOpenTag = true; + while (region != null && region.getStartOffset() < end) { + int regionStart = region.getStartOffset(); + ITextRegionList subRegions = region.getRegions(); + for (int i = 0, n = subRegions.size(); i < n; i++) { + ITextRegion subRegion = subRegions.get(i); + int subRegionStart = regionStart + subRegion.getStart(); + int subRegionEnd = regionStart + subRegion.getEnd(); + if (subRegionEnd < start || subRegionStart >= end) { + continue; + } + String type = subRegion.getType(); + + if (XML_TAG_OPEN.equals(type)) { + bracketBalance++; + inOpenTag = true; + } else if (XML_TAG_CLOSE.equals(type)) { + bracketBalance--; + if (inOpenTag) { + tagBalance++; + } else { + tagBalance--; + } + } else if (XML_END_TAG_OPEN.equals(type)) { + bracketBalance++; + inOpenTag = false; + } else if (XML_EMPTY_TAG_CLOSE.equals(type)) { + bracketBalance--; + } + } + + region = region.getNext(); + } + } + + return Pair.of(tagBalance, bracketBalance); + } + + /** + * Finds the corresponding open tag by searching backwards until the tag balance + * reaches a given target. + * + * @param doc the document + * @param offset the ending offset (where the search begins searching backwards from) + * @param targetTagBalance the balance to end the search at + */ + private int findTagBackwards(IStructuredDocument doc, int offset, int targetTagBalance) { + // Balance of open and closing tags + int tagBalance = 0; + // Balance of open and closing brackets + IStructuredDocumentRegion region = + doc.getRegionAtCharacterOffset(offset); + if (region != null) { + boolean inEmptyTag = true; + + while (region != null) { + int regionStart = region.getStartOffset(); + ITextRegionList subRegions = region.getRegions(); + for (int i = subRegions.size() - 1; i >= 0; i--) { + ITextRegion subRegion = subRegions.get(i); + int subRegionStart = regionStart + subRegion.getStart(); + if (subRegionStart >= offset) { + continue; + } + String type = subRegion.getType(); + + // Iterate backwards and keep track of the tag balance such that + // we can find the corresponding opening tag + + if (XML_TAG_OPEN.equals(type)) { + if (!inEmptyTag) { + tagBalance--; + } + if (tagBalance == targetTagBalance) { + return subRegionStart; + } + } else if (XML_END_TAG_OPEN.equals(type)) { + tagBalance++; + } else if (XML_EMPTY_TAG_CLOSE.equals(type)) { + inEmptyTag = true; + } else if (XML_TAG_CLOSE.equals(type)) { + inEmptyTag = false; + } + } + + region = region.getPrevious(); + } + } + + return -1; + } + + /** + * Determine if we're in smart insert mode (if so, don't do any edit magic) + * + * @return true if the editor is in smart mode (or if it's an unknown editor type) + */ + private boolean isSmartInsertMode() { + ITextEditor textEditor = AdtUtils.getActiveTextEditor(); + if (textEditor instanceof ITextEditorExtension3) { + ITextEditorExtension3 editor = (ITextEditorExtension3) textEditor; + return editor.getInsertMode() == ITextEditorExtension3.SMART_INSERT; + } + + return true; + } +} 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> |