diff options
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.java | 229 |
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 |