diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java index 535b864c7654..b97bacae4236 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/DefaultChooseByNameItemProvider.java @@ -15,9 +15,10 @@ */ package com.intellij.ide.util.gotoByName; -import com.intellij.openapi.application.ApplicationManager; +import com.intellij.concurrency.JobLauncher; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; @@ -32,14 +33,13 @@ import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.*; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicReferenceArray; public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.gotoByName.ChooseByNameIdea"); - private WeakReference myContext; + private final Reference myContext; public DefaultChooseByNameItemProvider(PsiElement context) { myContext = new WeakReference(context); @@ -60,10 +60,11 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider String matchingPattern = convertToMatchingPattern(base, namePattern); List namesList = new ArrayList(); String[] names = base.getNames(everywhere); - processNamesByPattern(base, names, matchingPattern, new CollectConsumer(namesList)); + CollectConsumer collect = new SynchronizedCollectConsumer(namesList); + processNamesByPattern(base, names, matchingPattern, indicator, collect); indicator.checkCanceled(); - sortNamesList(matchingPattern, namesList); + sortNamesList(matchingPattern, (List)collect.getResult()); indicator.checkCanceled(); @@ -71,7 +72,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider final Map qualifierMatchResults = new THashMap(); Comparator weightComparator = new Comparator() { - Comparator modelComparator = model instanceof Comparator ? (Comparator)model : new PathProximityComparator(model, myContext.get()); + Comparator modelComparator = model instanceof Comparator ? (Comparator)model : new PathProximityComparator(myContext.get()); @Override public int compare(Object o1, Object o2) { @@ -90,7 +91,7 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider for (MatchResult result : namesList) { indicator.checkCanceled(); String name = result.elementName; - + boolean needSeparator = sortedByMatchingDegree && !result.startMatch && afterStartMatch; // use interruptible call if possible @@ -115,14 +116,14 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider continue; } - if (needSeparator && !startMiddleMatchVariants(consumer, qualifierMiddleMatched)) return false; + if (needSeparator && !startMiddleMatchVariants(qualifierMiddleMatched, consumer)) return false; if (!consumer.process(element)) return false; needSeparator = false; afterStartMatch = result.startMatch; } } else if (elements.length == 1 && matchQualifier(elements[0], base, patternsAndMatchers) != null) { - if (needSeparator && !startMiddleMatchVariants(consumer, qualifierMiddleMatched)) return false; + if (needSeparator && !startMiddleMatchVariants(qualifierMiddleMatched, consumer)) return false; if (!consumer.process(elements[0])) return false; afterStartMatch = result.startMatch; } @@ -130,7 +131,8 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider return ContainerUtil.process(qualifierMiddleMatched, consumer); } - private static boolean startMiddleMatchVariants(Processor consumer, List qualifierMiddleMatched) { + private static boolean startMiddleMatchVariants(@NotNull List qualifierMiddleMatched, + @NotNull Processor consumer) { if (!consumer.process(ChooseByNameBase.NON_PREFIX_SEPARATOR)) return false; if (!ContainerUtil.process(qualifierMiddleMatched, consumer)) return false; qualifierMiddleMatched.clear(); @@ -179,8 +181,8 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider } private static MatchResult matchQualifier(@NotNull Object element, - @NotNull final ChooseByNameBase base, - @NotNull List> patternsAndMatchers) { + @NotNull final ChooseByNameBase base, + @NotNull List> patternsAndMatchers) { final String name = base.getModel().getFullName(element); if (name == null) return null; @@ -237,72 +239,41 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider @Override public List filterNames(@NotNull ChooseByNameBase base, @NotNull String[] names, @NotNull String pattern) { final List filtered = new ArrayList(); - processNamesByPattern(base, names, convertToMatchingPattern(base, pattern), new Consumer() { + processNamesByPattern(base, names, convertToMatchingPattern(base, pattern), ProgressIndicatorProvider.getGlobalProgressIndicator(), new Consumer() { @Override public void consume(MatchResult result) { - filtered.add(result.elementName); + synchronized (filtered) { + filtered.add(result.elementName); + } } }); - return filtered; + synchronized (filtered) { + return filtered; + } } private static void processNamesByPattern(@NotNull final ChooseByNameBase base, @NotNull final String[] names, - final String pattern, - final Consumer consumer - ) { + @NotNull final String pattern, + final ProgressIndicator indicator, + @NotNull final Consumer consumer) { final MinusculeMatcher matcher = buildPatternMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE); - int chunks = Math.min(Runtime.getRuntime().availableProcessors(), 4); - final AtomicReferenceArray> arr = new AtomicReferenceArray>(chunks); - Future[] futures = new Future[arr.length() - 1]; - for(int i = 0; i < futures.length; ++i) { - final int finalI = i; - futures[i] = ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - @Override - public void run() { - arr.set(finalI, matchNamesByPatternInChunk(base, finalI, arr.length(), names, pattern, matcher)); - } - }); - } - arr.set(futures.length, matchNamesByPatternInChunk(base, futures.length, arr.length(), names, pattern, matcher)); - - for(int i = arr.length() - 1; i >= 0; --i) { - if (i < futures.length) { - try { - futures[i].get(); - } catch (Exception e) { - throw new RuntimeException(e); + Processor processor = new Processor() { + @Override + public boolean process(String name) { + ProgressManager.checkCanceled(); + MatchResult result = matches(base, pattern, matcher, name); + if (result != null) { + consumer.consume(result); } + return true; } - for (MatchResult result: arr.get(i)) consumer.consume(result); - } + }; + JobLauncher.getInstance().invokeConcurrentlyUnderProgress(Arrays.asList(names), indicator, false, false, processor); } - private static ArrayList matchNamesByPatternInChunk(@NotNull final ChooseByNameBase base, - int index, - int totalWorkers, - @NotNull String[] names, - String pattern, - MinusculeMatcher matcher - - ) { - ArrayList namesList = new ArrayList(); - - int perThreadWork = (names.length / totalWorkers + 1); - int max = Math.min(perThreadWork * (index + 1), names.length); - - for (int i = perThreadWork * index; i < max; ++i) { - ProgressManager.checkCanceled(); - MatchResult result = matches(base, pattern, matcher, names[i]); - if (result != null) { - namesList.add(result); - } - } - - return namesList; - } - - private static String convertToMatchingPattern(ChooseByNameBase base, String pattern) { + @NotNull + private static String convertToMatchingPattern(@NotNull ChooseByNameBase base, @NotNull String pattern) { pattern = removeModelSpecificMarkup(base, pattern); if (!base.canShowListForEmptyPattern()) { @@ -312,7 +283,8 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider return addSearchAnywherePatternDecorationIfNeeded(base, pattern); } - private static String addSearchAnywherePatternDecorationIfNeeded(ChooseByNameBase base, String pattern) { + @NotNull + private static String addSearchAnywherePatternDecorationIfNeeded(@NotNull ChooseByNameBase base, @NotNull String pattern) { String trimmedPattern; if (base.isSearchInAnyPlace() && !(trimmedPattern = pattern.trim()).isEmpty() && trimmedPattern.length() > 1) { pattern = "*" + pattern; @@ -320,7 +292,8 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider return pattern; } - private static String removeModelSpecificMarkup(ChooseByNameBase base, String pattern) { + @NotNull + private static String removeModelSpecificMarkup(@NotNull ChooseByNameBase base, @NotNull String pattern) { if (base.getModel() instanceof GotoClassModel2 && pattern.startsWith("@")) { pattern = pattern.substring(1); } @@ -329,9 +302,9 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider @Nullable private static MatchResult matches(@NotNull ChooseByNameBase base, - @NotNull String pattern, - @NotNull MinusculeMatcher matcher, - @Nullable String name) { + @NotNull String pattern, + @NotNull MinusculeMatcher matcher, + @Nullable String name) { if (name == null) { return null; } @@ -353,11 +326,9 @@ public class DefaultChooseByNameItemProvider implements ChooseByNameItemProvider } private static class PathProximityComparator implements Comparator { - private final ChooseByNameModel myModel; @NotNull private final PsiProximityComparator myProximityComparator; - private PathProximityComparator(@NotNull ChooseByNameModel model, @Nullable final PsiElement context) { - myModel = model; + private PathProximityComparator(@Nullable final PsiElement context) { myProximityComparator = new PsiProximityComparator(context); }