From 5ef3902bb004a48ba8f93075bffc8cc51751de0f Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Sat, 8 Aug 2026 09:51:12 +0200 Subject: [PATCH] Resolve editor and view references without repeated linear scans getOrderedEditorReferences, getSortedEditorReferences and getSortedParts resolved every model element by scanning the reference list from the start, and then guarded against duplicates with List.contains, so both grew quadratically with the number of open editors and views. They now build an identity map once per call and use a set for the duplicate check. The resulting order is unchanged. ToolItemUpdater.updateContributionItems rescheduled the same runnable through Display.timerExec once per matching item, which cancels and recreates the timer on every iteration although only the last call has any effect. Schedule it once per batch instead. Both are cleanups without a measured runtime effect. The nested scans only matter with many open parts, and the redundant timer scheduling only with many contributed tool items, neither of which the current tests cover. --- .../renderers/swt/ToolItemUpdater.java | 8 +- .../eclipse/ui/internal/WorkbenchPage.java | 84 ++++++++++++------- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/ToolItemUpdater.java b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/ToolItemUpdater.java index 9f888cb2f46..e55f8643bc9 100644 --- a/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/ToolItemUpdater.java +++ b/bundles/org.eclipse.e4.ui.workbench.renderers.swt/src/org/eclipse/e4/ui/workbench/renderers/swt/ToolItemUpdater.java @@ -47,11 +47,13 @@ void removeItem(AbstractContributionItem item) { } public void updateContributionItems(Selector selector) { + boolean queued = false; boolean doRunNow = false; for (final AbstractContributionItem ci : itemsToCheck) { if (ci.getModel() != null && ci.getModel().getParent() != null) { if (selector.select(ci.getModel())) { itemsToUpdateLater.add(ci); + queued = true; if (timestampOfEarliestQueuedUpdate == 0) { timestampOfEarliestQueuedUpdate = System.nanoTime(); } @@ -61,14 +63,16 @@ public void updateContributionItems(Selector selector) { // again and again in less than given DELAY frequency. TimerExec would then // never be executed. doRunNow = true; - } else { - Display.getDefault().timerExec(DELAY, this); } } } else { orphanedToolItems.add(ci); } } + if (queued && !doRunNow) { + // one timer for the whole batch, rescheduling it per item only costs time + Display.getDefault().timerExec(DELAY, this); + } if (!orphanedToolItems.isEmpty()) { itemsToCheck.removeAll(orphanedToolItems); orphanedToolItems.clear(); diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/WorkbenchPage.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/WorkbenchPage.java index 11073f4f801..325e6d283fa 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/WorkbenchPage.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/WorkbenchPage.java @@ -42,6 +42,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -1061,12 +1062,14 @@ public EditorReference createEditorReferenceForPart(final MPart part, IEditorInp private List getOrderedEditorReferences() { List editorRefs = new ArrayList<>(); + Set seen = new HashSet<>(); + Map referencesByModel = getEditorReferencesByModel(); List visibleEditors = modelService.findElements(window, CompatibilityEditor.MODEL_ELEMENT_ID, MPart.class); for (MPart editor : visibleEditors) { if (editor.isToBeRendered()) { - EditorReference ref = getEditorReference(editor); - if (ref != null && !editorRefs.contains(ref)) { + EditorReference ref = referencesByModel.get(editor); + if (ref != null && seen.add(ref)) { editorRefs.add(ref); } } @@ -1075,23 +1078,36 @@ private List getOrderedEditorReferences() { return editorRefs; } + /** + * Maps the model element of every editor reference of this page to its + * reference, so that callers can resolve many parts without rescanning the + * reference list for each one. + */ + private Map getEditorReferencesByModel() { + Map referencesByModel = new IdentityHashMap<>(editorReferences.size()); + for (EditorReference ref : editorReferences) { + referencesByModel.putIfAbsent(ref.getModel(), ref); + } + return referencesByModel; + } + List getSortedEditorReferences() { return getSortedEditorReferences(false); } private List getSortedEditorReferences(boolean allPerspectives) { + Map referencesByModel = getEditorReferencesByModel(); List sortedReferences = new ArrayList<>(); + Set seen = new HashSet<>(); for (MPart part : activationList) { - for (EditorReference ref : editorReferences) { - if (ref.getModel() == part) { - sortedReferences.add(ref); - break; - } + EditorReference ref = referencesByModel.get(part); + if (ref != null && seen.add(ref)) { + sortedReferences.add(ref); } } for (EditorReference ref : editorReferences) { - if (!sortedReferences.contains(ref)) { + if (seen.add(ref)) { sortedReferences.add(ref); } } @@ -1101,13 +1117,17 @@ private List getSortedEditorReferences(boolean allPerspectives) int scope = allPerspectives ? WINDOW_SCOPE : EModelService.PRESENTATION; List placeholders = modelService.findElements(window, CompatibilityEditor.MODEL_ELEMENT_ID, MPart.class, null, scope); + // only rendered placeholders are valid references + Set rendered = Collections.newSetFromMap(new IdentityHashMap<>(placeholders.size())); + for (MPart placeholder : placeholders) { + if (placeholder.isToBeRendered()) { + rendered.add(placeholder); + } + } List visibleReferences = new ArrayList<>(); for (EditorReference reference : sortedReferences) { - for (MPart placeholder : placeholders) { - if (reference.getModel() == placeholder && placeholder.isToBeRendered()) { - // only rendered placeholders are valid references - visibleReferences.add(reference); - } + if (rendered.contains(reference.getModel())) { + visibleReferences.add(reference); } } @@ -2318,32 +2338,36 @@ private IWorkbenchPartReference[] getSortedParts(boolean editors, boolean views, } List sortedReferences = new ArrayList<>(); + Set seen = new HashSet<>(); IViewReference[] viewReferences = getViewReferences(allPerspectives); List editorReferences = getSortedEditorReferences(allPerspectives); - activationLoop: for (MPart part : activationList) { - if (views) { - for (IViewReference ref : viewReferences) { - if (((ViewReference) ref).getModel() == part) { - sortedReferences.add(ref); - continue activationLoop; - } - } + Map viewsByModel = new IdentityHashMap<>(viewReferences.length); + if (views) { + for (IViewReference ref : viewReferences) { + viewsByModel.putIfAbsent(((ViewReference) ref).getModel(), ref); + } + } + Map editorsByModel = new IdentityHashMap<>(editorReferences.size()); + if (editors) { + for (EditorReference ref : editorReferences) { + editorsByModel.putIfAbsent(ref.getModel(), ref); } + } - if (editors) { - for (EditorReference ref : editorReferences) { - if (ref.getModel() == part) { - sortedReferences.add(ref); - break; - } - } + for (MPart part : activationList) { + IWorkbenchPartReference ref = viewsByModel.get(part); + if (ref == null) { + ref = editorsByModel.get(part); + } + if (ref != null && seen.add(ref)) { + sortedReferences.add(ref); } } if (views) { for (IViewReference ref : viewReferences) { - if (!sortedReferences.contains(ref)) { + if (seen.add(ref)) { sortedReferences.add(ref); } } @@ -2351,7 +2375,7 @@ private IWorkbenchPartReference[] getSortedParts(boolean editors, boolean views, if (editors) { for (EditorReference ref : editorReferences) { - if (!sortedReferences.contains(ref)) { + if (seen.add(ref)) { sortedReferences.add(ref); } }