fix(search): keep Search Everywhere's keys from the editor's actions behind it (#1114)

The capture-phase keymap ran editor actions on keys the Search Everywhere field handles, since editor actions apply while a popup is open over an editor: ⌥↵ ran Show Context Actions instead of Assign Shortcut, and ⌥↑/⌥↓ and the other navigation keys could reach the editor too. The field now claims the keys its onKeyDown handles while it has focus.
Цей коміт міститься в:
l0ng-ai authored and GitHub committed 2026-11-04 07:26:56 +08:00
1 джерело 2bb8f3b091
коміт 809c6adcf1
2 files changed
+48 -1

No files matched your search

+36
Переглянути файл
@@ -0,0 +1,36 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { registerAction } from "@/platform/actions";
import { setContext } from "@/platform/context";
import { installKeyboardDispatcher, useKeymap } from "@/platform/keymap";
import { claimSearchKeys } from "../everywhere/SearchEverywhere";
const disposers: Array<() => void> = [];
afterEach(() => {
disposers.splice(0).forEach((d) => d());
document.body.innerHTML = "";
});
describe("Search Everywhere's query field", () => {
it("keeps ⌥↵ and the navigation keys from the editor's actions behind the popup", () => {
const intentions = vi.fn();
const extend = vi.fn();
useKeymap.setState({ overrides: { "t.intentions": ["Alt+Enter"], "t.extend": ["Alt+ArrowUp"] } } as never);
setContext("textEditorActive", true);
const input = document.body.appendChild(document.createElement("input"));
const other = document.body.appendChild(document.createElement("input"));
disposers.push(
registerAction({ id: "t.intentions", title: "Show Context Actions", run: intentions }),
registerAction({ id: "t.extend", title: "Extend Selection", run: extend }),
installKeyboardDispatcher(window),
claimSearchKeys(() => input),
);
const press = (target: HTMLElement, key: string) =>
target.dispatchEvent(new KeyboardEvent("keydown", { key, altKey: true, bubbles: true, cancelable: true }));
press(input, "Enter");
press(input, "ArrowUp");
expect(intentions).not.toHaveBeenCalled();
expect(extend).not.toHaveBeenCalled();
press(other, "Enter");
expect(intentions).toHaveBeenCalledOnce();
});
});
+12 -1
Переглянути файл
@@ -4,7 +4,7 @@ import clsx from "clsx";
import { actionToggled, executeAction, executeActionFrom, getAction } from "@/platform/actions";
import type { ProjectFileSet } from "@/platform/ipc";
import { tx as translate, useTx } from "@/platform/i18n";
import { hasPrimaryModifier, shortcutLabel } from "@/platform/keymap";
import { hasPrimaryModifier, registerKeyClaim, shortcutLabel } from "@/platform/keymap";
import {
Checkbox,
Dialog,
@@ -73,6 +73,13 @@ const SE_SCOPES: Array<{ value: SeScope; label: string }> = [
{ value: "test", label: "Project Test Files" },
];
/** Keys the query field handles itself (`onKeyDown`), in every modifier combination. */
const SEARCH_KEYS: ReadonlySet<string> = new Set(["Enter", "Tab", "ArrowUp", "ArrowDown", "PageUp", "PageDown"]);
/** Keeps the global keymap off the keys the query field handles while it has focus. */
export function claimSearchKeys(input: () => HTMLElement | null): () => void {
return registerKeyClaim((e) => e.target === input() && SEARCH_KEYS.has(e.key));
}
const ROW = 24;
const MAX_LIST = 672;
const MIN_LIST = 10 * ROW;
@@ -313,6 +320,10 @@ function SearchEverywhereDialog() {
activateSelection(pending.side);
});
// The keys the input handles below are its own: with an editor open, the global keymap would
// otherwise run editor actions on them (⌥↵ Show Context Actions, ⌥↑ Extend Selection…).
useEffect(() => claimSearchKeys(() => inputRef.current), []);
/** IDEA's ⌥↵ on an action: record a shortcut for it (written to the active keymap). */
const assignShortcut = (id: string) => closeThen(() => void executeAction("settings.assignShortcut", id));