package org.elixir_lang.psi.scope.call_definition_clause; import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementResolveResult; import com.intellij.psi.ResolveResult; import com.intellij.psi.ResolveState; import com.intellij.psi.util.PsiTreeUtil; import gnu.trove.THashSet; import org.apache.commons.lang.math.IntRange; import org.elixir_lang.psi.call.Call; import org.elixir_lang.psi.call.Named; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Set; import static org.elixir_lang.psi.impl.ElixirPsiImplUtil.ENTRANCE; import static org.elixir_lang.structure_view.element.CallDefinitionClause.nameArityRange; public class MultiResolve extends org.elixir_lang.psi.scope.CallDefinitionClause { /* * Public Static Methods */ @Nullable public static List<ResolveResult> resolveResultList(@NotNull String name, int resolvedFinalArity, boolean incompleteCode, @NotNull PsiElement entrance) { return resolveResultList(name, resolvedFinalArity, incompleteCode, entrance, ResolveState.initial()); } @Nullable public static List<ResolveResult> resolveResultList(@NotNull String name, int resolvedFinalArity, boolean incompleteCode, @NotNull PsiElement entrance, @NotNull ResolveState resolveState) { MultiResolve multiResolve = new MultiResolve(name, resolvedFinalArity, incompleteCode); PsiTreeUtil.treeWalkUp( multiResolve, entrance, entrance.getContainingFile(), resolveState.put(ENTRANCE, entrance) ); return multiResolve.getResolveResultList(); } /* * Fields */ private final boolean incompleteCode; @NotNull private final String name; private final int resolvedFinalArity; @Nullable private Set<PsiElement> resolvedSet = null; @Nullable private List<ResolveResult> resolveResultList = null; /* * Constructors */ private MultiResolve(@NotNull String name, int resolvedFinalArity, boolean incompleteCode) { super(); this.incompleteCode = incompleteCode; this.name = name; this.resolvedFinalArity = resolvedFinalArity; } /* * Public Instance Methods */ @Nullable public List<ResolveResult> getResolveResultList() { return resolveResultList; } /* * Private Instance Methods */ /* * Private Instance Methods */ private boolean addToResolveResultList(@NotNull Call call, boolean validResult, ResolveState state) { boolean keepProcessing = true; if (call instanceof Named) { Named named = (Named) call; PsiElement nameIdentifier = named.getNameIdentifier(); if (nameIdentifier != null) { if (PsiTreeUtil.isAncestor(state.get(ENTRANCE), nameIdentifier, false)) { addNewToResolveResultList(nameIdentifier, validResult); keepProcessing = false; } else { /* Doesn't use a Map<PsiElement, ResolveSet> so that MultiResolve's helpers that require a List<ResolveResult> can still work */ addNewToResolveResultList(nameIdentifier, validResult); Call importCall = state.get(IMPORT_CALL); if (importCall != null) { addNewToResolveResultList(importCall, validResult); } } } } return keepProcessing; } @Override protected boolean executeOnCallDefinitionClause(@NotNull Call element, @NotNull ResolveState state) { boolean keepProcessing = true; Pair<String, IntRange> nameArityRange = nameArityRange(element); if (nameArityRange != null) { String name = nameArityRange.first; if (name.equals(this.name)) { ArityInterval arityInterval = ArityInterval.arityInterval(nameArityRange, state); if (arityInterval.containsInteger(resolvedFinalArity)) { keepProcessing = addToResolveResultList(element, true, state); } else if (incompleteCode) { keepProcessing = addToResolveResultList(element, false, state); } } else if (incompleteCode && name.startsWith(this.name)) { keepProcessing = addToResolveResultList(element, false, state); } // Don't check MultiResolve.keepProcessing in case recursive call of function with multiple clauses } return keepProcessing; } @Override protected boolean keepProcessing() { return org.elixir_lang.psi.scope.MultiResolve.keepProcessing(incompleteCode, resolveResultList); } /* * Private Instance Methods */ private void addNewToResolveResultList(@NotNull PsiElement element, boolean validResult) { if (resolvedSet == null || !resolvedSet.contains(element)) { resolveResultList = org.elixir_lang.psi.scope.MultiResolve.addToResolveResultList( resolveResultList, new PsiElementResolveResult(element, validResult) ); if (resolvedSet == null) { resolvedSet = new THashSet<PsiElement>(); } resolvedSet.add(element); } } }