From d30f0ffda903fea8c306370e2a42139625c078c2 Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Wed, 6 May 2026 15:56:21 +0200 Subject: [PATCH] QuickAccessDialog: defer built-in provider construction until first use Wrap the nine built-in providers in a LazyQuickAccessProvider that exposes the id and requiresUiAccess flag as static metadata and resolves the delegate only on the first getElements/findElement call, so closing the dialog without typing skips constructing them entirely. Marginally helps stabilize QuickAccessDialogTest.testPreviousChoicesAvailableForExtension on Windows by reducing constructor work and the resulting initial-layout resize events that can preempt the compute job. --- .../quickaccess/LazyQuickAccessProvider.java | 96 +++++++++++++++++++ .../quickaccess/QuickAccessDialog.java | 31 +++--- 2 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/LazyQuickAccessProvider.java diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/LazyQuickAccessProvider.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/LazyQuickAccessProvider.java new file mode 100644 index 00000000000..a717a7985a4 --- /dev/null +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/LazyQuickAccessProvider.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2026 Vogella GmbH and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package org.eclipse.ui.internal.quickaccess; + +import java.util.function.Supplier; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.quickaccess.QuickAccessElement; + +/** + * Wraps a {@link QuickAccessProvider} behind a {@link Supplier} so the wrapped + * provider is constructed only when first queried for elements rather than at + * dialog open time. Identity ({@code getId()}) and the + * {@code requiresUiAccess()} flag are answered without forcing instantiation + * so the caller can sort, filter, and route providers by thread without + * triggering lazy construction. + */ +final class LazyQuickAccessProvider extends QuickAccessProvider { + + private final String id; + private final boolean requiresUiAccess; + private final Supplier supplier; + private volatile QuickAccessProvider delegate; + + LazyQuickAccessProvider(String id, boolean requiresUiAccess, Supplier supplier) { + this.id = id; + this.requiresUiAccess = requiresUiAccess; + this.supplier = supplier; + } + + private QuickAccessProvider delegate() { + QuickAccessProvider d = delegate; + if (d == null) { + synchronized (this) { + d = delegate; + if (d == null) { + d = supplier.get(); + delegate = d; + } + } + } + return d; + } + + @Override + public String getId() { + return id; + } + + @Override + public boolean requiresUiAccess() { + return requiresUiAccess; + } + + @Override + public String getName() { + return delegate().getName(); + } + + @Override + public ImageDescriptor getImageDescriptor() { + return delegate().getImageDescriptor(); + } + + @Override + public QuickAccessElement[] getElements() { + return delegate().getElements(); + } + + @Override + public QuickAccessElement[] getElementsSorted(String filter, IProgressMonitor monitor) { + return delegate().getElementsSorted(filter, monitor); + } + + @Override + public QuickAccessElement findElement(String id, String filterText) { + return delegate().findElement(id, filterText); + } + + @Override + protected void doReset() { + QuickAccessProvider d = delegate; + if (d != null) { + d.reset(); + } + } +} diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessDialog.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessDialog.java index 6ad2e113999..c8de31b70d6 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessDialog.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessDialog.java @@ -31,6 +31,7 @@ import org.eclipse.core.commands.Command; import org.eclipse.core.runtime.Adapters; import org.eclipse.core.runtime.Assert; +import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.ui.model.application.MApplication; import org.eclipse.e4.ui.model.application.ui.basic.MWindow; import org.eclipse.jface.bindings.TriggerSequence; @@ -116,20 +117,28 @@ public QuickAccessDialog(IWorkbenchWindow window, Command invokingCommand) { final MWindow model = workbenchWindow.getModel(); BusyIndicator.showWhile(window.getShell() == null ? null : window.getShell().getDisplay(), () -> { - final CommandProvider commandProvider = new CommandProvider(); - commandProvider.setContext(model.getContext().getActiveLeaf()); + final IEclipseContext commandContext = model.getContext().getActiveLeaf(); + final MApplication application = model.getContext().get(MApplication.class); List providers = new ArrayList<>(); previousPicksProvider = new PreviousPicksProvider(MAXIMUM_NUMBER_OF_ELEMENTS); previousPicksProvider.setElementsInitializer(() -> restorePreviousEntries(providers)); providers.add(previousPicksProvider); - providers.add(new EditorProvider()); - providers.add(new ViewProvider(model.getContext().get(MApplication.class), model)); - providers.add(new PerspectiveProvider()); - providers.add(commandProvider); - providers.add(new ActionProvider()); - providers.add(new WizardProvider()); - providers.add(new PreferenceProvider()); - providers.add(new PropertiesProvider()); + providers.add(new LazyQuickAccessProvider("org.eclipse.ui.editors", true, EditorProvider::new)); //$NON-NLS-1$ + providers.add(new LazyQuickAccessProvider("org.eclipse.e4.ui.parts", true, //$NON-NLS-1$ + () -> new ViewProvider(application, model))); + providers.add( + new LazyQuickAccessProvider("org.eclipse.ui.perspectives", true, PerspectiveProvider::new)); //$NON-NLS-1$ + providers.add(new LazyQuickAccessProvider("org.eclipse.ui.commands", true, () -> { //$NON-NLS-1$ + CommandProvider cp = new CommandProvider(); + cp.setContext(commandContext); + return cp; + })); + providers.add(new LazyQuickAccessProvider("org.eclipse.ui.actions", true, ActionProvider::new)); //$NON-NLS-1$ + providers.add(new LazyQuickAccessProvider("org.eclipse.ui.wizards", false, WizardProvider::new)); //$NON-NLS-1$ + providers.add( + new LazyQuickAccessProvider("org.eclipse.ui.preferences", true, PreferenceProvider::new)); //$NON-NLS-1$ + providers.add( + new LazyQuickAccessProvider("org.eclipse.ui.properties", true, PropertiesProvider::new)); //$NON-NLS-1$ providers.addAll(QuickAccessExtensionManager.getProviders(() -> { if (display != null) { display.asyncExec(() -> { @@ -139,7 +148,7 @@ public QuickAccessDialog(IWorkbenchWindow window, Command invokingCommand) { }); } })); - providers.add(new HelpSearchProvider()); + providers.add(new LazyQuickAccessProvider("search.help", false, HelpSearchProvider::new)); //$NON-NLS-1$ Collection previousPickProviderIds = getPreviousPickProviderIds(getDialogSettings()); previousPicksProvider.setInvolvedProviders(