From a78c052d867e0cd13a15672f53512ff3fbd94135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=A4=A7=E7=8C=AB?= <16399091+binaricat@users.noreply.github.com> Date: Mon, 25 May 2026 14:39:49 +0800 Subject: [PATCH] perf(autocomplete): skip completion queries when nothing is shown (#1088) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fetchSuggestions ran the full completion pipeline (history scan, fig specs, remote path lookups) on the main thread even when both the popup and ghost text were disabled — the results were then discarded. Add a shouldQueryCompletions(settings) gate and bail out early (clearing any stale state) when neither display mode is on. Co-authored-by: Claude Opus 4.7 (1M context) --- .../autocomplete/useTerminalAutocomplete.ts | 21 ++++++++++++++++ components/terminal/completionGate.test.ts | 25 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 components/terminal/completionGate.test.ts diff --git a/components/terminal/autocomplete/useTerminalAutocomplete.ts b/components/terminal/autocomplete/useTerminalAutocomplete.ts index 839e651c9..21dd444ea 100644 --- a/components/terminal/autocomplete/useTerminalAutocomplete.ts +++ b/components/terminal/autocomplete/useTerminalAutocomplete.ts @@ -47,6 +47,18 @@ export const DEFAULT_AUTOCOMPLETE_SETTINGS: AutocompleteSettings = { fastTypingThresholdMs: 40, }; +/** + * Whether completion work is worth doing — i.e. whether anything would + * actually be rendered. With both the popup and ghost text disabled, querying + * completions only to discard the result is pure main-thread waste, so callers + * skip it entirely. + */ +export function shouldQueryCompletions( + settings: Pick, +): boolean { + return settings.showPopupMenu || settings.showGhostText; +} + /** Shared empty state to avoid creating new objects on every reset */ const EMPTY_STATE: AutocompleteState = Object.freeze({ suggestions: [], @@ -640,6 +652,15 @@ export function useTerminalAutocomplete( return; } + // Nothing will be rendered when both the popup and ghost text are off, so + // don't run the (potentially expensive) completion query just to throw the + // result away. Clear any stale state and bail before touching history, + // fig specs, or remote path lookups. + if (!shouldQueryCompletions(settingsRef.current)) { + clearState(); + return; + } + // Capture version at start — if it changes during async work, discard results const version = ++fetchVersionRef.current; diff --git a/components/terminal/completionGate.test.ts b/components/terminal/completionGate.test.ts new file mode 100644 index 000000000..afb72d7b4 --- /dev/null +++ b/components/terminal/completionGate.test.ts @@ -0,0 +1,25 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +import { shouldQueryCompletions } from "./autocomplete/useTerminalAutocomplete.ts"; + +test("queries completions when the popup menu is enabled", () => { + assert.equal( + shouldQueryCompletions({ showPopupMenu: true, showGhostText: false }), + true, + ); +}); + +test("queries completions when ghost text is enabled", () => { + assert.equal( + shouldQueryCompletions({ showPopupMenu: false, showGhostText: true }), + true, + ); +}); + +test("skips completion work when both popup and ghost text are off", () => { + assert.equal( + shouldQueryCompletions({ showPopupMenu: false, showGhostText: false }), + false, + ); +});