package org.elixir_lang.reference; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import org.apache.commons.lang.math.IntRange; import org.elixir_lang.annonator.Kernel; import org.elixir_lang.psi.AtUnqualifiedNoParenthesesCall; import org.elixir_lang.psi.call.Call; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import static org.elixir_lang.psi.impl.ElixirPsiImplUtil.macroChildCalls; import static org.elixir_lang.structure_view.element.CallDefinitionClause.enclosingModularMacroCall; import static org.elixir_lang.structure_view.element.CallDefinitionSpecification.typeNameArity; public class CallDefinitionClause extends PsiReferenceBase<Call> implements PsiPolyVariantReference { /* * * Fields * */ @NotNull private final AtUnqualifiedNoParenthesesCall moduleAttribute; /* * * Constructors * */ /** * @param call {@code foo(arg1, ...) :: return1} in {@code @spec foo(arg1, ...) :: return1} * @param moduleAttribute {@code @spec foo(arg1, ...) ... return1}, so that the tree doesn't have to be walked up * again if there is a {@code when}. */ public CallDefinitionClause(@NotNull Call call, @NotNull AtUnqualifiedNoParenthesesCall moduleAttribute) { super(call, TextRange.create(0, call.getTextLength())); this.moduleAttribute = moduleAttribute; } /* * * Instance Methods * */ /* * Public Instance Methods */ /** * Returns the array of String, {@link PsiElement} and/or {@link LookupElement} * instances representing all identifiers that are visible at the location of the reference. The contents * of the returned array is used to build the lookup list for basic code completion. (The list * of visible identifiers may not be filtered by the completion prefix string - the * filtering is performed later by IDEA core.) * * @return the array of available identifiers. */ @NotNull @Override public Object[] getVariants() { return new Object[0]; } /** * Returns the results of resolving the reference. * * @param incompleteCode if true, the code in the context of which the reference is * being resolved is considered incomplete, and the method may return additional * invalid results. * @return the array of results for resolving the reference. */ @NotNull @Override public ResolveResult[] multiResolve(boolean incompleteCode) { Call enclosingModularMacroCall = enclosingModularMacroCall(moduleAttribute); List<ResolveResult> resolveResultList = null; if (enclosingModularMacroCall != null) { Call[] siblings = macroChildCalls(enclosingModularMacroCall); if (siblings != null && siblings.length > 0) { Pair<String, Integer> nameArity = typeNameArity(myElement); String name = nameArity.first; int arity = nameArity.second; for (Call call : siblings) { if (org.elixir_lang.structure_view.element.CallDefinitionClause.is(call)) { Pair<String, IntRange> callNameArityRange = org.elixir_lang.structure_view.element.CallDefinitionClause.nameArityRange(call); if (callNameArityRange != null) { String callName = callNameArityRange.first; if (callName.equals(name)) { IntRange callArityRange = callNameArityRange.second; if (callArityRange.containsInteger(arity)) { resolveResultList = add(resolveResultList, call, true); } else if (arity < callArityRange.getMaximumInteger()) { resolveResultList = add(resolveResultList, call, false); } } else if (incompleteCode && callName.startsWith(name)) { IntRange callArityRange = callNameArityRange.second; if (callArityRange.containsInteger(arity)) { resolveResultList = add(resolveResultList, call, false); } else if (arity < callArityRange.getMaximumInteger()) { resolveResultList = add(resolveResultList, call, false); } } } } } } } ResolveResult[] resolveResults; if (resolveResultList != null) { resolveResults = resolveResultList.toArray(new ResolveResult[resolveResultList.size()]); } else { resolveResults = new ResolveResult[0]; } return resolveResults; } /** * Returns the element which is the target of the reference. * * @return the target element, or null if it was not possible to resolve the reference to a valid target. */ @Nullable @Override public PsiElement resolve() { ResolveResult[] resolveResults = multiResolve(false); return resolveResults.length == 1 ? resolveResults[0].getElement() : null; } /* * Private Instance Methods */ @NotNull private List<ResolveResult> add(@Nullable List<ResolveResult> resolveResultList, @NotNull Call call, boolean validResult) { if (resolveResultList == null) { resolveResultList = new ArrayList<ResolveResult>(); } resolveResultList.add(new PsiElementResolveResult(call, validResult)); return resolveResultList; } }