From b5fad3573fbc28d4040ab403cc35acdfb0f46949 Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Fri, 26 Jun 2026 16:21:58 +0200 Subject: [PATCH] Debounce Quick Access keystrokes before computing proposals Every keystroke scheduled the background compute job immediately and cancelled the in-flight one, so fast typing triggered a compute per character even though only the final filter matters. Collapse a burst of keystrokes into a single computation: the modify listener now schedules the compute job with a 100 ms delay, and each new keystroke cancels the still-sleeping job from the previous one, so only the last keystroke in the window actually computes. The other callers (initial open, show-all toggle, resize) keep zero delay. The job is scheduled for the whole delay, so it stays visible to COMPUTE_JOB_FAMILY and tests that join that family still observe the pending compute. --- .../quickaccess/QuickAccessContents.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessContents.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessContents.java index 5d6851c98e2..21f230158ef 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessContents.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/quickaccess/QuickAccessContents.java @@ -105,6 +105,9 @@ public abstract class QuickAccessContents { /** Trailing-edge debounce window collapsing a burst of shell resizes. */ private static final int RESIZE_DEBOUNCE_MS = 100; + /** Trailing-edge debounce window collapsing a burst of keystrokes. */ + private static final int FILTER_DEBOUNCE_MS = 100; + /** * Family of the background job that computes the matching entries. Lets tests * join on the streaming compute deterministically instead of polling the table. @@ -165,6 +168,18 @@ private int computeNumberOfItems() { * @param filterInput The filter text to apply to results */ public void updateProposals(String filterInput) { + updateProposals(filterInput, 0); + } + + /** + * Recomputes the proposals, scheduling the background compute job after + * {@code scheduleDelay} milliseconds. A non-zero delay lets a burst of keystrokes + * collapse into a single computation: each keystroke cancels the still-sleeping job + * from the previous one, so only the last keystroke in the window actually computes. + * The job is scheduled (and therefore visible to {@link #COMPUTE_JOB_FAMILY}) for the + * whole delay, so callers waiting on that family still observe the pending compute. + */ + private void updateProposals(String filterInput, int scheduleDelay) { // Lower-case once so all callers share one filter string; the matcher and // previous-pick lookup also require a lower-cased filter. final String filter = filterInput == null ? "" : filterInput.toLowerCase(); //$NON-NLS-1$ @@ -264,8 +279,10 @@ public void done(IJobChangeEvent event) { if (Policy.DEBUG_QUICK_ACCESS) { trace("Set last proposals Job: " + computeProposalsJob); //$NON-NLS-1$ } - currentComputeEntriesJob.schedule(); - computingFeedbackJob.schedule(200); // delay a bit so if proposals compute fast enough, we don't show feedback + currentComputeEntriesJob.schedule(scheduleDelay); + // Keep the feedback delay relative to when the compute actually starts, so the + // debounce window never flashes the "computing" hint. + computingFeedbackJob.schedule(scheduleDelay + 200); } /** @@ -735,7 +752,8 @@ public void keyReleased(KeyEvent e) { if (Policy.DEBUG_QUICK_ACCESS) { trace("Modify listener triggering proposals update"); //$NON-NLS-1$ } - updateProposals(text); + // Coalesce a burst of keystrokes into a single computation. + updateProposals(text, FILTER_DEBOUNCE_MS); }); }