diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java index 79fb585ea60a..782b87ee4274 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java @@ -18,7 +18,6 @@ package com.intellij.psi.impl.search; import com.google.common.annotations.VisibleForTesting; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.application.AccessToken; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.QueryExecutorBase; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; @@ -28,7 +27,6 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.LanguageLevelModuleExtension; import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; @@ -45,6 +43,7 @@ import com.intellij.psi.stubs.StubIndex; import com.intellij.psi.stubs.StubIndexKey; import com.intellij.psi.util.*; import com.intellij.util.Processor; +import com.intellij.util.Processors; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; import com.intellij.util.containers.MultiMap; @@ -102,14 +101,7 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase queries = collectQueryKeys(descriptor, highLevelModules); - for (Map.Entry> entry : getCandidateOffsets(descriptor, queries).entrySet()) { - if (!processFile(consumer, funInterface, entry.getKey(), entry.getValue())) { - return; - } - } + if (descriptor == null || !descriptor.search(consumer, highLevelModules)) return; } } @@ -188,13 +180,12 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase collectQueryKeys(SamDescriptor descriptor, Set candidateModules) { + private static MultiMap collectQueryKeys(SamDescriptor descriptor, Iterable methods) { MultiMap queries = MultiMap.createSet(); queries.putValue(Location.UNKNOWN, descriptor.useScope); - queries.putValue(new FunctionalExpressionKey.TypedLocation(assertNotNull(descriptor.samClass.getName())), descriptor.useScope); + queries.putValue(new FunctionalExpressionKey.TypedLocation(descriptor.getClassName()), descriptor.useScope); - //collect all methods with parameter of functional interface or free type parameter type - for (final PsiMethod psiMethod : getCandidateMethodsWithSuitableParams(descriptor, candidateModules)) { + for (PsiMethod psiMethod : methods) { ReadAction.run(() -> { if (!psiMethod.isValid()) return; @@ -284,47 +275,17 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase getCandidateMethodsWithSuitableParams(SamDescriptor descriptor, Set candidateModules) { - return ApplicationManager.getApplication().runReadAction(new Computable>() { - @Override - public Collection compute() { - if (!descriptor.samClass.isValid()) return Collections.emptyList(); - - GlobalSearchScope visibleFromCandidates = combineResolveScopes(candidateModules, descriptor.samClass); - if (visibleFromCandidates == null) return Collections.emptyList(); - - Set usedMethodNames = collectMethodNamesCalledWithFunExpressions(descriptor); - - Set methods = ContainerUtil.newLinkedHashSet(); - Processor methodProcessor = method -> { - if (usedMethodNames.contains(method.getName())) { - methods.add(method); - } - return true; - }; - - StubIndexKey key = JavaMethodParameterTypesIndex.getInstance().getKey(); - StubIndex index = StubIndex.getInstance(); - Project project = descriptor.samClass.getProject(); - index.processElements(key, assertNotNull(descriptor.samClass.getName()), project, descriptor.useScope.intersectWith(visibleFromCandidates), PsiMethod.class, methodProcessor); - index.processElements(key, JavaMethodElementType.TYPE_PARAMETER_PSEUDO_NAME, project, visibleFromCandidates, PsiMethod.class, methodProcessor); - LOG.debug("#methods: " + methods.size()); - return methods; - } - }); - } - @NotNull private static Set collectMethodNamesCalledWithFunExpressions(SamDescriptor descriptor) { Set usedMethodNames = new HashSet<>(); - FileBasedIndex.getInstance().processAllKeys(JavaFunctionalExpressionIndex.INDEX_ID, key -> { + ReadAction.run(() -> FileBasedIndex.getInstance().processAllKeys(JavaFunctionalExpressionIndex.INDEX_ID, key -> { ProgressManager.checkCanceled(); if (key.canRepresent(descriptor.samParamCount, descriptor.booleanCompatible, descriptor.isVoid) && key.location instanceof CallLocation) { usedMethodNames.add(((CallLocation)key.location).methodName); } return true; - }, descriptor.useScope, null); + }, descriptor.useScope, null)); return usedMethodNames; } @@ -404,6 +365,84 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase consumer, Set highLevelModules) { + GlobalSearchScope visibleFromCandidates = ReadAction.compute( + () -> samClass.isValid() ? combineResolveScopes(highLevelModules, samClass) : null); + if (visibleFromCandidates == null) return true; + + Set usedMethodNames = collectMethodNamesCalledWithFunExpressions(this); + Set exactTypeMethods = getMethodsWithParameterType(usedMethodNames, useScope.intersectWith(visibleFromCandidates), getClassName()); + Set genericMethods = getMethodsWithParameterType(usedMethodNames, visibleFromCandidates, JavaMethodElementType.TYPE_PARAMETER_PSEUDO_NAME); + LOG.debug("#methods: " + (exactTypeMethods.size() + genericMethods.size())); + + MultiMap queries = collectQueryKeys(this, ContainerUtil.concat(exactTypeMethods, genericMethods)); + return processLikelyFirst(getMostLikelyFiles(exactTypeMethods), consumer, queries); + } + + private boolean processLikelyFirst(Set likelyFiles, + @NotNull Processor consumer, + MultiMap queries) { + MultiMap file2Offsets = getCandidateOffsets(this, queries); + for (VirtualFile file : likelyFiles) { + Collection offsets = file2Offsets.remove(file); + if (offsets != null && !processFile(consumer, samClass, file, offsets)) { + return false; + } + } + + for (Map.Entry> entry : file2Offsets.entrySet()) { + if (!processFile(consumer, samClass, entry.getKey(), entry.getValue())) { + return false; + } + } + return true; + } + + @NotNull + private Set getMostLikelyFiles(Set exactTypeMethods) { + Set files = ContainerUtil.newLinkedHashSet(); + ReadAction.run(() -> { + if (!samClass.isValid()) return; + + Set likelyNames = ContainerUtil.newLinkedHashSet(getClassName()); + for (PsiMethod method : exactTypeMethods) { + likelyNames.add(method.getName()); + } + + PsiSearchHelperImpl helper = (PsiSearchHelperImpl)PsiSearchHelper.SERVICE.getInstance(samClass.getProject()); + Processor processor = Processors.cancelableCollectProcessor(files); + for (String name : likelyNames) { + helper.processFilesWithText(this.useScope, UsageSearchContext.IN_CODE, true, name, processor); + } + }); + return files; + } + + private Set getMethodsWithParameterType(Set usedMethodNames, + GlobalSearchScope scope, + String type) { + Set methods = new HashSet<>(); + ReadAction.run(() -> { + if (!samClass.isValid()) return; + + StubIndexKey key = JavaMethodParameterTypesIndex.getInstance().getKey(); + StubIndex.getInstance().processElements(key, type, samClass.getProject(), scope, PsiMethod.class, method -> { + ProgressManager.checkCanceled(); + if (usedMethodNames.contains(method.getName())) { + methods.add(method); + } + + return true; + }); + }); + return methods; + } + + @NotNull + String getClassName() { + return assertNotNull(samClass.getName()); + } + List generateKeys(Location location) { List result = new ArrayList<>(); for (int lambdaParamCount : new int[]{FunctionalExpressionKey.UNKNOWN_PARAM_COUNT, samParamCount}) {