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 458efc9253d4..70c5ae89878f 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 @@ -33,7 +33,6 @@ import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileWithId; import com.intellij.psi.*; import com.intellij.psi.impl.PsiManagerEx; import com.intellij.psi.impl.cache.CacheManager; @@ -207,7 +206,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { } String text = searcher.getPattern(); - List fileSet = new ArrayList(); + Set fileSet = new THashSet(); getFilesWithText(scope, searchContext, caseSensitively, text, progress, fileSet); if (progress != null) { @@ -216,49 +215,45 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final Processor localProcessor = localProcessor(processor, progress, true, searcher); if (containerName != null) { - List containerFiles = new ArrayList(); - getFilesWithText(scope, searchContext, caseSensitively, containerName, progress, containerFiles); - if (!containerFiles.isEmpty()) { - Comparator byId = new Comparator() { - @Override - public int compare(VirtualFile o1, VirtualFile o2) { - return ((VirtualFileWithId)o1).getId() - ((VirtualFileWithId)o2).getId(); - } - }; - ContainerUtil.quickSort(fileSet, byId); - ContainerUtil.quickSort(containerFiles, byId); - - List intersection = new ArrayList(fileSet.size()); - final List rest = new ArrayList(); - splitSortedLists(fileSet, containerFiles, byId, intersection, rest); - if (!intersection.isEmpty()) { - int totalSize = rest.size() + intersection.size(); - AsyncFuture intersectionResult = processPsiFileRootsAsync(intersection, totalSize, 0, progress, localProcessor); - AsyncFuture result; - try { - if (intersectionResult.get()) { - AsyncFuture restResult = processPsiFileRootsAsync(rest, totalSize, intersection.size(), progress, localProcessor); - result = bind(intersectionResult, restResult); - } - else { + List intersectionWithContainerFiles = new ArrayList(); + // intersectionWithContainerFiles holds files containing words from both `text` and `containerName` + getFilesWithText(scope, searchContext, caseSensitively, text+" "+containerName, progress, intersectionWithContainerFiles); + if (!intersectionWithContainerFiles.isEmpty()) { + int totalSize = fileSet.size(); + AsyncFuture intersectionResult = processPsiFileRootsAsync(intersectionWithContainerFiles, totalSize, 0, progress, localProcessor); + AsyncFuture result; + try { + if (intersectionResult.get()) { + fileSet.removeAll(intersectionWithContainerFiles); + if (fileSet.isEmpty()) { result = intersectionResult; } + else { + AsyncFuture restResult = + processPsiFileRootsAsync(new ArrayList(fileSet), totalSize, intersectionWithContainerFiles.size(), progress, localProcessor); + result = bind(intersectionResult, restResult); + } } - 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); + else { + result = intersectionResult; } - catch (InterruptedException e) { - result = AsyncFutureFactory.wrapException(e); - } - return popStateAfter(result, progress); } + 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 result = processPsiFileRootsAsync(fileSet, fileSet.size(), 0, progress, localProcessor); + AsyncFuture result = fileSet.isEmpty() + ? AsyncFutureFactory.wrap(true) + : processPsiFileRootsAsync(new ArrayList(fileSet), fileSet.size(), 0, progress, localProcessor); return popStateAfter(result, progress); } @@ -318,6 +313,12 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { } } + /** + * @param files to scan for references in this pass. + * @param totalSize the number of files to scan in both passes. Can be different from files.size() 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. + */ @NotNull private AsyncFuture processPsiFileRootsAsync(@NotNull List files, final int totalSize, @@ -339,7 +340,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { } }); if (file != null && !(file instanceof PsiBinaryFile)) { - file.getViewProvider().getContents(); // load contents outside readaction + file.getViewProvider().getContents(); // load contents outside read action if (myManager.getProject().isDisposed()) throw new ProcessCanceledException(); List psiRoots = ApplicationManager.getApplication().runReadAction(new Computable>() { @Override @@ -707,7 +708,9 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { progress.setText(PsiBundle.message("psi.scanning.files.progress")); } + // intersectionCandidateFiles holds files containing words from all requests in `singles` and words in corresponding container names final MultiMap intersectionCandidateFiles = createMultiMap(); + // restCandidateFiles holds files containing words from all requests in `singles` but EXCLUDING words in corresponding container names final MultiMap restCandidateFiles = createMultiMap(); collectFiles(singles, progress, intersectionCandidateFiles, restCandidateFiles); @@ -829,7 +832,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final Collection data = singles.get(keys); final GlobalSearchScope commonScope = uniteScopes(data); - final Set commonContainerNameFiles = commonContainerNameFiles(commonScope, data, progress); + final Set intersectionWithContainerNameFiles = intersectionWithContainerNameFiles(commonScope, data, keys); List files = new ArrayList(); CommonProcessors.CollectProcessor processor = new CommonProcessors.CollectProcessor(files); @@ -848,7 +851,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final PsiSearchRequest request = single.request; if ((mask & request.searchContext) != 0 && ((GlobalSearchScope)request.searchScope).contains(file)) { MultiMap result = - commonContainerNameFiles == null || !commonContainerNameFiles.contains(file) ? restResult : intersectionResult; + intersectionWithContainerNameFiles == null || !intersectionWithContainerNameFiles.contains(file) ? restResult : intersectionResult; result.putValue(file, single); } } @@ -863,9 +866,9 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { } @Nullable("null means we did not find common container files") - private Set commonContainerNameFiles(GlobalSearchScope commonScope, - @NotNull Collection data, - ProgressIndicator progress) { + private Set intersectionWithContainerNameFiles(@NotNull GlobalSearchScope commonScope, + @NotNull Collection data, + @NotNull Set keys) { String commonName = null; short searchContext = 0; boolean caseSensitive = true; @@ -888,7 +891,19 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { } if (commonName == null) return null; Set containerFiles = new THashSet(); - getFilesWithText(commonScope, searchContext, caseSensitive, commonName, progress, containerFiles); + + List entries = getWordEntries(commonName, caseSensitive); + if (entries.isEmpty()) return null; + entries.addAll(keys); // should find words from both text and container names + + final short finalSearchContext = searchContext; + Condition contextMatches = new Condition() { + @Override + public boolean value(Integer context) { + return (context.intValue() & finalSearchContext) != 0; + } + }; + processFilesContainingAllKeys(myManager.getProject(), commonScope, contextMatches, entries, new CommonProcessors.CollectProcessor(containerFiles)); return containerFiles; } @@ -1027,6 +1042,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { @NotNull private static List getWordEntries(@NotNull String name, boolean caseSensitively) { List words = StringUtil.getWordsInStringLongestFirst(name); + if (words.isEmpty()) return Collections.emptyList(); List keys = new ArrayList(words.size()); for (String word : words) { keys.add(new IdIndexEntry(word, caseSensitively));