blob: b0416407b6b44ec478754d761e8235c7a5426d82 [file] [log] [blame]
/*
* Copyright 2000-2014 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 org.jetbrains.plugins.groovy.lang.completion.weighers;
import com.intellij.codeInsight.completion.CompletionLocation;
import com.intellij.codeInsight.completion.CompletionWeigher;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.PsiTypeLookupItem;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.light.LightElement;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor;
import org.jetbrains.plugins.groovy.lang.completion.GrMainCompletionProvider;
import org.jetbrains.plugins.groovy.lang.completion.GrPropertyForCompletion;
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationImpl;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
import java.util.Set;
/**
* @author Maxim.Medvedev
*/
public class GrKindWeigher extends CompletionWeigher {
private static final Set<String> TRASH_CLASSES = new HashSet<String>(10);
private static final Set<String> PRIORITY_KEYWORDS = ContainerUtil.newHashSet(
PsiKeyword.RETURN, PsiKeyword.INSTANCEOF, "in",
PsiKeyword.PRIVATE, PsiKeyword.PROTECTED, PsiKeyword.PUBLIC, PsiKeyword.STATIC, "def",
PsiKeyword.TRUE, PsiKeyword.FALSE, PsiKeyword.NULL);
static {
TRASH_CLASSES.add(CommonClassNames.JAVA_LANG_CLASS);
TRASH_CLASSES.add(CommonClassNames.JAVA_LANG_OBJECT);
TRASH_CLASSES.add(GroovyCommonClassNames.GROOVY_OBJECT_SUPPORT);
}
@Override
public Comparable weigh(@NotNull LookupElement element, @NotNull CompletionLocation location) {
final PsiElement position = location.getCompletionParameters().getPosition();
if (!(position.getContainingFile() instanceof GroovyFileBase)) return null;
Object o = element.getObject();
if (o instanceof ResolveResult) {
o = ((ResolveResult)o).getElement();
}
final PsiElement parent = position.getParent();
final PsiElement qualifier = parent instanceof GrReferenceElement ? ((GrReferenceElement)parent).getQualifier() : null;
if (qualifier == null) {
if (o instanceof NamedArgumentDescriptor) {
switch (((NamedArgumentDescriptor)o).getPriority()) {
case ALWAYS_ON_TOP:
return NotQualifiedKind.onTop;
case AS_LOCAL_VARIABLE:
return NotQualifiedKind.local;
default:
return NotQualifiedKind.unknown;
}
}
if (o instanceof PsiVariable && !(o instanceof PsiField)) {
return NotQualifiedKind.local;
}
PsiTypeLookupItem item = element.as(PsiTypeLookupItem.CLASS_CONDITION_KEY);
if (item != null && item.getBracketsCount() > 0) {
return NotQualifiedKind.arrayType;
}
if (isPriorityKeyword(o)) return NotQualifiedKind.local;
if (isLightElement(o)) return NotQualifiedKind.unknown;
if (o instanceof PsiClass) {
if (((PsiClass)o).isAnnotationType() && GrMainCompletionProvider.AFTER_AT.accepts(position)) {
final GrAnnotation annotation = PsiTreeUtil.getParentOfType(position, GrAnnotation.class);
if (annotation != null) {
PsiElement annoParent = annotation.getParent();
PsiElement ownerToUse = annoParent instanceof PsiModifierList ? annoParent.getParent() : annoParent;
PsiAnnotation.TargetType[] elementTypeFields = GrAnnotationImpl.getApplicableElementTypeFields(ownerToUse);
if (PsiImplUtil.findApplicableTarget((PsiClass)o, elementTypeFields) != null) {
return NotQualifiedKind.restrictedClass;
}
}
}
if (GrMainCompletionProvider.IN_CATCH_TYPE.accepts(position) &&
InheritanceUtil.isInheritor((PsiClass)o, CommonClassNames.JAVA_LANG_THROWABLE)) {
return NotQualifiedKind.restrictedClass;
}
}
if (o instanceof PsiMember) {
final PsiClass containingClass = ((PsiMember)o).getContainingClass();
if (isAccessor((PsiMember)o)) return NotQualifiedKind.accessor;
if (o instanceof PsiClass && ((PsiClass)o).getContainingClass() == null || o instanceof PsiPackage) return NotQualifiedKind.unknown;
if (o instanceof PsiClass) return NotQualifiedKind.innerClass;
if (PsiTreeUtil.isContextAncestor(containingClass, position, false)) return NotQualifiedKind.currentClassMember;
return NotQualifiedKind.member;
}
return NotQualifiedKind.unknown;
}
else {
if (o instanceof PsiEnumConstant) return QualifiedKind.enumConstant;
if (isLightElement(o)) return QualifiedKind.unknown;
if (o instanceof PsiMember) {
if (isTrashMethod((PsiMember)o)) return QualifiedKind.unknown;
if (isAccessor((PsiMember)o)) return QualifiedKind.accessor;
if (isQualifierClassMember((PsiMember)o, qualifier)) {
return QualifiedKind.currentClassMember;
}
if (o instanceof PsiClass && ((PsiClass)o).getContainingClass() == null || o instanceof PsiPackage) return QualifiedKind.unknown;
if (o instanceof PsiClass) return QualifiedKind.innerClass;
return QualifiedKind.member;
}
return QualifiedKind.unknown;
}
}
private static boolean isPriorityKeyword(Object o) {
//noinspection SuspiciousMethodCalls
return PRIORITY_KEYWORDS.contains(o);
}
private static boolean isLightElement(Object o) {
return o instanceof LightElement && !(o instanceof GrPropertyForCompletion) && !(o instanceof GrAccessorMethod);
}
private static boolean isTrashMethod(PsiMember o) {
final PsiClass containingClass = o.getContainingClass();
return containingClass != null && TRASH_CLASSES.contains(containingClass.getQualifiedName());
}
private static boolean isAccessor(PsiMember member) {
return member instanceof PsiMethod && (GroovyPropertyUtils.isSimplePropertyAccessor((PsiMethod)member) || "setProperty".equals(((PsiMethod)member).getName()));
}
private static boolean isQualifierClassMember(PsiMember member, PsiElement qualifier) {
if (!(qualifier instanceof GrExpression)) return false;
final PsiType type = ((GrExpression)qualifier).getType();
if (!(type instanceof PsiClassType)) return false;
final PsiClass psiClass = ((PsiClassType)type).resolve();
if (psiClass == null) return false;
return qualifier.getManager().areElementsEquivalent(member.getContainingClass(), psiClass);
}
private enum NotQualifiedKind {
arrayType,
innerClass,
unknown,
accessor,
member,
currentClassMember,
restrictedClass,
local,
onTop
}
private enum QualifiedKind {
innerClass,
unknown,
accessor,
member,
currentClassMember,
enumConstant,
}
}