diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java | 460 |
1 files changed, 0 insertions, 460 deletions
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 deleted file mode 100644 index 8a078efc2..000000000 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * 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_CONTENT; -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.EclipseXmlFormatPreferences; -import com.android.utils.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 { - - @Override - 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 lineStart = findLineStart(doc, offset); - int textStart = findTextStart(doc, lineStart, offset); - - IStructuredDocumentRegion region = doc.getRegionAtCharacterOffset(textStart); - if (region != null && region.getType().equals(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 = AndroidXmlCharacterMatcher.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 = EclipseXmlFormatPreferences.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 if (region != null && region.getType().equals(XML_CONTENT)) { - // Indenting in text content. If you're in the middle of editing - // text, just copy the current line indentation. - // However, if you're editing in leading whitespace (e.g. you press - // newline on a blank line following say an element) then figure - // out the indentation as if the newline had been pressed at the - // end of the element, and insert that amount of indentation. - // In this case we need to also make sure to subtract any existing - // whitespace on the current line such that if we have - // - // <foo> - // ^ <bar/> - // </foo> - // - // you end up with - // - // <foo> - // - // ^<bar/> - // </foo> - // - String text = region.getText(); - int regionStart = region.getStartOffset(); - int delta = offset - regionStart; - boolean inWhitespacePrefix = true; - for (int i = 0, n = Math.min(delta, text.length()); i < n; i++) { - char ch = text.charAt(i); - if (!Character.isWhitespace(ch)) { - inWhitespacePrefix = false; - break; - } - } - if (inWhitespacePrefix) { - IStructuredDocumentRegion previous = region.getPrevious(); - if (previous != null && previous.getType() == XML_TAG_NAME) { - ITextRegionList subRegions = previous.getRegions(); - ITextRegion last = subRegions.get(subRegions.size() - 1); - if (last.getType() == XML_TAG_CLOSE || - last.getType() == XML_EMPTY_TAG_CLOSE) { - // See if the last tag was a closing tag - boolean wasClose = last.getType() == XML_EMPTY_TAG_CLOSE; - if (!wasClose) { - // Search backwards to see if the XML_TAG_CLOSE - // is the end of an </endtag> - for (int i = subRegions.size() - 2; i >= 0; i--) { - ITextRegion current = subRegions.get(i); - String type = current.getType(); - if (type != XML_TAG_NAME) { - wasClose = type == XML_END_TAG_OPEN; - break; - } - } - } - - int begin = AndroidXmlCharacterMatcher.findTagBackwards(doc, - previous.getStartOffset() + last.getStart(), 0); - int prevLineStart = findLineStart(doc, begin); - int prevTextStart = findTextStart(doc, prevLineStart, begin); - - String lineIndent = ""; //$NON-NLS-1$ - if (prevTextStart > prevLineStart) { - lineIndent = doc.get(prevLineStart, - prevTextStart - prevLineStart); - } - StringBuilder sb = new StringBuilder(c.text); - sb.append(lineIndent); - - // See if there is whitespace on the insert line that - // we should also remove - for (int i = delta, n = text.length(); i < n; i++) { - char ch = text.charAt(i); - if (ch == ' ') { - c.length++; - } else { - break; - } - } - - boolean addIndent = (last.getType() == XML_TAG_CLOSE) - && !wasClose; - - // Is there just whitespace left of this text tag - // until we reach an end tag? - boolean whitespaceToEndTag = true; - for (int i = delta; i < text.length(); i++) { - char ch = text.charAt(i); - if (ch == '\n' || !Character.isWhitespace(ch)) { - whitespaceToEndTag = false; - break; - } - } - if (whitespaceToEndTag) { - IStructuredDocumentRegion next = region.getNext(); - if (next != null && next.getType() == XML_TAG_NAME) { - String nextType = next.getRegions().get(0).getType(); - if (nextType == XML_END_TAG_OPEN) { - addIndent = false; - } - } - } - - if (addIndent) { - sb.append(EclipseXmlFormatPreferences.create() - .getOneIndentUnit()); - } - c.text = sb.toString(); - - return; - } - } - } - copyPreviousLineIndentation(doc, c); - } else { - copyPreviousLineIndentation(doc, c); - } - } catch (BadLocationException e) { - AdtPlugin.log(e, null); - } finally { - model.releaseFromRead(); - } - } - } - } - - /** - * Returns the offset of the start of the line (which might be whitespace) - * - * @param document the document - * @param offset an offset for a character anywhere on the line - * @return the offset of the first character on the line - * @throws BadLocationException if the offset is invalid - */ - public static int findLineStart(IDocument document, int offset) throws BadLocationException { - offset = Math.max(0, Math.min(offset, document.getLength() - 1)); - IRegion info = document.getLineInformationOfOffset(offset); - return info.getOffset(); - } - - /** - * 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 - */ - public static 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 lineStart = findLineStart(doc, command.offset); - 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 static 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 static 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); - } - - /** - * 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 static boolean isSmartInsertMode() { - ITextEditor textEditor = AdtUtils.getActiveTextEditor(); - if (textEditor instanceof ITextEditorExtension3) { - ITextEditorExtension3 editor = (ITextEditorExtension3) textEditor; - return editor.getInsertMode() == ITextEditorExtension3.SMART_INSERT; - } - - return true; - } -} |