/*
* Copyright 2013-2017 consulo.io
*
* 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 consulo.csharp.ide.completion.weigher;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import consulo.annotations.RequiredReadAction;
import consulo.csharp.ide.completion.CSharpCompletionUtil;
import consulo.csharp.ide.completion.expected.ExpectedTypeInfo;
import consulo.csharp.ide.completion.item.CSharpTypeLikeLookupElement;
import consulo.csharp.lang.psi.CSharpConstructorDeclaration;
import consulo.csharp.lang.psi.CSharpMethodDeclaration;
import consulo.csharp.lang.psi.CSharpReferenceExpression;
import consulo.csharp.lang.psi.CSharpReferenceExpressionEx;
import consulo.csharp.lang.psi.CSharpSoftTokens;
import consulo.csharp.lang.psi.CSharpTokens;
import consulo.csharp.lang.psi.impl.CSharpTypeUtil;
import consulo.csharp.lang.psi.impl.source.CSharpReferenceExpressionImplUtil;
import consulo.csharp.lang.psi.impl.source.CSharpTypeDeclarationImplUtil;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpTypeRefByQName;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpTypeRefByTypeDeclaration;
import consulo.csharp.lang.psi.impl.source.resolve.type.wrapper.GenericUnwrapTool;
import consulo.dotnet.DotNetTypes;
import consulo.dotnet.psi.DotNetTypeDeclaration;
import consulo.dotnet.resolve.DotNetGenericExtractor;
import consulo.dotnet.resolve.DotNetTypeRef;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementWeigher;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
/**
* @author VISTALL
* @since 27.07.2015
*/
public class CSharpInheritProximityWeigher extends LookupElementWeigher
{
public enum Position
{
HIGH,
UP_KEYWORD,
UP_REF,
NONE,
DOWN,
}
private PsiElement myPosition;
private List<ExpectedTypeInfo> myExpectedTypeInfos;
public CSharpInheritProximityWeigher(PsiElement position, List<ExpectedTypeInfo> expectedTypeInfos)
{
super("CSharpInheritProximityWeigher");
myPosition = position;
myExpectedTypeInfos = expectedTypeInfos;
}
@Nullable
@Override
@RequiredReadAction
public Comparable weigh(@NotNull LookupElement element)
{
if(element.getPsiElement() instanceof CSharpConstructorDeclaration)
{
return null;
}
if(myExpectedTypeInfos.isEmpty())
{
return Position.NONE;
}
CSharpReferenceExpressionEx referenceExpressionEx = (CSharpReferenceExpressionEx) myPosition.getParent();
DotNetGenericExtractor extractor = DotNetGenericExtractor.EMPTY;
if(element instanceof CSharpTypeLikeLookupElement)
{
extractor = ((CSharpTypeLikeLookupElement) element).getExtractor();
}
PsiElement psiElement = element.getPsiElement();
if(psiElement == null)
{
Object object = element.getObject();
if(object instanceof IElementType)
{
DotNetTypeRef typeRef = typeRefFromTokeType((IElementType) object, referenceExpressionEx);
if(typeRef == null)
{
return Position.NONE;
}
PsiElement resolvedElement = typeRef.resolve().getElement();
if(resolvedElement == null)
{
return Position.NONE;
}
return weighElement(resolvedElement, extractor, referenceExpressionEx, myExpectedTypeInfos, Position.UP_KEYWORD);
}
return Position.NONE;
}
else
{
return weighElement(psiElement, extractor, referenceExpressionEx, myExpectedTypeInfos, Position.UP_REF);
}
}
@RequiredReadAction
public Comparable weighElement(@NotNull PsiElement psiElement,
DotNetGenericExtractor extractor,
@NotNull CSharpReferenceExpressionEx referenceExpressionEx,
@NotNull List<ExpectedTypeInfo> expectedTypeRefs,
@NotNull Position upPosition)
{
// if we have not type declaration, make types lower, dont allow int i = Int32 completion more high
if(referenceExpressionEx.kind() != CSharpReferenceExpression.ResolveToKind.TYPE_LIKE && CSharpCompletionUtil.isTypeLikeElementWithNamespace(psiElement) && upPosition == Position.UP_REF)
{
return Position.DOWN;
}
DotNetTypeRef typeOfElement;
if(psiElement instanceof CSharpMethodDeclaration)
{
CSharpMethodDeclaration methodDeclaration = (CSharpMethodDeclaration) psiElement;
typeOfElement = GenericUnwrapTool.exchangeTypeRef(methodDeclaration.getReturnTypeRef(), extractor, psiElement);
for(ExpectedTypeInfo expectedTypeInfo : expectedTypeRefs)
{
if(expectedTypeInfo.getTypeProvider() == psiElement)
{
continue;
}
if(CSharpTypeUtil.isInheritable(expectedTypeInfo.getTypeRef(), typeOfElement, referenceExpressionEx))
{
return upPosition;
}
/*else
{
DotNetTypeResolveResult typeResolveResult = expectedTypeInfo.getTypeRef().resolve(position);
if(typeResolveResult instanceof CSharpLambdaResolveResult)
{
if(CSharpTypeUtil.isInheritable(expectedTypeInfo.getTypeRef(), new CSharpLambdaTypeRef(methodDeclaration),
position))
{
next = buildForMethodReference(methodDeclaration);
iterator.set(PrioritizedLookupElement.withPriority(next, CSharpCompletionUtil.EXPR_REF_PRIORITY));
}
}
} */
}
}
else
{
typeOfElement = CSharpReferenceExpressionImplUtil.toTypeRef(psiElement, extractor);
for(ExpectedTypeInfo expectedTypeInfo : expectedTypeRefs)
{
if(expectedTypeInfo.getTypeProvider() == psiElement)
{
return Position.DOWN;
}
if(CSharpTypeUtil.isInheritable(expectedTypeInfo.getTypeRef(), typeOfElement, referenceExpressionEx))
{
return upPosition;
}
}
}
return Position.NONE;
}
@Nullable
@RequiredReadAction
private static DotNetTypeRef typeRefFromTokeType(@NotNull IElementType e, CSharpReferenceExpressionEx parent)
{
if(e == CSharpTokens.TRUE_KEYWORD || e == CSharpTokens.FALSE_KEYWORD)
{
return new CSharpTypeRefByQName(parent, DotNetTypes.System.Boolean);
}
else if(e == CSharpTokens.TYPEOF_KEYWORD)
{
return new CSharpTypeRefByQName(parent, DotNetTypes.System.Type);
}
else if(e == CSharpSoftTokens.NAMEOF_KEYWORD)
{
return new CSharpTypeRefByQName(parent, DotNetTypes.System.String);
}
else if(e == CSharpTokens.SIZEOF_KEYWORD)
{
return new CSharpTypeRefByQName(parent, DotNetTypes.System.Int32);
}
else if(e == CSharpTokens.__MAKEREF_KEYWORD)
{
return new CSharpTypeRefByQName(parent, DotNetTypes.System.TypedReference);
}
else if(e == CSharpTokens.__REFTYPE_KEYWORD)
{
return new CSharpTypeRefByQName(parent, DotNetTypes.System.Type);
}
else if(e == CSharpTokens.THIS_KEYWORD)
{
DotNetTypeDeclaration thisTypeDeclaration = PsiTreeUtil.getParentOfType(parent, DotNetTypeDeclaration.class);
if(thisTypeDeclaration != null)
{
return new CSharpTypeRefByTypeDeclaration(thisTypeDeclaration);
}
}
else if(e == CSharpTokens.BASE_KEYWORD)
{
DotNetTypeDeclaration thisTypeDeclaration = PsiTreeUtil.getParentOfType(parent, DotNetTypeDeclaration.class);
if(thisTypeDeclaration != null)
{
Pair<DotNetTypeDeclaration, DotNetGenericExtractor> pair = CSharpTypeDeclarationImplUtil.resolveBaseType(thisTypeDeclaration, parent);
if(pair != null)
{
return new CSharpTypeRefByTypeDeclaration(pair.getFirst(), pair.getSecond());
}
}
}
return null;
}
}