diff options
Diffstat (limited to 'platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java')
-rw-r--r-- | platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java | 350 |
1 files changed, 121 insertions, 229 deletions
diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index b5c3dd17afb7..ba8d9ce3500e 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -23,7 +23,6 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressIndicatorProvider; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.util.TooManyUsagesStatus; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.FileIndexFacade; @@ -56,7 +55,6 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.*; -import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -132,9 +130,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { short searchContext, boolean caseSensitive, boolean processInjectedPsi) { - final AsyncFuture<Boolean> result = - processElementsWithWordAsync(processor, searchScope, text, searchContext, caseSensitive, processInjectedPsi, null); - return AsyncUtil.get(result); + return processElementsWithWord(processor, searchScope, text, searchContext, caseSensitive, processInjectedPsi, null); } @NotNull @@ -144,50 +140,39 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { @NotNull final String text, final short searchContext, final boolean caseSensitively) { - return processElementsWithWordAsync(processor, searchScope, text, searchContext, caseSensitively, shouldProcessInjectedPsi(searchScope), null); - } - - @NotNull - private AsyncFuture<Boolean> processElementsWithWordAsync(@NotNull final TextOccurenceProcessor processor, - @NotNull SearchScope searchScope, - @NotNull final String text, - final short searchContext, - final boolean caseSensitively, - boolean processInjectedPsi, - @Nullable String containerName) { + boolean result = + processElementsWithWord(processor, searchScope, text, searchContext, caseSensitively, shouldProcessInjectedPsi(searchScope), null); + return AsyncUtil.wrapBoolean(result); + } + + private boolean processElementsWithWord(@NotNull final TextOccurenceProcessor processor, + @NotNull SearchScope searchScope, + @NotNull final String text, + final short searchContext, + final boolean caseSensitively, + boolean processInjectedPsi, + @Nullable String containerName) { if (text.isEmpty()) { - return AsyncFutureFactory.wrapException(new IllegalArgumentException("Cannot search for elements with empty text")); + throw new IllegalArgumentException("Cannot search for elements with empty text"); } final ProgressIndicator progress = ProgressIndicatorProvider.getGlobalProgressIndicator(); if (searchScope instanceof GlobalSearchScope) { StringSearcher searcher = new StringSearcher(text, caseSensitively, true, searchContext == UsageSearchContext.IN_STRINGS); - return processElementsWithTextInGlobalScopeAsync(processor, - (GlobalSearchScope)searchScope, - searcher, - searchContext, caseSensitively, containerName, progress, processInjectedPsi); + return processElementsWithTextInGlobalScope(processor, + (GlobalSearchScope)searchScope, + searcher, + searchContext, caseSensitively, containerName, progress, processInjectedPsi); } LocalSearchScope scope = (LocalSearchScope)searchScope; PsiElement[] scopeElements = scope.getScope(); final StringSearcher searcher = new StringSearcher(text, caseSensitively, true, searchContext == UsageSearchContext.IN_STRINGS); Processor<PsiElement> localProcessor = localProcessor(processor, progress, processInjectedPsi, searcher); - return wrapInFuture(Arrays.asList(scopeElements), progress, localProcessor); - } - - private static <T> AsyncFuture<Boolean> wrapInFuture(@NotNull List<T> files, final ProgressIndicator progress, @NotNull Processor<T> processor) { - AsyncFutureResult<Boolean> asyncFutureResult = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - try { - boolean result = JobLauncher.getInstance().invokeConcurrentlyUnderProgress(files, progress, true, true, processor); - asyncFutureResult.set(result); - } - catch (Throwable t) { - asyncFutureResult.setException(t); - } - return asyncFutureResult; + return JobLauncher.getInstance().invokeConcurrentlyUnderProgress(Arrays.asList(scopeElements), progress, true, true, localProcessor); } private static boolean shouldProcessInjectedPsi(SearchScope scope) { - return scope instanceof LocalSearchScope ? !((LocalSearchScope)scope).isIgnoreInjectedPsi() : true; + return !(scope instanceof LocalSearchScope) || !((LocalSearchScope)scope).isIgnoreInjectedPsi(); } @NotNull @@ -213,14 +198,13 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { }; } - @NotNull - private AsyncFuture<Boolean> processElementsWithTextInGlobalScopeAsync(@NotNull final TextOccurenceProcessor processor, - @NotNull final GlobalSearchScope scope, - @NotNull final StringSearcher searcher, - final short searchContext, - final boolean caseSensitively, - String containerName, - final ProgressIndicator progress, final boolean processInjectedPsi) { + private boolean processElementsWithTextInGlobalScope(@NotNull final TextOccurenceProcessor processor, + @NotNull final GlobalSearchScope scope, + @NotNull final StringSearcher searcher, + final short searchContext, + final boolean caseSensitively, + String containerName, + final ProgressIndicator progress, final boolean processInjectedPsi) { if (Thread.holdsLock(PsiLock.LOCK)) { throw new AssertionError("You must not run search from within updating PSI activity. Please consider invokeLatering it instead."); } @@ -244,70 +228,28 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { getFilesWithText(scope, searchContext, caseSensitively, text+" "+containerName, progress, intersectionWithContainerFiles); if (!intersectionWithContainerFiles.isEmpty()) { int totalSize = fileSet.size(); - AsyncFuture<Boolean> intersectionResult = processPsiFileRootsAsync(intersectionWithContainerFiles, totalSize, 0, progress, localProcessor); - AsyncFuture<Boolean> result; - try { - if (intersectionResult.get()) { - fileSet.removeAll(intersectionWithContainerFiles); - if (fileSet.isEmpty()) { - result = intersectionResult; - } - else { - AsyncFuture<Boolean> restResult = - processPsiFileRootsAsync(new ArrayList<VirtualFile>(fileSet), totalSize, intersectionWithContainerFiles.size(), progress, localProcessor); - result = bind(intersectionResult, restResult); - } - } - else { - result = intersectionResult; + boolean result = processPsiFileRoots(intersectionWithContainerFiles, totalSize, 0, progress, + localProcessor); + + if (result) { + fileSet.removeAll(intersectionWithContainerFiles); + if (!fileSet.isEmpty()) { + result = processPsiFileRoots(new ArrayList<VirtualFile>(fileSet), totalSize, intersectionWithContainerFiles.size(), progress, localProcessor); } } - catch (ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException) throw (RuntimeException)cause; - if (cause instanceof Error) throw (Error)cause; - result = AsyncFutureFactory.wrapException(cause); - } - catch (InterruptedException e) { - result = AsyncFutureFactory.wrapException(e); - } - return popStateAfter(result, progress); - } - } - - AsyncFuture<Boolean> result = fileSet.isEmpty() - ? AsyncFutureFactory.wrap(Boolean.TRUE) - : processPsiFileRootsAsync(new ArrayList<VirtualFile>(fileSet), fileSet.size(), 0, progress, localProcessor); - return popStateAfter(result, progress); - } - - @NotNull - private static FinallyFuture<Boolean> popStateAfter(@NotNull AsyncFuture<Boolean> result, final ProgressIndicator progress) { - return new FinallyFuture<Boolean>(result, new Runnable() { - @Override - public void run() { if (progress != null) { progress.popState(); } + return result; } - }); - } - - @NotNull - private static AsyncFuture<Boolean> bind(@NotNull AsyncFuture<Boolean> first, @NotNull final AsyncFuture<Boolean> second) { - final AsyncFutureResult<Boolean> totalResult = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - first.addConsumer(SameThreadExecutor.INSTANCE, new ResultConsumer<Boolean>() { - @Override - public void onSuccess(Boolean value) { - second.addConsumer(SameThreadExecutor.INSTANCE, new DefaultResultConsumer<Boolean>(totalResult)); - } + } - @Override - public void onFailure(Throwable t) { - totalResult.setException(t); - } - }); - return totalResult; + boolean result = + fileSet.isEmpty() || processPsiFileRoots(new ArrayList<VirtualFile>(fileSet), fileSet.size(), 0, progress, localProcessor); + if (progress != null) { + progress.popState(); + } + return result; } /** @@ -315,20 +257,19 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { * @param totalSize the number of files to scan in both passes. Can be different from <code>files.size()</code> in case of * two-pass scan, where we first scan files containing container name and then all the rest files. * @param alreadyProcessedFiles the number of files scanned in previous pass. + * @return true if completed */ - @NotNull - private AsyncFuture<Boolean> processPsiFileRootsAsync(@NotNull List<VirtualFile> files, - final int totalSize, - int alreadyProcessedFiles, - final ProgressIndicator progress, - @NotNull final Processor<? super PsiFile> localProcessor) { + private boolean processPsiFileRoots(@NotNull List<VirtualFile> files, + final int totalSize, + int alreadyProcessedFiles, + final ProgressIndicator progress, + @NotNull final Processor<? super PsiFile> localProcessor) { myManager.startBatchFilesProcessingMode(); - final AtomicInteger counter = new AtomicInteger(alreadyProcessedFiles); - final AtomicBoolean canceled = new AtomicBoolean(false); - - AsyncFutureResult<Boolean> asyncFutureResult = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - final List<VirtualFile> failedFiles = Collections.synchronizedList(new SmartList<VirtualFile>()); try { + final AtomicInteger counter = new AtomicInteger(alreadyProcessedFiles); + final AtomicBoolean canceled = new AtomicBoolean(false); + + final List<VirtualFile> failedFiles = Collections.synchronizedList(new SmartList<VirtualFile>()); boolean completed = JobLauncher.getInstance().invokeConcurrentlyUnderProgress(files, progress, false, false, new Processor<VirtualFile>() { @Override @@ -357,14 +298,12 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { }); } } - asyncFutureResult.set(completed); - myManager.finishBatchFilesProcessingMode(); + return completed; } - catch (Throwable t) { - asyncFutureResult.setException(t); + finally { + myManager.finishBatchFilesProcessingMode(); } - return asyncFutureResult; } private void processVirtualFile(@NotNull final VirtualFile vfile, @@ -423,9 +362,6 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { if (progress != null) { progress.checkCanceled(); } - else { - ProgressManager.checkCanceled(); - } } private void getFilesWithText(@NotNull GlobalSearchScope scope, @@ -648,65 +584,32 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final Map<SearchRequestCollector, Processor<PsiReference>> collectors = ContainerUtil.newHashMap(); collectors.put(collector, processor); + ProgressIndicator progress = ProgressIndicatorProvider.getGlobalProgressIndicator(); appendCollectorsFromQueryRequests(collectors); - - final ProgressIndicator progress = ProgressIndicatorProvider.getGlobalProgressIndicator(); - final DoWhile doWhile = new DoWhile() { - @NotNull - @Override - protected AsyncFuture<Boolean> body() { - final AsyncFutureResult<Boolean> result = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - MultiMap<Set<IdIndexEntry>, RequestWithProcessor> globals = new MultiMap<Set<IdIndexEntry>, RequestWithProcessor>(); - final List<Computable<Boolean>> customs = ContainerUtil.newArrayList(); - final Set<RequestWithProcessor> locals = ContainerUtil.newLinkedHashSet(); - Map<RequestWithProcessor, Processor<PsiElement>> localProcessors = new THashMap<RequestWithProcessor, Processor<PsiElement>>(); - distributePrimitives(collectors, locals, globals, customs, localProcessors, progress); - AsyncFuture<Boolean> future = processGlobalRequestsOptimizedAsync(globals, progress, localProcessors); - future.addConsumer(SameThreadExecutor.INSTANCE, new DefaultResultConsumer<Boolean>(result) { - @Override - public void onSuccess(Boolean value) { - if (!value.booleanValue()) { - result.set(value); - } - else { - final Iterate<RequestWithProcessor> iterate = new Iterate<RequestWithProcessor>(locals) { - @NotNull - @Override - protected AsyncFuture<Boolean> process(RequestWithProcessor local) { - return processSingleRequestAsync(local.request, local.refProcessor); - } - }; - - iterate.getResult() - .addConsumer(SameThreadExecutor.INSTANCE, new DefaultResultConsumer<Boolean>(result) { - @Override - public void onSuccess(Boolean value) { - if (!value.booleanValue()) { - result.set(false); - return; - } - for (Computable<Boolean> custom : customs) { - if (!custom.compute()) { - result.set(false); - return; - } - } - result.set(true); - } - }); - } + boolean result; + do { + MultiMap<Set<IdIndexEntry>, RequestWithProcessor> globals = new MultiMap<Set<IdIndexEntry>, RequestWithProcessor>(); + final List<Computable<Boolean>> customs = ContainerUtil.newArrayList(); + final Set<RequestWithProcessor> locals = ContainerUtil.newLinkedHashSet(); + Map<RequestWithProcessor, Processor<PsiElement>> localProcessors = new THashMap<RequestWithProcessor, Processor<PsiElement>>(); + distributePrimitives(collectors, locals, globals, customs, localProcessors, progress); + result = processGlobalRequestsOptimized(globals, progress, localProcessors); + if (result) { + for (RequestWithProcessor local : locals) { + result = processSingleRequest(local.request, local.refProcessor); + if (!result) break; + } + if (result) { + for (Computable<Boolean> custom : customs) { + result = custom.compute(); + if (!result) break; } - }); - return result; - } - - @Override - protected boolean condition() { - return appendCollectorsFromQueryRequests(collectors); + } + if (!result) break; } - }; - - return doWhile.getResult(); + } + while(appendCollectorsFromQueryRequests(collectors)); + return AsyncUtil.wrapBoolean(result); } private static boolean appendCollectorsFromQueryRequests(@NotNull Map<SearchRequestCollector, Processor<PsiReference>> collectors) { @@ -725,19 +628,18 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return changed; } - @NotNull - private AsyncFuture<Boolean> processGlobalRequestsOptimizedAsync(@NotNull MultiMap<Set<IdIndexEntry>, RequestWithProcessor> singles, - final ProgressIndicator progress, - @NotNull final Map<RequestWithProcessor, Processor<PsiElement>> localProcessors) { + private boolean processGlobalRequestsOptimized(@NotNull MultiMap<Set<IdIndexEntry>, RequestWithProcessor> singles, + final ProgressIndicator progress, + @NotNull final Map<RequestWithProcessor, Processor<PsiElement>> localProcessors) { if (singles.isEmpty()) { - return AsyncFutureFactory.wrap(true); + return true; } if (singles.size() == 1) { final Collection<? extends RequestWithProcessor> requests = singles.values(); if (requests.size() == 1) { final RequestWithProcessor theOnly = requests.iterator().next(); - return processSingleRequestAsync(theOnly.request, theOnly.refProcessor); + return processSingleRequest(theOnly.request, theOnly.refProcessor); } } @@ -745,64 +647,55 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { progress.pushState(); progress.setText(PsiBundle.message("psi.scanning.files.progress")); } + boolean result; - // intersectionCandidateFiles holds files containing words from all requests in `singles` and words in corresponding container names - final MultiMap<VirtualFile, RequestWithProcessor> intersectionCandidateFiles = createMultiMap(); - // restCandidateFiles holds files containing words from all requests in `singles` but EXCLUDING words in corresponding container names - final MultiMap<VirtualFile, RequestWithProcessor> restCandidateFiles = createMultiMap(); - collectFiles(singles, progress, intersectionCandidateFiles, restCandidateFiles); - - if (intersectionCandidateFiles.isEmpty() && restCandidateFiles.isEmpty()) { - return AsyncFutureFactory.wrap(true); - } + try { + // intersectionCandidateFiles holds files containing words from all requests in `singles` and words in corresponding container names + final MultiMap<VirtualFile, RequestWithProcessor> intersectionCandidateFiles = createMultiMap(); + // restCandidateFiles holds files containing words from all requests in `singles` but EXCLUDING words in corresponding container names + final MultiMap<VirtualFile, RequestWithProcessor> restCandidateFiles = createMultiMap(); + collectFiles(singles, progress, intersectionCandidateFiles, restCandidateFiles); - if (progress != null) { - final Set<String> allWords = new TreeSet<String>(); - for (RequestWithProcessor singleRequest : localProcessors.keySet()) { - allWords.add(singleRequest.request.word); + if (intersectionCandidateFiles.isEmpty() && restCandidateFiles.isEmpty()) { + return true; } - progress.setText(PsiBundle.message("psi.search.for.word.progress", getPresentableWordsDescription(allWords))); - } - AsyncFuture<Boolean> result; - if (intersectionCandidateFiles.isEmpty()) { - result = processCandidatesAsync(progress, localProcessors, restCandidateFiles, restCandidateFiles.size(), 0); - } - else { - int totalSize = restCandidateFiles.size() + intersectionCandidateFiles.size(); - AsyncFuture<Boolean> intersectionResult = processCandidatesAsync(progress, localProcessors, intersectionCandidateFiles, totalSize, 0); - try { - if (intersectionResult.get()) { - AsyncFuture<Boolean> restResult = processCandidatesAsync(progress, localProcessors, restCandidateFiles, totalSize, intersectionCandidateFiles.size()); - result = bind(intersectionResult, restResult); - } - else { - result = intersectionResult; + if (progress != null) { + final Set<String> allWords = new TreeSet<String>(); + for (RequestWithProcessor singleRequest : localProcessors.keySet()) { + allWords.add(singleRequest.request.word); } + progress.setText(PsiBundle.message("psi.search.for.word.progress", getPresentableWordsDescription(allWords))); } - catch (ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException) throw (RuntimeException)cause; - if (cause instanceof Error) throw (Error)cause; - result = AsyncFutureFactory.wrapException(cause); + + if (intersectionCandidateFiles.isEmpty()) { + result = processCandidates(progress, localProcessors, restCandidateFiles, restCandidateFiles.size(), 0); + } + else { + int totalSize = restCandidateFiles.size() + intersectionCandidateFiles.size(); + result = processCandidates(progress, localProcessors, intersectionCandidateFiles, totalSize, 0); + if (result) { + result = processCandidates(progress, localProcessors, restCandidateFiles, totalSize, intersectionCandidateFiles.size()); + } } - catch (InterruptedException e) { - result = AsyncFutureFactory.wrapException(e); + } + finally { + if (progress != null) { + progress.popState(); } } - return popStateAfter(result, progress); + return result; } - @NotNull - private AsyncFuture<Boolean> processCandidatesAsync(final ProgressIndicator progress, - @NotNull final Map<RequestWithProcessor, Processor<PsiElement>> localProcessors, - @NotNull final MultiMap<VirtualFile, RequestWithProcessor> candidateFiles, - int totalSize, - int alreadyProcessedFiles) { + private boolean processCandidates(final ProgressIndicator progress, + @NotNull final Map<RequestWithProcessor, Processor<PsiElement>> localProcessors, + @NotNull final MultiMap<VirtualFile, RequestWithProcessor> candidateFiles, + int totalSize, + int alreadyProcessedFiles) { List<VirtualFile> files = new ArrayList<VirtualFile>(candidateFiles.keySet()); - return processPsiFileRootsAsync(files, totalSize, alreadyProcessedFiles, progress, new Processor<PsiFile>() { + return processPsiFileRoots(files, totalSize, alreadyProcessedFiles, progress, new Processor<PsiFile>() { @Override public boolean process(final PsiFile psiRoot) { return ApplicationUtil.tryRunReadAction(new Computable<Boolean>() { @@ -1023,10 +916,9 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { collection.add(singleRequest); } - @NotNull - private AsyncFuture<Boolean> processSingleRequestAsync(@NotNull PsiSearchRequest single, @NotNull Processor<PsiReference> consumer) { - return processElementsWithWordAsync(adaptProcessor(single, consumer), single.searchScope, single.word, single.searchContext, - single.caseSensitive, shouldProcessInjectedPsi(single.searchScope), single.containerName); + private boolean processSingleRequest(@NotNull PsiSearchRequest single, @NotNull Processor<PsiReference> consumer) { + return processElementsWithWord(adaptProcessor(single, consumer), single.searchScope, single.word, single.searchContext, + single.caseSensitive, shouldProcessInjectedPsi(single.searchScope), single.containerName); } @NotNull |