optimized find usages for nested searches; groovy/++ constructor usages using that API

This commit is contained in:
peter
2010-07-27 23:33:28 +01:00
parent 7a0fad2ea3
commit 9eb2032dab
9 changed files with 219 additions and 100 deletions
@@ -17,11 +17,9 @@ package com.intellij.psi.search.searches;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.SearchRequestCollector;
import com.intellij.psi.search.SearchRequestQuery;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.*;
import com.intellij.util.MergeQuery;
import com.intellij.util.PairProcessor;
import com.intellij.util.Query;
import com.intellij.util.UniqueResultsQuery;
import gnu.trove.TObjectHashingStrategy;
@@ -75,6 +73,12 @@ public class MethodReferencesSearch extends ExtensibleQueryFactory<PsiReference,
return search(new SearchParameters(method, scope, strictSignatureSearch));
}
public static void searchOptimized(final PsiMethod method, SearchScope scope, final boolean strictSignatureSearch, SearchRequestCollector collector, final boolean inReadAction, PairProcessor<PsiReference, SearchRequestCollector> processor) {
final SearchRequestCollector nested = new SearchRequestCollector();
collector.searchQuery(new QuerySearchRequest(search(new SearchParameters(method, scope, strictSignatureSearch, nested)), nested,
inReadAction, processor));
}
public static Query<PsiReference> search(final SearchParameters parameters) {
final Query<PsiReference> result = INSTANCE.createQuery(parameters);
if (parameters.isSharedOptimizer) {
@@ -25,4 +25,8 @@ public class PsiSearchRequest {
this.processor = processor;
}
@Override
public String toString() {
return "PsiSearchRequest: " + word + "; " + processor;
}
}
@@ -0,0 +1,43 @@
package com.intellij.psi.search;
import com.intellij.openapi.application.ReadActionProcessor;
import com.intellij.psi.PsiReference;
import com.intellij.util.PairProcessor;
import com.intellij.util.Processor;
import com.intellij.util.Query;
/**
* @author peter
*/
public class QuerySearchRequest {
public final Query<PsiReference> query;
public final SearchRequestCollector collector;
public final Processor<PsiReference> processor;
public QuerySearchRequest(Query<PsiReference> query,
final SearchRequestCollector collector,
boolean inReadAction, final PairProcessor<PsiReference, SearchRequestCollector> processor) {
this.query = query;
this.collector = collector;
if (inReadAction) {
this.processor = new ReadActionProcessor<PsiReference>() {
@Override
public boolean processInReadAction(PsiReference psiReference) {
return processor.process(psiReference, collector);
}
};
} else {
this.processor = new Processor<PsiReference>() {
@Override
public boolean process(PsiReference psiReference) {
return processor.process(psiReference, collector);
}
};
}
}
public void runQuery() {
query.forEach(processor);
}
}
@@ -3,6 +3,7 @@ package com.intellij.psi.search;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.util.Processor;
import com.intellij.util.containers.CollectionFactory;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@@ -12,8 +13,10 @@ import java.util.List;
* @author peter
*/
public class SearchRequestCollector {
private final List<PsiSearchRequest> myRequests = new ArrayList<PsiSearchRequest>();
private final List<Processor<Processor<PsiReference>>> myCustomSearchActions = new ArrayList<Processor<Processor<PsiReference>>>();
private final Object lock = new Object();
private final List<PsiSearchRequest> myWordRequests = CollectionFactory.arrayList();
private final List<QuerySearchRequest> myQueryRequests = CollectionFactory.arrayList();
private final List<Processor<Processor<PsiReference>>> myCustomSearchActions = CollectionFactory.arrayList();
public void searchWord(@NotNull String word, @NotNull SearchScope searchScope, boolean caseSensitive, @NotNull PsiElement searchTarget) {
final short searchContext = UsageSearchContext.IN_CODE | UsageSearchContext.IN_FOREIGN_LANGUAGES | UsageSearchContext.IN_COMMENTS;
@@ -29,18 +32,48 @@ public class SearchRequestCollector {
return;
}
myRequests.add(new PsiSearchRequest(searchScope, word, searchContext, caseSensitive, processor));
synchronized (lock) {
myWordRequests.add(new PsiSearchRequest(searchScope, word, searchContext, caseSensitive, processor));
}
}
public void searchQuery(QuerySearchRequest request) {
assert request.collector != this;
synchronized (lock) {
myQueryRequests.add(request);
}
}
public void searchCustom(Processor<Processor<PsiReference>> searchAction) {
myCustomSearchActions.add(searchAction);
synchronized (lock) {
myCustomSearchActions.add(searchAction);
}
}
public List<PsiSearchRequest> getSearchRequests() {
return myRequests;
public boolean hasRequests() {
synchronized (lock) {
return !myWordRequests.isEmpty() || !myCustomSearchActions.isEmpty() || !myQueryRequests.isEmpty();
}
}
public List<Processor<Processor<PsiReference>>> getCustomSearchActions() {
return myCustomSearchActions;
public List<QuerySearchRequest> takeQueryRequests() {
return takeRequests(myQueryRequests);
}
private <T> List<T> takeRequests(List<T> list) {
synchronized (lock) {
final List<T> requests = new ArrayList<T>(list);
requests.addAll(list);
list.clear();
return requests;
}
}
public List<PsiSearchRequest> takeSearchRequests() {
return takeRequests(myWordRequests);
}
public List<Processor<Processor<PsiReference>>> takeCustomSearchActions() {
return takeRequests(myCustomSearchActions);
}
}
@@ -17,10 +17,7 @@ package com.intellij.psi.search.searches;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.SearchRequestCollector;
import com.intellij.psi.search.SearchRequestQuery;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.*;
import com.intellij.util.*;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.NotNull;
@@ -111,4 +108,12 @@ public class ReferencesSearch extends ExtensibleQueryFactory<PsiReference, Refer
return new UniqueResultsQuery(composite, TObjectHashingStrategy.CANONICAL, ReferenceDescriptor.MAPPER);
}
public static void searchOptimized(@NotNull PsiElement element, @NotNull SearchScope searchScope, boolean ignoreAccessScope,
@NotNull SearchRequestCollector collector, final boolean inReadAction, PairProcessor<PsiReference, SearchRequestCollector> processor) {
final SearchRequestCollector nested = new SearchRequestCollector();
collector.searchQuery(new QuerySearchRequest(search(new SearchParameters(element, searchScope, ignoreAccessScope, nested)), nested,
inReadAction, processor));
}
}
@@ -31,6 +31,7 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
@@ -487,34 +488,62 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
myManager.getCacheManager().processFilesWithWord(processor, word, UsageSearchContext.IN_STRINGS, scope, true);
}
public boolean processRequests(@NotNull SearchRequestCollector request, Processor<PsiReference> processor) {
final MultiMap<Set<IdIndexEntry>, PsiSearchRequest> singles = new MultiMap<Set<IdIndexEntry>, PsiSearchRequest>();
final List<Processor<Processor<PsiReference>>> customs = new ArrayList<Processor<Processor<PsiReference>>>();
distributePrimitives(request, singles, customs);
if (!processRequestsOptimized(singles, processor)) {
return false;
private static class RequestWithProcessor extends Pair<PsiSearchRequest, Processor<PsiReference>> {
private RequestWithProcessor(PsiSearchRequest first, Processor<PsiReference> second) {
super(first, second);
}
for (Processor<Processor<PsiReference>> custom : customs) {
if (!custom.process(processor)) {
return false;
}
}
return true;
}
private boolean processRequestsOptimized(MultiMap<Set<IdIndexEntry>, PsiSearchRequest> singles, final Processor<PsiReference> consumer) {
public boolean processRequests(@NotNull SearchRequestCollector collector, Processor<PsiReference> processor) {
Map<SearchRequestCollector, Processor<PsiReference>> collectors = new HashMap<SearchRequestCollector, Processor<PsiReference>>();
collectors.put(collector, processor);
appendCollectorsFromQueryRequests(collectors);
do {
final MultiMap<Set<IdIndexEntry>, RequestWithProcessor> singles = new MultiMap<Set<IdIndexEntry>, RequestWithProcessor>();
final List<Computable<Boolean>> customs = new ArrayList<Computable<Boolean>>();
distributePrimitives(collectors, singles, customs);
if (!processRequestsOptimized(singles)) {
return false;
}
for (Computable<Boolean> custom : customs) {
if (!custom.compute()) {
return false;
}
}
} while (appendCollectorsFromQueryRequests(collectors));
return true;
}
private static boolean appendCollectorsFromQueryRequests(Map<SearchRequestCollector, Processor<PsiReference>> collectors) {
boolean changed = false;
LinkedList<SearchRequestCollector> queue = new LinkedList<SearchRequestCollector>(collectors.keySet());
while (!queue.isEmpty()) {
final SearchRequestCollector each = queue.removeFirst();
for (QuerySearchRequest request : each.takeQueryRequests()) {
request.runQuery();
collectors.put(request.collector, request.processor);
queue.addLast(request.collector);
changed = true;
}
}
return changed;
}
private boolean processRequestsOptimized(MultiMap<Set<IdIndexEntry>, RequestWithProcessor> singles) {
if (singles.isEmpty()) {
return true;
}
if (singles.size() == 1) {
final Collection<PsiSearchRequest> requests = singles.get(singles.keySet().iterator().next());
final Collection<RequestWithProcessor> requests = singles.get(singles.keySet().iterator().next());
if (requests.size() == 1) {
return processSingleRequest(requests.iterator().next(), consumer);
final RequestWithProcessor theOnly = requests.iterator().next();
return processSingleRequest(theOnly.first, theOnly.second);
}
}
@@ -524,13 +553,13 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
progress.setText(PsiBundle.message("psi.scanning.files.progress"));
}
final MultiMap<VirtualFile, PsiSearchRequest> candidateFiles = collectFiles(singles);
final MultiMap<VirtualFile, RequestWithProcessor> candidateFiles = collectFiles(singles);
final Map<PsiSearchRequest, StringSearcher> searchers = new HashMap<PsiSearchRequest, StringSearcher>();
final Map<RequestWithProcessor, StringSearcher> searchers = new HashMap<RequestWithProcessor, StringSearcher>();
final Set<String> allWords = new TreeSet<String>();
for (PsiSearchRequest singleRequest : candidateFiles.values()) {
searchers.put(singleRequest, new StringSearcher(singleRequest.word, singleRequest.caseSensitive, true));
allWords.add(singleRequest.word);
for (RequestWithProcessor singleRequest : candidateFiles.values()) {
searchers.put(singleRequest, new StringSearcher(singleRequest.first.word, singleRequest.first.caseSensitive, true));
allWords.add(singleRequest.first.word);
}
if (progress != null) {
@@ -540,9 +569,9 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
return processPsiFileRoots(progress, new ArrayList<VirtualFile>(candidateFiles.keySet()), new Processor<PsiElement>() {
public boolean process(PsiElement psiRoot) {
final VirtualFile vfile = psiRoot.getContainingFile().getVirtualFile();
for (final PsiSearchRequest singleRequest : candidateFiles.get(vfile)) {
for (final RequestWithProcessor singleRequest : candidateFiles.get(vfile)) {
StringSearcher searcher = searchers.get(singleRequest);
if (!LowLevelSearchUtil.processElementsContainingWordInElement(adaptProcessor(singleRequest, consumer), psiRoot, searcher, false, progress)) {
if (!LowLevelSearchUtil.processElementsContainingWordInElement(adaptProcessor(singleRequest.first, singleRequest.second), psiRoot, searcher, false, progress)) {
return false;
}
}
@@ -565,18 +594,18 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
};
}
private MultiMap<VirtualFile, PsiSearchRequest> collectFiles(MultiMap<Set<IdIndexEntry>, PsiSearchRequest> singles) {
private MultiMap<VirtualFile, RequestWithProcessor> collectFiles(MultiMap<Set<IdIndexEntry>, RequestWithProcessor> singles) {
final ProjectFileIndex index = ProjectRootManager.getInstance(myManager.getProject()).getFileIndex();
final MultiMap<VirtualFile, PsiSearchRequest> result = new MultiMap<VirtualFile, PsiSearchRequest>();
final MultiMap<VirtualFile, RequestWithProcessor> result = new MultiMap<VirtualFile, RequestWithProcessor>();
for (Set<IdIndexEntry> key : singles.keySet()) {
final Collection<PsiSearchRequest> data = singles.get(key);
final Collection<RequestWithProcessor> data = singles.get(key);
GlobalSearchScope commonScope = uniteScopes(data);
MultiMap<VirtualFile, PsiSearchRequest> intersection = null;
MultiMap<VirtualFile, RequestWithProcessor> intersection = null;
boolean first = true;
for (IdIndexEntry entry : key) {
final MultiMap<VirtualFile, PsiSearchRequest> local = findFilesWithIndexEntry(entry, index, data, commonScope);
final MultiMap<VirtualFile, RequestWithProcessor> local = findFilesWithIndexEntry(entry, index, data, commonScope);
if (first) {
intersection = local;
first = false;
@@ -593,31 +622,34 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
return result;
}
private static GlobalSearchScope uniteScopes(Collection<PsiSearchRequest> requests) {
private static GlobalSearchScope uniteScopes(Collection<RequestWithProcessor> requests) {
GlobalSearchScope commonScope = null;
for (PsiSearchRequest r : requests) {
final GlobalSearchScope scope = (GlobalSearchScope)r.searchScope;
for (RequestWithProcessor r : requests) {
final GlobalSearchScope scope = (GlobalSearchScope)r.first.searchScope;
commonScope = commonScope == null ? scope : commonScope.uniteWith(scope);
}
assert commonScope != null;
return commonScope;
}
private static MultiMap<VirtualFile, PsiSearchRequest> findFilesWithIndexEntry(final IdIndexEntry entry,
private static MultiMap<VirtualFile, RequestWithProcessor> findFilesWithIndexEntry(final IdIndexEntry entry,
final ProjectFileIndex index,
final Collection<PsiSearchRequest> data,
final Collection<RequestWithProcessor> data,
final GlobalSearchScope commonScope) {
final MultiMap<VirtualFile, PsiSearchRequest> local = new MultiMap<VirtualFile, PsiSearchRequest>();
final MultiMap<VirtualFile, RequestWithProcessor> local = new MultiMap<VirtualFile, RequestWithProcessor>();
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
ProgressManager.checkCanceled();
FileBasedIndex.getInstance().processValues(IdIndex.NAME, entry, null, new FileBasedIndex.ValueProcessor<Integer>() {
public boolean process(VirtualFile file, Integer value) {
ProgressManager.checkCanceled();
if (!IndexCacheManagerImpl.shouldBeFound(file, index)) {
return true;
}
int mask = value.intValue();
for (PsiSearchRequest single : data) {
if ((mask & single.searchContext) != 0 && ((GlobalSearchScope)single.searchScope).contains(file)) {
for (RequestWithProcessor single : data) {
final PsiSearchRequest request = single.first;
if ((mask & request.searchContext) != 0 && ((GlobalSearchScope)request.searchScope).contains(file)) {
local.putValue(file, single);
}
}
@@ -630,27 +662,38 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
return local;
}
private void distributePrimitives(SearchRequestCollector request,
MultiMap<Set<IdIndexEntry>, PsiSearchRequest> singles,
List<Processor<Processor<PsiReference>>> customs) {
for (final PsiSearchRequest primitive : request.getSearchRequests()) {
final SearchScope scope = primitive.searchScope;
if (scope instanceof LocalSearchScope) {
customs.add(new Processor<Processor<PsiReference>>() {
public boolean process(Processor<PsiReference> processor) {
return processSingleRequest(primitive, processor);
private void distributePrimitives(final Map<SearchRequestCollector, Processor<PsiReference>> collectors,
MultiMap<Set<IdIndexEntry>, RequestWithProcessor> singles,
List<Computable<Boolean>> customs) {
for (final SearchRequestCollector collector : collectors.keySet()) {
final Processor<PsiReference> processor = collectors.get(collector);
for (final PsiSearchRequest primitive : collector.takeSearchRequests()) {
final SearchScope scope = primitive.searchScope;
if (scope instanceof LocalSearchScope) {
customs.add(new Computable<Boolean>() {
@Override
public Boolean compute() {
return processSingleRequest(primitive, processor);
}
});
} else {
final List<String> words = StringUtil.getWordsIn(primitive.word);
final Set<IdIndexEntry> key = new HashSet<IdIndexEntry>(words.size() * 2);
for (String word : words) {
key.add(new IdIndexEntry(word, primitive.caseSensitive));
}
singles.putValue(key, new RequestWithProcessor(primitive, processor));
}
}
for (final Processor<Processor<PsiReference>> customAction : collector.takeCustomSearchActions()) {
customs.add(new Computable<Boolean>() {
@Override
public Boolean compute() {
return customAction.process(processor);
}
});
} else {
final List<String> words = StringUtil.getWordsIn(primitive.word);
final Set<IdIndexEntry> key = new HashSet<IdIndexEntry>(words.size() * 2);
for (String word : words) {
key.add(new IdIndexEntry(word, primitive.caseSensitive));
}
singles.putValue(key, primitive);
}
}
customs.addAll(request.getCustomSearchActions());
}
private boolean processSingleRequest(PsiSearchRequest single, Processor<PsiReference> consumer) {
@@ -31,7 +31,7 @@ public class ConstructorReferencesSearcher implements QueryExecutor<PsiReference
if (element instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)element;
if (method.isConstructor()) {
return GroovyConstructorUsagesSearchHelper.execute(method, queryParameters.getScope(), consumer);
return GroovyConstructorUsagesSearchHelper.processConstructorUsages(method, queryParameters.getScope(), consumer, queryParameters.getOptimizer());
}
}
return true;
@@ -17,7 +17,6 @@
package org.jetbrains.plugins.groovy.findUsages;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadActionProcessor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Computable;
@@ -26,9 +25,7 @@ import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightMemberReference;
import com.intellij.psi.search.DelegatingGlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.*;
import com.intellij.psi.search.searches.AnnotatedElementsSearch;
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
import com.intellij.psi.search.searches.MethodReferencesSearch;
@@ -37,6 +34,7 @@ import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.PairProcessor;
import com.intellij.util.Processor;
import com.intellij.util.containers.ConcurrentHashSet;
import com.intellij.util.containers.ContainerUtil;
@@ -79,7 +77,7 @@ public class GroovyConstructorUsagesSearchHelper {
private GroovyConstructorUsagesSearchHelper() {
}
public static boolean execute(final PsiMethod constructor, final SearchScope searchScope, final Processor<PsiReference> consumer) {
public static boolean processConstructorUsages(final PsiMethod constructor, final SearchScope searchScope, final Processor<PsiReference> consumer, final SearchRequestCollector collector) {
if (!constructor.isConstructor()) return true;
SearchScope onlyGroovy = searchScope;
@@ -117,10 +115,11 @@ public class GroovyConstructorUsagesSearchHelper {
final Set<PsiMethod> processedMethods = new ConcurrentHashSet<PsiMethod>();
ReferencesSearch.search(clazz, searchScope, true).forEach(new Processor<PsiReference>() {
ReferencesSearch.searchOptimized(clazz, searchScope, true, collector, true, new PairProcessor<PsiReference, SearchRequestCollector>() {
@Override
public boolean process(PsiReference ref) {
return processClassReference(ref, clazz, constructor, consumer, processedMethods, searchScope);
public boolean process(PsiReference ref, SearchRequestCollector collector) {
return processClassReference(ref, clazz, constructor, consumer, processedMethods, searchScope, collector);
}
});
@@ -166,29 +165,21 @@ public class GroovyConstructorUsagesSearchHelper {
final PsiClass clazz,
final PsiMethod constructor,
final Processor<PsiReference> consumer,
final Set<PsiMethod> processedMethods, SearchScope scope) {
final Set<PsiMethod> processedMethods, SearchScope scope, SearchRequestCollector collector) {
final PsiElement element = ref.getElement();
if (element instanceof GrCodeReferenceElement) {
if (!ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
return processGroovyConstructorUsages((GrCodeReferenceElement)element, constructor, consumer, ref);
}
})) {
if (!processGroovyConstructorUsages((GrCodeReferenceElement)element, constructor, consumer, ref)) {
return false;
}
}
final PsiMethod method = ApplicationManager.getApplication().runReadAction(new Computable<PsiMethod>() {
public PsiMethod compute() {
return getMethodToSearchForCallsWithLiteralArguments(element, clazz, processedMethods);
}
});
final PsiMethod method = getMethodToSearchForCallsWithLiteralArguments(element, clazz, processedMethods);
if (method != null) {
return MethodReferencesSearch.search(method, getGppScope(clazz.getProject()).intersectWith(scope), true).forEach(new ReadActionProcessor<PsiReference>() {
final GlobalSearchScope gppScope = getGppScope(clazz.getProject());
MethodReferencesSearch.searchOptimized(method, gppScope.intersectWith(scope), true, collector, true, new PairProcessor<PsiReference, SearchRequestCollector>() {
@Override
public boolean processInReadAction(PsiReference psiReference) {
public boolean process(PsiReference psiReference, SearchRequestCollector collector) {
if (psiReference instanceof GrReferenceElement) {
final PsiElement parent = ((GrReferenceElement)psiReference).getParent();
if (parent instanceof GrCall) {
@@ -16,9 +16,7 @@
package org.jetbrains.plugins.groovy.findUsages;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.util.Processor;
import com.intellij.util.QueryExecutor;
@@ -28,8 +26,6 @@ import com.intellij.util.QueryExecutor;
*/
public class GroovyConstructorUsagesSearcher implements QueryExecutor<PsiReference, MethodReferencesSearch.SearchParameters> {
public boolean execute(MethodReferencesSearch.SearchParameters p, final Processor<PsiReference> consumer) {
final PsiMethod method = p.getMethod();
final SearchScope searchScope = p.getScope();
return GroovyConstructorUsagesSearchHelper.execute(method, searchScope, consumer);
return GroovyConstructorUsagesSearchHelper.processConstructorUsages(p.getMethod(), p.getScope(), consumer, p.getOptimizer());
}
}