blob: b6b13653003228ce330e72f6851aca3e84aab0be [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.
*/
/**
* @author cdr
*/
package com.intellij.codeInsight.daemon.impl.analysis;
import com.intellij.application.options.colors.ScopeAttributesUtil;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.colors.TextAttributesScheme;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.packageDependencies.DependencyValidationManager;
import com.intellij.packageDependencies.DependencyValidationManagerImpl;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.impl.source.tree.TreeUtil;
import com.intellij.psi.search.scope.packageSet.NamedScope;
import com.intellij.psi.search.scope.packageSet.NamedScopesHolder;
import com.intellij.psi.search.scope.packageSet.PackageSet;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class HighlightNamesUtil {
private static final Logger LOG = Logger.getInstance("#" + HighlightNamesUtil.class.getName());
@Nullable
public static HighlightInfo highlightMethodName(@NotNull PsiMethod method,
final PsiElement elementToHighlight,
final boolean isDeclaration,
@NotNull TextAttributesScheme colorsScheme) {
return highlightMethodName(method, elementToHighlight, elementToHighlight.getTextRange(), colorsScheme, isDeclaration);
}
@Nullable
public static HighlightInfo highlightMethodName(@NotNull PsiMethod method,
final PsiElement elementToHighlight,
TextRange range, @NotNull TextAttributesScheme colorsScheme, final boolean isDeclaration) {
boolean isInherited = false;
if (!isDeclaration) {
if (isCalledOnThis(elementToHighlight)) {
PsiClass enclosingClass = PsiTreeUtil.getParentOfType(elementToHighlight, PsiClass.class);
isInherited = enclosingClass != null && enclosingClass.isInheritor(method.getContainingClass(), true);
}
}
HighlightInfoType type = getMethodNameHighlightType(method, isDeclaration, isInherited);
if (type != null && elementToHighlight != null) {
TextAttributes attributes = mergeWithScopeAttributes(method, type, colorsScheme);
HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(type).range(range);
if (attributes != null) {
builder.textAttributes(attributes);
}
return builder.createUnconditionally();
}
return null;
}
private static boolean isCalledOnThis(PsiElement elementToHighlight) {
PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(elementToHighlight, PsiMethodCallExpression.class);
if (methodCallExpression != null) {
PsiElement qualifier = methodCallExpression.getMethodExpression().getQualifier();
if (qualifier == null || qualifier instanceof PsiThisExpression) {
return true;
}
}
return false;
}
private static TextAttributes mergeWithScopeAttributes(final PsiElement element,
@NotNull HighlightInfoType type,
@NotNull TextAttributesScheme colorsScheme) {
TextAttributes regularAttributes = HighlightInfo.getAttributesByType(element, type, colorsScheme);
if (element == null) return regularAttributes;
TextAttributes scopeAttributes = getScopeAttributes(element, colorsScheme);
return TextAttributes.merge(scopeAttributes, regularAttributes);
}
@Nullable
public static HighlightInfo highlightClassName(PsiClass aClass, PsiElement elementToHighlight, @NotNull TextAttributesScheme colorsScheme) {
HighlightInfoType type = getClassNameHighlightType(aClass, elementToHighlight);
if (elementToHighlight != null) {
TextAttributes attributes = mergeWithScopeAttributes(aClass, type, colorsScheme);
TextRange range = elementToHighlight.getTextRange();
if (elementToHighlight instanceof PsiJavaCodeReferenceElement) {
final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)elementToHighlight;
PsiReferenceParameterList parameterList = referenceElement.getParameterList();
if (parameterList != null) {
final TextRange paramListRange = parameterList.getTextRange();
if (paramListRange.getEndOffset() > paramListRange.getStartOffset()) {
range = new TextRange(range.getStartOffset(), paramListRange.getStartOffset());
}
}
}
// This will highlight @ sign in annotation as well.
final PsiElement parent = elementToHighlight.getParent();
if (parent instanceof PsiAnnotation) {
final PsiAnnotation psiAnnotation = (PsiAnnotation)parent;
range = new TextRange(psiAnnotation.getTextRange().getStartOffset(), range.getEndOffset());
}
HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(type).range(range);
if (attributes != null) {
builder.textAttributes(attributes);
}
return builder.createUnconditionally();
}
return null;
}
@Nullable
public static HighlightInfo highlightVariableName(final PsiVariable variable,
final PsiElement elementToHighlight,
@NotNull TextAttributesScheme colorsScheme) {
HighlightInfoType varType = getVariableNameHighlightType(variable);
if (varType != null) {
if (variable instanceof PsiField) {
TextAttributes attributes = mergeWithScopeAttributes(variable, varType, colorsScheme);
HighlightInfo.Builder builder = HighlightInfo.newHighlightInfo(varType).range(elementToHighlight.getTextRange());
if (attributes != null) {
builder.textAttributes(attributes);
}
return builder.createUnconditionally();
}
return HighlightInfo.newHighlightInfo(varType).range(elementToHighlight).create();
}
return null;
}
@Nullable
public static HighlightInfo highlightClassNameInQualifier(final PsiJavaCodeReferenceElement element,
@NotNull TextAttributesScheme colorsScheme) {
PsiExpression qualifierExpression = null;
if (element instanceof PsiReferenceExpression) {
qualifierExpression = ((PsiReferenceExpression)element).getQualifierExpression();
}
if (qualifierExpression instanceof PsiJavaCodeReferenceElement) {
PsiElement resolved = ((PsiJavaCodeReferenceElement)qualifierExpression).resolve();
if (resolved instanceof PsiClass) {
return highlightClassName((PsiClass)resolved, qualifierExpression, colorsScheme);
}
}
return null;
}
private static HighlightInfoType getMethodNameHighlightType(@NotNull PsiMethod method, boolean isDeclaration, boolean isInheritedMethod) {
if (method.isConstructor()) {
return isDeclaration ? HighlightInfoType.CONSTRUCTOR_DECLARATION : HighlightInfoType.CONSTRUCTOR_CALL;
}
if (isDeclaration) return HighlightInfoType.METHOD_DECLARATION;
if (method.hasModifierProperty(PsiModifier.STATIC)) {
return HighlightInfoType.STATIC_METHOD;
}
if (isInheritedMethod) return HighlightInfoType.INHERITED_METHOD;
if(method.hasModifierProperty(PsiModifier.ABSTRACT)) {
return HighlightInfoType.ABSTRACT_METHOD;
}
return HighlightInfoType.METHOD_CALL;
}
@Nullable
private static HighlightInfoType getVariableNameHighlightType(PsiVariable var) {
if (var instanceof PsiLocalVariable
|| var instanceof PsiParameter && ((PsiParameter)var).getDeclarationScope() instanceof PsiForeachStatement) {
return HighlightInfoType.LOCAL_VARIABLE;
}
if (var instanceof PsiField) {
return var.hasModifierProperty(PsiModifier.STATIC) ? var.hasModifierProperty(PsiModifier.FINAL)
? HighlightInfoType.STATIC_FINAL_FIELD
: HighlightInfoType.STATIC_FIELD : HighlightInfoType.INSTANCE_FIELD;
}
if (var instanceof PsiParameter) {
return HighlightInfoType.PARAMETER;
}
return null;
}
@NotNull
private static HighlightInfoType getClassNameHighlightType(@Nullable PsiClass aClass, @Nullable PsiElement element) {
if (element instanceof PsiJavaCodeReferenceElement && element.getParent() instanceof PsiAnonymousClass) {
return HighlightInfoType.ANONYMOUS_CLASS_NAME;
}
if (aClass != null) {
if (aClass.isAnnotationType()) return HighlightInfoType.ANNOTATION_NAME;
if (aClass.isInterface()) return HighlightInfoType.INTERFACE_NAME;
if (aClass.isEnum()) return HighlightInfoType.ENUM_NAME;
if (aClass instanceof PsiTypeParameter) return HighlightInfoType.TYPE_PARAMETER_NAME;
final PsiModifierList modList = aClass.getModifierList();
if (modList != null && modList.hasModifierProperty(PsiModifier.ABSTRACT)) return HighlightInfoType.ABSTRACT_CLASS_NAME;
}
// use class by default
return HighlightInfoType.CLASS_NAME;
}
@Nullable
public static HighlightInfo highlightReassignedVariable(PsiVariable variable, PsiElement elementToHighlight) {
if (variable instanceof PsiLocalVariable) {
return HighlightInfo.newHighlightInfo(HighlightInfoType.REASSIGNED_LOCAL_VARIABLE).range(elementToHighlight).create();
}
if (variable instanceof PsiParameter) {
return HighlightInfo.newHighlightInfo(HighlightInfoType.REASSIGNED_PARAMETER).range(elementToHighlight).create();
}
return null;
}
private static TextAttributes getScopeAttributes(@NotNull PsiElement element, @NotNull TextAttributesScheme colorsScheme) {
PsiFile file = element.getContainingFile();
if (file == null) return null;
TextAttributes result = null;
DependencyValidationManagerImpl validationManager = (DependencyValidationManagerImpl)DependencyValidationManager.getInstance(file.getProject());
List<Pair<NamedScope,NamedScopesHolder>> scopes = validationManager.getScopeBasedHighlightingCachedScopes();
for (Pair<NamedScope, NamedScopesHolder> scope : scopes) {
final NamedScope namedScope = scope.getFirst();
final TextAttributesKey scopeKey = ScopeAttributesUtil.getScopeTextAttributeKey(namedScope.getName());
final TextAttributes attributes = colorsScheme.getAttributes(scopeKey);
if (attributes == null || attributes.isEmpty()) {
continue;
}
final PackageSet packageSet = namedScope.getValue();
if (packageSet != null && packageSet.contains(file, scope.getSecond())) {
result = TextAttributes.merge(attributes, result);
}
}
return result;
}
@NotNull
public static TextRange getMethodDeclarationTextRange(@NotNull PsiMethod method) {
if (method instanceof SyntheticElement) return TextRange.EMPTY_RANGE;
int start = stripAnnotationsFromModifierList(method.getModifierList());
final TextRange throwsRange = method.getThrowsList().getTextRange();
LOG.assertTrue(throwsRange != null, method);
int end = throwsRange.getEndOffset();
return new TextRange(start, end);
}
@NotNull
public static TextRange getFieldDeclarationTextRange(@NotNull PsiField field) {
int start = stripAnnotationsFromModifierList(field.getModifierList());
int end = field.getNameIdentifier().getTextRange().getEndOffset();
return new TextRange(start, end);
}
@NotNull
public static TextRange getClassDeclarationTextRange(@NotNull PsiClass aClass) {
if (aClass instanceof PsiEnumConstantInitializer) {
return ((PsiEnumConstantInitializer)aClass).getEnumConstant().getNameIdentifier().getTextRange();
}
final PsiElement psiElement = aClass instanceof PsiAnonymousClass
? ((PsiAnonymousClass)aClass).getBaseClassReference()
: aClass.getModifierList() == null ? aClass.getNameIdentifier() : aClass.getModifierList();
if(psiElement == null) return new TextRange(aClass.getTextRange().getStartOffset(), aClass.getTextRange().getStartOffset());
int start = stripAnnotationsFromModifierList(psiElement);
PsiElement endElement = aClass instanceof PsiAnonymousClass ?
((PsiAnonymousClass)aClass).getBaseClassReference() :
aClass.getImplementsList();
if (endElement == null) endElement = aClass.getNameIdentifier();
TextRange endTextRange = endElement == null ? null : endElement.getTextRange();
int end = endTextRange == null ? start : endTextRange.getEndOffset();
return new TextRange(start, end);
}
private static int stripAnnotationsFromModifierList(@NotNull PsiElement element) {
TextRange textRange = element.getTextRange();
if (textRange == null) return 0;
PsiAnnotation lastAnnotation = null;
for (PsiElement child : element.getChildren()) {
if (child instanceof PsiAnnotation) lastAnnotation = (PsiAnnotation)child;
}
if (lastAnnotation == null) {
return textRange.getStartOffset();
}
ASTNode node = lastAnnotation.getNode();
if (node != null) {
do {
node = TreeUtil.nextLeaf(node);
}
while (node != null && ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(node.getElementType()));
}
if (node != null) return node.getTextRange().getStartOffset();
return textRange.getStartOffset();
}
}