package consulo.csharp.ide.codeInsight.hits;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.intellij.codeInsight.hints.InlayInfo;
import com.intellij.codeInsight.hints.InlayParameterHintsProvider;
import com.intellij.codeInsight.hints.MethodInfo;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import consulo.annotations.RequiredReadAction;
import consulo.csharp.lang.psi.CSharpCallArgument;
import consulo.csharp.lang.psi.CSharpCallArgumentListOwner;
import consulo.csharp.lang.psi.CSharpConstructorDeclaration;
import consulo.csharp.lang.psi.CSharpMethodDeclaration;
import consulo.csharp.lang.psi.CSharpSimpleLikeMethodAsElement;
import consulo.csharp.lang.psi.CSharpSimpleParameterInfo;
import consulo.csharp.lang.psi.CSharpTypeDeclaration;
import consulo.csharp.lang.psi.impl.source.CSharpConstantExpressionImpl;
import consulo.csharp.lang.psi.impl.source.CSharpExpressionWithOperatorImpl;
import consulo.csharp.lang.psi.impl.source.CSharpOperatorReferenceImpl;
import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.MethodResolver;
import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.arguments.NCallArgument;
import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.arguments.NErrorCallArgument;
import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.arguments.NNamedCallArgument;
import consulo.csharp.lang.psi.impl.source.resolve.methodResolving.arguments.NParamsCallArgument;
import consulo.csharp.lang.psi.impl.source.resolve.type.CSharpLambdaResolveResult;
import consulo.dotnet.psi.DotNetExpression;
import consulo.dotnet.psi.DotNetParameterListOwner;
import consulo.dotnet.psi.DotNetVariable;
import consulo.dotnet.resolve.DotNetTypeRef;
import consulo.dotnet.resolve.DotNetTypeResolveResult;
/**
* @author VISTALL
* @since 16-Jan-17
*/
public class CSharpParameterHintsProvider implements InlayParameterHintsProvider
{
private static String[] ourDefaultBlacklist = {
"*.Write(*)",
"*.WriteLine(*)",
"System.Func*",
"System.Action*",
"*.Add(value)",
"*.Add(item)",
"*.Add(key, value)",
"*.TryParse(*, *)"
};
@NotNull
@Override
@RequiredReadAction
public List<InlayInfo> getParameterHints(@NotNull PsiElement psiElement)
{
if(psiElement instanceof CSharpExpressionWithOperatorImpl || psiElement instanceof CSharpOperatorReferenceImpl)
{
return Collections.emptyList();
}
if(psiElement instanceof CSharpCallArgumentListOwner)
{
PsiElement callable = ((CSharpCallArgumentListOwner) psiElement).resolveToCallable();
if(!(callable instanceof CSharpMethodDeclaration) && !(callable instanceof CSharpConstructorDeclaration) && !(callable instanceof DotNetVariable))
{
return Collections.emptyList();
}
List<InlayInfo> list = new SmartList<>();
CSharpCallArgument[] callArguments = ((CSharpCallArgumentListOwner) psiElement).getCallArguments();
List<NCallArgument> argumentList = buildCallArguments(callArguments, callable, callable);
for(NCallArgument nCallArgument : argumentList)
{
if(nCallArgument instanceof NNamedCallArgument || nCallArgument instanceof NErrorCallArgument || nCallArgument instanceof NParamsCallArgument)
{
continue;
}
PsiElement parameterElement = nCallArgument.getParameterElement();
DotNetExpression argumentExpression = nCallArgument.getCallArgument() == null ? null : nCallArgument.getCallArgument().getArgumentExpression();
if(!(argumentExpression instanceof CSharpConstantExpressionImpl) || parameterElement == null)
{
continue;
}
list.add(new InlayInfo(nCallArgument.getParameterName(), argumentExpression.getTextOffset()));
}
return list;
}
return Collections.emptyList();
}
@RequiredReadAction
private static List<NCallArgument> buildCallArguments(@NotNull CSharpCallArgument[] callArguments, @NotNull PsiElement callable, @NotNull PsiElement scope)
{
if(callable instanceof DotNetVariable)
{
DotNetTypeRef ref = ((DotNetVariable) callable).toTypeRef(true);
DotNetTypeResolveResult resolve = ref.resolve();
if(resolve instanceof CSharpLambdaResolveResult)
{
return MethodResolver.buildCallArguments(callArguments, ((CSharpLambdaResolveResult) resolve).getParameterInfos(), scope);
}
}
else if(callable instanceof DotNetParameterListOwner)
{
return MethodResolver.buildCallArguments(callArguments, (DotNetParameterListOwner) callable, scope);
}
return Collections.emptyList();
}
@Nullable
@Override
@RequiredReadAction
public MethodInfo getMethodInfo(@NotNull PsiElement call)
{
PsiElement callable;
if(call instanceof CSharpCallArgumentListOwner)
{
callable = ((CSharpCallArgumentListOwner) call).resolveToCallable();
if(!(callable instanceof CSharpMethodDeclaration) && !(callable instanceof DotNetVariable) && !(callable instanceof CSharpConstructorDeclaration))
{
return null;
}
}
else
{
return null;
}
String name = null;
CSharpSimpleParameterInfo[] params = CSharpSimpleParameterInfo.EMPTY_ARRAY;
if(callable instanceof DotNetVariable)
{
DotNetTypeRef ref = ((DotNetVariable) callable).toTypeRef(true);
DotNetTypeResolveResult resolve = ref.resolve();
if(resolve instanceof CSharpLambdaResolveResult)
{
name = ((CSharpLambdaResolveResult) resolve).getTarget().getPresentableQName();
params = ((CSharpLambdaResolveResult) resolve).getParameterInfos();
}
else
{
return null;
}
}
else if(callable instanceof CSharpMethodDeclaration || callable instanceof CSharpConstructorDeclaration)
{
params = ((CSharpSimpleLikeMethodAsElement) callable).getParameterInfos();
CSharpTypeDeclaration declaration = PsiTreeUtil.getParentOfType(callable, CSharpTypeDeclaration.class);
if(declaration != null)
{
name = declaration.getPresentableQName() + ".";
}
name += ((PsiNamedElement) callable).getName();
}
List<String> paramNames = ContainerUtil.map(params, it -> it.getNotNullName());
return new MethodInfo(name, paramNames);
}
@NotNull
@Override
public Set<String> getDefaultBlackList()
{
return ContainerUtil.newHashSet(ourDefaultBlacklist);
}
}