diff --git a/platform/analysis-api/src/com/intellij/psi/search/GlobalSearchScopesCore.java b/platform/analysis-api/src/com/intellij/psi/search/GlobalSearchScopesCore.java index 01e986373567..8d1911331416 100644 --- a/platform/analysis-api/src/com/intellij/psi/search/GlobalSearchScopesCore.java +++ b/platform/analysis-api/src/com/intellij/psi/search/GlobalSearchScopesCore.java @@ -28,12 +28,14 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.search.scope.packageSet.*; -import com.intellij.util.ArrayUtil; import com.intellij.util.ObjectUtils; -import com.intellij.util.containers.SmartHashSet; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; public class GlobalSearchScopesCore { @NotNull @@ -58,14 +60,12 @@ public class GlobalSearchScopesCore { @NotNull public static GlobalSearchScope directoriesScope(@NotNull Project project, boolean withSubdirectories, @NotNull VirtualFile... directories) { - if (directories.length ==1) { + if (directories.length == 1) { return directoryScope(project, directories[0], withSubdirectories); } - BitSet withSubdirectoriesBS = new BitSet(directories.length); - if (withSubdirectories) { - withSubdirectoriesBS.set(0, directories.length); - } - return new DirectoriesScope(project, directories, withSubdirectoriesBS); + return new DirectoriesScope(project, + withSubdirectories ? Collections.emptySet() : ContainerUtil.newHashSet(directories), + withSubdirectories ? ContainerUtil.newHashSet(directories) : Collections.emptySet()); } public static GlobalSearchScope filterScope(@NotNull Project project, @NotNull NamedScope set) { @@ -220,7 +220,12 @@ public class GlobalSearchScopesCore { @Override public boolean contains(@NotNull VirtualFile file) { - return myWithSubdirectories ? VfsUtilCore.isAncestor(myDirectory, file, false) : myDirectory.equals(file.getParent()); + VirtualFile parent = file.getParent(); + return parent != null && in(parent); + } + + private boolean in(@NotNull VirtualFile parent) { + return myWithSubdirectories ? VfsUtilCore.isAncestor(myDirectory, parent, false) : myDirectory.equals(parent); } @Override @@ -262,17 +267,22 @@ public class GlobalSearchScopesCore { if (equals(scope)) return this; if (scope instanceof DirectoryScope) { DirectoryScope other = (DirectoryScope)scope; - VirtualFile otherDirectory = other.myDirectory; - if (myWithSubdirectories && VfsUtilCore.isAncestor(myDirectory, otherDirectory, false)) return this; - if (other.myWithSubdirectories && VfsUtilCore.isAncestor(otherDirectory, myDirectory, false)) return other; - BitSet newWithSubdirectories = new BitSet(); - newWithSubdirectories.set(0, myWithSubdirectories); - newWithSubdirectories.set(1, other.myWithSubdirectories); - return new DirectoriesScope(getProject(), new VirtualFile[]{myDirectory,otherDirectory}, newWithSubdirectories); + if (in(other.myDirectory)) return this; + if (other.in(myDirectory)) return other; + return new DirectoriesScope(getProject(), + union(!myWithSubdirectories, myDirectory, !other.myWithSubdirectories, other.myDirectory), + union(myWithSubdirectories, myDirectory, other.myWithSubdirectories, other.myDirectory)); } return super.uniteWith(scope); } + private static Set union(boolean addDir1, @NotNull VirtualFile dir1, boolean addDir2, @NotNull VirtualFile dir2) { + if (addDir1 && addDir2) return ContainerUtil.newHashSet(dir1, dir2); + if (addDir1) return Collections.singleton(dir1); + if (addDir2) return Collections.singleton(dir2); + return Collections.emptySet(); + } + @NotNull @Override public Project getProject() { @@ -288,15 +298,18 @@ public class GlobalSearchScopesCore { } static class DirectoriesScope extends GlobalSearchScope { - private final VirtualFile[] myDirectories; - private final BitSet myWithSubdirectories; + private final Set myDirectories; + private final Set myDirectoriesWithSubdirectories; - private DirectoriesScope(@NotNull Project project, @NotNull VirtualFile[] directories, @NotNull BitSet withSubdirectories) { + private DirectoriesScope(@NotNull Project project, + @NotNull Set directories, + @NotNull Set directoriesWithSubdirectories) { super(project); - myWithSubdirectories = withSubdirectories; myDirectories = directories; - if (directories.length < 2) { - throw new IllegalArgumentException("Expected >1 directories, but got: " + Arrays.asList(directories)); + myDirectoriesWithSubdirectories = directoriesWithSubdirectories; + if (directories.size() + directoriesWithSubdirectories.size() < 2) { + throw new IllegalArgumentException("Expected >1 directories, but got: directories " + directories + + ", directories with subdirectories " + directoriesWithSubdirectories); } } @@ -307,23 +320,10 @@ public class GlobalSearchScopesCore { } private boolean in(@NotNull VirtualFile parent) { - Set directoriesWithSubdirectories = null; - for (int i = 0; i < myDirectories.length; i++) { - VirtualFile directory = myDirectories[i]; - boolean withSubdirectories = myWithSubdirectories.get(i); - if (withSubdirectories) { - if (directory.getFileSystem().equals(parent.getFileSystem())) { - if (directoriesWithSubdirectories == null) { - directoriesWithSubdirectories = new SmartHashSet<>(); - } - directoriesWithSubdirectories.add(directory); - } - } - else if (directory.equals(parent)) { - return true; - } + if (myDirectories.contains(parent)) { + return true; } - return VfsUtilCore.isUnder(parent, directoriesWithSubdirectories); + return VfsUtilCore.isUnder(parent, myDirectoriesWithSubdirectories); } @Override @@ -344,25 +344,21 @@ public class GlobalSearchScopesCore { @Override public String toString() { //noinspection HardCodedStringLiteral - return "Directories scope: " + Arrays.asList(myDirectories); + return "Directories scope: directories " + myDirectories + ", directories with subdirectories " + myDirectoriesWithSubdirectories; } @Override public int hashCode() { - int result = 1; - for (int i = 0; i < myDirectories.length; i++) { - VirtualFile directory = myDirectories[i]; - boolean withSubdirectories = myWithSubdirectories.get(i); - result = result*31 + directory.hashCode() *31 + (withSubdirectories?1:0); - } + int result = myDirectories.hashCode(); + result = result * 31 + myDirectoriesWithSubdirectories.hashCode(); return result; } @Override public boolean equals(Object obj) { return obj instanceof DirectoriesScope && - Arrays.equals(myDirectories, ((DirectoriesScope)obj).myDirectories) && - myWithSubdirectories.equals(((DirectoriesScope)obj).myWithSubdirectories); + myDirectories.equals(((DirectoriesScope)obj).myDirectories) && + myDirectoriesWithSubdirectories.equals(((DirectoriesScope)obj).myDirectoriesWithSubdirectories); } @NotNull @@ -372,28 +368,35 @@ public class GlobalSearchScopesCore { return this; } if (scope instanceof DirectoryScope) { - if (in(((DirectoryScope)scope).myDirectory)) { + DirectoryScope other = (DirectoryScope)scope; + if (in(other.myDirectory)) { return this; } - VirtualFile[] newDirectories = ArrayUtil.append(myDirectories, ((DirectoryScope)scope).myDirectory, VirtualFile.class); - BitSet newWithSubdirectories = (BitSet)myWithSubdirectories.clone(); - newWithSubdirectories.set(myDirectories.length, ((DirectoryScope)scope).myWithSubdirectories); - return new DirectoriesScope(getProject(), newDirectories, newWithSubdirectories); + Set directories = myDirectories; + Set directoriesWithSubdirectories = myDirectoriesWithSubdirectories; + if (other.myWithSubdirectories) { + directoriesWithSubdirectories = new HashSet<>(directoriesWithSubdirectories); + directoriesWithSubdirectories.add(other.myDirectory); + } + else { + directories = new HashSet<>(directories); + directories.add(other.myDirectory); + } + return new DirectoriesScope(getProject(), directories, directoriesWithSubdirectories); } if (scope instanceof DirectoriesScope) { DirectoriesScope other = (DirectoriesScope)scope; - List newDirectories = new ArrayList<>(myDirectories.length + other.myDirectories.length); - newDirectories.addAll(Arrays.asList(myDirectories)); - BitSet newWithSubdirectories = (BitSet)myWithSubdirectories.clone(); - VirtualFile[] otherDirectories = other.myDirectories; - for (int i = 0; i < otherDirectories.length; i++) { - VirtualFile otherDirectory = otherDirectories[i]; - if (!in(otherDirectory)) { - newWithSubdirectories.set(newDirectories.size(), other.myWithSubdirectories.get(i)); - newDirectories.add(otherDirectory); - } + Set directories = myDirectories; + Set directoriesWithSubdirectories = myDirectoriesWithSubdirectories; + if (!other.myDirectories.isEmpty()) { + directories = new HashSet<>(directories); + directories.addAll(other.myDirectories); } - return new DirectoriesScope(getProject(), newDirectories.toArray(new VirtualFile[newDirectories.size()]), newWithSubdirectories); + if (!other.myDirectoriesWithSubdirectories.isEmpty()) { + directoriesWithSubdirectories = new HashSet<>(directoriesWithSubdirectories); + directoriesWithSubdirectories.addAll(other.myDirectoriesWithSubdirectories); + } + return new DirectoriesScope(getProject(), directories, directoriesWithSubdirectories); } return super.uniteWith(scope); } @@ -408,11 +411,13 @@ public class GlobalSearchScopesCore { @NotNull @Override public String getDisplayName() { - if (myDirectories.length == 1) { - VirtualFile root = myDirectories[0]; + if (myDirectories.size() + myDirectoriesWithSubdirectories.size() == 1) { + Set dirs = myDirectories.size() == 1 ? myDirectories : myDirectoriesWithSubdirectories; + VirtualFile root = Objects.requireNonNull(ContainerUtil.getFirstItem(dirs)); return "Directory '" + root.getName() + "'"; } - return "Directories " + StringUtil.join(myDirectories, file -> "'" + file.getName() + "'", ", "); + Iterable allDirs = ContainerUtil.concat(myDirectories, myDirectoriesWithSubdirectories); + return "Directories " + StringUtil.join(allDirs, file -> "'" + file.getName() + "'", ", "); } }