From 0ffc54bee4da24d9e02a0de1b1f171b1457a2dd1 Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Thu, 20 Aug 2026 13:12:13 +0200 Subject: [PATCH 1/2] Fix pipelined parent lookup in NavigatorContentServiceContentProvider getParent() passed the Object[] holder instead of its element to pipelineParent(), so whenever no pipelined overriding extension returned a parent, pipelineParent() handed that array back as the fallback and getParent() returned an Object[] rather than a parent element. pipelineParent() also started with a null suggestion, so the first pipelined provider was asked for a parent without being told the parent the overridden extension had already computed. Seed the suggestion with the current parent, which is what findParent() does on the equivalent getParents() path. --- .../navigator/NavigatorContentServiceContentProvider.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/org.eclipse.ui.navigator/src/org/eclipse/ui/internal/navigator/NavigatorContentServiceContentProvider.java b/bundles/org.eclipse.ui.navigator/src/org/eclipse/ui/internal/navigator/NavigatorContentServiceContentProvider.java index 538d1777e0d..3647642429e 100644 --- a/bundles/org.eclipse.ui.navigator/src/org/eclipse/ui/internal/navigator/NavigatorContentServiceContentProvider.java +++ b/bundles/org.eclipse.ui.navigator/src/org/eclipse/ui/internal/navigator/NavigatorContentServiceContentProvider.java @@ -304,7 +304,7 @@ public void run() throws Exception { overridingExtensions = foundExtension .getOverridingExtensionsForPossibleChild(anElement); if (overridingExtensions.length > 0) { - parent[0] = pipelineParent(anElement, overridingExtensions, parent); + parent[0] = pipelineParent(anElement, overridingExtensions, parent[0]); } } } @@ -354,7 +354,7 @@ private Object pipelineParent(Object anInputElement, NavigatorContentExtension[] Object theCurrentParent) { IPipelinedTreeContentProvider pipelinedContentProvider; NavigatorContentExtension[] overridingExtensions; - Object aSuggestedParent = null; + Object aSuggestedParent = theCurrentParent; for (NavigatorContentExtension theOverridingExtension : theOverridingExtensions) { if (theOverridingExtension.internalGetContentProvider().isPipelined()) { From 6034db82b441a535bf0f8220f7cdecc9acb52aba Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Thu, 20 Aug 2026 13:12:13 +0200 Subject: [PATCH 2/2] Avoid full ancestor chain walk when removing elements from CommonViewer AbstractTreeViewer.getParentElement() prefers ITreePathContentProvider, so for the Common Navigator it calls getParents(), which compiles complete paths up to the root. Only the last segment of the first path is used, so every ancestor level above the immediate parent is computed and discarded, and each level asks every enabled content extension for a parent. With JDT enabled that runs JavaCore.create and hasJavaNature per level, which hit the workspace tree. AbstractTreeViewer.internalRemove() takes this branch for elements that have no widget, so deleting many never expanded files paid the full walk per element and froze the UI. Override getParentElement() to ask getParent() first and fall back to the inherited behaviour when it returns null. --- .../org/eclipse/ui/navigator/CommonViewer.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bundles/org.eclipse.ui.navigator/src/org/eclipse/ui/navigator/CommonViewer.java b/bundles/org.eclipse.ui.navigator/src/org/eclipse/ui/navigator/CommonViewer.java index abf482b97d9..aa1e3bc63f3 100644 --- a/bundles/org.eclipse.ui.navigator/src/org/eclipse/ui/navigator/CommonViewer.java +++ b/bundles/org.eclipse.ui.navigator/src/org/eclipse/ui/navigator/CommonViewer.java @@ -24,6 +24,7 @@ import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProviderChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.viewers.ViewerSorter; @@ -39,6 +40,7 @@ import org.eclipse.ui.internal.navigator.CommonNavigatorFrameSource; import org.eclipse.ui.internal.navigator.ContributorTrackingSet; import org.eclipse.ui.internal.navigator.NavigatorContentService; +import org.eclipse.ui.internal.navigator.NavigatorContentServiceContentProvider; import org.eclipse.ui.internal.navigator.NavigatorDecoratingLabelProvider; import org.eclipse.ui.internal.navigator.NavigatorPipelineService; import org.eclipse.ui.internal.navigator.dnd.NavigatorDnDService; @@ -242,6 +244,19 @@ protected void handleLabelProviderChanged(LabelProviderChangedEvent event) { super.handleLabelProviderChanged(event); } + @Override + protected Object getParentElement(Object elementOrTreePath) { + if (!(elementOrTreePath instanceof TreePath) + && getContentProvider() instanceof NavigatorContentServiceContentProvider provider) { + // getParents() compiles the whole ancestor chain although only the immediate parent is used + Object parent = provider.getParent(elementOrTreePath); + if (parent != null) { + return parent; + } + } + return super.getParentElement(elementOrTreePath); + } + @Override protected void handleDispose(DisposeEvent event) { dispose();