From 9eb2032dab4f90c996dec692cce470bd55f9ac8e Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 27 Jul 2010 23:33:28 +0100 Subject: [PATCH] optimized find usages for nested searches; groovy/++ constructor usages using that API --- .../searches/MethodReferencesSearch.java | 12 +- .../intellij/psi/search/PsiSearchRequest.java | 4 + .../psi/search/QuerySearchRequest.java | 43 +++++ .../psi/search/SearchRequestCollector.java | 49 +++++- .../psi/search/searches/ReferencesSearch.java | 13 +- .../psi/impl/search/PsiSearchHelperImpl.java | 155 +++++++++++------- .../ConstructorReferencesSearcher.java | 2 +- .../GroovyConstructorUsagesSearchHelper.java | 35 ++-- .../GroovyConstructorUsagesSearcher.java | 6 +- 9 files changed, 219 insertions(+), 100 deletions(-) create mode 100644 platform/lang-api/src/com/intellij/psi/search/QuerySearchRequest.java diff --git a/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java b/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java index 93bfe3cb66c3..a29b98613b42 100644 --- a/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java +++ b/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java @@ -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 processor) { + final SearchRequestCollector nested = new SearchRequestCollector(); + collector.searchQuery(new QuerySearchRequest(search(new SearchParameters(method, scope, strictSignatureSearch, nested)), nested, + inReadAction, processor)); + } + public static Query search(final SearchParameters parameters) { final Query result = INSTANCE.createQuery(parameters); if (parameters.isSharedOptimizer) { diff --git a/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java b/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java index 5e16c40b8c91..b2a61d521ae7 100644 --- a/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java +++ b/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java @@ -25,4 +25,8 @@ public class PsiSearchRequest { this.processor = processor; } + @Override + public String toString() { + return "PsiSearchRequest: " + word + "; " + processor; + } } diff --git a/platform/lang-api/src/com/intellij/psi/search/QuerySearchRequest.java b/platform/lang-api/src/com/intellij/psi/search/QuerySearchRequest.java new file mode 100644 index 000000000000..158ee91dc151 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/search/QuerySearchRequest.java @@ -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 query; + public final SearchRequestCollector collector; + public final Processor processor; + + public QuerySearchRequest(Query query, + final SearchRequestCollector collector, + boolean inReadAction, final PairProcessor processor) { + this.query = query; + this.collector = collector; + if (inReadAction) { + this.processor = new ReadActionProcessor() { + @Override + public boolean processInReadAction(PsiReference psiReference) { + return processor.process(psiReference, collector); + } + }; + } else { + this.processor = new Processor() { + @Override + public boolean process(PsiReference psiReference) { + return processor.process(psiReference, collector); + } + }; + } + + } + + public void runQuery() { + query.forEach(processor); + } +} diff --git a/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java b/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java index b4e434ebcf6f..596282772625 100644 --- a/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java +++ b/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java @@ -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 myRequests = new ArrayList(); - private final List>> myCustomSearchActions = new ArrayList>>(); + private final Object lock = new Object(); + private final List myWordRequests = CollectionFactory.arrayList(); + private final List myQueryRequests = CollectionFactory.arrayList(); + private final List>> 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> searchAction) { - myCustomSearchActions.add(searchAction); + synchronized (lock) { + myCustomSearchActions.add(searchAction); + } } - public List getSearchRequests() { - return myRequests; + public boolean hasRequests() { + synchronized (lock) { + return !myWordRequests.isEmpty() || !myCustomSearchActions.isEmpty() || !myQueryRequests.isEmpty(); + } } - public List>> getCustomSearchActions() { - return myCustomSearchActions; + public List takeQueryRequests() { + return takeRequests(myQueryRequests); + } + + private List takeRequests(List list) { + synchronized (lock) { + final List requests = new ArrayList(list); + requests.addAll(list); + list.clear(); + return requests; + } + } + + public List takeSearchRequests() { + return takeRequests(myWordRequests); + } + + public List>> takeCustomSearchActions() { + return takeRequests(myCustomSearchActions); } } diff --git a/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java b/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java index c43a8875a6f5..e2dc5c6ff22f 100644 --- a/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java +++ b/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java @@ -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 processor) { + final SearchRequestCollector nested = new SearchRequestCollector(); + collector.searchQuery(new QuerySearchRequest(search(new SearchParameters(element, searchScope, ignoreAccessScope, nested)), nested, + inReadAction, processor)); + } + + } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index 425e5c86fd81..24e17f5319c4 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -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 processor) { - final MultiMap, PsiSearchRequest> singles = new MultiMap, PsiSearchRequest>(); - final List>> customs = new ArrayList>>(); - distributePrimitives(request, singles, customs); - - if (!processRequestsOptimized(singles, processor)) { - return false; + private static class RequestWithProcessor extends Pair> { + private RequestWithProcessor(PsiSearchRequest first, Processor second) { + super(first, second); } - - for (Processor> custom : customs) { - if (!custom.process(processor)) { - return false; - } - } - - return true; - } - private boolean processRequestsOptimized(MultiMap, PsiSearchRequest> singles, final Processor consumer) { + public boolean processRequests(@NotNull SearchRequestCollector collector, Processor processor) { + Map> collectors = new HashMap>(); + collectors.put(collector, processor); + + appendCollectorsFromQueryRequests(collectors); + + do { + final MultiMap, RequestWithProcessor> singles = new MultiMap, RequestWithProcessor>(); + final List> customs = new ArrayList>(); + distributePrimitives(collectors, singles, customs); + + if (!processRequestsOptimized(singles)) { + return false; + } + + for (Computable custom : customs) { + if (!custom.compute()) { + return false; + } + } + } while (appendCollectorsFromQueryRequests(collectors)); + + return true; + } + + private static boolean appendCollectorsFromQueryRequests(Map> collectors) { + boolean changed = false; + LinkedList queue = new LinkedList(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, RequestWithProcessor> singles) { if (singles.isEmpty()) { return true; } if (singles.size() == 1) { - final Collection requests = singles.get(singles.keySet().iterator().next()); + final Collection 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 candidateFiles = collectFiles(singles); + final MultiMap candidateFiles = collectFiles(singles); - final Map searchers = new HashMap(); + final Map searchers = new HashMap(); final Set allWords = new TreeSet(); - 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(candidateFiles.keySet()), new Processor() { 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 collectFiles(MultiMap, PsiSearchRequest> singles) { + private MultiMap collectFiles(MultiMap, RequestWithProcessor> singles) { final ProjectFileIndex index = ProjectRootManager.getInstance(myManager.getProject()).getFileIndex(); - final MultiMap result = new MultiMap(); + final MultiMap result = new MultiMap(); for (Set key : singles.keySet()) { - final Collection data = singles.get(key); + final Collection data = singles.get(key); GlobalSearchScope commonScope = uniteScopes(data); - MultiMap intersection = null; + MultiMap intersection = null; boolean first = true; for (IdIndexEntry entry : key) { - final MultiMap local = findFilesWithIndexEntry(entry, index, data, commonScope); + final MultiMap 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 requests) { + private static GlobalSearchScope uniteScopes(Collection 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 findFilesWithIndexEntry(final IdIndexEntry entry, + private static MultiMap findFilesWithIndexEntry(final IdIndexEntry entry, final ProjectFileIndex index, - final Collection data, + final Collection data, final GlobalSearchScope commonScope) { - final MultiMap local = new MultiMap(); + final MultiMap local = new MultiMap(); ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { + ProgressManager.checkCanceled(); FileBasedIndex.getInstance().processValues(IdIndex.NAME, entry, null, new FileBasedIndex.ValueProcessor() { 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, PsiSearchRequest> singles, - List>> customs) { - for (final PsiSearchRequest primitive : request.getSearchRequests()) { - final SearchScope scope = primitive.searchScope; - if (scope instanceof LocalSearchScope) { - customs.add(new Processor>() { - public boolean process(Processor processor) { - return processSingleRequest(primitive, processor); + private void distributePrimitives(final Map> collectors, + MultiMap, RequestWithProcessor> singles, + List> customs) { + for (final SearchRequestCollector collector : collectors.keySet()) { + final Processor processor = collectors.get(collector); + for (final PsiSearchRequest primitive : collector.takeSearchRequests()) { + final SearchScope scope = primitive.searchScope; + if (scope instanceof LocalSearchScope) { + customs.add(new Computable() { + @Override + public Boolean compute() { + return processSingleRequest(primitive, processor); + } + }); + } else { + final List words = StringUtil.getWordsIn(primitive.word); + final Set key = new HashSet(words.size() * 2); + for (String word : words) { + key.add(new IdIndexEntry(word, primitive.caseSensitive)); + } + singles.putValue(key, new RequestWithProcessor(primitive, processor)); + } + } + for (final Processor> customAction : collector.takeCustomSearchActions()) { + customs.add(new Computable() { + @Override + public Boolean compute() { + return customAction.process(processor); } }); - } else { - final List words = StringUtil.getWordsIn(primitive.word); - final Set key = new HashSet(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 consumer) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/ConstructorReferencesSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/ConstructorReferencesSearcher.java index 138a9034642a..6af2559d1122 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/ConstructorReferencesSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/ConstructorReferencesSearcher.java @@ -31,7 +31,7 @@ public class ConstructorReferencesSearcher implements QueryExecutor consumer) { + public static boolean processConstructorUsages(final PsiMethod constructor, final SearchScope searchScope, final Processor consumer, final SearchRequestCollector collector) { if (!constructor.isConstructor()) return true; SearchScope onlyGroovy = searchScope; @@ -117,10 +115,11 @@ public class GroovyConstructorUsagesSearchHelper { final Set processedMethods = new ConcurrentHashSet(); - ReferencesSearch.search(clazz, searchScope, true).forEach(new Processor() { + + ReferencesSearch.searchOptimized(clazz, searchScope, true, collector, true, new PairProcessor() { @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 consumer, - final Set processedMethods, SearchScope scope) { + final Set processedMethods, SearchScope scope, SearchRequestCollector collector) { final PsiElement element = ref.getElement(); if (element instanceof GrCodeReferenceElement) { - if (!ApplicationManager.getApplication().runReadAction(new Computable() { - @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() { - 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() { + final GlobalSearchScope gppScope = getGppScope(clazz.getProject()); + MethodReferencesSearch.searchOptimized(method, gppScope.intersectWith(scope), true, collector, true, new PairProcessor() { @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) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java index da1ef93bd10f..8a2a8ebbfa36 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java @@ -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 { public boolean execute(MethodReferencesSearch.SearchParameters p, final Processor 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()); } }