aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/CompletionProposal.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/CompletionProposal.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/CompletionProposal.java229
1 files changed, 229 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/CompletionProposal.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/CompletionProposal.java
new file mode 100644
index 000000000..2d4467799
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/CompletionProposal.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2012 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 com.android.SdkConstants.XMLNS;
+
+import com.android.ide.common.api.IAttributeInfo;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
+import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.utils.XmlUtils;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.ISourceRange;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Just like {@link org.eclipse.jface.text.contentassist.CompletionProposal},
+ * but computes the documentation string lazily since they are typically only
+ * displayed for a small subset (the currently focused item) of the available
+ * proposals, and producing the strings requires some computation.
+ * <p>
+ * It also attempts to compute documentation for value strings like
+ * ?android:attr/dividerHeight.
+ * <p>
+ * TODO: Enhance this to compute documentation for additional values, such as
+ * the various enum values (which are available in the attrs.xml file, but not
+ * in the AttributeInfo objects for each enum value). To do this, I should
+ * basically keep around the maps computed by the attrs.xml parser.
+ */
+class CompletionProposal implements ICompletionProposal {
+ private static final Pattern ATTRIBUTE_PATTERN =
+ Pattern.compile("[@?]android:attr/(.*)"); //$NON-NLS-1$
+
+ private final AndroidContentAssist mAssist;
+ private final Object mChoice;
+ private final int mCursorPosition;
+ private int mReplacementOffset;
+ private final int mReplacementLength;
+ private final String mReplacementString;
+ private final Image mImage;
+ private final String mDisplayString;
+ private final IContextInformation mContextInformation;
+ private final String mNsPrefix;
+ private final String mNsUri;
+ private String mAdditionalProposalInfo;
+
+ CompletionProposal(AndroidContentAssist assist,
+ Object choice, String replacementString, int replacementOffset,
+ int replacementLength, int cursorPosition, Image image, String displayString,
+ IContextInformation contextInformation, String additionalProposalInfo,
+ String nsPrefix, String nsUri) {
+ assert replacementString != null;
+ assert replacementOffset >= 0;
+ assert replacementLength >= 0;
+ assert cursorPosition >= 0;
+
+ mAssist = assist;
+ mChoice = choice;
+ mCursorPosition = cursorPosition;
+ mReplacementOffset = replacementOffset;
+ mReplacementLength = replacementLength;
+ mReplacementString = replacementString;
+ mImage = image;
+ mDisplayString = displayString;
+ mContextInformation = contextInformation;
+ mAdditionalProposalInfo = additionalProposalInfo;
+ mNsPrefix = nsPrefix;
+ mNsUri = nsUri;
+ }
+
+ @Override
+ public Point getSelection(IDocument document) {
+ return new Point(mReplacementOffset + mCursorPosition, 0);
+ }
+
+ @Override
+ public IContextInformation getContextInformation() {
+ return mContextInformation;
+ }
+
+ @Override
+ public Image getImage() {
+ return mImage;
+ }
+
+ @Override
+ public String getDisplayString() {
+ if (mDisplayString != null) {
+ return mDisplayString;
+ }
+ return mReplacementString;
+ }
+
+ @Override
+ public String getAdditionalProposalInfo() {
+ if (mAdditionalProposalInfo == null) {
+ if (mChoice instanceof ElementDescriptor) {
+ String tooltip = ((ElementDescriptor)mChoice).getTooltip();
+ mAdditionalProposalInfo = DescriptorsUtils.formatTooltip(tooltip);
+ } else if (mChoice instanceof TextAttributeDescriptor) {
+ mAdditionalProposalInfo = ((TextAttributeDescriptor) mChoice).getTooltip();
+ } else if (mChoice instanceof String) {
+ // Try to produce it lazily for strings like @android
+ String value = (String) mChoice;
+ Matcher matcher = ATTRIBUTE_PATTERN.matcher(value);
+ if (matcher.matches()) {
+ String attrName = matcher.group(1);
+ AndroidTargetData data = mAssist.getEditor().getTargetData();
+ if (data != null) {
+ IDescriptorProvider descriptorProvider =
+ data.getDescriptorProvider(mAssist.getRootDescriptorId());
+ if (descriptorProvider != null) {
+ ElementDescriptor[] rootElementDescriptors =
+ descriptorProvider.getRootElementDescriptors();
+ for (ElementDescriptor elementDesc : rootElementDescriptors) {
+ for (AttributeDescriptor desc : elementDesc.getAttributes()) {
+ String name = desc.getXmlLocalName();
+ if (attrName.equals(name)) {
+ IAttributeInfo attributeInfo = desc.getAttributeInfo();
+ if (attributeInfo != null) {
+ return attributeInfo.getJavaDoc();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+ } else if (mChoice instanceof IType) {
+ IType type = (IType) mChoice;
+ try {
+ ISourceRange javadocRange = type.getJavadocRange();
+ if (javadocRange != null && javadocRange.getLength() > 0) {
+ ISourceRange sourceRange = type.getSourceRange();
+ if (sourceRange != null) {
+ String source = type.getSource();
+ int start = javadocRange.getOffset() - sourceRange.getOffset();
+ int length = javadocRange.getLength();
+ String doc = source.substring(start, start + length);
+ return doc;
+ }
+ }
+ return type.getAttachedJavadoc(new NullProgressMonitor());
+ } catch (JavaModelException e) {
+ AdtPlugin.log(e, null);
+ }
+ }
+ }
+
+ return mAdditionalProposalInfo;
+ }
+
+ @Override
+ public void apply(IDocument document) {
+ try {
+ Position position = new Position(mReplacementOffset);
+ document.addPosition(position);
+
+ // Ensure that the namespace is defined in the document
+ String prefix = mNsPrefix;
+ if (mNsUri != null && prefix != null) {
+ Document dom = DomUtilities.getDocument(mAssist.getEditor());
+ if (dom != null) {
+ Element root = dom.getDocumentElement();
+ if (root != null) {
+ // Is the namespace already defined?
+ boolean found = false;
+ NamedNodeMap attributes = root.getAttributes();
+ for (int i = 0, n = attributes.getLength(); i < n; i++) {
+ Attr attribute = (Attr) attributes.item(i);
+ String name = attribute.getName();
+ if (name.startsWith(XMLNS) && mNsUri.equals(attribute.getValue())) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ if (prefix.endsWith(":")) { //$NON-NLS-1$
+ prefix = prefix.substring(0, prefix.length() - 1);
+ }
+ XmlUtils.lookupNamespacePrefix(root, mNsUri, prefix, true);
+ }
+ }
+ }
+ }
+
+ mReplacementOffset = position.getOffset();
+ document.removePosition(position);
+ document.replace(mReplacementOffset, mReplacementLength, mReplacementString);
+ } catch (BadLocationException x) {
+ // ignore
+ }
+ }
+} \ No newline at end of file