mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
CPP-8459: Calculate controls state in background
This commit is contained in:
committed by
Dmitry Kozhevnikov
parent
0207796666
commit
4f5cb24fe0
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
package com.intellij.ide;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -38,6 +41,13 @@ public abstract class OccurenceNavigatorSupport implements OccurenceNavigator {
|
||||
@Nullable
|
||||
protected abstract Navigatable createDescriptorForNode(DefaultMutableTreeNode node);
|
||||
|
||||
@Nullable
|
||||
private Navigatable createDescriptorAndCheckCanNavigate(DefaultMutableTreeNode node) {
|
||||
Navigatable navigatable = createDescriptorForNode(node);
|
||||
if (navigatable == null || !navigatable.canNavigate()) return null;
|
||||
return navigatable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OccurenceInfo goNextOccurence() {
|
||||
Counters counters = new Counters();
|
||||
@@ -45,7 +55,7 @@ public abstract class OccurenceNavigatorSupport implements OccurenceNavigator {
|
||||
if (node == null) return null;
|
||||
TreePath treePath = new TreePath(node.getPath());
|
||||
TreeUtil.selectPath(myTree, treePath);
|
||||
Navigatable editSourceDescriptor = createDescriptorForNode(node);
|
||||
Navigatable editSourceDescriptor = createDescriptorAndCheckCanNavigate(node);
|
||||
if (editSourceDescriptor == null) return null;
|
||||
return new OccurenceInfo(editSourceDescriptor, counters.myFoundOccurenceNumber, counters.myOccurencesCount);
|
||||
}
|
||||
@@ -57,21 +67,19 @@ public abstract class OccurenceNavigatorSupport implements OccurenceNavigator {
|
||||
if (node == null) return null;
|
||||
TreePath treePath = new TreePath(node.getPath());
|
||||
TreeUtil.selectPath(myTree, treePath);
|
||||
Navigatable editSourceDescriptor = createDescriptorForNode(node);
|
||||
Navigatable editSourceDescriptor = createDescriptorAndCheckCanNavigate(node);
|
||||
if (editSourceDescriptor == null) return null;
|
||||
return new OccurenceInfo(editSourceDescriptor, counters.myFoundOccurenceNumber, counters.myOccurencesCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNextOccurence() {
|
||||
DefaultMutableTreeNode node = findNode(myTree, true, null);
|
||||
return node != null;
|
||||
return getAllDescriptors().canNavigate(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPreviousOccurence() {
|
||||
DefaultMutableTreeNode node = findNode(myTree, false, null);
|
||||
return node != null;
|
||||
return getAllDescriptors().canNavigate(false);
|
||||
}
|
||||
|
||||
protected static class Counters {
|
||||
@@ -86,51 +94,13 @@ public abstract class OccurenceNavigatorSupport implements OccurenceNavigator {
|
||||
}
|
||||
|
||||
private DefaultMutableTreeNode findNode(@NotNull JTree tree, boolean forward, Counters counters) {
|
||||
TreePath selectionPath = tree.getSelectionPath();
|
||||
TreeNode selectedNode = null;
|
||||
if (selectionPath != null) {
|
||||
selectedNode = (TreeNode)selectionPath.getLastPathComponent();
|
||||
}
|
||||
return findNode(tree, selectedNode, forward, counters);
|
||||
return findNode(tree, getSelectedNode(tree), forward, counters);
|
||||
}
|
||||
|
||||
public DefaultMutableTreeNode findNode(@NotNull JTree tree, TreeNode selectedNode, boolean forward, Counters counters) {
|
||||
boolean[] ready = {selectedNode == null};
|
||||
List<TreeNode> nodes = getAllNodes(tree);
|
||||
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode)tree.getModel().getRoot();
|
||||
|
||||
Enumeration enumeration = root.preorderEnumeration();
|
||||
List<TreeNode> nodes = new ArrayList<>();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
TreeNode node = (TreeNode)enumeration.nextElement();
|
||||
nodes.add(node);
|
||||
}
|
||||
|
||||
DefaultMutableTreeNode result = null;
|
||||
|
||||
if (forward) {
|
||||
for (TreeNode node : nodes) {
|
||||
DefaultMutableTreeNode nextNode = getNode(node, selectedNode, ready);
|
||||
if (nextNode != null) {
|
||||
result = nextNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i=nodes.size() - 1; i >= 0; i--) {
|
||||
TreeNode node = nodes.get(i);
|
||||
DefaultMutableTreeNode nextNode = getNode(node, selectedNode, ready);
|
||||
if (nextNode != null) {
|
||||
result = nextNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
DefaultMutableTreeNode result = getNextNode(selectedNode, forward, nodes);
|
||||
|
||||
if (counters != null) {
|
||||
counters.myFoundOccurenceNumber = 0;
|
||||
@@ -138,7 +108,7 @@ public abstract class OccurenceNavigatorSupport implements OccurenceNavigator {
|
||||
for (TreeNode node : nodes) {
|
||||
if (!(node instanceof DefaultMutableTreeNode)) continue;
|
||||
|
||||
Navigatable descriptor = createDescriptorForNode((DefaultMutableTreeNode)node);
|
||||
Navigatable descriptor = createDescriptorAndCheckCanNavigate((DefaultMutableTreeNode)node);
|
||||
if (descriptor == null) continue;
|
||||
|
||||
counters.myOccurencesCount++;
|
||||
@@ -151,17 +121,98 @@ public abstract class OccurenceNavigatorSupport implements OccurenceNavigator {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected DefaultMutableTreeNode getNode(TreeNode node, TreeNode selectedNode, boolean[] ready) {
|
||||
if (!ready[0]) {
|
||||
if (node == selectedNode) {
|
||||
ready[0] = true;
|
||||
private DefaultMutableTreeNode getNextNode(TreeNode selectedNode, boolean forward, List<TreeNode> nodes) {
|
||||
boolean ready = false;
|
||||
for (TreeNode node : forward ? nodes : ContainerUtil.reverse(nodes)) {
|
||||
if (!ready) {
|
||||
if (node == selectedNode) ready = true;
|
||||
continue;
|
||||
}
|
||||
if (node instanceof DefaultMutableTreeNode) {
|
||||
Navigatable descriptor = createDescriptorAndCheckCanNavigate((DefaultMutableTreeNode)node);
|
||||
if (descriptor != null) {
|
||||
return (DefaultMutableTreeNode)node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (!(node instanceof DefaultMutableTreeNode)) return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
Navigatable descriptor = createDescriptorForNode((DefaultMutableTreeNode)node);
|
||||
if (descriptor == null) return null;
|
||||
return (DefaultMutableTreeNode)node;
|
||||
@Nullable
|
||||
private static TreeNode getSelectedNode(@NotNull JTree tree) {
|
||||
TreePath selectionPath = tree.getSelectionPath();
|
||||
TreeNode selectedNode = null;
|
||||
if (selectionPath != null) {
|
||||
selectedNode = (TreeNode)selectionPath.getLastPathComponent();
|
||||
}
|
||||
return selectedNode;
|
||||
}
|
||||
|
||||
public static class Descriptors {
|
||||
@NotNull
|
||||
private final List<Navigatable> myDescriptors;
|
||||
private final int mySelectedPos;
|
||||
|
||||
private Descriptors(@NotNull List<Navigatable> descriptors, int pos) {
|
||||
myDescriptors = descriptors;
|
||||
mySelectedPos = pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be called in a non-EDT thread in a read action.
|
||||
* To prevent races, all related canNavigate() call should be made
|
||||
* in the same read action.
|
||||
*/
|
||||
public boolean canNavigate(boolean forward) {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
|
||||
if (!(mySelectedPos >= 0 && mySelectedPos < myDescriptors.size())) return false;
|
||||
|
||||
List<Navigatable> subList = forward
|
||||
? myDescriptors.subList(mySelectedPos + 1, myDescriptors.size())
|
||||
: ContainerUtil.reverse(myDescriptors.subList(0, mySelectedPos));
|
||||
|
||||
for (Navigatable descriptor : subList) {
|
||||
if (descriptor != null && descriptor.canNavigate()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return descriptors for all nodes (to be able to inspect it later, possibly in a background thread)
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NotNull
|
||||
public Descriptors getAllDescriptors() {
|
||||
List<TreeNode> nodes = getAllNodes(myTree);
|
||||
TreeNode selectedNode = getSelectedNode(myTree);
|
||||
List<Navigatable> result = new ArrayList<>(nodes.size());
|
||||
int selectedPos = -1;
|
||||
for (TreeNode node : nodes) {
|
||||
Navigatable descriptor = node instanceof DefaultMutableTreeNode
|
||||
? createDescriptorForNode((DefaultMutableTreeNode)node)
|
||||
: null;
|
||||
if (node == selectedNode) {
|
||||
selectedPos = result.size();
|
||||
}
|
||||
result.add(descriptor);
|
||||
}
|
||||
return new Descriptors(result, selectedPos);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<TreeNode> getAllNodes(@NotNull JTree tree) {
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode)tree.getModel().getRoot();
|
||||
|
||||
Enumeration enumeration = root.preorderEnumeration();
|
||||
List<TreeNode> nodes = new ArrayList<>();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
TreeNode node = (TreeNode)enumeration.nextElement();
|
||||
nodes.add(node);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class RerunSearchAction extends DumbAwareAction {
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent e) {
|
||||
UsageView usageView = UsageView.USAGE_VIEW_KEY.getData(e.getDataContext());
|
||||
boolean enabled = usageView instanceof UsageViewImpl && ((UsageViewImpl)usageView).canPerformReRun();
|
||||
boolean enabled = usageView instanceof UsageViewImpl && ((UsageViewImpl)usageView).isReRunActionEnabled();
|
||||
e.getPresentation().setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.navigation.NavigationItem;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
@@ -55,6 +56,8 @@ import com.intellij.util.ui.JBUI;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.util.ui.tree.TreeModelAdapter;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
import com.intellij.util.ui.update.MergingUpdateQueue;
|
||||
import com.intellij.util.ui.update.Update;
|
||||
import gnu.trove.THashSet;
|
||||
import gnu.trove.TIntArrayList;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -85,6 +88,7 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
private final UsageNodeTreeBuilder myBuilder;
|
||||
private MyPanel myRootPanel; // accessed in EDT only
|
||||
private JTree myTree; // accessed in EDT only
|
||||
private ActionToolbar myActionsToolbar; // accessed in EDT only
|
||||
private final ScheduledFuture<?> myFireEventsFuture;
|
||||
private Content myContent;
|
||||
|
||||
@@ -154,6 +158,10 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
private boolean myDisposeSmartPointersOnClose = true;
|
||||
private final Queue<Future<?>> updateRequests = new Queue<>(10); // guarded by insertionRequests
|
||||
|
||||
private final MergingUpdateQueue myControlsUpdateQueue = new MergingUpdateQueue("UsageView controls", 200, true, myRootPanel, this, null, false);;
|
||||
@NotNull
|
||||
private volatile ControlsState myCachedControlsState = new ControlsState(false, false, false);
|
||||
|
||||
public UsageViewImpl(@NotNull final Project project,
|
||||
@NotNull UsageViewPresentation presentation,
|
||||
@NotNull UsageTarget[] targets,
|
||||
@@ -230,8 +238,9 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
myRootPanel.add(toolWindowPanel, BorderLayout.CENTER);
|
||||
|
||||
JPanel toolbarPanel = new JPanel(new BorderLayout());
|
||||
toolbarPanel.add(createActionsToolbar(), BorderLayout.WEST);
|
||||
toolbarPanel.add(createFiltersToolbar(), BorderLayout.CENTER);
|
||||
myActionsToolbar = createActionsToolbar();
|
||||
toolbarPanel.add(myActionsToolbar.getComponent(), BorderLayout.WEST);
|
||||
toolbarPanel.add(createFiltersToolbar().getComponent(), BorderLayout.CENTER);
|
||||
toolWindowPanel.setToolbar(toolbarPanel);
|
||||
|
||||
myCentralPanel = new JPanel(new BorderLayout());
|
||||
@@ -285,6 +294,8 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
updateControlsImmediately();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -695,7 +706,7 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JComponent createActionsToolbar() {
|
||||
private ActionToolbar createActionsToolbar() {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
|
||||
DefaultActionGroup group = new DefaultActionGroup() {
|
||||
@@ -721,11 +732,11 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JComponent toUsageViewToolbar(@NotNull DefaultActionGroup group) {
|
||||
private ActionToolbar toUsageViewToolbar(@NotNull DefaultActionGroup group) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.USAGE_VIEW_TOOLBAR, group, false);
|
||||
actionToolbar.setTargetComponent(myRootPanel);
|
||||
return actionToolbar.getComponent();
|
||||
return actionToolbar;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") // used in rider
|
||||
@@ -734,7 +745,7 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JComponent createFiltersToolbar() {
|
||||
private ActionToolbar createFiltersToolbar() {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
final DefaultActionGroup group = new DefaultActionGroup();
|
||||
|
||||
@@ -1269,6 +1280,42 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
checkNodeValidity(root, new TreePath(root), toUpdate);
|
||||
queueUpdateBulk(toUpdate, EmptyRunnable.getInstance());
|
||||
updateOnSelectionChanged();
|
||||
queueControlsUpdate();
|
||||
}
|
||||
|
||||
private void queueControlsUpdate() {
|
||||
if (isDisposed()) return;
|
||||
|
||||
OccurenceNavigatorSupport.Descriptors descriptors = myRootPanel.getAllDescriptors();
|
||||
myControlsUpdateQueue.queue(new Update("controls update") {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isDisposed()) return;
|
||||
|
||||
if (!runReadActionWithRetries(() -> updateControlsImmediately(descriptors))) {
|
||||
ApplicationManager.getApplication().invokeLater(UsageViewImpl.this::queueControlsUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEat(Update update) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateControlsImmediately() {
|
||||
updateControlsImmediately(myRootPanel.getAllDescriptors());
|
||||
}
|
||||
|
||||
private void updateControlsImmediately(@Nullable OccurenceNavigatorSupport.Descriptors descriptors) {
|
||||
boolean prevOccurence = descriptors != null && descriptors.canNavigate(false);
|
||||
boolean nextOccurence = descriptors != null && descriptors.canNavigate(true);
|
||||
boolean rerun = canPerformReRun();
|
||||
|
||||
myCachedControlsState = new ControlsState(prevOccurence, nextOccurence, rerun);
|
||||
|
||||
GuiUtils.invokeLaterIfNeeded(() -> myActionsToolbar.updateActionsImmediately(), ModalityState.defaultModalityState());
|
||||
}
|
||||
|
||||
private void queueUpdateBulk(@NotNull List<Node> toUpdate, @NotNull Runnable onCompletedInEdt) {
|
||||
@@ -1341,6 +1388,7 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
catch (IndexNotReadyException ignore) {
|
||||
}
|
||||
}
|
||||
queueControlsUpdate();
|
||||
}
|
||||
|
||||
private void checkNodeValidity(@NotNull TreeNode node, @NotNull TreePath path, @NotNull List<? super Node> result) {
|
||||
@@ -1403,6 +1451,7 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
ToolTipManager.sharedInstance().unregisterComponent(myTree);
|
||||
}
|
||||
myUpdateAlarm.cancelAllRequests();
|
||||
myControlsUpdateQueue.cancelAllUpdates();
|
||||
}
|
||||
if (myDisposeSmartPointersOnClose) {
|
||||
disposeSmartPointers();
|
||||
@@ -1553,6 +1602,10 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isReRunActionEnabled() {
|
||||
return myCachedControlsState.canPerformRerun;
|
||||
}
|
||||
|
||||
private boolean checkReadonlyUsages() {
|
||||
final Set<VirtualFile> readOnlyUsages = getReadOnlyUsagesFiles();
|
||||
|
||||
@@ -1728,7 +1781,7 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
Object userObject = node.getUserObject();
|
||||
if (userObject instanceof Navigatable) {
|
||||
final Navigatable navigatable = (Navigatable)userObject;
|
||||
return navigatable.canNavigate() ? new Navigatable() {
|
||||
return new Navigatable() {
|
||||
@Override
|
||||
public void navigate(boolean requestFocus) {
|
||||
navigatable.navigate(allowRequestFocus && requestFocus);
|
||||
@@ -1743,7 +1796,7 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
public boolean canNavigateToSource() {
|
||||
return navigatable.canNavigateToSource();
|
||||
}
|
||||
} : null;
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1826,24 +1879,33 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
mySupport = null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
OccurenceNavigatorSupport.Descriptors getAllDescriptors() {
|
||||
return mySupport == null ? null : mySupport.getAllDescriptors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNextOccurence() {
|
||||
return mySupport != null && mySupport.hasNextOccurence();
|
||||
return myCachedControlsState.hasNextOccurence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPreviousOccurence() {
|
||||
return mySupport != null && mySupport.hasPreviousOccurence();
|
||||
return myCachedControlsState.hasPrevOccurence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OccurenceInfo goNextOccurence() {
|
||||
return mySupport != null ? mySupport.goNextOccurence() : null;
|
||||
OccurenceInfo result = mySupport != null ? mySupport.goNextOccurence() : null;
|
||||
updateControlsImmediately();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OccurenceInfo goPreviousOccurence() {
|
||||
return mySupport != null ? mySupport.goPreviousOccurence() : null;
|
||||
OccurenceInfo result = mySupport != null ? mySupport.goPreviousOccurence() : null;
|
||||
updateControlsImmediately();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1927,6 +1989,18 @@ public class UsageViewImpl implements UsageViewEx {
|
||||
}
|
||||
}
|
||||
|
||||
private static class ControlsState {
|
||||
final boolean hasPrevOccurence;
|
||||
final boolean hasNextOccurence;
|
||||
final boolean canPerformRerun;
|
||||
|
||||
private ControlsState(boolean hasPrevOccurence, boolean hasNextOccurence, boolean canPerformRerun) {
|
||||
this.hasPrevOccurence = hasPrevOccurence;
|
||||
this.hasNextOccurence = hasNextOccurence;
|
||||
this.canPerformRerun = canPerformRerun;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyAutoScrollToSourceOptionProvider implements AutoScrollToSourceOptionProvider {
|
||||
@NotNull private final UsageViewSettings myUsageViewSettings;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user