flatten united scopes instead of storing them in a binary tree: EA-64802 - ISE: GlobalSearchScope$UnionScope.

This commit is contained in:
Alexey Kudravtsev
2015-03-12 14:34:09 +03:00
parent 6af2ae6b64
commit 6d9dbfe88d
2 changed files with 103 additions and 40 deletions
@@ -21,12 +21,17 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiBundle;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -164,6 +169,18 @@ public abstract class GlobalSearchScope extends SearchScope implements ProjectAw
return new UnionScope(this, scope);
}
@NotNull
@Contract(pure = true)
public static GlobalSearchScope union(@NotNull GlobalSearchScope[] scopes) {
if (scopes.length == 0) {
throw new IllegalArgumentException("Empty scope array");
}
if (scopes.length == 1) {
return scopes[0];
}
return new UnionScope(scopes);
}
@NotNull
public static GlobalSearchScope allScope(@NotNull Project project) {
return ProjectScope.getAllScope(project);
@@ -403,17 +420,31 @@ public abstract class GlobalSearchScope extends SearchScope implements ProjectAw
}
private static class UnionScope extends GlobalSearchScope {
private final GlobalSearchScope myScope1;
private final GlobalSearchScope myScope2;
private final GlobalSearchScope[] myScopes;
private final int myNestingLevel;
private UnionScope(@NotNull GlobalSearchScope scope1, @NotNull GlobalSearchScope scope2) {
super(scope1.getProject() == null ? scope2.getProject() : scope1.getProject());
myScope1 = scope1;
myScope2 = scope2;
myNestingLevel = 1 +
Math.max(scope1 instanceof UnionScope ? ((UnionScope)scope1).myNestingLevel : 0,
scope2 instanceof UnionScope ? ((UnionScope)scope2).myNestingLevel : 0);
this(new GlobalSearchScope[]{scope1, scope2});
}
private UnionScope(@NotNull GlobalSearchScope[] scopes) {
super(ContainerUtil.getFirstItem(ContainerUtil.mapNotNull(scopes, new Function<GlobalSearchScope, Project>() {
@Override
public Project fun(GlobalSearchScope scope) {
return scope.getProject();
}
}), null));
assert scopes.length > 1 : Arrays.asList(scopes);
myScopes = scopes;
final int[] nested = {0};
ContainerUtil.process(scopes, new Processor<GlobalSearchScope>() {
@Override
public boolean process(GlobalSearchScope scope) {
nested[0] = Math.max(nested[0], scope instanceof UnionScope ? ((UnionScope)scope).myNestingLevel : 0);
return true;
}
});
myNestingLevel = 1 + nested[0];
if (myNestingLevel > 1000) {
throw new IllegalStateException("Too many scopes combined: " + myNestingLevel + StringUtil.first(toString(), 500, true));
}
@@ -422,47 +453,78 @@ public abstract class GlobalSearchScope extends SearchScope implements ProjectAw
@NotNull
@Override
public String getDisplayName() {
return PsiBundle.message("psi.search.scope.union", myScope1.getDisplayName(), myScope2.getDisplayName());
return PsiBundle.message("psi.search.scope.union", myScopes[0].getDisplayName(), myScopes[1].getDisplayName());
}
@Override
public boolean contains(@NotNull VirtualFile file) {
return myScope1.contains(file) || myScope2.contains(file);
public boolean contains(@NotNull final VirtualFile file) {
return ContainerUtil.find(myScopes, new Condition<GlobalSearchScope>() {
@Override
public boolean value(GlobalSearchScope scope) {
return scope.contains(file);
}
}) != null;
}
@Override
public boolean isSearchOutsideRootModel() {
return myScope1.isSearchOutsideRootModel() || myScope2.isSearchOutsideRootModel();
return ContainerUtil.find(myScopes, new Condition<GlobalSearchScope>() {
@Override
public boolean value(GlobalSearchScope scope) {
return scope.isSearchOutsideRootModel();
}
}) != null;
}
@Override
public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
int res1 = myScope1.contains(file1) && myScope1.contains(file2) ? myScope1.compare(file1, file2) : 0;
int res2 = myScope2.contains(file1) && myScope2.contains(file2) ? myScope2.compare(file1, file2) : 0;
if (res1 == 0) return res2;
if (res2 == 0) return res1;
res1 /= Math.abs(res1);
res2 /= Math.abs(res2);
if (res1 == res2) return res1;
return 0;
public int compare(@NotNull final VirtualFile file1, @NotNull final VirtualFile file2) {
final int[] result = {0};
ContainerUtil.process(myScopes, new Processor<GlobalSearchScope>() {
@Override
public boolean process(GlobalSearchScope scope) {
int res1 = scope.contains(file1) && scope.contains(file2) ? scope.compare(file1, file2) : 0;
if (result[0] == 0) {
result[0] = res1;
return true;
}
if ((result[0] > 0) != (res1 > 0)) {
result[0] = 0;
return false;
}
return true;
}
});
return result[0];
}
@Override
public boolean isSearchInModuleContent(@NotNull Module aModule) {
return myScope1.isSearchInModuleContent(aModule) || myScope2.isSearchInModuleContent(aModule);
public boolean isSearchInModuleContent(@NotNull final Module module) {
return ContainerUtil.find(myScopes, new Condition<GlobalSearchScope>() {
@Override
public boolean value(GlobalSearchScope scope) {
return scope.isSearchInModuleContent(module);
}
}) != null;
}
@Override
public boolean isSearchInModuleContent(@NotNull final Module aModule, final boolean testSources) {
return myScope1.isSearchInModuleContent(aModule, testSources) || myScope2.isSearchInModuleContent(aModule, testSources);
public boolean isSearchInModuleContent(@NotNull final Module module, final boolean testSources) {
return ContainerUtil.find(myScopes, new Condition<GlobalSearchScope>() {
@Override
public boolean value(GlobalSearchScope scope) {
return scope.isSearchInModuleContent(module, testSources);
}
}) != null;
}
@Override
public boolean isSearchInLibraries() {
return myScope1.isSearchInLibraries() || myScope2.isSearchInLibraries();
return ContainerUtil.find(myScopes, new Condition<GlobalSearchScope>() {
@Override
public boolean value(GlobalSearchScope scope) {
return scope.isSearchInLibraries();
}
}) != null;
}
@Override
@@ -472,18 +534,18 @@ public abstract class GlobalSearchScope extends SearchScope implements ProjectAw
UnionScope that = (UnionScope)o;
return myScope1.equals(that.myScope1) && myScope2.equals(that.myScope2);
return new HashSet<GlobalSearchScope>(Arrays.asList(myScopes)).equals(new HashSet<GlobalSearchScope>(Arrays.asList(that.myScopes)));
}
@Override
public int hashCode() {
return 31 * myScope1.hashCode() + myScope2.hashCode();
return Arrays.hashCode(myScopes);
}
@NonNls
@Override
public String toString() {
return "Union: (" + myScope1 + ", " + myScope2 + ")";
return "Union: (" + StringUtil.join(Arrays.asList(myScopes), ",") + ")";
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,6 +46,7 @@ import com.intellij.psi.util.PsiUtilCore;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageInfoFactory;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Function;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import com.intellij.util.codeInsight.CommentUtilCore;
@@ -866,13 +867,13 @@ public class PsiSearchHelperImpl implements PsiSearchHelper {
@NotNull
private static GlobalSearchScope uniteScopes(@NotNull Collection<RequestWithProcessor> requests) {
GlobalSearchScope commonScope = null;
for (RequestWithProcessor r : requests) {
final GlobalSearchScope scope = (GlobalSearchScope)r.request.searchScope;
commonScope = commonScope == null ? scope : commonScope.uniteWith(scope);
}
assert commonScope != null;
return commonScope;
Set<GlobalSearchScope> scopes = ContainerUtil.map2Set(requests, new Function<RequestWithProcessor, GlobalSearchScope>() {
@Override
public GlobalSearchScope fun(RequestWithProcessor request) {
return (GlobalSearchScope)request.request.searchScope;
}
});
return GlobalSearchScope.union(scopes.toArray(new GlobalSearchScope[scopes.size()]));
}
private static void distributePrimitives(@NotNull Map<SearchRequestCollector, Processor<PsiReference>> collectors,