blob: 7a60cb05dd07a33f6c01c3e13fdcd18c8aa5126b [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.intellij.codeInsight.completion;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.impl.source.xml.XmlAttributeImpl;
import com.intellij.psi.impl.source.xml.XmlAttributeReference;
import com.intellij.psi.meta.PsiPresentableMetaData;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ProcessingContext;
import com.intellij.xml.NamespaceAwareXmlAttributeDescriptor;
import com.intellij.xml.XmlAttributeDescriptor;
import com.intellij.xml.XmlElementDescriptor;
import com.intellij.xml.XmlExtension;
import com.intellij.xml.util.HtmlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.codeInsight.completion.CompletionInitializationContext.DUMMY_IDENTIFIER_TRIMMED;
public class XmlAttributeReferenceCompletionProvider extends CompletionProvider<CompletionParameters> {
private static final Logger LOG = Logger.getInstance(XmlAttributeReferenceCompletionProvider.class);
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull CompletionResultSet result) {
PsiReference reference = parameters.getPosition().getContainingFile().findReferenceAt(parameters.getOffset());
if (reference instanceof XmlAttributeReference) {
addAttributeReferenceCompletionVariants((XmlAttributeReference)reference, result, null);
}
}
public static void addAttributeReferenceCompletionVariants(XmlAttributeReference reference, CompletionResultSet result,
@Nullable InsertHandler<LookupElement> replacementInsertHandler) {
final XmlTag declarationTag = reference.getElement().getParent();
LOG.assertTrue(declarationTag.isValid());
final XmlElementDescriptor parentDescriptor = declarationTag.getDescriptor();
if (parentDescriptor != null) {
final XmlAttribute[] attributes = declarationTag.getAttributes();
XmlAttributeDescriptor[] descriptors = parentDescriptor.getAttributesDescriptors(declarationTag);
descriptors = HtmlUtil.appendHtmlSpecificAttributeCompletions(declarationTag, descriptors, reference.getElement());
addVariants(result, attributes, descriptors, reference.getElement(), replacementInsertHandler);
}
}
private static void addVariants(final CompletionResultSet result,
final XmlAttribute[] attributes,
final XmlAttributeDescriptor[] descriptors,
XmlAttribute attribute,
@Nullable InsertHandler<LookupElement> replacementInsertHandler) {
final XmlTag tag = attribute.getParent();
final PsiFile file = tag.getContainingFile();
final XmlExtension extension = XmlExtension.getExtension(file);
final String prefix = attribute.getName().contains(":") && ((XmlAttributeImpl) attribute).getRealLocalName().length() > 0
? attribute.getNamespacePrefix() + ":"
: null;
CompletionData
completionData = CompletionUtil.getCompletionDataByElement(attribute, attribute.getContainingFile().getOriginalFile());
boolean caseSensitive = !(completionData instanceof HtmlCompletionData) || ((HtmlCompletionData)completionData).isCaseSensitive();
for (XmlAttributeDescriptor descriptor : descriptors) {
if (isValidVariant(attribute, descriptor, attributes, extension)) {
String name = descriptor.getName(tag);
InsertHandler<LookupElement> insertHandler = XmlAttributeInsertHandler.INSTANCE;
if (replacementInsertHandler != null) {
insertHandler = replacementInsertHandler;
}
else if (descriptor instanceof NamespaceAwareXmlAttributeDescriptor) {
final String namespace = ((NamespaceAwareXmlAttributeDescriptor)descriptor).getNamespace(tag);
if (file instanceof XmlFile &&
namespace != null &&
namespace.length() > 0 &&
!name.contains(":") &&
tag.getPrefixByNamespace(namespace) == null) {
insertHandler = new XmlAttributeInsertHandler(namespace);
}
}
if (prefix == null || name.startsWith(prefix)) {
if (prefix != null && name.length() > prefix.length()) {
name = descriptor.getName(tag).substring(prefix.length());
}
LookupElementBuilder element = LookupElementBuilder.create(name);
if (descriptor instanceof PsiPresentableMetaData) {
element = element.withIcon(((PsiPresentableMetaData)descriptor).getIcon());
}
final int separator = name.indexOf(':');
if (separator > 0) {
element = element.withLookupString(name.substring(separator + 1));
}
element = element
.withCaseSensitivity(caseSensitive)
.withInsertHandler(insertHandler);
result.addElement(
descriptor.isRequired() ? PrioritizedLookupElement.withPriority(element.appendTailText("(required)", true), 100) :
HtmlUtil.isOwnHtmlAttribute(descriptor) ? PrioritizedLookupElement.withPriority(element, 50) : element);
}
}
}
}
private static boolean isValidVariant(XmlAttribute attribute,
@NotNull XmlAttributeDescriptor descriptor,
final XmlAttribute[] attributes,
final XmlExtension extension) {
if (extension.isIndirectSyntax(descriptor)) return false;
String descriptorName = descriptor.getName(attribute.getParent());
if (descriptorName == null) {
LOG.error("Null descriptor name for " + descriptor + " " + descriptor.getClass() + " ");
return false;
}
for (final XmlAttribute otherAttr : attributes) {
if (otherAttr != attribute && otherAttr.getName().equals(descriptorName)) return false;
}
return !descriptorName.contains(DUMMY_IDENTIFIER_TRIMMED);
}
}