aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/AndroidXmlAutoEditStrategy.java
diff options
context:
space:
mode:
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.java460
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;
- }
-}