diff --git a/java/idea-ui/src/com/intellij/util/ui/classpath/ChooseLibrariesDialogBase.java b/java/idea-ui/src/com/intellij/util/ui/classpath/ChooseLibrariesDialogBase.java index 8cc1d42ebea1..ec719b74b759 100644 --- a/java/idea-ui/src/com/intellij/util/ui/classpath/ChooseLibrariesDialogBase.java +++ b/java/idea-ui/src/com/intellij/util/ui/classpath/ChooseLibrariesDialogBase.java @@ -123,7 +123,7 @@ public abstract class ChooseLibrariesDialogBase extends DialogWrapper { protected void queueUpdateAndSelect(@NotNull final Library library) { myModel.invalidateAsync().thenRun(() -> { - ((AsyncTreeModel)myTree.getModel()).accept(path -> { + ((TreeVisitor.Acceptor)myTree.getModel()).accept(path -> { return TreeVisitor.Action.CONTINUE; // traverse to update myParentsMap }).onProcessed(path -> { myModel.select(library, myTree, p -> {}); diff --git a/platform/editor-ui-api/api-dump.txt b/platform/editor-ui-api/api-dump.txt index 0ce8da202562..6a7f5115da72 100644 --- a/platform/editor-ui-api/api-dump.txt +++ b/platform/editor-ui-api/api-dump.txt @@ -3028,6 +3028,10 @@ c:com.intellij.ui.tree.TreeVisitor$ByTreePath - visit(javax.swing.tree.TreePath):com.intellij.ui.tree.TreeVisitor$Action - p:visit(javax.swing.tree.TreePath,java.lang.Object):com.intellij.ui.tree.TreeVisitor$Action - p:visit(javax.swing.tree.TreePath,java.lang.Object,I):com.intellij.ui.tree.TreeVisitor$Action +com.intellij.ui.tree.TreeVisitor$LoadingAwareAcceptor +- com.intellij.ui.tree.TreeVisitor$Acceptor +- accept(com.intellij.ui.tree.TreeVisitor):org.jetbrains.concurrency.Promise +- a:accept(com.intellij.ui.tree.TreeVisitor,Z):org.jetbrains.concurrency.Promise *e:com.intellij.ui.tree.TreeVisitor$VisitThread - java.lang.Enum - sf:BGT:com.intellij.ui.tree.TreeVisitor$VisitThread diff --git a/platform/editor-ui-api/src/com/intellij/ui/tree/TreeVisitor.java b/platform/editor-ui-api/src/com/intellij/ui/tree/TreeVisitor.java index 8dde253f426c..b72473b72e98 100644 --- a/platform/editor-ui-api/src/com/intellij/ui/tree/TreeVisitor.java +++ b/platform/editor-ui-api/src/com/intellij/ui/tree/TreeVisitor.java @@ -71,6 +71,23 @@ public interface TreeVisitor { Promise accept(@NotNull TreeVisitor visitor); } + /** + * Represents a tree model that accepts a tree visitor and promises a result, optionally allowing to skip not loaded nodes. + */ + interface LoadingAwareAcceptor extends Acceptor { + @Override + default @NotNull Promise accept(@NotNull TreeVisitor visitor) { + return accept(visitor, true); + } + + /** + * @param visitor an object that controls visiting a tree structure + * @param allowLoading a flag that determines whether the nodes that weren't loaded yet will be loaded and visited or skipped + * @return a promise that will be resolved when visiting is finished + */ + @NotNull + Promise accept(@NotNull TreeVisitor visitor, boolean allowLoading); + } abstract class ByComponent implements TreeVisitor { private final Function converter; diff --git a/platform/lang-impl/exposed-private-api.txt b/platform/lang-impl/exposed-private-api.txt index 4a459e89136e..6b7b504011df 100644 --- a/platform/lang-impl/exposed-private-api.txt +++ b/platform/lang-impl/exposed-private-api.txt @@ -21,7 +21,6 @@ com/intellij/ide/actions/searcheverywhere/SearchEverywhereSpellingCorrector com/intellij/ide/actions/searcheverywhere/SearchListModel com/intellij/ide/plugins/ContainerDescriptor com/intellij/ide/plugins/IdeaPluginDescriptorImpl -com/intellij/ide/projectView/impl/AsyncProjectViewSupport com/intellij/ide/todo/FileTree com/intellij/ide/todo/TodoNodeVisitor com/intellij/ide/todo/TodoTreeBuilderCoroutineHelper diff --git a/platform/lang-impl/src/com/intellij/ide/bookmark/ui/tree/FolderNodeUpdater.kt b/platform/lang-impl/src/com/intellij/ide/bookmark/ui/tree/FolderNodeUpdater.kt index 61639cb1239b..d4744bd1c5c5 100644 --- a/platform/lang-impl/src/com/intellij/ide/bookmark/ui/tree/FolderNodeUpdater.kt +++ b/platform/lang-impl/src/com/intellij/ide/bookmark/ui/tree/FolderNodeUpdater.kt @@ -6,8 +6,8 @@ import com.intellij.ide.bookmark.FileBookmark import com.intellij.ide.bookmark.ui.BookmarksView import com.intellij.openapi.vfs.VfsUtilCore.isAncestor import com.intellij.openapi.vfs.VirtualFile -import com.intellij.ui.tree.AsyncTreeModel import com.intellij.ui.tree.TreeCollector +import com.intellij.ui.tree.TreeVisitor import com.intellij.ui.tree.project.ProjectFileNodeUpdater import javax.swing.tree.TreePath @@ -42,7 +42,7 @@ internal class FolderNodeUpdater(val view: BookmarksView) : ProjectFileNodeUpdat } private fun forEachTreePath(file: VirtualFile, action: (TreePath) -> Unit) { - val model = view.tree.model as? AsyncTreeModel ?: return + val model = view.tree.model as? TreeVisitor.LoadingAwareAcceptor ?: return val paths = mutableListOf() model.accept(VirtualFileVisitor(file, paths), false).onSuccess { paths.forEach(action) } } diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java index 94986349b34b..63801363bf8e 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/AbstractProjectViewPane.java @@ -38,6 +38,7 @@ import com.intellij.ui.tree.AsyncTreeModel; import com.intellij.ui.tree.TreePathUtil; import com.intellij.ui.tree.TreeVisitor; import com.intellij.ui.tree.project.ProjectFileNode; +import com.intellij.ui.treeStructure.BgtAwareTreeModel; import com.intellij.ui.treeStructure.TreeStateListener; import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtilRt; @@ -637,7 +638,7 @@ public abstract class AbstractProjectViewPane implements UiCompatibleDataProvide private boolean isExpandAllAllowed() { JTree tree = getTree(); TreeModel model = tree == null ? null : tree.getModel(); - return model == null || model instanceof AsyncTreeModel || model instanceof InvokerSupplier; + return model == null || model instanceof BgtAwareTreeModel || model instanceof InvokerSupplier; } @Override diff --git a/platform/lang-impl/src/com/intellij/ide/todo/TodoPanel.java b/platform/lang-impl/src/com/intellij/ide/todo/TodoPanel.java index 5f9caab2ad22..eb41a8ce01ce 100644 --- a/platform/lang-impl/src/com/intellij/ide/todo/TodoPanel.java +++ b/platform/lang-impl/src/com/intellij/ide/todo/TodoPanel.java @@ -143,7 +143,7 @@ public abstract class TodoPanel extends SimpleToolWindowPanel implements Occuren TodoView todoView = this.myProject.getService(TodoView.class); todoView.setSelectedContent(this); - AsyncTreeModel model = (AsyncTreeModel)myTree.getModel(); + var model = (TreeVisitor.Acceptor)myTree.getModel(); model.accept(new TreeVisitor() { @Override public @NotNull Action visit(@NotNull TreePath path) { diff --git a/platform/platform-api/api-dump-unreviewed.txt b/platform/platform-api/api-dump-unreviewed.txt index e98e0f0d19d5..1c65e2e7d2d9 100644 --- a/platform/platform-api/api-dump-unreviewed.txt +++ b/platform/platform-api/api-dump-unreviewed.txt @@ -10908,6 +10908,8 @@ c:com.intellij.ui.treeStructure.AutoExpandSimpleNodeListener - com.intellij.util.ui.tree.TreeModelAdapter - (javax.swing.JTree):V - treeNodesInserted(javax.swing.event.TreeModelEvent):V +com.intellij.ui.treeStructure.BgtAwareTreeModel +- javax.swing.tree.TreeModel a:com.intellij.ui.treeStructure.CachingSimpleNode - com.intellij.ui.treeStructure.SimpleNode - p:(com.intellij.openapi.project.Project,com.intellij.ide.util.treeView.NodeDescriptor):V diff --git a/platform/platform-api/src/com/intellij/ui/treeStructure/BgtAwareTreeModel.java b/platform/platform-api/src/com/intellij/ui/treeStructure/BgtAwareTreeModel.java new file mode 100644 index 000000000000..4c3e79337665 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/treeStructure/BgtAwareTreeModel.java @@ -0,0 +1,9 @@ +// 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.ui.treeStructure; + +import javax.swing.tree.TreeModel; + +/** + * Marker interface to indicate that the model can load nodes in the background without freezing the EDT + */ +public interface BgtAwareTreeModel extends TreeModel { } diff --git a/platform/platform-impl/api-dump-unreviewed.txt b/platform/platform-impl/api-dump-unreviewed.txt index c67082a411c1..8e827e838826 100644 --- a/platform/platform-impl/api-dump-unreviewed.txt +++ b/platform/platform-impl/api-dump-unreviewed.txt @@ -25514,7 +25514,8 @@ a:com.intellij.ui.tree.AbstractTreeWalker - start(javax.swing.tree.TreePath,java.lang.Object):V f:com.intellij.ui.tree.AsyncTreeModel - com.intellij.ui.tree.Searchable -- com.intellij.ui.tree.TreeVisitor$Acceptor +- com.intellij.ui.tree.TreeVisitor$LoadingAwareAcceptor +- com.intellij.ui.treeStructure.BgtAwareTreeModel - com.intellij.util.ui.tree.AbstractTreeModel - (javax.swing.tree.TreeModel,com.intellij.openapi.Disposable):V - (javax.swing.tree.TreeModel,Z,com.intellij.openapi.Disposable):V diff --git a/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java b/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java index 5d4b6c607d5e..c56687b5e15c 100644 --- a/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java +++ b/platform/platform-impl/src/com/intellij/ui/tree/AsyncTreeModel.java @@ -11,6 +11,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.Disposer; import com.intellij.ui.LoadingNode; +import com.intellij.ui.treeStructure.BgtAwareTreeModel; import com.intellij.ui.treeStructure.CachingTreePath; import com.intellij.util.concurrency.Invoker; import com.intellij.util.concurrency.InvokerSupplier; @@ -40,7 +41,9 @@ import static java.util.Collections.emptyList; import static org.jetbrains.concurrency.Promises.rejectedPromise; import static org.jetbrains.concurrency.Promises.resolvedPromise; -public final class AsyncTreeModel extends AbstractTreeModel implements Searchable, TreeVisitor.Acceptor, CachedTreePresentationSupport { +public final class AsyncTreeModel extends AbstractTreeModel + implements Searchable, TreeVisitor.LoadingAwareAcceptor, CachedTreePresentationSupport, BgtAwareTreeModel +{ private static final Logger LOG = Logger.getInstance(AsyncTreeModel.class); private final Invoker foreground; private final Invoker background; @@ -236,6 +239,7 @@ public final class AsyncTreeModel extends AbstractTreeModel implements Searchabl * @param allowLoading load all needed children if {@code true} * @return a promise that will be resolved when visiting is finished */ + @Override public @NotNull Promise accept(@NotNull TreeVisitor visitor, boolean allowLoading) { var walker = createWalker(visitor, allowLoading); if (allowLoading) { diff --git a/platform/platform-impl/src/com/intellij/ui/tree/ui/DefaultTreeUI.java b/platform/platform-impl/src/com/intellij/ui/tree/ui/DefaultTreeUI.java index f03e32eb1a36..d19b2a99c988 100644 --- a/platform/platform-impl/src/com/intellij/ui/tree/ui/DefaultTreeUI.java +++ b/platform/platform-impl/src/com/intellij/ui/tree/ui/DefaultTreeUI.java @@ -11,8 +11,8 @@ import com.intellij.ui.*; import com.intellij.ui.hover.TreeHoverListener; import com.intellij.ui.render.RenderingHelper; import com.intellij.ui.render.RenderingUtil; -import com.intellij.ui.tree.AsyncTreeModel; import com.intellij.ui.tree.TreePathBackgroundSupplier; +import com.intellij.ui.treeStructure.BgtAwareTreeModel; import com.intellij.ui.treeStructure.Tree; import com.intellij.ui.treeStructure.TreeUiBulkExpandCollapseSupport; import com.intellij.util.ObjectUtils; @@ -602,7 +602,7 @@ public class DefaultTreeUI extends BasicTreeUI implements TreeUiBulkExpandCollap @Override protected void setRootVisible(boolean newValue) { - if (treeModel instanceof AsyncTreeModel) { + if (treeModel instanceof BgtAwareTreeModel) { // this method must be called on EDT to be consistent with ATM, // because it modifies a list of visible nodes in the layout cache EdtInvocationManager.invokeLaterIfNeeded(() -> super.setRootVisible(newValue)); @@ -720,7 +720,7 @@ public class DefaultTreeUI extends BasicTreeUI implements TreeUiBulkExpandCollap JTree tree = getTree(); if (!shouldAutoExpand(tree, path)) return; TreeModel model = tree.getModel(); - if (model instanceof AsyncTreeModel && 1 == model.getChildCount(path.getLastPathComponent())) { + if (model instanceof BgtAwareTreeModel && 1 == model.getChildCount(path.getLastPathComponent())) { int pathCount = 1 + path.getPathCount(); for (int i = 0; i <= oldRowCount; i++) { TreePath row = getPathForRow(i); @@ -799,10 +799,10 @@ public class DefaultTreeUI extends BasicTreeUI implements TreeUiBulkExpandCollap if (!shouldAutoExpand(tree, row.getParentPath())) { return; } - if (tree.getModel() instanceof AsyncTreeModel asyncTreeModel) { + if (tree.getModel() instanceof BgtAwareTreeModel) { Object node = row.getLastPathComponent(); if (isAutoExpandAllowed(tree, node)) { - asyncTreeModel.onValidThread(() -> tree.expandPath(row)); + EdtInvocationManager.invokeLaterIfNeeded(() -> tree.expandPath(row)); } } } diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/structure/MavenProjectsStructure.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/structure/MavenProjectsStructure.java index 7db22327920d..7864fe30c10d 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/structure/MavenProjectsStructure.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/navigator/structure/MavenProjectsStructure.java @@ -236,7 +236,7 @@ public class MavenProjectsStructure extends SimpleTreeStructure { } public void accept(@NotNull TreeVisitor visitor) { - ((AsyncTreeModel)myTree.getModel()).accept(visitor); + ((TreeVisitor.Acceptor)myTree.getModel()).accept(visitor); } public void updateGoals() {