EA-220158 [service-view] do not request view descriptor from removed services

GitOrigin-RevId: 390c9f37aff1ff163f5963c45476fe2049055bdd
This commit is contained in:
Konstantin Aleev
2020-01-30 13:40:58 +00:00
committed by intellij-monorepo-bot
parent 7f79cb0f62
commit 7b3fba15f8
7 changed files with 101 additions and 74 deletions
@@ -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<ServiceViewItem> myRoots = new CopyOnWriteArrayList<>();
private volatile boolean myRootsInitialized;
private final List<ServiceModelEventListener> 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<? extends ServiceViewItem> 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<ServiceViewItem> 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);
}
}
@@ -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);
}
@@ -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<ServiceViewItem> 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<ServiceViewItem> 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<TreePath> selectedPaths =
currentPaths == null || currentPaths.length == 0 ? Collections.emptyList() : Arrays.asList(currentPaths);
myTreeModel.rootsChanged();
if (selectedPaths.isEmpty()) return;
myTreeModel.getInvoker().invokeLater(() -> {
List<Promise<TreePath>> 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<TreePath> selectedPaths =
currentPaths == null || currentPaths.length == 0 ? Collections.emptyList() : Arrays.asList(currentPaths);
myTreeModel.rootsChanged();
if (selectedPaths.isEmpty()) return;
myTreeModel.getInvoker().invokeLater(() -> {
List<Promise<TreePath>> 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<Object> myPath;
@@ -160,7 +160,7 @@ abstract class ServiceView extends JPanel implements Disposable {
}
List<ServiceViewItem> 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));
@@ -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<ServiceViewContributor<?>> contributors = myGroups.get(toolWindowId);
if (contributors == null) return;
@@ -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<? extends ServiceViewItem> 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<ServiceViewItem> toRemove = new ArrayList<>();
@@ -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