package org.elixir_lang.structure_view.element; import com.intellij.ide.util.treeView.smartTree.TreeElement; import com.intellij.navigation.ItemPresentation; import com.intellij.psi.ElementDescriptionLocation; import com.intellij.psi.PsiElement; import com.intellij.usageView.UsageViewTypeLocation; import org.elixir_lang.navigation.item_presentation.NameArity; import org.elixir_lang.navigation.item_presentation.Parent; import org.elixir_lang.psi.*; import org.elixir_lang.psi.call.Call; import org.elixir_lang.psi.impl.ElixirPsiImplUtil; import org.elixir_lang.psi.operation.Type; import org.elixir_lang.structure_view.element.modular.Modular; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class Callback extends Element<AtUnqualifiedNoParenthesesCall> implements Timed { /* * Fields */ @NotNull private final Modular modular; /* * * Static Methods * */ /* * Public Static Methods */ public static String elementDescription(Call call, ElementDescriptionLocation location) { String elementDescription = null; if (location == UsageViewTypeLocation.INSTANCE) { elementDescription = "callback"; } return elementDescription; } @Contract(pure = true) @Nullable public static Call headCall(AtUnqualifiedNoParenthesesCall atUnqualifiedNoParenthesesCall) { ElixirNoParenthesesOneArgument noParenthesesOneArgument = atUnqualifiedNoParenthesesCall.getNoParenthesesOneArgument(); PsiElement[] grandChildren = noParenthesesOneArgument.getChildren(); Call headCall = null; if (grandChildren.length == 1) { headCall = specificationHeadCall(grandChildren[0]); } return headCall; } @Contract(pure = true) public static boolean is(@NotNull final Call call) { boolean is = false; if (call instanceof AtUnqualifiedNoParenthesesCall) { AtUnqualifiedNoParenthesesCall atUnqualifiedNoParenthesesCall = (AtUnqualifiedNoParenthesesCall) call; String moduleAttributeName = ElixirPsiImplUtil.moduleAttributeName(atUnqualifiedNoParenthesesCall); if (moduleAttributeName.equals("@callback") || moduleAttributeName.equals("@macrocallback")) { is = true; } } return is; } @Contract(pure = true) @Nullable public static PsiElement nameIdentifier(@NotNull Call call) { PsiElement nameIdentifier = null; if (call instanceof AtUnqualifiedNoParenthesesCall) { nameIdentifier = nameIdentifier((AtUnqualifiedNoParenthesesCall) call); } return nameIdentifier; } @Contract(pure = true) @Nullable public static PsiElement nameIdentifier(@NotNull AtUnqualifiedNoParenthesesCall atUnqualifiedNoParenthesesCall) { Call headCall = headCall(atUnqualifiedNoParenthesesCall); PsiElement nameIdentifier = null; if (headCall != null) { nameIdentifier = CallDefinitionHead.nameIdentifier(headCall); } return nameIdentifier; } /* * Private Static Methods */ @Nullable private static Call parameterizedTypeHeadCall(ElixirMatchedWhenOperation whenOperation) { PsiElement leftOperand = whenOperation.leftOperand(); Call headCall = null; if (leftOperand instanceof Type) { headCall = typeHeadCall((Type) leftOperand); } return headCall; } @Nullable private static Call specificationHeadCall(PsiElement specification) { Call headCall = null; if (specification instanceof Type) { headCall = typeHeadCall((Type) specification); } else if (specification instanceof ElixirMatchedWhenOperation) { headCall = parameterizedTypeHeadCall((ElixirMatchedWhenOperation) specification); } return headCall; } @Nullable private static Call typeHeadCall(Type typeOperation) { PsiElement leftOperand = typeOperation.leftOperand(); Call headCall = null; if (leftOperand instanceof Call) { headCall = (Call) leftOperand; } return headCall; } /* * Constructors */ public Callback(@NotNull Modular modular, Call navigationItem) { super((AtUnqualifiedNoParenthesesCall) navigationItem); this.modular = modular; } /* * * Instance Methods * */ /* * Public Instance Methods */ /** * A callback's {@link #getPresentation()} is a {@link NameArity} like a {@link CallDefinition}, so like a call * definition, it's children are the specifications and clauses, since the callback has no clauses, the only child * is the specification. * * @return the list of children. */ @NotNull @Override public TreeElement[] getChildren() { // pseudo-named-arguments boolean callback = true; //noinspection ConstantConditions return new TreeElement[]{ new CallDefinitionSpecification( modular, navigationItem, callback, time() ) }; } /** * Returns the presentation of the tree element. * * @return the element presentation. */ @NotNull @Override public ItemPresentation getPresentation() { Parent parentPresentation = (Parent) modular.getPresentation(); String location = parentPresentation.getLocatedPresentableText(); PsiElement[] arguments = ElixirPsiImplUtil.finalArguments(navigationItem); assert arguments != null; // pseudo-named-arguments boolean callback = true; Visible.Visibility visibility = Visible.Visibility.PUBLIC; boolean overridable = false; boolean override = false; String name = "?"; int arity = -1; Call headCall = headCall(navigationItem); if (headCall != null) { name = headCall.functionName(); arity = headCall.resolvedFinalArity(); } //noinspection ConstantConditions return new NameArity( location, callback, time(), visibility, overridable, override, name, arity ); } /** * When the defined call is usable * * @return {@link Time#COMPILE} for compile time ({@code defmacro}, {@code defmacrop}); * {@link Time#RUN} for run time {@code def}, {@code defp}) */ @NotNull @Override public Time time() { String moduleAttributeName = ElixirPsiImplUtil.moduleAttributeName(navigationItem); Time time = null; if (moduleAttributeName.equals("@callback")) { time = Time.RUN; } else if (moduleAttributeName.equals("@macrocallback")) { time = Time.COMPILE; } assert time != null; return time; } }