package org.elixir_lang.psi;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.intellij.psi.ElementDescriptionLocation;
import com.intellij.psi.PsiElement;
import com.intellij.usageView.UsageViewLongNameLocation;
import com.intellij.usageView.UsageViewShortNameLocation;
import com.intellij.usageView.UsageViewTypeLocation;
import org.elixir_lang.annonator.Parameter;
import org.elixir_lang.psi.call.Call;
import org.elixir_lang.reference.Callable;
import org.elixir_lang.structure_view.element.*;
import org.elixir_lang.structure_view.element.modular.Implementation;
import org.elixir_lang.structure_view.element.modular.Module;
import org.elixir_lang.structure_view.element.modular.Protocol;
import org.elixir_lang.structure_view.element.structure.Structure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static org.elixir_lang.psi.call.name.Function.ALIAS;
import static org.elixir_lang.psi.call.name.Module.KERNEL;
import static org.elixir_lang.psi.impl.ElixirPsiImplUtil.hasKeywordKey;
import static org.elixir_lang.reference.module.ResolvableName.resolvableName;
/**
* Dual to {@link org.elixir_lang.FindUsagesProvider}, where instead of each location being a separate method, they
* are all one method, which means the same code can be used to detect the type of an element and then group all the
* text ({@link org.elixir_lang.FindUsagesProvider#getDescriptiveName(PsiElement)},
* {@link org.elixir_lang.FindUsagesProvider#getHelpId(PsiElement)},
* {@link org.elixir_lang.FindUsagesProvider#getNodeText(PsiElement, boolean)}
* {@link org.elixir_lang.FindUsagesProvider#getType(PsiElement)}) together together.
*/
public class ElementDescriptionProvider implements com.intellij.psi.ElementDescriptionProvider {
/*
*
* Instance Methods
*
*/
/*
* Public Instance Methods
*/
@Nullable
@Override
public String getElementDescription(@NotNull PsiElement element, @NotNull ElementDescriptionLocation location) {
String elementDescription = null;
if (element instanceof Call) {
elementDescription = getElementDescription((Call) element, location);
} else if (element instanceof ElixirIdentifier) {
elementDescription = getElementDescription((ElixirIdentifier) element, location);
} else if (element instanceof ElixirKeywordKey) {
elementDescription = getElementDescription((ElixirKeywordKey) element, location);
} else if (element instanceof MaybeModuleName) {
elementDescription = getElementDescription((MaybeModuleName) element, location);
}
return elementDescription;
}
/*
* Private Instance Methods
*/
@Nullable
private String getElementDescription(@NotNull ElixirIdentifier identifier, @NotNull ElementDescriptionLocation location) {
String elementDescription = null;
Parameter parameter = new Parameter(identifier);
Parameter.Type type = Parameter.putParameterized(parameter).type;
if (type == Parameter.Type.FUNCTION_NAME) {
if (location == UsageViewShortNameLocation.INSTANCE) {
elementDescription = identifier.getText();
} else if (location == UsageViewTypeLocation.INSTANCE) {
elementDescription = "function";
}
} else if (type == Parameter.Type.MACRO_NAME) {
if (location == UsageViewShortNameLocation.INSTANCE) {
elementDescription = identifier.getText();
} else if (location == UsageViewTypeLocation.INSTANCE) {
elementDescription = "macro";
}
}
return elementDescription;
}
@Nullable
private String getElementDescription(@NotNull ElixirKeywordKey keywordKey,
@NotNull ElementDescriptionLocation location) {
String elementDescription = null;
PsiElement innerKeywordPair = keywordKey.getParent();
if (innerKeywordPair instanceof ElixirKeywordPair) {
PsiElement innerKeywords = innerKeywordPair.getParent();
if (innerKeywords instanceof ElixirKeywords) {
PsiElement innerList = innerKeywords.getParent();
if (innerList instanceof ElixirList) {
PsiElement outerAccessExpression = innerList.getParent();
if (outerAccessExpression instanceof ElixirAccessExpression) {
PsiElement outerKeywordPair = outerAccessExpression.getParent();
if (outerKeywordPair instanceof QuotableKeywordPair) {
QuotableKeywordPair outerQuotableKeywordPair = (QuotableKeywordPair) outerKeywordPair;
Quotable outerKeywordKey = outerQuotableKeywordPair.getKeywordKey();
if (outerKeywordKey.getText().equals("bind_quoted")) {
if (location == UsageViewTypeLocation.INSTANCE) {
elementDescription = "quote bound variable";
}
}
}
}
}
}
}
if (elementDescription == null) {
if (location == UsageViewTypeLocation.INSTANCE) {
elementDescription = "keyword key";
}
}
return elementDescription;
}
@Nullable
private String getElementDescription(@NotNull MaybeModuleName maybeModuleName,
@NotNull ElementDescriptionLocation location) {
String elementDescription = null;
if (maybeModuleName instanceof QualifiableAlias) {
QualifiableAlias qualifiableAlias = (QualifiableAlias) maybeModuleName;
if (location == UsageViewShortNameLocation.INSTANCE) {
if (isAliasCallAs(qualifiableAlias)) {
String resolvableName = resolvableName(qualifiableAlias);
if (resolvableName != null) {
elementDescription = "as: " + resolvableName;
}
} else {
elementDescription = resolvableName(qualifiableAlias);
}
}
}
if (location == UsageViewTypeLocation.INSTANCE) {
if (isAliasCallArgument(maybeModuleName)) {
elementDescription = "alias";
} else {
elementDescription = "module";
}
}
return elementDescription;
}
@Nullable
private String getElementDescription(@NotNull Call call, @NotNull ElementDescriptionLocation location) {
String elementDescription = null;
if (CallDefinitionClause.is(call)) {
elementDescription = CallDefinitionClause.elementDescription(call, location);
} else if (CallDefinitionSpecification.is(call)) {
elementDescription = CallDefinitionSpecification.elementDescription(call, location);
} else if (Callback.is(call)) {
elementDescription = Callback.elementDescription(call, location);
} else if (Delegation.is(call)) {
elementDescription = Delegation.elementDescription(call, location);
} else if (org.elixir_lang.structure_view.element.Exception.is(call)) {
elementDescription = org.elixir_lang.structure_view.element.Exception.elementDescription(call, location);
} else if (Implementation.is(call)) {
elementDescription = Implementation.elementDescription(call, location);
} else if (Import.is(call)) {
elementDescription = Import.elementDescription(call, location);
} else if (Module.is(call)) {
elementDescription = Module.elementDescription(call, location);
} else if (Overridable.is(call)) {
elementDescription = Overridable.elementDescription(call, location);
} else if (Protocol.is((call))) {
elementDescription = Protocol.elementDescription(call, location);
} else if (org.elixir_lang.structure_view.element.Quote.is(call)) {
elementDescription = org.elixir_lang.structure_view.element.Quote.elementDescription(call, location);
} else if (Structure.is(call)) {
elementDescription = Structure.elementDescription(call, location);
} else if (Type.is(call)) {
elementDescription = Type.elementDescription(call, location);
} else if (Use.is(call)) {
elementDescription = Use.elementDescription(call, location);
} else if (call instanceof AtUnqualifiedNoParenthesesCall) {
elementDescription = getElementDescription((AtUnqualifiedNoParenthesesCall) call, location);
} else if (Callable.isBitStreamSegmentOption(call)) {
elementDescription = Callable.bitStringSegmentOptionElementDescription(call, location);
} else if (Callable.isIgnored(call)) {
elementDescription = Callable.ignoredElementDescription(call, location);
} else if (Callable.isParameter(call)) {
elementDescription = Callable.parameterElementDescription(call, location);
} else if (Callable.isParameterWithDefault(call)) {
elementDescription = Callable.parameterWithDefaultElementDescription(call, location);
} else if (Callable.isVariable(call)) {
elementDescription = Callable.variableElementDescription(call, location);
} else {
if (location == UsageViewTypeLocation.INSTANCE) {
elementDescription = "call";
}
}
return elementDescription;
}
private String getElementDescription(@NotNull AtUnqualifiedNoParenthesesCall atUnqualifiedNoParenthesesCall,
ElementDescriptionLocation location) {
String elementDescription = null;
if (location == UsageViewLongNameLocation.INSTANCE || location == UsageViewShortNameLocation.INSTANCE) {
elementDescription = atUnqualifiedNoParenthesesCall.getName();
} else if (location == UsageViewTypeLocation.INSTANCE) {
elementDescription = "module attribute";
}
return elementDescription;
}
private boolean isAliasCallArgument(@NotNull Call element) {
return element.isCalling(KERNEL, ALIAS);
}
private boolean isAliasCallArgument(@NotNull PsiElement element) {
boolean isAliasCallArgument = false;
if (element instanceof Call) {
isAliasCallArgument = isAliasCallArgument((Call) element);
} else if (element instanceof Arguments ||
element instanceof ElixirAccessExpression ||
element instanceof ElixirMultipleAliases ||
element instanceof QualifiableAlias ||
element instanceof QualifiedMultipleAliases) {
isAliasCallArgument = isAliasCallArgument(element.getParent());
} else if (element instanceof QuotableKeywordPair) {
isAliasCallArgument = isAliasCallAs((QuotableKeywordPair) element);
}
return isAliasCallArgument;
}
private boolean isAliasCallAs(@NotNull QuotableKeywordPair element) {
boolean isAliasCallArgument = false;
if (hasKeywordKey(element, "as")) {
PsiElement parent = element.getParent();
if (parent instanceof QuotableKeywordList) {
PsiElement grandParent = parent.getParent();
isAliasCallArgument = isAliasCallArgument(grandParent);
}
}
return isAliasCallArgument;
}
private boolean isAliasCallAs(@NotNull PsiElement element) {
boolean isAliasCallAs = false;
if (element instanceof ElixirAccessExpression ||
element instanceof QualifiableAlias) {
isAliasCallAs = isAliasCallAs(element.getParent());
} else if (element instanceof QuotableKeywordPair) {
isAliasCallAs = isAliasCallAs((QuotableKeywordPair) element);
}
return isAliasCallAs;
}
}