mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
IJPL-158493 Extract a superclass from AsyncProjectViewSupport
To avoid duplicating a lot of logic in the new coroutine-based implementation, extract the parts of AsyncProjectViewSupport that don't depend on the models into a superclass. Refactor the code around acceptAndUpdate a bit so the only model-dependent part is now in this function with a new signature, so it can be used in all places where a similar logic was used. Other than that, the code was mostly just moved. The part about myNodeUpdater is a bit ugly, as it's now initialized in the derived class and then used in the base class in setupListeners. It's OK, as it's an internal implementation class, it won't have a lot of subclasses, so we're not forcing anyone to figure out this ugliness before using it. GitOrigin-RevId: a8d858c5c579a538a4c917cf830bc0704468f43d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8ee289751b
commit
b211b4adeb
@@ -30,6 +30,7 @@ import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.PlatformUtils;
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -50,8 +51,9 @@ public class PackageViewPane extends AbstractProjectViewPaneWithAsyncSupport {
|
||||
super(project);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Override
|
||||
protected void configureAsyncSupport(@NotNull AsyncProjectViewSupport support) {
|
||||
protected void configureAsyncSupport(@NotNull ProjectViewPaneSupport support) {
|
||||
support.setMultiSelectionEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -16329,7 +16329,6 @@ c:com.intellij.ide.projectView.impl.ProjectViewPane
|
||||
- sf:ID:java.lang.String
|
||||
- <init>(com.intellij.openapi.project.Project):V
|
||||
- s:canBeSelectedInProjectView(com.intellij.openapi.project.Project,com.intellij.openapi.vfs.VirtualFile):Z
|
||||
- configureAsyncSupport(com.intellij.ide.projectView.impl.AsyncProjectViewSupport):V
|
||||
- createSelectInTarget():com.intellij.ide.SelectInTarget
|
||||
- p:createStructure():com.intellij.ide.projectView.impl.ProjectAbstractTreeStructureBase
|
||||
- p:createTree(javax.swing.tree.DefaultTreeModel):com.intellij.ide.projectView.impl.ProjectViewTree
|
||||
|
||||
@@ -222,11 +222,11 @@ public abstract class AbstractProjectViewPane implements UiCompatibleDataProvide
|
||||
|
||||
public void updateFrom(Object element, boolean forceResort, boolean updateStructure) {
|
||||
if (element instanceof PsiElement) {
|
||||
AsyncProjectViewSupport support = getAsyncSupport();
|
||||
var support = getAsyncSupport();
|
||||
if (support != null) support.updateByElement((PsiElement)element, updateStructure);
|
||||
}
|
||||
else if (element instanceof TreePath) {
|
||||
AsyncProjectViewSupport support = getAsyncSupport();
|
||||
var support = getAsyncSupport();
|
||||
if (support != null) support.update((TreePath)element, updateStructure);
|
||||
}
|
||||
}
|
||||
@@ -1116,7 +1116,7 @@ public abstract class AbstractProjectViewPane implements UiCompatibleDataProvide
|
||||
return TreeUtil.getLastUserObject(AbstractTreeNode.class, path);
|
||||
}
|
||||
|
||||
AsyncProjectViewSupport getAsyncSupport() {
|
||||
@Nullable ProjectViewPaneSupport getAsyncSupport() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ import java.util.Comparator;
|
||||
import static com.intellij.ide.projectView.ProjectViewSelectionTopicKt.PROJECT_VIEW_SELECTION_TOPIC;
|
||||
|
||||
public abstract class AbstractProjectViewPaneWithAsyncSupport extends AbstractProjectViewPane {
|
||||
private AsyncProjectViewSupport myAsyncSupport;
|
||||
private ProjectViewPaneSupport myAsyncSupport;
|
||||
private JComponent myComponent;
|
||||
|
||||
protected AbstractProjectViewPaneWithAsyncSupport(@NotNull Project project) {
|
||||
@@ -118,7 +118,7 @@ public abstract class AbstractProjectViewPaneWithAsyncSupport extends AbstractPr
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
protected void configureAsyncSupport(@NotNull AsyncProjectViewSupport support) {
|
||||
protected void configureAsyncSupport(@NotNull ProjectViewPaneSupport support) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -236,7 +236,7 @@ public abstract class AbstractProjectViewPaneWithAsyncSupport extends AbstractPr
|
||||
}
|
||||
|
||||
@Override
|
||||
AsyncProjectViewSupport getAsyncSupport() {
|
||||
ProjectViewPaneSupport getAsyncSupport() {
|
||||
return myAsyncSupport;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,39 +1,25 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide.projectView.impl;
|
||||
|
||||
import com.intellij.ide.CopyPasteUtil;
|
||||
import com.intellij.ide.bookmark.BookmarksListener;
|
||||
import com.intellij.ide.bookmark.FileBookmarksListener;
|
||||
import com.intellij.ide.projectView.ProjectViewPsiTreeChangeListener;
|
||||
import com.intellij.ide.projectView.impl.ProjectViewPaneSelectionHelper.SelectionDescriptor;
|
||||
import com.intellij.ide.util.treeView.AbstractTreeNode;
|
||||
import com.intellij.ide.util.treeView.AbstractTreeStructure;
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.ActionCallback;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.vcs.FileStatusListener;
|
||||
import com.intellij.openapi.vcs.FileStatusManager;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.problems.ProblemListener;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.ui.tree.*;
|
||||
import com.intellij.ui.tree.project.ProjectFileNodeUpdater;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
@@ -44,12 +30,10 @@ import static com.intellij.ide.util.treeView.TreeState.expand;
|
||||
import static com.intellij.ui.tree.project.ProjectFileNode.findArea;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class AsyncProjectViewSupport {
|
||||
public final class AsyncProjectViewSupport extends ProjectViewPaneSupport {
|
||||
private static final Logger LOG = Logger.getInstance(AsyncProjectViewSupport.class);
|
||||
private final ProjectFileNodeUpdater myNodeUpdater;
|
||||
private final StructureTreeModel myStructureTreeModel;
|
||||
private final AsyncTreeModel myAsyncTreeModel;
|
||||
private boolean myMultiSelectionEnabled = true;
|
||||
|
||||
public AsyncProjectViewSupport(@NotNull Disposable parent,
|
||||
@NotNull Project project,
|
||||
@@ -77,84 +61,20 @@ public final class AsyncProjectViewSupport {
|
||||
}
|
||||
}
|
||||
};
|
||||
MessageBusConnection connection = project.getMessageBus().connect(parent);
|
||||
connection.subscribe(BookmarksListener.TOPIC, new FileBookmarksListener(file -> updateByFile(file, !file.isDirectory())));
|
||||
PsiManager.getInstance(project).addPsiTreeChangeListener(new ProjectViewPsiTreeChangeListener(project) {
|
||||
@Override
|
||||
protected boolean isFlattenPackages() {
|
||||
return structure instanceof AbstractProjectTreeStructure && ((AbstractProjectTreeStructure)structure).isFlattenPackages();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DefaultMutableTreeNode getRootNode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSubtreeToUpdateByRoot() {
|
||||
myNodeUpdater.updateFromRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean addSubtreeToUpdateByElement(@NotNull PsiElement element) {
|
||||
VirtualFile file = PsiUtilCore.getVirtualFile(element);
|
||||
if (file != null) {
|
||||
myNodeUpdater.updateFromFile(file);
|
||||
}
|
||||
else {
|
||||
updateByElement(element, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, parent);
|
||||
FileStatusManager.getInstance(project).addFileStatusListener(new FileStatusListener() {
|
||||
@Override
|
||||
public void fileStatusesChanged() {
|
||||
updateAllPresentations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileStatusChanged(@NotNull VirtualFile file) {
|
||||
updateByFile(file, false);
|
||||
}
|
||||
}, parent);
|
||||
CopyPasteUtil.addDefaultListener(parent, element -> updateByElement(element, false));
|
||||
project.getMessageBus().connect(parent).subscribe(ProblemListener.TOPIC, new ProblemListener() {
|
||||
@Override
|
||||
public void problemsAppeared(@NotNull VirtualFile file) {
|
||||
updatePresentationsFromRootTo(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void problemsDisappeared(@NotNull VirtualFile file) {
|
||||
updatePresentationsFromRootTo(file);
|
||||
}
|
||||
});
|
||||
project.getMessageBus().connect(parent).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerListener() {
|
||||
|
||||
// structure = true because files may have children too, e.g. if the Show Members option is selected, and children inherit file colors
|
||||
|
||||
@Override
|
||||
public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
|
||||
updateByFile(file, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
|
||||
updateByFile(file, true);
|
||||
}
|
||||
});
|
||||
setupListeners(parent, project, structure);
|
||||
}
|
||||
|
||||
public AsyncTreeModel getTreeModel() {
|
||||
return myAsyncTreeModel;
|
||||
}
|
||||
|
||||
public void setComparator(@NotNull Comparator<? super NodeDescriptor<?>> comparator) {
|
||||
@Override
|
||||
public void setComparator(@Nullable Comparator<? super NodeDescriptor<?>> comparator) {
|
||||
myStructureTreeModel.setComparator(comparator);
|
||||
}
|
||||
|
||||
public ActionCallback select(JTree tree, Object object, VirtualFile file) {
|
||||
@Override
|
||||
public @NotNull ActionCallback select(@NotNull JTree tree, @Nullable Object object, @Nullable VirtualFile file) {
|
||||
if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) {
|
||||
SelectInProjectViewImplKt.getLOG().debug(
|
||||
"AsyncProjectViewSupport.select: " +
|
||||
@@ -230,45 +150,7 @@ public final class AsyncProjectViewSupport {
|
||||
myAsyncTreeModel.accept(visitor).onProcessed(path -> myAsyncTreeModel.onValidThread(task));
|
||||
}
|
||||
|
||||
public void setMultiSelectionEnabled(boolean enabled) {
|
||||
myMultiSelectionEnabled = enabled;
|
||||
}
|
||||
|
||||
private boolean selectPaths(@NotNull JTree tree, @NotNull List<TreePath> paths, @NotNull TreeVisitor visitor) {
|
||||
if (paths.isEmpty()) {
|
||||
SelectInProjectViewImplKt.getLOG().debug("Nothing to select");
|
||||
return false;
|
||||
}
|
||||
if (paths.size() > 1 && myMultiSelectionEnabled) {
|
||||
if (visitor instanceof ProjectViewNodeVisitor nodeVisitor) {
|
||||
return selectPaths(tree, new SelectionDescriptor(nodeVisitor.getElement(), nodeVisitor.getFile(), paths));
|
||||
}
|
||||
if (visitor instanceof ProjectViewFileVisitor fileVisitor) {
|
||||
return selectPaths(tree, new SelectionDescriptor(null, fileVisitor.getElement(), paths));
|
||||
}
|
||||
}
|
||||
if (!myMultiSelectionEnabled) {
|
||||
SelectInProjectViewImplKt.getLOG().debug("Selecting only the first path because multi-selection is disabled");
|
||||
}
|
||||
TreePath path = paths.get(0);
|
||||
tree.expandPath(path); // request to expand found path
|
||||
TreeUtil.selectPaths(tree, path); // select and scroll to center
|
||||
if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) {
|
||||
SelectInProjectViewImplKt.getLOG().debug("Selected the only path: " + path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean selectPaths(@NotNull JTree tree, @NotNull SelectionDescriptor selectionDescriptor) {
|
||||
List<? extends TreePath> adjustedPaths = ProjectViewPaneSelectionHelper.getAdjustedPaths(selectionDescriptor);
|
||||
adjustedPaths.forEach(it -> tree.expandPath(it));
|
||||
TreeUtil.selectPaths(tree, adjustedPaths);
|
||||
if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) {
|
||||
SelectInProjectViewImplKt.getLOG().debug("Selected paths adjusted according to " + selectionDescriptor + ": " + adjustedPaths);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAll(Runnable onDone) {
|
||||
LOG.debug(new RuntimeException("reload a whole tree"));
|
||||
CompletableFuture<?> future = myStructureTreeModel.invalidateAsync();
|
||||
@@ -277,67 +159,25 @@ public final class AsyncProjectViewSupport {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull TreePath path, boolean structure) {
|
||||
myStructureTreeModel.invalidate(path, structure);
|
||||
}
|
||||
|
||||
public void update(@NotNull List<? extends TreePath> list, boolean structure) {
|
||||
for (TreePath path : list) update(path, structure);
|
||||
}
|
||||
|
||||
public void updateByFile(@NotNull VirtualFile file, boolean structure) {
|
||||
LOG.debug(structure ? "updateChildrenByFile: " : "updatePresentationByFile: ", file);
|
||||
update(null, file, structure);
|
||||
}
|
||||
|
||||
public void updateByElement(@NotNull PsiElement element, boolean structure) {
|
||||
LOG.debug(structure ? "updateChildrenByElement: " : "updatePresentationByElement: ", element);
|
||||
update(element, null, structure);
|
||||
}
|
||||
|
||||
private void update(PsiElement element, VirtualFile file, boolean structure) {
|
||||
SmartList<TreePath> list = new SmartList<>();
|
||||
TreeVisitor visitor = AbstractProjectViewPane.createVisitor(element, file, list);
|
||||
if (visitor != null) acceptAndUpdate(visitor, list, structure);
|
||||
}
|
||||
|
||||
private void acceptAndUpdate(@NotNull TreeVisitor visitor, List<? extends TreePath> list, boolean structure) {
|
||||
myAsyncTreeModel.accept(visitor, false).onSuccess(path -> update(list, structure));
|
||||
}
|
||||
|
||||
private void updatePresentationsFromRootTo(@NotNull VirtualFile file) {
|
||||
// find first valid parent for removed file
|
||||
while (!file.isValid()) {
|
||||
file = file.getParent();
|
||||
if (file == null) return;
|
||||
}
|
||||
SmartList<TreePath> structures = new SmartList<>();
|
||||
SmartList<TreePath> presentations = new SmartList<>();
|
||||
myAsyncTreeModel.accept(new ProjectViewFileVisitor(file, structures::add) {
|
||||
@Override
|
||||
protected @NotNull Action visit(@NotNull TreePath path, @NotNull AbstractTreeNode node, @NotNull VirtualFile element) {
|
||||
Action action = super.visit(path, node, element);
|
||||
if (action == Action.CONTINUE) presentations.add(path);
|
||||
return action;
|
||||
}
|
||||
}, false).onSuccess(path -> {
|
||||
update(presentations, false);
|
||||
update(structures, true);
|
||||
@Override
|
||||
protected void acceptAndUpdate(
|
||||
@NotNull TreeVisitor visitor,
|
||||
@Nullable List<? extends TreePath> presentations,
|
||||
@Nullable List<? extends TreePath> structures
|
||||
) {
|
||||
myAsyncTreeModel.accept(visitor, false).onSuccess(path -> {
|
||||
if (presentations != null) update(presentations, false);
|
||||
if (structures != null) update(structures, true);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateAllPresentations() {
|
||||
SmartList<TreePath> list = new SmartList<>();
|
||||
acceptAndUpdate(new TreeVisitor() {
|
||||
@Override
|
||||
public @NotNull Action visit(@NotNull TreePath path) {
|
||||
list.add(path);
|
||||
return Action.CONTINUE;
|
||||
}
|
||||
}, list, false);
|
||||
}
|
||||
|
||||
void setModelTo(@NotNull JTree tree) {
|
||||
@Override
|
||||
public void setModelTo(@NotNull JTree tree) {
|
||||
RestoreSelectionListener listener = new RestoreSelectionListener();
|
||||
tree.addTreeSelectionListener(listener);
|
||||
tree.setModel(myAsyncTreeModel);
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.ArchiveFileSystem;
|
||||
import com.intellij.util.PlatformUtils;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -44,8 +45,9 @@ public class ProjectViewPane extends AbstractProjectViewPaneWithAsyncSupport {
|
||||
super(project);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Override
|
||||
public void configureAsyncSupport(@NotNull AsyncProjectViewSupport support) {
|
||||
public void configureAsyncSupport(@NotNull ProjectViewPaneSupport support) {
|
||||
support.setMultiSelectionEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide.projectView.impl;
|
||||
|
||||
import com.intellij.ide.CopyPasteUtil;
|
||||
import com.intellij.ide.bookmark.BookmarksListener;
|
||||
import com.intellij.ide.bookmark.FileBookmarksListener;
|
||||
import com.intellij.ide.projectView.ProjectViewPsiTreeChangeListener;
|
||||
import com.intellij.ide.util.treeView.AbstractTreeNode;
|
||||
import com.intellij.ide.util.treeView.AbstractTreeStructure;
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.ActionCallback;
|
||||
import com.intellij.openapi.vcs.FileStatusListener;
|
||||
import com.intellij.openapi.vcs.FileStatusManager;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.problems.ProblemListener;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.ui.tree.TreeVisitor;
|
||||
import com.intellij.ui.tree.project.ProjectFileNodeUpdater;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public abstract class ProjectViewPaneSupport {
|
||||
private static final Logger LOG = Logger.getInstance(ProjectViewPaneSupport.class);
|
||||
|
||||
private boolean myMultiSelectionEnabled = true;
|
||||
protected ProjectFileNodeUpdater myNodeUpdater;
|
||||
|
||||
protected void setupListeners(@NotNull Disposable parent, @NotNull Project project, @NotNull AbstractTreeStructure structure) {
|
||||
MessageBusConnection connection = project.getMessageBus().connect(parent);
|
||||
connection.subscribe(BookmarksListener.TOPIC, new FileBookmarksListener(file -> updateByFile(file, !file.isDirectory())));
|
||||
PsiManager.getInstance(project).addPsiTreeChangeListener(new ProjectViewPsiTreeChangeListener(project) {
|
||||
@Override
|
||||
protected boolean isFlattenPackages() {
|
||||
return structure instanceof AbstractProjectTreeStructure && ((AbstractProjectTreeStructure)structure).isFlattenPackages();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DefaultMutableTreeNode getRootNode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSubtreeToUpdateByRoot() {
|
||||
myNodeUpdater.updateFromRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean addSubtreeToUpdateByElement(@NotNull PsiElement element) {
|
||||
VirtualFile file = PsiUtilCore.getVirtualFile(element);
|
||||
if (file != null) {
|
||||
myNodeUpdater.updateFromFile(file);
|
||||
}
|
||||
else {
|
||||
updateByElement(element, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, parent);
|
||||
FileStatusManager.getInstance(project).addFileStatusListener(new FileStatusListener() {
|
||||
@Override
|
||||
public void fileStatusesChanged() {
|
||||
updateAllPresentations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileStatusChanged(@NotNull VirtualFile file) {
|
||||
updateByFile(file, false);
|
||||
}
|
||||
}, parent);
|
||||
CopyPasteUtil.addDefaultListener(parent, element -> updateByElement(element, false));
|
||||
project.getMessageBus().connect(parent).subscribe(ProblemListener.TOPIC, new ProblemListener() {
|
||||
@Override
|
||||
public void problemsAppeared(@NotNull VirtualFile file) {
|
||||
updatePresentationsFromRootTo(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void problemsDisappeared(@NotNull VirtualFile file) {
|
||||
updatePresentationsFromRootTo(file);
|
||||
}
|
||||
});
|
||||
project.getMessageBus().connect(parent).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerListener() {
|
||||
|
||||
// structure = true because files may have children too, e.g. if the Show Members option is selected, and children inherit file colors
|
||||
|
||||
@Override
|
||||
public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
|
||||
updateByFile(file, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
|
||||
updateByFile(file, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public abstract void setModelTo(@NotNull JTree tree);
|
||||
|
||||
public abstract void setComparator(@Nullable Comparator<? super NodeDescriptor<?>> comparator);
|
||||
|
||||
public void setMultiSelectionEnabled(boolean enabled) {
|
||||
myMultiSelectionEnabled = enabled;
|
||||
}
|
||||
|
||||
public abstract void updateAll(Runnable onDone);
|
||||
|
||||
public void update(@NotNull List<? extends TreePath> list, boolean structure) {
|
||||
for (TreePath path : list) update(path, structure);
|
||||
}
|
||||
|
||||
public abstract void update(@NotNull TreePath path, boolean structure);
|
||||
|
||||
public void updateByFile(@NotNull VirtualFile file, boolean structure) {
|
||||
LOG.debug(structure ? "updateChildrenByFile: " : "updatePresentationByFile: ", file);
|
||||
update(null, file, structure);
|
||||
}
|
||||
|
||||
public void updateByElement(@NotNull PsiElement element, boolean structure) {
|
||||
LOG.debug(structure ? "updateChildrenByElement: " : "updatePresentationByElement: ", element);
|
||||
update(element, null, structure);
|
||||
}
|
||||
|
||||
protected void update(@Nullable PsiElement element, @Nullable VirtualFile file, boolean structure) {
|
||||
SmartList<TreePath> list = new SmartList<>();
|
||||
TreeVisitor visitor = AbstractProjectViewPane.createVisitor(element, file, list);
|
||||
if (visitor != null) acceptAndUpdate(visitor, structure ? null : list, structure ? list : null);
|
||||
}
|
||||
|
||||
protected void updateAllPresentations() {
|
||||
SmartList<TreePath> list = new SmartList<>();
|
||||
acceptAndUpdate(new TreeVisitor() {
|
||||
@Override
|
||||
public @NotNull Action visit(@NotNull TreePath path) {
|
||||
list.add(path);
|
||||
return Action.CONTINUE;
|
||||
}
|
||||
}, list, null);
|
||||
}
|
||||
|
||||
protected void updatePresentationsFromRootTo(@NotNull VirtualFile file) {
|
||||
// find first valid parent for removed file
|
||||
while (!file.isValid()) {
|
||||
file = file.getParent();
|
||||
if (file == null) return;
|
||||
}
|
||||
SmartList<TreePath> structures = new SmartList<>();
|
||||
SmartList<TreePath> presentations = new SmartList<>();
|
||||
var visitor = new ProjectViewFileVisitor(file, structures::add) {
|
||||
@Override
|
||||
protected @NotNull Action visit(@NotNull TreePath path, @NotNull AbstractTreeNode node, @NotNull VirtualFile element) {
|
||||
Action action = super.visit(path, node, element);
|
||||
if (action == Action.CONTINUE) presentations.add(path);
|
||||
return action;
|
||||
}
|
||||
};
|
||||
acceptAndUpdate(visitor, presentations, structures);
|
||||
}
|
||||
|
||||
protected abstract void acceptAndUpdate(
|
||||
@NotNull TreeVisitor visitor,
|
||||
@Nullable List<? extends TreePath> presentations,
|
||||
@Nullable List<? extends TreePath> structures
|
||||
);
|
||||
|
||||
public abstract @NotNull ActionCallback select(@NotNull JTree tree, @Nullable Object object, @Nullable VirtualFile file);
|
||||
|
||||
protected boolean selectPaths(@NotNull JTree tree, @NotNull List<TreePath> paths, @NotNull TreeVisitor visitor) {
|
||||
if (paths.isEmpty()) {
|
||||
SelectInProjectViewImplKt.getLOG().debug("Nothing to select");
|
||||
return false;
|
||||
}
|
||||
if (paths.size() > 1 && myMultiSelectionEnabled) {
|
||||
if (visitor instanceof ProjectViewNodeVisitor nodeVisitor) {
|
||||
return selectPaths(tree, new ProjectViewPaneSelectionHelper.SelectionDescriptor(nodeVisitor.getElement(), nodeVisitor.getFile(), paths));
|
||||
}
|
||||
if (visitor instanceof ProjectViewFileVisitor fileVisitor) {
|
||||
return selectPaths(tree, new ProjectViewPaneSelectionHelper.SelectionDescriptor(null, fileVisitor.getElement(), paths));
|
||||
}
|
||||
}
|
||||
if (!myMultiSelectionEnabled) {
|
||||
SelectInProjectViewImplKt.getLOG().debug("Selecting only the first path because multi-selection is disabled");
|
||||
}
|
||||
TreePath path = paths.get(0);
|
||||
tree.expandPath(path); // request to expand found path
|
||||
TreeUtil.selectPaths(tree, path); // select and scroll to center
|
||||
if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) {
|
||||
SelectInProjectViewImplKt.getLOG().debug("Selected the only path: " + path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static boolean selectPaths(@NotNull JTree tree, @NotNull ProjectViewPaneSelectionHelper.SelectionDescriptor selectionDescriptor) {
|
||||
List<? extends TreePath> adjustedPaths = ProjectViewPaneSelectionHelper.getAdjustedPaths(selectionDescriptor);
|
||||
adjustedPaths.forEach(it -> tree.expandPath(it));
|
||||
TreeUtil.selectPaths(tree, adjustedPaths);
|
||||
if (SelectInProjectViewImplKt.getLOG().isDebugEnabled()) {
|
||||
SelectInProjectViewImplKt.getLOG().debug("Selected paths adjusted according to " + selectionDescriptor + ": " + adjustedPaths);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user