diff --git a/platform/lang-impl/src/com/intellij/ide/scopeView/ScopeViewTreeModel.java b/platform/lang-impl/src/com/intellij/ide/scopeView/ScopeViewTreeModel.java index 7a62ec14a277..f957c4471c23 100644 --- a/platform/lang-impl/src/com/intellij/ide/scopeView/ScopeViewTreeModel.java +++ b/platform/lang-impl/src/com/intellij/ide/scopeView/ScopeViewTreeModel.java @@ -74,6 +74,7 @@ import java.util.function.BiFunction; import java.util.stream.Collectors; import static com.intellij.ide.projectView.impl.ProjectRootsUtil.findSourceFolder; +import static com.intellij.ide.projectView.impl.ShowModulesAction.hasModules; import static com.intellij.openapi.roots.ui.configuration.SourceRootPresentation.getSourceRootIcon; import static com.intellij.openapi.util.io.FileUtil.getLocationRelativeToUserHome; import static com.intellij.openapi.vfs.VfsUtilCore.VFS_SEPARATOR_CHAR; @@ -215,6 +216,7 @@ public final class ScopeViewTreeModel extends BaseTreeModel im model.onValidThread(() -> { root.childrenValid = false; LOG.debug("whole structure changed"); + model.setShowModules(hasModules() && root.getSettings().isShowModules()); treeStructureChanged(null, null, null); if (onDone != null) onDone.run(); }); @@ -534,13 +536,7 @@ public final class ScopeViewTreeModel extends BaseTreeModel im model.getChildren(model.getRoot()).forEach(child -> newRoots.put(child, mapper.apply(this, child))); roots = newRoots; if (newRoots.isEmpty()) return emptyList(); - ModuleManager manager = getModuleManager(getProject()); - if (manager == null) return emptyList(); - boolean hidden = !getSettings().isShowModules(); - if (!hidden && manager.getModules().length <= 1) hidden = true; - boolean flatten = hidden || getSettings().isFlattenModules(); - if (!flatten && !manager.hasModuleGroups() && !Registry.is("project.qualified.module.names")) flatten = true; - return new Group(newRoots.values(), hidden, flatten).createChildren(this, old); + return new Group(newRoots.values(), getSettings().isFlattenModules() || !hasModuleGroups(getProject())).createChildren(this, old); } @NotNull @@ -666,13 +662,21 @@ public final class ScopeViewTreeModel extends BaseTreeModel im @Override protected void update(PresentationData presentation) { + VirtualFile file = getVirtualFile(); String title = getTitle(); - presentation.setPresentableText(title != null ? title : toString()); + SimpleTextAttributes attributes = SimpleTextAttributes.REGULAR_ATTRIBUTES; + if (node.getRootID() instanceof VirtualFile) { + ProjectFileIndex index = getProjectFileIndex(getProject()); + if (index != null && file.equals(index.getContentRootForFile(file))) { + attributes = SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES; + } + } + presentation.addText(title != null ? title : toString(), attributes); Icon icon = getIcon(); - if (icon == null && getVirtualFile().isValid()) { - icon = getVirtualFile().isDirectory() + if (icon == null && file.isValid()) { + icon = file.isDirectory() ? getFolderIcon(node, null) - : getVirtualFile().getFileType().getIcon(); + : file.getFileType().getIcon(); } presentation.setIcon(icon); decorate(presentation); @@ -956,38 +960,33 @@ public final class ScopeViewTreeModel extends BaseTreeModel im id = module; } - Group(@NotNull Collection nodes, boolean hidden, boolean flatten) { + Group(@NotNull Collection nodes, boolean flatten) { id = null; if (!nodes.isEmpty()) { - if (hidden) { - roots.addAll(nodes); - } - else { - HashMap map = new HashMap<>(); - nodes.forEach(node -> { - Object id = node.node.getRootID(); - if (id instanceof Module) { - Module module = (Module)id; - Group group = map.get(module); - if (group == null) { - group = new Group(module); - map.put(module, group); - } - group.roots.add(node); + HashMap map = new HashMap<>(); + nodes.forEach(node -> { + Object id = node.node.getRootID(); + if (id instanceof Module) { + Module module = (Module)id; + Group group = map.get(module); + if (group == null) { + group = new Group(module); + map.put(module, group); } - else { - roots.add(node); - } - }); - if (flatten) { - groups.putAll(map); + group.roots.add(node); } else { - map.forEach((module, group) -> { - List path = getModuleNameAsList(module, Registry.is("project.qualified.module.names")); - group.roots.forEach(node -> add(node, path, 0)); - }); + roots.add(node); } + }); + if (flatten) { + groups.putAll(map); + } + else { + map.forEach((module, group) -> { + List path = getModuleNameAsList(module, Registry.is("project.qualified.module.names")); + group.roots.forEach(node -> add(node, path, 0)); + }); } } } @@ -1149,6 +1148,7 @@ public final class ScopeViewTreeModel extends BaseTreeModel im return index == null ? null : index.getModuleForFile(file); } + @Nullable private static String getPackageName(@Nullable PsiElement element) { if (element instanceof PsiDirectory && element.isValid()) { PsiDirectoryFactory factory = PsiDirectoryFactory.getInstance(element.getProject()); @@ -1160,6 +1160,12 @@ public final class ScopeViewTreeModel extends BaseTreeModel im return null; } + private static boolean hasModuleGroups(@Nullable Project project) { + if (Registry.is("project.qualified.module.names")) return true; + ModuleManager manager = getModuleManager(project); + return manager != null && manager.hasModuleGroups(); + } + @NotNull private static List getModuleNameAsList(@NotNull Module module, boolean split) { String name = module.getName(); diff --git a/platform/platform-impl/src/com/intellij/ui/tree/project/ProjectFileListener.java b/platform/platform-impl/src/com/intellij/ui/tree/project/ProjectFileListener.java index 72011d16460d..82d410a36c08 100644 --- a/platform/platform-impl/src/com/intellij/ui/tree/project/ProjectFileListener.java +++ b/platform/platform-impl/src/com/intellij/ui/tree/project/ProjectFileListener.java @@ -125,10 +125,13 @@ public abstract class ProjectFileListener { @Nullable public static AreaInstance findArea(@NotNull VirtualFile file, @Nullable Project project) { if (project == null || project.isDisposed()) return null; - Module module = ProjectFileIndex.getInstance(project).getModuleForFile(file); + Module module = ProjectFileIndex.getInstance(project).getModuleForFile(file, false); if (module != null) return module.isDisposed() ? null : module; VirtualFile ancestor = project.getBaseDir(); // file does not belong to any content root, but it is located under the project directory - return ancestor == null || !isAncestor(ancestor, file, false) ? null : project; + if (ancestor == null || !isAncestor(ancestor, file, false)) return null; + PsiManager manager = PsiManager.getInstance(project); + PsiElement element = file.isDirectory() ? manager.findDirectory(file) : manager.findFile(file); + return element == null ? null : project; // ensure that the corresponding file can be shown } } diff --git a/platform/platform-impl/src/com/intellij/ui/tree/project/ProjectFileTreeModel.java b/platform/platform-impl/src/com/intellij/ui/tree/project/ProjectFileTreeModel.java index 91cf81901ad5..5c75f5a48c2d 100644 --- a/platform/platform-impl/src/com/intellij/ui/tree/project/ProjectFileTreeModel.java +++ b/platform/platform-impl/src/com/intellij/ui/tree/project/ProjectFileTreeModel.java @@ -6,12 +6,12 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.vfs.VFileProperty; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileFilter; import com.intellij.ui.tree.BaseTreeModel; +import com.intellij.ui.tree.TreeCollector; import com.intellij.util.SmartList; import com.intellij.util.ThreeState; import com.intellij.util.concurrency.Invoker; @@ -30,6 +30,7 @@ import java.util.function.Predicate; import static com.intellij.openapi.vfs.VfsUtilCore.isAncestor; import static com.intellij.ui.tree.TreePathUtil.pathToCustomNode; +import static com.intellij.ui.tree.project.ProjectFileListener.findArea; import static java.util.Collections.emptyList; public final class ProjectFileTreeModel extends BaseTreeModel implements InvokerSupplier { @@ -120,10 +121,6 @@ public final class ProjectFileTreeModel extends BaseTreeModel i return visible; } - private static boolean isExpectedModule(@Nullable Module module, @NotNull VirtualFile file, @NotNull Project project) { - return file.isValid() && !project.isDisposed() && module == ProjectFileIndex.getInstance(project).getModuleForFile(file); - } - @NotNull private static Module[] getModules(@NotNull Project project) { ModuleManager manager = ModuleManager.getInstance(project); @@ -145,6 +142,15 @@ public final class ProjectFileTreeModel extends BaseTreeModel i }); } + public void setShowModules(boolean showModules) { + onValidThread(() -> { + if (root.showModules == showModules) return; + root.showModules = showModules; + root.valid = false; // need to reload content roots + pathChanged(null); + }); + } + private void pathChanged(@Nullable TreePath path) { onValidThread(() -> treeStructureChanged(path, null, null)); } @@ -211,6 +217,7 @@ public final class ProjectFileTreeModel extends BaseTreeModel i private static class ProjectNode extends Node { volatile VirtualFileFilter filter; + volatile boolean showModules; final Project project; ProjectNode(@NotNull Project project) { @@ -227,15 +234,27 @@ public final class ProjectFileTreeModel extends BaseTreeModel i List getChildren(@NotNull List oldList) { List list = new SmartList<>(); Mapper mapper = new Mapper<>(oldList, RootNode::new); + TreeCollector collector = showModules ? null : TreeCollector.createFileRootsCollector(); VirtualFile ancestor = project.getBaseDir(); - if (ancestor != null && isExpectedModule(null, ancestor, project)) { - list.add(mapper.apply(ancestor, project)); + if (ancestor != null && project == findArea(ancestor, project)) { + if (collector != null) { + collector.add(ancestor); + } + else { + list.add(mapper.apply(ancestor, project)); + } } for (Module module: getModules(project)) { for (VirtualFile file: getContentRoots(module)) { - list.add(mapper.apply(file, module)); + if (collector != null) { + collector.add(file); + } + else { + list.add(mapper.apply(file, module)); + } } } + if (collector != null) collector.get().forEach(file -> list.add(mapper.apply(file, file))); // invalidate all changed file nodes without notifications list.forEach(node -> node.invalidateNow(null)); return list; @@ -276,6 +295,9 @@ public final class ProjectFileTreeModel extends BaseTreeModel i VirtualFile file = getVirtualFile(); if (!file.isValid()) return emptyList(); + ProjectNode parent = findParent(ProjectNode.class); + if (parent == null) return emptyList(); + visibility = ThreeState.UNSURE; VirtualFile[] children = file.getChildren(); @@ -288,20 +310,10 @@ public final class ProjectFileTreeModel extends BaseTreeModel i continue; // ignore invalid symlink } Object id = getRootID(); - if (id instanceof VirtualFile) { + AreaInstance area = findArea(child, parent.project); + if (area != null && (id instanceof VirtualFile || area.equals(id))) { list.add(mapper.apply(child, id)); } - else if (id instanceof Project) { - if (isExpectedModule(null, child, (Project)id)) { - list.add(mapper.apply(child, id)); - } - } - else if (id instanceof Module) { - Module module = (Module)id; - if (!module.isDisposed() && isExpectedModule(module, child, module.getProject())) { - list.add(mapper.apply(child, id)); - } - } } return list; }