/*
* 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;
import static com.intellij.patterns.StandardPatterns.psiElement;
import gnu.trove.THashSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.Icon;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionInitializationContext;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.completion.CompletionUtilCore;
import com.intellij.codeInsight.completion.InsertHandler;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.PrioritizedLookupElement;
import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.ProcessingContext;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import consulo.annotations.RequiredDispatchThread;
import consulo.annotations.RequiredReadAction;
import consulo.annotations.RequiredWriteAction;
import consulo.codeInsight.completion.CompletionProvider;
import consulo.csharp.ide.CSharpLookupElementBuilder;
import consulo.csharp.ide.codeInsight.actions.MethodGenerateUtil;
import consulo.csharp.ide.codeStyle.CSharpCodeGenerationSettings;
import consulo.csharp.ide.completion.expected.ExpectedTypeInfo;
import consulo.csharp.ide.completion.expected.ExpectedTypeVisitor;
import consulo.csharp.ide.completion.insertHandler.CSharpTailInsertHandler;
import consulo.csharp.ide.completion.item.CSharpTypeLikeLookupElement;
import consulo.csharp.ide.completion.patterns.CSharpPatterns;
import consulo.csharp.ide.completion.util.SpaceInsertHandler;
import consulo.csharp.lang.psi.*;
import consulo.csharp.lang.psi.impl.CSharpTypeUtil;
import consulo.csharp.lang.psi.impl.source.*;
import consulo.csharp.lang.psi.impl.source.resolve.CSharpResolveOptions;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpArrayTypeRef;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpGenericExtractor;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpLambdaResolveResult;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpLambdaTypeRef;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpRefTypeRef;
import consulo.csharp.lang.psi.impl.source.resolve.util.CSharpMethodImplUtil;
import consulo.csharp.module.extension.CSharpLanguageVersion;
import consulo.csharp.module.extension.CSharpModuleUtil;
import consulo.dotnet.DotNetRunUtil;
import consulo.dotnet.DotNetTypes;
import consulo.dotnet.ide.DotNetElementPresentationUtil;
import consulo.dotnet.psi.*;
import consulo.dotnet.resolve.DotNetGenericExtractor;
import consulo.dotnet.resolve.DotNetTypeRef;
import consulo.dotnet.resolve.DotNetTypeResolveResult;
import consulo.ide.IconDescriptorUpdaters;
import consulo.util.NotNullPairFunction;
/**
* @author VISTALL
* @since 23.11.14
*/
public class CSharpExpressionCompletionContributor extends CompletionContributor
{
private static final TokenSet ourExpressionLiterals = TokenSet.create(CSharpTokens.NULL_LITERAL, CSharpTokens.FALSE_KEYWORD, CSharpTokens.TRUE_KEYWORD, CSharpTokens.DEFAULT_KEYWORD,
CSharpTokens.TYPEOF_KEYWORD, CSharpTokens.SIZEOF_KEYWORD, CSharpTokens.THIS_KEYWORD, CSharpTokens.BASE_KEYWORD, CSharpSoftTokens.AWAIT_KEYWORD, CSharpTokens.NEW_KEYWORD,
CSharpTokens.__MAKEREF_KEYWORD, CSharpTokens.__REFTYPE_KEYWORD, CSharpTokens.__REFVALUE_KEYWORD, CSharpSoftTokens.NAMEOF_KEYWORD);
public CSharpExpressionCompletionContributor()
{
extend(CompletionType.BASIC, psiElement().afterLeaf(psiElement().withElementType(CSharpTokens.NEW_KEYWORD)), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
PsiElement position = parameters.getPosition();
CSharpNewExpressionImpl newExpression = PsiTreeUtil.getParentOfType(position, CSharpNewExpressionImpl.class);
if(newExpression == null)
{
return;
}
List<ExpectedTypeInfo> expectedTypeRefs = ExpectedTypeVisitor.findExpectedTypeRefs(newExpression);
if(!expectedTypeRefs.isEmpty())
{
for(ExpectedTypeInfo expectedTypeInfo : expectedTypeRefs)
{
DotNetTypeRef typeRef = expectedTypeInfo.getTypeRef();
if(typeRef instanceof CSharpArrayTypeRef)
{
if(((CSharpArrayTypeRef) typeRef).getDimensions() != 0)
{
continue;
}
String typeText = CSharpTypeRefPresentationUtil.buildShortText(typeRef, position);
LookupElementBuilder builder = LookupElementBuilder.create(typeRef, typeText);
builder = builder.withIcon(getIconForInnerTypeRef((CSharpArrayTypeRef) typeRef, position));
// add without {...}
result.addElement(PrioritizedLookupElement.withPriority(builder, 1));
builder = builder.withTailText("{...}", true);
builder = builder.withInsertHandler(new InsertHandler<LookupElement>()
{
@Override
public void handleInsert(InsertionContext context, LookupElement item)
{
if(context.getCompletionChar() != '{')
{
int offset = context.getEditor().getCaretModel().getOffset();
TailType.insertChar(context.getEditor(), offset, '{');
TailType.insertChar(context.getEditor(), offset + 1, '}');
context.getEditor().getCaretModel().moveToOffset(offset + 1);
}
}
});
result.addElement(PrioritizedLookupElement.withPriority(builder, 1));
}
}
}
}
@Nullable
@RequiredReadAction
private Icon getIconForInnerTypeRef(@NotNull CSharpArrayTypeRef typeRef, @NotNull PsiElement scope)
{
DotNetTypeRef innerTypeRef = typeRef.getInnerTypeRef();
if(!(innerTypeRef instanceof CSharpArrayTypeRef))
{
PsiElement element = innerTypeRef.resolve().getElement();
if(element != null)
{
if(element instanceof DotNetTypeDeclaration)
{
String vmQName = ((DotNetTypeDeclaration) element).getVmQName();
String keyword = CSharpTypeRefPresentationUtil.ourTypesAsKeywords.get(vmQName);
if(keyword != null && CSharpCodeGenerationSettings.getInstance(scope.getProject()).USE_LANGUAGE_DATA_TYPES)
{
return null;
}
}
return IconDescriptorUpdaters.getIcon(element, Iconable.ICON_FLAG_VISIBILITY);
}
else
{
return AllIcons.Nodes.Class;
}
}
else
{
return getIconForInnerTypeRef((CSharpArrayTypeRef) ((CSharpArrayTypeRef) innerTypeRef).getInnerTypeRef(), scope);
}
}
});
extend(CompletionType.BASIC, CSharpPatterns.referenceExpression(), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull final CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
final CSharpReferenceExpressionEx parent = (CSharpReferenceExpressionEx) parameters.getPosition().getParent();
if(parent.getQualifier() == null && (parent.kind() == CSharpReferenceExpression.ResolveToKind.ANY_MEMBER || parent.kind() == CSharpReferenceExpression.ResolveToKind
.EXPRESSION_OR_TYPE_LIKE))
{
CSharpCompletionUtil.tokenSetToLookup(result, ourExpressionLiterals, new NotNullPairFunction<LookupElementBuilder, IElementType, LookupElement>()
{
@NotNull
@Override
public LookupElement fun(LookupElementBuilder t, IElementType elementType)
{
if(elementType == CSharpTokens.DEFAULT_KEYWORD ||
elementType == CSharpTokens.TYPEOF_KEYWORD ||
elementType == CSharpSoftTokens.NAMEOF_KEYWORD ||
elementType == CSharpTokens.__MAKEREF_KEYWORD ||
elementType == CSharpTokens.__REFTYPE_KEYWORD ||
elementType == CSharpTokens.__REFVALUE_KEYWORD ||
elementType == CSharpTokens.SIZEOF_KEYWORD)
{
t = t.withTailText("(...)", true);
t = t.withInsertHandler(ParenthesesInsertHandler.getInstance(true));
}
else if(elementType == CSharpTokens.NEW_KEYWORD)
{
t = t.withInsertHandler(SpaceInsertHandler.INSTANCE);
}
else if(elementType == CSharpSoftTokens.AWAIT_KEYWORD)
{
t = t.withInsertHandler(new InsertHandler<LookupElement>()
{
@Override
@RequiredDispatchThread
public void handleInsert(InsertionContext context, LookupElement item)
{
CSharpSimpleLikeMethodAsElement methodAsElement = PsiTreeUtil.getParentOfType(parameters.getOriginalPosition(), CSharpSimpleLikeMethodAsElement.class);
if(methodAsElement != null && !methodAsElement.hasModifier(CSharpModifier.ASYNC))
{
DotNetModifierList modifierList = methodAsElement.getModifierList();
assert modifierList != null;
modifierList.addModifier(CSharpModifier.ASYNC);
PsiDocumentManager.getInstance(context.getProject()).doPostponedOperationsAndUnblockDocument(context.getDocument());
}
SpaceInsertHandler.INSTANCE.handleInsert(context, item);
}
});
}
return t;
}
}, new Condition<IElementType>()
{
@Override
@RequiredReadAction
public boolean value(IElementType elementType)
{
if(elementType == CSharpTokens.BASE_KEYWORD || elementType == CSharpTokens.THIS_KEYWORD)
{
DotNetModifierListOwner owner = (DotNetModifierListOwner) PsiTreeUtil.getContextOfType(parent, DotNetQualifiedElement.class);
if(owner == null || owner.hasModifier(DotNetModifier.STATIC))
{
return false;
}
return true;
}
if(elementType == CSharpSoftTokens.AWAIT_KEYWORD)
{
CSharpSimpleLikeMethodAsElement methodAsElement = PsiTreeUtil.getParentOfType(parameters.getOriginalPosition(), CSharpSimpleLikeMethodAsElement.class);
if(methodAsElement == null || methodAsElement instanceof DotNetMethodDeclaration && DotNetRunUtil.isEntryPoint((DotNetMethodDeclaration) methodAsElement))
{
return false;
}
return CSharpModuleUtil.findLanguageVersion(parent).isAtLeast(CSharpLanguageVersion._4_0);
}
if(elementType == CSharpSoftTokens.NAMEOF_KEYWORD)
{
return CSharpModuleUtil.findLanguageVersion(parent).isAtLeast(CSharpLanguageVersion._6_0);
}
return true;
}
}
);
}
}
});
extend(CompletionType.BASIC, CSharpPatterns.referenceExpression(), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
CSharpReferenceExpressionEx parent = (CSharpReferenceExpressionEx) parameters.getPosition().getParent();
if(parent.getQualifier() != null || parent.kind() != CSharpReferenceExpression.ResolveToKind.ANY_MEMBER)
{
return;
}
boolean allowAsync = CSharpModuleUtil.findLanguageVersion(parent).isAtLeast(CSharpLanguageVersion._4_0);
List<ExpectedTypeInfo> expectedTypeRefs = getExpectedTypeInfosForExpression(parameters, context);
for(ExpectedTypeInfo expectedTypeRef : expectedTypeRefs)
{
DotNetTypeRef typeRef = expectedTypeRef.getTypeRef();
DotNetTypeResolveResult typeResolveResult = typeRef.resolve();
if(typeResolveResult instanceof CSharpLambdaResolveResult)
{
addLambdaExpressionLookup((CSharpLambdaResolveResult) typeResolveResult, result, false);
if(allowAsync)
{
addLambdaExpressionLookup((CSharpLambdaResolveResult) typeResolveResult, result, true);
}
addDelegateExpressionLookup((CSharpLambdaResolveResult) typeResolveResult, result, parent, false);
if(allowAsync)
{
addDelegateExpressionLookup((CSharpLambdaResolveResult) typeResolveResult, result, parent, true);
}
}
}
}
@RequiredReadAction
private void addLambdaExpressionLookup(CSharpLambdaResolveResult typeResolveResult, CompletionResultSet result, boolean async)
{
CSharpSimpleParameterInfo[] parameterInfos = typeResolveResult.getParameterInfos();
StringBuilder builder = new StringBuilder();
if(async)
{
builder.append("async ");
}
if(parameterInfos.length == 0 || parameterInfos.length > 1)
{
builder.append("(");
}
for(int i = 0; i < parameterInfos.length; i++)
{
CSharpSimpleParameterInfo parameterInfo = parameterInfos[i];
if(i != 0)
{
builder.append(", ");
}
builder.append(parameterInfo.getNotNullName());
}
if(parameterInfos.length == 0 || parameterInfos.length > 1)
{
builder.append(")");
}
builder.append(" => ");
LookupElementBuilder lookupElementBuilder = LookupElementBuilder.create(builder.toString());
lookupElementBuilder = lookupElementBuilder.withPresentableText(builder.append("{ }").toString());
lookupElementBuilder = lookupElementBuilder.withIcon(AllIcons.Nodes.Lambda);
CSharpCompletionSorting.force(lookupElementBuilder, CSharpCompletionSorting.KindSorter.Type.lambda);
result.addElement(lookupElementBuilder);
}
@RequiredReadAction
private void addDelegateExpressionLookup(CSharpLambdaResolveResult typeResolveResult, CompletionResultSet result, PsiElement parent, boolean async)
{
CSharpSimpleParameterInfo[] parameterInfos = typeResolveResult.getParameterInfos();
StringBuilder builder = new StringBuilder();
if(async)
{
builder.append("async ");
}
builder.append("delegate ");
if(parameterInfos.length > 0)
{
builder.append("(");
}
for(int i = 0; i < parameterInfos.length; i++)
{
CSharpSimpleParameterInfo parameterInfo = parameterInfos[i];
if(i != 0)
{
builder.append(", ");
}
builder.append(CSharpTypeRefPresentationUtil.buildShortText(parameterInfo.getTypeRef(), parent));
builder.append(" ");
builder.append(parameterInfo.getNotNullName());
}
if(parameterInfos.length > 0)
{
builder.append(")");
}
DotNetTypeRef returnTypeRef = typeResolveResult.getReturnTypeRef();
String defaultValueForType = MethodGenerateUtil.getDefaultValueForType(returnTypeRef, parent);
builder.append(" { ");
if(defaultValueForType != null)
{
builder.append("return ").append(defaultValueForType).append(";");
}
builder.append(" }");
LookupElementBuilder lookupElementBuilder = LookupElementBuilder.create(builder.toString());
lookupElementBuilder = lookupElementBuilder.withIcon(AllIcons.Nodes.Lambda);
lookupElementBuilder = reformatInsertHandler(lookupElementBuilder);
CSharpCompletionSorting.force(lookupElementBuilder, CSharpCompletionSorting.KindSorter.Type.delegate);
result.addElement(lookupElementBuilder);
}
private LookupElementBuilder reformatInsertHandler(LookupElementBuilder lookupElementBuilder)
{
lookupElementBuilder = lookupElementBuilder.withInsertHandler(new InsertHandler<LookupElement>()
{
@Override
@RequiredReadAction
public void handleInsert(InsertionContext context, LookupElement item)
{
PsiElement elementAt = context.getFile().findElementAt(context.getEditor().getCaretModel().getOffset() - 1);
if(elementAt != null)
{
CSharpAnonymousMethodExpression methodExpression = PsiTreeUtil.getParentOfType(elementAt, CSharpAnonymousMethodExpression.class);
if(methodExpression != null)
{
CodeStyleManager.getInstance(context.getProject()).reformat(methodExpression);
}
}
}
});
return lookupElementBuilder;
}
});
extend(CompletionType.BASIC, CSharpPatterns.referenceExpression(), new CompletionProvider()
{
@Override
@RequiredReadAction
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
List<ExpectedTypeInfo> expectedTypeInfos = getExpectedTypeInfosForExpression(parameters, context);
for(ExpectedTypeInfo expectedTypeInfo : expectedTypeInfos)
{
DotNetTypeRef typeRef = expectedTypeInfo.getTypeRef();
if(typeRef instanceof CSharpRefTypeRef)
{
CSharpRefTypeRef.Type type = ((CSharpRefTypeRef) typeRef).getType();
IElementType elementType = null;
switch(type)
{
case out:
elementType = CSharpTokens.OUT_KEYWORD;
break;
case ref:
elementType = CSharpTokens.REF_KEYWORD;
break;
}
assert elementType != null;
CSharpCompletionUtil.elementToLookup(result, elementType, new NotNullPairFunction<LookupElementBuilder, IElementType, LookupElement>()
{
@NotNull
@Override
public LookupElement fun(LookupElementBuilder lookupElementBuilder, IElementType iElementType)
{
lookupElementBuilder = lookupElementBuilder.withInsertHandler(SpaceInsertHandler.INSTANCE);
return lookupElementBuilder;
}
}, null);
}
}
}
});
extend(CompletionType.BASIC, psiElement(CSharpTokens.IDENTIFIER).withParent(CSharpReferenceExpression.class).withSuperParent(2, CSharpCallArgument.class), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
CSharpReferenceExpression referenceExpression = (CSharpReferenceExpression) parameters.getPosition().getParent();
if(referenceExpression.getQualifier() != null)
{
return;
}
CSharpCallArgument callArgument = (CSharpCallArgument) referenceExpression.getParent();
if(callArgument instanceof CSharpNamedCallArgument)
{
return;
}
CSharpCallArgumentListOwner argumentListOwner = PsiTreeUtil.getParentOfType(referenceExpression, CSharpCallArgumentListOwner.class);
assert argumentListOwner != null;
ResolveResult[] resolveResults = argumentListOwner.multiResolve(false);
boolean visitedNotNamed = false;
Set<String> alreadyDeclared = new THashSet<String>(5);
CSharpCallArgument[] callArguments = argumentListOwner.getCallArguments();
for(CSharpCallArgument c : callArguments)
{
if(c == callArgument)
{
continue;
}
if(c instanceof CSharpNamedCallArgument)
{
alreadyDeclared.add(((CSharpNamedCallArgument) c).getName());
}
else
{
visitedNotNamed = true;
}
}
int thisCallArgumentPosition = visitedNotNamed ? ArrayUtil.indexOf(callArguments, callArgument) : -1;
Set<String> wantToCompleteParameters = new THashSet<String>();
for(ResolveResult resolveResult : resolveResults)
{
PsiElement element = resolveResult.getElement();
if(element instanceof CSharpSimpleLikeMethodAsElement)
{
CSharpSimpleParameterInfo[] parameterInfos = ((CSharpSimpleLikeMethodAsElement) element).getParameterInfos();
if(thisCallArgumentPosition != -1)
{
if(parameterInfos.length > thisCallArgumentPosition)
{
for(int i = thisCallArgumentPosition; i < parameterInfos.length; i++)
{
CSharpSimpleParameterInfo parameterInfo = parameterInfos[i];
ContainerUtil.addIfNotNull(wantToCompleteParameters, parameterInfo.getName());
}
}
}
else
{
for(CSharpSimpleParameterInfo parameterInfo : parameterInfos)
{
ContainerUtil.addIfNotNull(wantToCompleteParameters, parameterInfo.getName());
}
}
}
}
wantToCompleteParameters.removeAll(alreadyDeclared);
for(String wantToCompleteParameter : wantToCompleteParameters)
{
LookupElementBuilder builder = LookupElementBuilder.create(wantToCompleteParameter + ": ");
builder = builder.withIcon(AllIcons.Nodes.Parameter);
CSharpCompletionSorting.force(builder, CSharpCompletionSorting.KindSorter.Type.parameterInCall);
result.consume(builder);
}
}
});
extend(CompletionType.BASIC, CSharpPatterns.referenceExpression(), new CompletionProvider()
{
@Override
@RequiredReadAction
public void addCompletions(@NotNull final CompletionParameters parameters, ProcessingContext context, @NotNull final CompletionResultSet result)
{
final CSharpReferenceExpressionEx expression = (CSharpReferenceExpressionEx) parameters.getPosition().getParent();
CSharpReferenceExpression.ResolveToKind kind = expression.kind();
if(needRemapToAnyResolving(kind, expression))
{
kind = CSharpReferenceExpression.ResolveToKind.ANY_MEMBER;
}
if(kind == CSharpReferenceExpression.ResolveToKind.CONSTRUCTOR)
{
kind = CSharpReferenceExpression.ResolveToKind.TYPE_LIKE;
}
final CSharpTypeDeclaration contextType = getContextType(expression);
final List<ExpectedTypeInfo> expectedTypeRefs = getExpectedTypeInfosForExpression(parameters, context);
for(ExpectedTypeInfo expectedTypeRef : expectedTypeRefs)
{
PsiElement element = expectedTypeRef.getTypeRef().resolve().getElement();
if(element instanceof CSharpTypeDeclaration && ((CSharpTypeDeclaration) element).isEnum() && !element.isEquivalentTo(contextType))
{
DotNetNamedElement[] members = ((CSharpTypeDeclaration) element).getMembers();
for(DotNetNamedElement member : members)
{
if(member instanceof CSharpEnumConstantDeclaration)
{
String name = member.getName();
if(name == null)
{
continue;
}
LookupElementBuilder builder = LookupElementBuilder.create(member, ((CSharpTypeDeclaration) element).getName() + "." + name);
builder = builder.withIcon(IconDescriptorUpdaters.getIcon(member, 0));
builder = builder.withTypeText(((CSharpTypeDeclaration) element).getPresentableParentQName());
builder = builder.withLookupString(name);
CSharpCompletionSorting.force(builder, CSharpCompletionSorting.KindSorter.Type.constants);
result.addElement(builder);
}
}
}
}
final CSharpNewExpression newExpression = getNewExpression(expression);
CSharpCallArgumentListOwner callArgumentListOwner = CSharpReferenceExpressionImplUtil.findCallArgumentListOwner(kind, expression);
CSharpReferenceExpressionImplUtil.collectResults(new CSharpResolveOptions(kind, null, expression, callArgumentListOwner, true, true), new Processor<ResolveResult>()
{
@Override
@RequiredReadAction
public boolean process(ResolveResult resolveResult)
{
ProgressManager.checkCanceled();
PsiElement element = resolveResult.getElement();
if(element == null)
{
return true;
}
DotNetGenericExtractor extractor = DotNetGenericExtractor.EMPTY;
if(newExpression != null && CSharpPsiUtilImpl.isTypeLikeElement(element))
{
if(element instanceof CSharpTypeDeclaration && ((CSharpTypeDeclaration) element).hasModifier(DotNetModifier.ABSTRACT))
{
return true;
}
if(!expectedTypeRefs.isEmpty())
{
for(ExpectedTypeInfo newExpectedTypeRef : expectedTypeRefs)
{
DotNetTypeResolveResult expectedTypeResult = newExpectedTypeRef.getTypeRef().resolve();
PsiElement expectedTypeResultElement = expectedTypeResult.getElement();
if(expectedTypeResult instanceof CSharpLambdaResolveResult)
{
expectedTypeResultElement = ((CSharpLambdaResolveResult) expectedTypeResult).getTarget();
}
if(element instanceof CSharpTypeDeclaration && expectedTypeResultElement instanceof CSharpTypeDeclaration)
{
DotNetGenericParameter[] genericParameters = ((CSharpTypeDeclaration) element).getGenericParameters();
Map<DotNetGenericParameter, DotNetTypeRef> map = new HashMap<DotNetGenericParameter, DotNetTypeRef>(genericParameters.length);
resolveGenericParameterValues((CSharpTypeDeclaration) element, (DotNetTypeDeclaration) expectedTypeResultElement, expectedTypeResult.getGenericExtractor(),
map, expression);
if(!map.isEmpty())
{
extractor = CSharpGenericExtractor.create(map);
}
}
else if(element instanceof CSharpMethodDeclaration && element.isEquivalentTo(expectedTypeResultElement))
{
extractor = expectedTypeResult.getGenericExtractor();
}
}
}
}
LookupElement lookupElement = CSharpLookupElementBuilder.buildLookupElementWithContextType(element, contextType, extractor, expression);
if(lookupElement == null)
{
return true;
}
if(element instanceof DotNetGenericParameter && expression.getParent() instanceof CSharpGenericConstraint && lookupElement instanceof LookupElementBuilder)
{
lookupElement = ((LookupElementBuilder) lookupElement).withInsertHandler(new CSharpTailInsertHandler(TailType.COND_EXPR_COLON));
}
if(element instanceof CSharpMethodDeclaration && !((CSharpMethodDeclaration) element).isDelegate())
{
CSharpMethodDeclaration methodDeclaration = (CSharpMethodDeclaration) element;
DotNetTypeRef typeOfElement = methodDeclaration.getReturnTypeRef();
for(ExpectedTypeInfo expectedTypeInfo : expectedTypeRefs)
{
if(expectedTypeInfo.getTypeProvider() == element)
{
continue;
}
if(!CSharpTypeUtil.isInheritable(expectedTypeInfo.getTypeRef(), typeOfElement, expression))
{
DotNetTypeResolveResult typeResolveResult = expectedTypeInfo.getTypeRef().resolve();
if(typeResolveResult instanceof CSharpLambdaResolveResult)
{
if(CSharpTypeUtil.isInheritable(expectedTypeInfo.getTypeRef(), new CSharpLambdaTypeRef(methodDeclaration), expression))
{
result.consume(buildForMethodReference(methodDeclaration, contextType, expression));
return true;
}
}
}
}
}
if(element instanceof CSharpIndexMethodDeclaration)
{
lookupElement = PrioritizedLookupElement.withPriority(lookupElement, 1);
}
result.consume(lookupElement);
return true;
}
});
}
@RequiredReadAction
private CSharpTypeDeclaration getContextType(CSharpReferenceExpression referenceExpression)
{
PsiElement qualifier = referenceExpression.getQualifier();
if(qualifier != null)
{
PsiElement element = ((DotNetExpression) qualifier).toTypeRef(true).resolve().getElement();
return element instanceof CSharpTypeDeclaration ? (CSharpTypeDeclaration) element : null;
}
else
{
return PsiTreeUtil.getContextOfType(referenceExpression, CSharpTypeDeclaration.class);
}
}
});
extend(CompletionType.BASIC, CSharpPatterns.referenceExpression(), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result)
{
if(isCorrectPosition(parameters.getPosition()))
{
TokenSet set = TokenSet.create(CSharpTokens.AS_KEYWORD, CSharpTokens.IS_KEYWORD);
CSharpCompletionUtil.tokenSetToLookup(result, set, new NotNullPairFunction<LookupElementBuilder, IElementType, LookupElement>()
{
@NotNull
@Override
public LookupElement fun(LookupElementBuilder lookupElementBuilder, IElementType iElementType)
{
lookupElementBuilder = lookupElementBuilder.withInsertHandler(SpaceInsertHandler.INSTANCE);
return lookupElementBuilder;
}
}, null);
}
}
@RequiredReadAction
private boolean isCorrectPosition(PsiElement position)
{
PsiElement prev = PsiTreeUtil.prevVisibleLeaf(position);
if(prev == null)
{
return false;
}
PsiElement expr = PsiTreeUtil.getParentOfType(prev, DotNetExpression.class);
if(expr != null && expr.getTextRange().getEndOffset() == prev.getTextRange().getEndOffset())
{
return true;
}
return false;
}
});
extend(CompletionType.BASIC, psiElement(CSharpTokens.IDENTIFIER).withParent(CSharpReferenceExpression.class).withSuperParent(2, CSharpArrayInitializerImpl.class).withSuperParent(3,
CSharpNewExpressionImpl.class), new CompletionProvider()
{
@Override
@RequiredReadAction
public void addCompletions(@NotNull final CompletionParameters parameters, ProcessingContext context, @NotNull final CompletionResultSet result)
{
final CSharpReferenceExpressionEx expression = (CSharpReferenceExpressionEx) parameters.getPosition().getParent();
CSharpArrayInitializerImpl arrayInitializationExpression = PsiTreeUtil.getParentOfType(expression, CSharpArrayInitializerImpl.class);
assert arrayInitializationExpression != null;
CSharpArrayInitializerValue[] arrayInitializerValues = arrayInitializationExpression.getValues();
if(arrayInitializerValues.length != 1 || !(arrayInitializerValues[0] instanceof CSharpArrayInitializerSingleValueImpl) ||
((CSharpArrayInitializerSingleValueImpl) arrayInitializerValues[0]).getArgumentExpression() != expression)
{
return;
}
CSharpResolveOptions options = CSharpResolveOptions.build().element(expression).resolveFromParent();
options.kind(CSharpReferenceExpression.ResolveToKind.FIELD_OR_PROPERTY);
options.completion(CSharpContextUtil.ContextType.INSTANCE);
CSharpReferenceExpressionImplUtil.collectResults(options, new Processor<ResolveResult>()
{
@Override
@RequiredReadAction
public boolean process(ResolveResult resolveResult)
{
ProgressManager.checkCanceled();
PsiElement element = resolveResult.getElement();
if(element == null)
{
return true;
}
LookupElementBuilder lookupElementBuilder = CSharpLookupElementBuilder.createLookupElementBuilder(element, DotNetGenericExtractor.EMPTY, expression);
if(lookupElementBuilder != null)
{
lookupElementBuilder = lookupElementBuilder.withTailText(" = ", true);
lookupElementBuilder = lookupElementBuilder.withInsertHandler(new InsertHandler<LookupElement>()
{
@Override
public void handleInsert(InsertionContext context, LookupElement item)
{
if(context.getCompletionChar() != '=')
{
Editor editor = context.getEditor();
int offset = context.getTailOffset();
TailType.insertChar(editor, offset, ' ');
TailType.insertChar(editor, offset + 1, '=');
TailType.insertChar(editor, offset + 2, ' ');
editor.getCaretModel().moveToOffset(offset + 3);
}
}
});
if(CSharpPsiUtilImpl.isTypeLikeElement(element))
{
result.consume(CSharpTypeLikeLookupElement.create(lookupElementBuilder, DotNetGenericExtractor.EMPTY, expression));
}
else
{
result.consume(lookupElementBuilder);
}
}
return true;
}
});
}
});
extend(CompletionType.BASIC, psiElement(CSharpTokens.IDENTIFIER).withParent(CSharpReferenceExpression.class), new CompletionProvider()
{
@RequiredReadAction
@Override
public void addCompletions(@NotNull final CompletionParameters completionParameters, ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet)
{
CSharpCodeGenerationSettings codeGenerationSettings = CSharpCodeGenerationSettings.getInstance(completionParameters.getPosition().getProject());
if(!codeGenerationSettings.USE_LANGUAGE_DATA_TYPES)
{
return;
}
final CSharpReferenceExpression parent = (CSharpReferenceExpression) completionParameters.getPosition().getParent();
if(parent.getQualifier() != null)
{
return;
}
CSharpReferenceExpression.ResolveToKind kind = parent.kind();
if(kind == CSharpReferenceExpression.ResolveToKind.TYPE_LIKE)
{
DotNetType type = PsiTreeUtil.getParentOfType(parent, DotNetType.class);
if(type != null)
{
// disable native type completion, due they dont extends System.Exception
if(type.getParent() instanceof CSharpLocalVariable && type.getParent().getParent() instanceof CSharpCatchStatementImpl)
{
return;
}
if(type.getParent() instanceof CSharpNewExpression)
{
return;
}
}
}
if(kind == CSharpReferenceExpression.ResolveToKind.TYPE_LIKE ||
kind == CSharpReferenceExpression.ResolveToKind.EXPRESSION_OR_TYPE_LIKE ||
kind == CSharpReferenceExpression.ResolveToKind.ANY_MEMBER)
{
CSharpCompletionUtil.tokenSetToLookup(completionResultSet, CSharpTokenSets.NATIVE_TYPES, new NotNullPairFunction<LookupElementBuilder, IElementType, LookupElement>()
{
@NotNull
@Override
public LookupElement fun(LookupElementBuilder lookupElementBuilder, IElementType elementType)
{
if(elementType == CSharpTokens.VOID_KEYWORD)
{
DotNetType userType = PsiTreeUtil.getParentOfType(parent, DotNetType.class);
if(userType == null)
{
return lookupElementBuilder;
}
PsiElement userTypeParent = userType.getParent();
if(userTypeParent instanceof CSharpTypeOfExpressionImpl)
{
return lookupElementBuilder;
}
return lookupElementBuilder.withInsertHandler(SpaceInsertHandler.INSTANCE);
}
return lookupElementBuilder;
}
}, new Condition<IElementType>()
{
@Override
@RequiredReadAction
public boolean value(IElementType elementType)
{
if(elementType == CSharpTokens.EXPLICIT_KEYWORD || elementType == CSharpTokens.IMPLICIT_KEYWORD)
{
PsiElement invalidParent = PsiTreeUtil.getParentOfType(parent, DotNetStatement.class, DotNetParameterList
.class);
return invalidParent == null;
}
else if(elementType == CSharpTokens.VOID_KEYWORD)
{
DotNetType userType = PsiTreeUtil.getParentOfType(parent, DotNetType.class);
if(userType == null)
{
return false;
}
PsiElement userTypeParent = userType.getParent();
if(userTypeParent instanceof DotNetLikeMethodDeclaration)
{
DotNetLikeMethodDeclaration methodDeclaration = (DotNetLikeMethodDeclaration) userType.getParent();
return methodDeclaration.getReturnType() == userType;
}
else if(userTypeParent instanceof DotNetFieldDeclaration)
{
DotNetFieldDeclaration fieldDeclaration = (DotNetFieldDeclaration) userTypeParent;
if(fieldDeclaration.isConstant() || fieldDeclaration.getInitializer() != null)
{
return false;
}
return fieldDeclaration.getType() == userType;
}
else if(userTypeParent instanceof CSharpTypeOfExpressionImpl)
{
return true;
}
return false;
}
else if(elementType == CSharpTokens.__ARGLIST_KEYWORD)
{
DotNetParameter parameter = PsiTreeUtil.getParentOfType(parent, DotNetParameter.class);
return parameter != null;
}
else if(elementType == CSharpSoftTokens.VAR_KEYWORD)
{
if(PsiTreeUtil.getParentOfType(parent, DotNetStatement.class) == null)
{
return false;
}
if(!CSharpModuleUtil.findLanguageVersion(parent).isAtLeast(CSharpLanguageVersion._2_0))
{
return false;
}
}
return true;
}
}
);
}
}
});
}
@Override
public void beforeCompletion(@NotNull CompletionInitializationContext context)
{
context.setDummyIdentifier(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED);
}
@RequiredReadAction
@Override
public void fillCompletionVariants(CompletionParameters parameters, CompletionResultSet result)
{
super.fillCompletionVariants(parameters, CSharpCompletionSorting.modifyResultSet(parameters, result));
}
private static boolean needRemapToAnyResolving(CSharpReferenceExpression.ResolveToKind kind, CSharpReferenceExpression expression)
{
if(kind == CSharpReferenceExpression.ResolveToKind.PARAMETER || kind == CSharpReferenceExpression.ResolveToKind.PARAMETER_FROM_PARENT)
{
return false;
}
if(kind == CSharpReferenceExpression.ResolveToKind.TYPE_LIKE)
{
PsiElement parent = expression.getParent();
if(parent instanceof CSharpUserType)
{
PsiElement nextParent = parent.getParent();
if(nextParent instanceof CSharpIsExpressionImpl ||
nextParent instanceof CSharpAsExpressionImpl ||
nextParent instanceof CSharpNewExpression ||
nextParent instanceof CSharpTypeOfExpressionImpl ||
nextParent instanceof CSharpSizeOfExpressionImpl ||
nextParent instanceof CSharpTypeCastExpressionImpl ||
nextParent instanceof CSharpLocalVariable && nextParent.getParent() instanceof CSharpCatchStatementImpl)
{
return false;
}
}
}
if(kind != CSharpReferenceExpression.ResolveToKind.LABEL &&
kind != CSharpReferenceExpression.ResolveToKind.QUALIFIED_NAMESPACE &&
kind != CSharpReferenceExpression.ResolveToKind.FIELD_OR_PROPERTY &&
kind != CSharpReferenceExpression.ResolveToKind.SOFT_QUALIFIED_NAMESPACE)
{
if(PsiTreeUtil.getParentOfType(expression, DotNetStatement.class) != null)
{
return true;
}
}
return false;
}
@NotNull
@RequiredReadAction
private static LookupElement buildForMethodReference(final CSharpMethodDeclaration methodDeclaration, CSharpTypeDeclaration contextType, final CSharpReferenceExpressionEx expression)
{
LookupElementBuilder builder = LookupElementBuilder.create(methodDeclaration.getName());
builder = builder.withIcon(AllIcons.Nodes.MethodReference);
final DotNetTypeRef[] parameterTypes = methodDeclaration.getParameterTypeRefs();
String genericText = DotNetElementPresentationUtil.formatGenericParameters(methodDeclaration);
String parameterText = genericText + "(" + StringUtil.join(parameterTypes, new Function<DotNetTypeRef, String>()
{
@Override
@RequiredReadAction
public String fun(DotNetTypeRef parameter)
{
return CSharpTypeRefPresentationUtil.buildShortText(parameter, methodDeclaration);
}
}, ", ") + ")";
if(CSharpMethodImplUtil.isExtensionWrapper(methodDeclaration))
{
builder = builder.withItemTextUnderlined(true);
}
builder = builder.withTypeText(CSharpTypeRefPresentationUtil.buildShortText(methodDeclaration.getReturnTypeRef(), methodDeclaration), true);
builder = builder.withTailText(parameterText, true);
if(DotNetAttributeUtil.hasAttribute(methodDeclaration, DotNetTypes.System.ObsoleteAttribute))
{
builder = builder.withStrikeoutness(true);
}
builder = builder.withInsertHandler(new InsertHandler<LookupElement>()
{
@Override
@RequiredWriteAction
public void handleInsert(InsertionContext context, LookupElement item)
{
char completionChar = context.getCompletionChar();
switch(completionChar)
{
case ',':
if(expression != null && expression.getParent() instanceof CSharpCallArgument)
{
context.setAddCompletionChar(false);
TailType.COMMA.processTail(context.getEditor(), context.getTailOffset());
}
break;
}
}
});
if(contextType != null && contextType.isEquivalentTo(methodDeclaration.getParent()))
{
builder = builder.bold();
}
CSharpCompletionSorting.force(builder, CSharpCompletionSorting.KindSorter.Type.member);
return builder;
}
@RequiredReadAction
private static void resolveGenericParameterValues(CSharpTypeDeclaration targetType,
DotNetTypeDeclaration expectedType,
DotNetGenericExtractor expectedGenericExtractor,
Map<DotNetGenericParameter, DotNetTypeRef> map,
PsiElement scope)
{
if(targetType.isEquivalentTo(expectedType))
{
for(DotNetGenericParameter genericParameter : targetType.getGenericParameters())
{
DotNetTypeRef typeRef = map.get(genericParameter);
if(typeRef == null)
{
map.put(genericParameter, expectedGenericExtractor.extract(genericParameter));
}
}
return;
}
DotNetTypeRef[] extendTypeRefs = targetType.getExtendTypeRefs();
for(DotNetTypeRef extendTypeRef : extendTypeRefs)
{
DotNetTypeResolveResult typeResolveResult = extendTypeRef.resolve();
PsiElement element = typeResolveResult.getElement();
if(element instanceof DotNetTypeDeclaration && element.isEquivalentTo(expectedType))
{
DotNetGenericExtractor genericExtractor = typeResolveResult.getGenericExtractor();
for(DotNetGenericParameter genericParameter : ((DotNetTypeDeclaration) element).getGenericParameters())
{
DotNetTypeRef tempTypeRef = genericExtractor.extract(genericParameter);
PsiElement tempElement = tempTypeRef == null ? null : tempTypeRef.resolve().getElement();
if(tempElement != null)
{
for(DotNetGenericParameter targetParameter : targetType.getGenericParameters())
{
if(targetParameter.isEquivalentTo(tempElement))
{
DotNetTypeRef typeRef = map.get(targetParameter);
if(typeRef == null)
{
map.put(targetParameter, expectedGenericExtractor.extract(genericParameter));
}
}
}
}
}
}
}
}
@Nullable
public static CSharpNewExpression getNewExpression(PsiElement expression)
{
PsiElement parent = expression.getParent();
if(parent instanceof CSharpUserType)
{
PsiElement typeParent = parent.getParent();
if(typeParent instanceof CSharpNewExpression)
{
return (CSharpNewExpression) typeParent;
}
}
return null;
}
@NotNull
@RequiredReadAction
public static List<ExpectedTypeInfo> getExpectedTypeInfosForExpression(CompletionParameters parameters, @Nullable ProcessingContext context)
{
PsiElement position = parameters.getPosition();
if(PsiUtilBase.getElementType(position) != CSharpTokens.IDENTIFIER)
{
return Collections.emptyList();
}
PsiElement parent = position.getParent();
if(!(parent instanceof CSharpReferenceExpressionEx))
{
return Collections.emptyList();
}
List<ExpectedTypeInfo> expectedTypeInfos = context == null ? null : context.get(ExpectedTypeVisitor.EXPECTED_TYPE_INFOS);
if(expectedTypeInfos != null)
{
return expectedTypeInfos;
}
expectedTypeInfos = ExpectedTypeVisitor.findExpectedTypeRefs(parent);
if(context != null)
{
context.put(ExpectedTypeVisitor.EXPECTED_TYPE_INFOS, expectedTypeInfos);
}
return expectedTypeInfos;
}
}