mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
EA-220158 [service-view] do not request view descriptor from removed services
GitOrigin-RevId: 390c9f37aff1ff163f5963c45476fe2049055bdd
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7f79cb0f62
commit
7b3fba15f8
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user