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 ede1b607bd0f..7bc2d83a8007 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 @@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.extensions.ProjectExtensionPointName; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; @@ -70,6 +71,14 @@ import java.util.function.Predicate; public abstract class AbstractProjectViewPane implements DataProvider, Disposable, BusyObject { private static final Logger LOG = Logger.getInstance(AbstractProjectViewPane.class); + public static final ProjectExtensionPointName EP + = new ProjectExtensionPointName<>("com.intellij.projectViewPane"); + + /** + * @deprecated use {@link #EP} instead + */ + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "2022.2") public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.projectViewPane"); protected final @NotNull Project myProject; diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java index 74581ed075f1..2dec6531046f 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java @@ -553,6 +553,8 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo toolWindowVisible = window.isVisible(); } }); + + AbstractProjectViewPane.EP.addExtensionPointListener(project, this::reloadPanes, project); } private void constructUi() { @@ -855,30 +857,51 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo viewSelectionChanged(); } - private void ensurePanesLoaded() { - // avoid recursive loading - if (myExtensionsLoaded.getAndSet(true)) { - return; - } + private synchronized void reloadPanes() { + if (myProject.isDisposed() || !myExtensionsLoaded.get()) return; // panes will be loaded later - AbstractProjectViewPane[] extensions = AbstractProjectViewPane.EP_NAME.getExtensions(myProject); - Arrays.sort(extensions, PANE_WEIGHT_COMPARATOR); - for (AbstractProjectViewPane pane : extensions) { - if (myUninitializedPaneState.containsKey(pane.getId())) { - try { - pane.readExternal(myUninitializedPaneState.get(pane.getId())); - } - catch (InvalidDataException e) { - // ignore - } - myUninitializedPaneState.remove(pane.getId()); + Map newPanes = loadPanes(); + Map oldPanes = new IdentityHashMap<>(); + myUninitializedPanes.forEach(pane -> oldPanes.put(pane, pane == newPanes.get(pane.getId()))); + myId2Pane.forEach((id, pane) -> oldPanes.put(pane, pane == newPanes.get(id))); + oldPanes.forEach((pane, exists) -> { + if (Boolean.FALSE.equals(exists)) { + removeProjectPane(pane); + Disposer.dispose(pane); } - if (pane.isInitiallyVisible() && !myId2Pane.containsKey(pane.getId())) { + }); + for (AbstractProjectViewPane pane : newPanes.values()) { + if (!Boolean.TRUE.equals(oldPanes.get(pane)) && pane.isInitiallyVisible()) { addProjectPane(pane); } } } + private void ensurePanesLoaded() { + if (myProject.isDisposed() || myExtensionsLoaded.getAndSet(true)) return; // avoid recursive loading + + for (AbstractProjectViewPane pane : loadPanes().values()) { + if (pane.isInitiallyVisible()) { + addProjectPane(pane); + } + } + } + + private Map loadPanes() { + HashMap map = new LinkedHashMap<>(); + AbstractProjectViewPane.EP.getExtensions(myProject).stream().sorted(PANE_WEIGHT_COMPARATOR).forEach(pane -> { + AbstractProjectViewPane added = map.computeIfAbsent(pane.getId(), id -> pane); + if (pane != added) { + LOG.warn("ignore duplicated pane with id=" + pane.getId() + "\nold " + added.getClass() + "\nnew " + pane.getClass()); + } + else { + Element element = myUninitializedPaneState.remove(pane.getId()); + if (element != null) applyPaneState(pane, element); + } + }); + return map; + } + private void viewSelectionChanged() { Content content = getContentManager().getSelectedContent(); if (content == null) { @@ -1561,7 +1584,7 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo return "AndroidView"; } else { - for (AbstractProjectViewPane extension : AbstractProjectViewPane.EP_NAME.getExtensions(myProject)) { + for (AbstractProjectViewPane extension : AbstractProjectViewPane.EP.getExtensions(myProject)) { if (extension.isDefaultPane(myProject)) { return extension.getId(); } @@ -1580,11 +1603,7 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo AbstractProjectViewPane pane = myId2Pane.get(paneId); if (pane != null) { - try { - pane.readExternal(paneElement); - } - catch (InvalidDataException ignore) { - } + applyPaneState(pane, paneElement); } else { myUninitializedPaneState.put(paneId, paneElement); @@ -1592,6 +1611,14 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo } } + private static void applyPaneState(@NotNull AbstractProjectViewPane pane, @NotNull Element element) { + try { + pane.readExternal(element); + } + catch (InvalidDataException ignored) { + } + } + @Override public Element getState() { Element parentNode = new Element("projectView"); diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index 5e5220dbebff..4ec0b63ad3b2 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -493,7 +493,7 @@ - + diff --git a/platform/tasks-platform-impl/src/com/intellij/tasks/context/ProjectViewContextProvider.java b/platform/tasks-platform-impl/src/com/intellij/tasks/context/ProjectViewContextProvider.java index 68cfbc0d968c..7ef72f2ee0e8 100644 --- a/platform/tasks-platform-impl/src/com/intellij/tasks/context/ProjectViewContextProvider.java +++ b/platform/tasks-platform-impl/src/com/intellij/tasks/context/ProjectViewContextProvider.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.tasks.context; @@ -30,7 +30,7 @@ final class ProjectViewContextProvider extends WorkingContextProvider { @Override public void saveContext(@NotNull Project project, @NotNull Element toElement) throws WriteExternalException { - for (AbstractProjectViewPane pane : AbstractProjectViewPane.EP_NAME.getExtensionList(project)) { + for (AbstractProjectViewPane pane : AbstractProjectViewPane.EP.getExtensions(project)) { Element paneElement = new Element(pane.getId()); pane.writeExternal(paneElement); toElement.addContent(paneElement); @@ -39,7 +39,7 @@ final class ProjectViewContextProvider extends WorkingContextProvider { @Override public void loadContext(@NotNull Project project, @NotNull Element fromElement) throws InvalidDataException { - for (AbstractProjectViewPane pane : AbstractProjectViewPane.EP_NAME.getExtensionList(project)) { + for (AbstractProjectViewPane pane : AbstractProjectViewPane.EP.getExtensions(project)) { Element paneElement = fromElement.getChild(pane.getId()); if (paneElement != null) { pane.readExternal(paneElement); @@ -52,7 +52,7 @@ final class ProjectViewContextProvider extends WorkingContextProvider { @Override public void clearContext(@NotNull Project project) { - for (AbstractProjectViewPane pane : AbstractProjectViewPane.EP_NAME.getExtensionList(project)) { + for (AbstractProjectViewPane pane : AbstractProjectViewPane.EP.getExtensions(project)) { JTree tree = pane.getTree(); if (tree != null) { TreeUtil.collapseAll(tree, 0);