From 7b3fba15f8717f3a1fe38ea0e8ac6eefab9ebc5b Mon Sep 17 00:00:00 2001 From: Konstantin Aleev Date: Thu, 30 Jan 2020 16:08:05 +0300 Subject: [PATCH] EA-220158 [service-view] do not request view descriptor from removed services GitOrigin-RevId: 390c9f37aff1ff163f5963c45476fe2049055bdd --- .../execution/services/ServiceModel.java | 45 ++++++++++ .../execution/services/ServiceSingleView.java | 7 +- .../execution/services/ServiceTreeView.java | 87 ++++++++++--------- .../execution/services/ServiceView.java | 2 +- .../services/ServiceViewManagerImpl.java | 9 +- .../execution/services/ServiceViewModel.java | 16 ++-- .../execution/services/ServiceViewTree.java | 9 +- 7 files changed, 101 insertions(+), 74 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceModel.java b/platform/lang-impl/src/com/intellij/execution/services/ServiceModel.java index ce9482bc3088..7a48bf01c65e 100644 --- a/platform/lang-impl/src/com/intellij/execution/services/ServiceModel.java +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceModel.java @@ -3,8 +3,10 @@ package com.intellij.execution.services; import com.intellij.diagnostic.PluginException; import com.intellij.execution.services.ServiceEventListener.ServiceEvent; +import com.intellij.ide.projectView.PresentationData; import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.ide.util.treeView.WeighedItem; +import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.ExtensionPointName; @@ -40,6 +42,7 @@ class ServiceModel implements Disposable, InvokerSupplier { private final Invoker myInvoker = Invoker.forBackgroundThreadWithReadAction(this); private final List myRoots = new CopyOnWriteArrayList<>(); private volatile boolean myRootsInitialized; + private final List myListeners = new CopyOnWriteArrayList<>(); ServiceModel(@NotNull Project project) { myProject = project; @@ -55,6 +58,14 @@ class ServiceModel implements Disposable, InvokerSupplier { return myInvoker; } + void addEventListener(@NotNull ServiceModelEventListener listener) { + myListeners.add(listener); + } + + void removeEventListener(@NotNull ServiceModelEventListener listener) { + myListeners.remove(listener); + } + @NotNull List getRoots() { return myRootsInitialized ? myRoots : Collections.emptyList(); @@ -157,6 +168,9 @@ class ServiceModel implements Disposable, InvokerSupplier { default: reset(e.contributorClass); } + for (ServiceModelEventListener listener : myListeners) { + listener.eventProcessed(e); + } }); } @@ -265,6 +279,7 @@ class ServiceModel implements Disposable, InvokerSupplier { ServiceViewItem parent = item.getParent(); while (parent instanceof ServiceGroupNode) { + item.markRemoved(); parent.getChildren().remove(item); if (!parent.getChildren().isEmpty()) return; @@ -272,12 +287,14 @@ class ServiceModel implements Disposable, InvokerSupplier { parent = parent.getParent(); } if (parent instanceof ContributorNode) { + item.markRemoved(); parent.getChildren().remove(item); if (!parent.getChildren().isEmpty()) return; item = parent; parent = parent.getParent(); } + item.markRemoved(); if (parent == null) { myRoots.remove(item); } @@ -506,6 +523,8 @@ class ServiceModel implements Disposable, InvokerSupplier { private ServiceViewDescriptor myViewDescriptor; private final List myChildren = new CopyOnWriteArrayList<>(); private volatile boolean myPresentationUpdated; + private volatile boolean myRemoved; + private PresentationData myPresentation; protected ServiceViewItem(@NotNull Object value, @Nullable ServiceViewItem parent, @NotNull ServiceViewContributor contributor, @NotNull ServiceViewDescriptor viewDescriptor) { @@ -569,6 +588,28 @@ class ServiceModel implements Disposable, InvokerSupplier { return descriptor instanceof ColoredItem ? ((ColoredItem)descriptor).getColor() : null; } + private void markRemoved() { + myRemoved = true; + } + + boolean isRemoved() { + return myRemoved || myParent != null && myParent.isRemoved(); + } + + ItemPresentation getItemPresentation(@Nullable ServiceViewOptions viewOptions) { + if (isRemoved()) return myPresentation; + + ItemPresentation presentation = + viewOptions == null ? getViewDescriptor().getPresentation() : getViewDescriptor().getCustomPresentation(viewOptions); + myPresentation = presentation instanceof PresentationData ? + (PresentationData)presentation : + new PresentationData(presentation.getPresentableText(), + presentation.getLocationString(), + presentation.getIcon(false), + null); + return myPresentation; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -686,4 +727,8 @@ class ServiceModel implements Disposable, InvokerSupplier { return result; } } + + interface ServiceModelEventListener { + void eventProcessed(ServiceEvent e); + } } diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceSingleView.java b/platform/lang-impl/src/com/intellij/execution/services/ServiceSingleView.java index fba5469479aa..4073954a83cd 100644 --- a/platform/lang-impl/src/com/intellij/execution/services/ServiceSingleView.java +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceSingleView.java @@ -24,12 +24,7 @@ class ServiceSingleView extends ServiceView { super(new BorderLayout(), project, model, ui); ui.setServiceToolbar(ServiceViewActionProvider.getInstance()); add(ui.getComponent(), BorderLayout.CENTER); - myListener = new ServiceViewModelListener() { - @Override - public void rootsChanged() { - updateItem(); - } - }; + myListener = this::updateItem; model.addModelListener(myListener); model.getInvoker().invokeLater(this::updateItem); } diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceTreeView.java b/platform/lang-impl/src/com/intellij/execution/services/ServiceTreeView.java index bf77a3a18c38..bf8b1e4bc4d9 100644 --- a/platform/lang-impl/src/com/intellij/execution/services/ServiceTreeView.java +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceTreeView.java @@ -63,7 +63,7 @@ class ServiceTreeView extends ServiceView { myTreeModel = new ServiceViewTreeModel(model); myTree = new ServiceViewTree(myTreeModel, this); - myListener = new MyViewModelListener(); + myListener = this::rootsChanged; model.addModelListener(myListener); ServiceViewActionProvider actionProvider = ServiceViewActionProvider.getInstance(); @@ -78,7 +78,6 @@ class ServiceTreeView extends ServiceView { myTree.addTreeSelectionListener(new RestoreSelectionListener()); myTree.addTreeSelectionListener(e -> onSelectionChanged()); - model.addModelListener(this::rootsChanged); Consumer selector = item -> select(item.getValue(), item.getRootContributor().getClass()) @@ -231,7 +230,12 @@ class ServiceTreeView extends ServiceView { } private void rootsChanged() { + updateSelectionPaths(); updateNavBar(); + updateLastSelection(); + } + + private void updateLastSelection() { ServiceViewItem lastSelection = myLastSelection; ServiceViewItem updatedItem = lastSelection == null ? null : getModel().findItem(lastSelection); AppUIUtil.invokeOnEdt(() -> { @@ -251,7 +255,8 @@ class ServiceTreeView extends ServiceView { if (Comparing.equal(newSelection, myLastSelection)) { myLastSelection = newSelection; if (mySelected) { - ServiceViewDescriptor descriptor = newSelection == null ? null : newSelection.getViewDescriptor(); + ServiceViewDescriptor descriptor = newSelection == null || (newSelection.isRemoved() && updatedItem == null) ? + null : newSelection.getViewDescriptor(); myUi.setDetailsComponent(descriptor == null ? null : descriptor.getContentComponent()); } } @@ -259,8 +264,8 @@ class ServiceTreeView extends ServiceView { } private void updateNavBar() { - AsyncPromise itemPromise = new AsyncPromise<>(); - itemPromise.onSuccess(item -> { + AppUIUtil.invokeOnEdt(() -> { + ServiceViewItem item = getNavBarItem(); if (item == null) return; getModel().getInvoker().invoke(() -> { @@ -268,14 +273,13 @@ class ServiceTreeView extends ServiceView { if (updatedItem != null) { AppUIUtil.invokeOnEdt(() -> { ServiceViewItem navBarItem = getNavBarItem(); - if (updatedItem.equals(navBarItem)) { + if (updatedItem.equals(navBarItem) && !updatedItem.isRemoved()) { myNavBarPanel.getModel().updateModel(updatedItem); } }, getProject().getDisposed()); } }); - }); - AppUIUtil.invokeOnEdt(() -> itemPromise.setResult(getNavBarItem()), getProject().getDisposed()); + }, getProject().getDisposed()); } private ServiceViewItem getNavBarItem() { @@ -285,6 +289,38 @@ class ServiceTreeView extends ServiceView { return ObjectUtils.tryCast(navBarModel.getElement(navBarModel.size() - 1), ServiceViewItem.class); } + private void updateSelectionPaths() { + AppUIUtil.invokeOnEdt(() -> { + TreePath[] currentPaths = myTree.getSelectionPaths(); + List selectedPaths = + currentPaths == null || currentPaths.length == 0 ? Collections.emptyList() : Arrays.asList(currentPaths); + myTreeModel.rootsChanged(); + if (selectedPaths.isEmpty()) return; + + myTreeModel.getInvoker().invokeLater(() -> { + List> pathPromises = + ContainerUtil.mapNotNull(selectedPaths, path -> { + ServiceViewItem item = ObjectUtils.tryCast(path.getLastPathComponent(), ServiceViewItem.class); + return item == null ? null : myTreeModel.findPath(item.getValue(), item.getRootContributor().getClass()); + }); + Promises.collectResults(pathPromises, true).onProcessed(paths -> { + if (paths != null && !paths.isEmpty() && !paths.equals(selectedPaths)) { + Promise newSelectPromise = TreeUtil.promiseSelect(myTree, paths.stream().map(PathSelectionVisitor::new)); + cancelSelectionUpdate(); + if (newSelectPromise instanceof AsyncPromise) { + ((AsyncPromise)newSelectPromise).onError(t -> { + if (t instanceof CancellationException) { + TreeUtil.promiseExpand(myTree, paths.stream().map(path -> new PathSelectionVisitor(path.getParentPath()))); + } + }); + } + myUpdateSelectionPromise = newSelectPromise; + } + }); + }); + }, getProject().getDisposed()); + } + @Override void setAutoScrollToSourceHandler(@NotNull AutoScrollToSourceHandler autoScrollToSourceHandler) { super.setAutoScrollToSourceHandler(autoScrollToSourceHandler); @@ -368,41 +404,6 @@ class ServiceTreeView extends ServiceView { return result; } - private class MyViewModelListener implements ServiceViewModel.ServiceViewModelListener { - @Override - public void rootsChanged() { - AppUIUtil.invokeOnEdt(() -> { - TreePath[] currentPaths = myTree.getSelectionPaths(); - List selectedPaths = - currentPaths == null || currentPaths.length == 0 ? Collections.emptyList() : Arrays.asList(currentPaths); - myTreeModel.rootsChanged(); - if (selectedPaths.isEmpty()) return; - - myTreeModel.getInvoker().invokeLater(() -> { - List> pathPromises = - ContainerUtil.mapNotNull(selectedPaths, path -> { - ServiceViewItem item = ObjectUtils.tryCast(path.getLastPathComponent(), ServiceViewItem.class); - return item == null ? null : myTreeModel.findPath(item.getValue(), item.getRootContributor().getClass()); - }); - Promises.collectResults(pathPromises, true).onProcessed(paths -> { - if (paths != null && !paths.isEmpty() && !paths.equals(selectedPaths)) { - Promise newSelectPromise = TreeUtil.promiseSelect(myTree, paths.stream().map(PathSelectionVisitor::new)); - cancelSelectionUpdate(); - if (newSelectPromise instanceof AsyncPromise) { - ((AsyncPromise)newSelectPromise).onError(t -> { - if (t instanceof CancellationException) { - TreeUtil.promiseExpand(myTree, paths.stream().map(path -> new PathSelectionVisitor(path.getParentPath()))); - } - }); - } - myUpdateSelectionPromise = newSelectPromise; - } - }); - }); - }, getProject().getDisposed()); - } - } - private static class PathSelectionVisitor implements TreeVisitor { private final Queue myPath; diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceView.java b/platform/lang-impl/src/com/intellij/execution/services/ServiceView.java index 783cd4d6ffda..811dd055a3ad 100644 --- a/platform/lang-impl/src/com/intellij/execution/services/ServiceView.java +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceView.java @@ -160,7 +160,7 @@ abstract class ServiceView extends JPanel implements Disposable { } List selectedItems = serviceView.getSelectedItems(); ServiceViewItem selectedItem = ContainerUtil.getOnlyItem(selectedItems); - ServiceViewDescriptor descriptor = selectedItem == null ? null : selectedItem.getViewDescriptor(); + ServiceViewDescriptor descriptor = selectedItem == null || selectedItem.isRemoved() ? null : selectedItem.getViewDescriptor(); DataProvider dataProvider = descriptor == null ? null : descriptor.getDataProvider(); if (dataProvider != null) { return RecursionManager.doPreventingRecursion(serviceView, false, () -> dataProvider.getData(dataId)); diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewManagerImpl.java index f9f6d4e90213..12fa82cef8ac 100644 --- a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewManagerImpl.java @@ -35,7 +35,6 @@ import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.ex.ToolWindowEx; import com.intellij.openapi.wm.ex.ToolWindowManagerEx; -import com.intellij.openapi.wm.impl.ToolWindowImpl; import com.intellij.ui.AppUIUtil; import com.intellij.ui.AutoScrollToSourceHandler; import com.intellij.ui.content.*; @@ -99,12 +98,6 @@ public final class ServiceViewManagerImpl implements ServiceViewManager, Persist updateToolWindow(toolWindowId, ContainerUtil.intersects(activeContributors, toolWindowContributors), false); } } - AppUIUtil.invokeOnEdt(() -> { - ServiceViewContentHolder holder = getContentHolder(e.contributorClass); - if (holder != null) { - holder.processAllModels(viewModel -> viewModel.eventProcessed(e)); - } - }, myProject.getDisposed()); } private void initRoots() { @@ -215,7 +208,7 @@ public final class ServiceViewManagerImpl implements ServiceViewManager, Persist } void createToolWindowContent(@NotNull ToolWindow toolWindow) { - String toolWindowId = toolWindow instanceof ToolWindowImpl ? ((ToolWindowImpl)toolWindow).getId() : null; + String toolWindowId = toolWindow.getId(); Collection> contributors = myGroups.get(toolWindowId); if (contributors == null) return; diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewModel.java b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewModel.java index c7096c6c8325..957860376115 100644 --- a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewModel.java +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewModel.java @@ -21,7 +21,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; -abstract class ServiceViewModel implements Disposable, InvokerSupplier { +abstract class ServiceViewModel implements Disposable, InvokerSupplier, ServiceModel.ServiceModelEventListener { protected final ServiceModel myModel; protected final ServiceModelFilter myModelFilter; private final ServiceViewFilter myFilter; @@ -33,6 +33,7 @@ abstract class ServiceViewModel implements Disposable, InvokerSupplier { myModel = model; myModelFilter = modelFilter; myFilter = filter; + myModel.addEventListener(this); } @NotNull @@ -64,8 +65,6 @@ abstract class ServiceViewModel implements Disposable, InvokerSupplier { @NotNull protected abstract List doGetRoots(); - abstract void eventProcessed(ServiceEvent e); - void saveState(ServiceViewState viewState) { viewState.groupByServiceGroups = myShowGroups; viewState.groupByContributor = myShowContributorRoots; @@ -137,6 +136,7 @@ abstract class ServiceViewModel implements Disposable, InvokerSupplier { @Override public void dispose() { + myModel.removeEventListener(this); } @NotNull @@ -335,7 +335,7 @@ abstract class ServiceViewModel implements Disposable, InvokerSupplier { } @Override - void eventProcessed(ServiceEvent e) { + public void eventProcessed(ServiceEvent e) { notifyListeners(); } @@ -368,7 +368,7 @@ abstract class ServiceViewModel implements Disposable, InvokerSupplier { } @Override - void eventProcessed(ServiceEvent e) { + public void eventProcessed(ServiceEvent e) { if (e.contributorClass.isInstance(myContributor)) { notifyListeners(); } @@ -414,7 +414,7 @@ abstract class ServiceViewModel implements Disposable, InvokerSupplier { } @Override - void eventProcessed(ServiceEvent e) { + public void eventProcessed(ServiceEvent e) { ServiceGroupNode group = myGroupRef.get(); if (group == null || !e.contributorClass.isInstance(group.getRootContributor())) return; @@ -458,7 +458,7 @@ abstract class ServiceViewModel implements Disposable, InvokerSupplier { } @Override - void eventProcessed(ServiceEvent e) { + public void eventProcessed(ServiceEvent e) { ServiceViewItem service = myServiceRef.get(); if (service == null || !e.contributorClass.isInstance(service.getRootContributor())) return; @@ -501,7 +501,7 @@ abstract class ServiceViewModel implements Disposable, InvokerSupplier { } @Override - void eventProcessed(ServiceEvent e) { + public void eventProcessed(ServiceEvent e) { boolean update = false; List toRemove = new ArrayList<>(); diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewTree.java b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewTree.java index a9e583b17ed4..d10573fac282 100644 --- a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewTree.java +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewTree.java @@ -3,7 +3,6 @@ package com.intellij.execution.services; import com.intellij.execution.services.ServiceModel.ServiceViewItem; import com.intellij.ide.DataManager; -import com.intellij.ide.projectView.PresentationData; import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.Disposable; import com.intellij.ui.ComponentUtil; @@ -89,13 +88,7 @@ class ServiceViewTree extends Tree { if (!(node instanceof ServiceViewItem)) return null; ServiceViewOptions viewOptions = DataManager.getInstance().getDataContext(myComponent).getData(ServiceViewActionUtils.OPTIONS_KEY); - ServiceViewDescriptor viewDescriptor = ((ServiceViewItem)node).getViewDescriptor(); - ItemPresentation presentation = - viewOptions == null ? viewDescriptor.getPresentation() : viewDescriptor.getCustomPresentation(viewOptions); - return presentation instanceof PresentationData ? presentation : new PresentationData(presentation.getPresentableText(), - presentation.getLocationString(), - presentation.getIcon(false), - null); + return ((ServiceViewItem)node).getItemPresentation(viewOptions); } @Override