From ff80b5e6a5d0e27fbdfac71a44f96757af7cf50b Mon Sep 17 00:00:00 2001 From: "sergey.vasiliev" Date: Tue, 17 Jan 2017 12:13:04 +0100 Subject: [PATCH] fixed incorrect collecting of PsiReference[] from PsiProviders with lower priority --- .../ReferenceProvidersRegistryImpl.java | 120 ++++++++++++------ 1 file changed, 84 insertions(+), 36 deletions(-) diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistryImpl.java b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistryImpl.java index feae5aa673a4..d00488555ea9 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistryImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistryImpl.java @@ -18,12 +18,13 @@ package com.intellij.psi.impl.source.resolve.reference; import com.intellij.lang.Language; import com.intellij.lang.LanguageExtension; import com.intellij.openapi.project.IndexNotReadyException; -import com.intellij.openapi.util.Comparing; import com.intellij.patterns.ElementPattern; import com.intellij.psi.*; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.*; @@ -31,15 +32,6 @@ public class ReferenceProvidersRegistryImpl extends ReferenceProvidersRegistry { private static final LanguageExtension CONTRIBUTOR_EXTENSION = new LanguageExtension(PsiReferenceContributor.EP_NAME.getName()); private static final LanguageExtension REFERENCE_PROVIDER_EXTENSION = new LanguageExtension(PsiReferenceProviderBean.EP_NAME.getName()); - private static final Comparator> PRIORITY_COMPARATOR = - new Comparator>() { - @Override - public int compare(ProviderBinding.ProviderInfo o1, - ProviderBinding.ProviderInfo o2) { - return Comparing.compare(o2.priority, o1.priority); - } - }; - private final Map myRegistrars = ContainerUtil.newConcurrentMap(); @NotNull @@ -97,46 +89,102 @@ public class ReferenceProvidersRegistryImpl extends ReferenceProvidersRegistry { @NotNull @Override + // 1. we create priorities map: "priority" -> non-empty references from providers + // if provider returns EMPTY_ARRAY or array with "null" references then this provider isn't added in priorities map. + // 2. references with the highest priority are added "as is" + // 3. all other references are added only they could be correctly merged with any reference with higher priority (ReferenceRange.containsRangeInElement(higherPriorityRef, lowerPriorityRef) protected PsiReference[] doGetReferencesFromProviders(@NotNull PsiElement context, @NotNull PsiReferenceService.Hints hints) { List> providers = getRegistrar(context.getLanguage()).getPairsByElement(context, hints); - if (providers.isEmpty()) { - return PsiReference.EMPTY_ARRAY; - } + final MultiMap allReferencesMap = mapNotEmptyReferencesFromProviders(context, providers); - if (providers.size() == 1) { - return providers.get(0).provider.getReferencesByElement(context, providers.get(0).processingContext); - } + if (allReferencesMap.isEmpty()) return PsiReference.EMPTY_ARRAY; - ContainerUtil.sort(providers, PRIORITY_COMPARATOR); + final List result = ContainerUtil.newSmartList(); + final double maxPriority = getMaxPriority(allReferencesMap.keySet()); + final List maxPriorityRefs = collectReferences(allReferencesMap.get(maxPriority)); - List result = new ArrayList(); - final double maxPriority = providers.get(0).priority; - next: + ContainerUtil.addAllNotNull(result, maxPriorityRefs); + ContainerUtil.addAllNotNull(result, getLowerPriorityReferences(allReferencesMap, maxPriority, maxPriorityRefs)); + + return result.toArray(new PsiReference[result.size()]); + } + + @NotNull + // we create priorities map: "priority" -> non-empty references from providers + // if provider returns EMPTY_ARRAY or array with "null" references then this provider isn't added in priorities map. + private static MultiMap mapNotEmptyReferencesFromProviders(@NotNull PsiElement context, + @NotNull List> providers) { + MultiMap map = new MultiMap(); for (ProviderBinding.ProviderInfo trinity : providers) { - final PsiReference[] refs; - try { - refs = trinity.provider.getReferencesByElement(context, trinity.processingContext); + final PsiReference[] refs = getReferences(context, trinity); + if (refs.length > 0) { + map.putValue(trinity.priority, refs); } - catch(IndexNotReadyException ex) { - continue; - } - if (trinity.priority != maxPriority) { - for (PsiReference ref : refs) { - for (PsiReference reference : result) { - if (ref != null && ReferenceRange.containsRangeInElement(reference, ref.getRangeInElement())) { - continue next; - } + } + return map; + } + + @NotNull + private static PsiReference[] getReferences(@NotNull PsiElement context, + @NotNull ProviderBinding.ProviderInfo providerInfo) { + try { + return providerInfo.provider.getReferencesByElement(context, providerInfo.processingContext); + } + catch (IndexNotReadyException ignored) { + } + return PsiReference.EMPTY_ARRAY; + } + + @NotNull + private static List getLowerPriorityReferences(@NotNull MultiMap allReferencesMap, + double maxPriority, + @NotNull List maxPriorityRefs) { + List result = ContainerUtil.newSmartList(); + for (Map.Entry> entry : allReferencesMap.entrySet()) { + if (maxPriority != entry.getKey().doubleValue()) { + for (PsiReference[] references : entry.getValue()) { + if (haveNotIntersectedTextRanges(maxPriorityRefs, references)) { + ContainerUtil.addAllNotNull(result, references); } } } - for (PsiReference ref : refs) { - if (ref != null) { - result.add(ref); + } + return result; + } + + private static boolean haveNotIntersectedTextRanges(@NotNull List higherPriorityRefs, + @NotNull PsiReference[] lowerPriorityRefs) { + for (PsiReference ref : lowerPriorityRefs) { + if (ref != null) { + for (PsiReference reference : higherPriorityRefs) { + if (reference != null && ReferenceRange.containsRangeInElement(reference, ref.getRangeInElement())) { + return false; + } } } } - return result.isEmpty() ? PsiReference.EMPTY_ARRAY : ContainerUtil.toArray(result, new PsiReference[result.size()]); + return true; + } + + @NotNull + private static List collectReferences(@Nullable Collection references) { + if (references == null) return Collections.emptyList(); + List list = ContainerUtil.newSmartList(); + for (PsiReference[] reference : references) { + ContainerUtil.addAllNotNull(list, reference); + } + + return list; + } + + private static double getMaxPriority(@NotNull Set doubles) { + //return doubles.stream().mapToDouble(Double::doubleValue).max().getAsDouble(); + double maxPriority = PsiReferenceRegistrar.LOWER_PRIORITY; + for (Double aDouble : doubles) { + if (aDouble.doubleValue() > maxPriority) maxPriority = aDouble.doubleValue(); + } + return maxPriority; } }