package com.siberika.idea.pascal.ide.actions; import com.intellij.openapi.application.QueryExecutorBase; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.psi.search.searches.DefinitionsScopedSearch; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Processor; import com.siberika.idea.pascal.lang.psi.PasEntityScope; import com.siberika.idea.pascal.lang.psi.PasExportedRoutine; import com.siberika.idea.pascal.lang.psi.PasRoutineImplDecl; import com.siberika.idea.pascal.lang.psi.PascalStructType; import com.siberika.idea.pascal.lang.psi.impl.PascalRoutineImpl; import com.siberika.idea.pascal.util.PsiUtil; import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.LinkedHashSet; /** * Author: George Bakhtadze * Date: 02/07/2015 */ public class PascalDefinitionsSearch extends QueryExecutorBase<PsiElement, DefinitionsScopedSearch.SearchParameters> { private static final Logger LOG = Logger.getInstance(IntfImplNavAction.class.getName()); private static final int MAX_RECURSION = 10; public PascalDefinitionsSearch() { super(true); } @Override public void processQuery(@NotNull DefinitionsScopedSearch.SearchParameters queryParameters, @NotNull Processor<PsiElement> consumer) { Collection<PasEntityScope> targets = findImplementations(queryParameters.getElement(), GotoSuper.LIMIT_NONE, 0); for (PsiElement target : targets) { consumer.process(target); } } public static Collection<PasEntityScope> findImplementations(PsiElement element, Integer limit, int rCnt) { Collection<PasEntityScope> targets = new LinkedHashSet<PasEntityScope>(); PascalRoutineImpl routine = element instanceof PascalRoutineImpl ? (PascalRoutineImpl) element : PsiTreeUtil.getParentOfType(element, PascalRoutineImpl.class); if (routine != null) { findImplementingMethods(targets, routine, limit, 0); } else { findDescendingStructs(targets, PsiUtil.getStructByElement(element), limit, 0); } return targets; } public static void findImplementingMethods(Collection<PasEntityScope> targets, PascalRoutineImpl routine, Integer limit, int rCnt) { Collection<PasEntityScope> found = new LinkedHashSet<PasEntityScope>(); Collection<PasEntityScope> scopes = new LinkedHashSet<PasEntityScope>(); if (routine instanceof PasRoutineImplDecl) { PsiElement el = SectionToggle.retrieveDeclaration(routine, false); if (el instanceof PasExportedRoutine) { routine = (PascalRoutineImpl) el; } else { return; } } PascalStructType struct = PsiUtil.getStructByElement(routine); findDescendingStructs(scopes, struct, limit != null ? GotoSuper.LIMIT_FIRST_ATTEMPT : GotoSuper.LIMIT_NONE, rCnt); GotoSuper.extractMethodsByName(found, scopes, routine, false, GotoSuper.calcRemainingLimit(targets, limit), 0); if ((limit != null) && (scopes.size() == GotoSuper.LIMIT_FIRST_ATTEMPT) && (found.size() < limit)) { // second attempt if the first one not found all results scopes = new LinkedHashSet<PasEntityScope>(); findDescendingStructs(scopes, PsiUtil.getStructByElement(routine), GotoSuper.LIMIT_NONE, rCnt); GotoSuper.extractMethodsByName(targets, scopes, routine, false, GotoSuper.calcRemainingLimit(targets, limit), 0); } else { targets.addAll(found); } } public static void findDescendingStructs(Collection<PasEntityScope> targets, PascalStructType struct, Integer limit, int rCnt) { if ((limit != null) && (limit <= 0)) { return; } if (rCnt > MAX_RECURSION) { LOG.error("Max recursion reached"); return; } if ((null == struct) || (null == struct.getNameIdentifier())) { return; } for (PsiReference psiReference : ReferencesSearch.search(struct.getNameIdentifier())) { if ((limit != null) && (limit <= targets.size())) { return; } if (PsiUtil.isClassParent(psiReference.getElement())) { struct = PsiUtil.getStructByElement(psiReference.getElement()); if (PsiUtil.isElementUsable(struct)) { targets.add(struct); findDescendingStructs(targets, struct, GotoSuper.calcRemainingLimit(targets, limit), rCnt + 1); } } } } }