diff --git a/src/editor/core/actions.ts b/src/editor/core/actions.ts index fb82ead1b..0288c81a1 100644 --- a/src/editor/core/actions.ts +++ b/src/editor/core/actions.ts @@ -806,6 +806,7 @@ export function registerEditorActions(): () => void { title: "Add or Remove Caret", category: "Editor Actions", when: "textEditorActive", + hidden: true, // The mouse selection style handles the pointer position and dragging. run: () => {}, }, diff --git a/src/features/settings/__tests__/keymap.test.ts b/src/features/settings/__tests__/keymap.test.ts index 470a34988..89d5ff6da 100644 --- a/src/features/settings/__tests__/keymap.test.ts +++ b/src/features/settings/__tests__/keymap.test.ts @@ -36,9 +36,11 @@ import { withCustomOverrides, type KeymapDraft, } from "../keymap/model"; -import { buildKeymapTree } from "../keymap/KeymapPage"; +import { buildKeymapTree, keymapSearchLabels } from "../keymap/KeymapPage"; import { groupPath } from "../keymap/groups"; +import { registerEditorActions } from "@/editor/core/actions"; import { registerLspActions } from "@/editor/lsp/actions"; +import { matchActions } from "@/features/search/everywhere/model"; import { _resetI18nForTests, setLocale } from "@/platform/i18n"; it("finds the translated declaration action by 声明 and its English title", async () => { @@ -204,6 +206,27 @@ describe("conflicts", () => { }); describe("shortcut search", () => { + it.each(Object.keys(PRESETS))("keeps the mouse-only caret action configurable in %s", (preset) => { + const dispose = registerEditorActions(); + const originalKeymap = useKeymap.getState(); + const id = "editor.addOrRemoveCaret"; + const title = "Add or Remove Caret"; + try { + expect(matchActions(allActions(), title, { isEnabled: () => true, recent: [id] })).toEqual([]); + const variants: KeymapDraft["overrides"][] = [{}, { [id]: ["Alt+Button1"] }, { [id]: [] }]; + for (const overrides of variants) { + useKeymap.setState({ preset, overrides }); + expect(keymapSearchLabels()).toContain(title); + const tree = buildKeymapTree(allActions(), { preset, overrides }, title, ""); + const group = tree.find((node) => node.key === "grp:Editor Actions"); + expect(group?.kind === "group" && group.children.map((node) => node.key)).toEqual([id]); + } + } finally { + useKeymap.setState(originalKeymap); + dispose(); + } + }); + it("matches exact bindings and chord first strokes", () => { expect(bindingMatches(["Meta+K Meta+C"], "Meta+K")).toBe(true); expect(bindingMatches(["Shift+Meta+O"], "Meta+Shift+O")).toBe(true); diff --git a/src/features/settings/keymap/KeymapPage.tsx b/src/features/settings/keymap/KeymapPage.tsx index f97ad6654..abd325a50 100644 --- a/src/features/settings/keymap/KeymapPage.tsx +++ b/src/features/settings/keymap/KeymapPage.tsx @@ -9,7 +9,7 @@ import { useMemo, useRef, useState, type MouseEvent } from "react"; import clsx from "clsx"; import { useActions, allActions, type Action } from "@/platform/actions"; -import { defaultPreset, formatBinding, type Keymap, PRESET_TITLES, PRESETS } from "@/platform/keymap"; +import { defaultPreset, formatBinding, type Keymap, PRESET_TITLES, PRESETS, useKeymap } from "@/platform/keymap"; import { confirm } from "@/workbench/messages"; import { HighlightedText, @@ -36,6 +36,7 @@ import { deleteKeymap, duplicateKeymap, isModified, + isKeymapAction, keymapName, removeBinding, removeConflicts, @@ -61,8 +62,9 @@ type KNode = type GroupNode = Extract; export function keymapSearchLabels(): string[] { + const draft = useKeymap.getState(); return allActions() - .filter((a) => !a.hidden) + .filter((a) => isKeymapAction(a, draft)) .map((a) => a.title); } @@ -107,7 +109,7 @@ export function buildKeymapTree( const q = query.toLowerCase(); for (const action of actions) { const bindings = resolveBindings(draft, action.id); - if (action.hidden && !bindings.length) continue; + if (!isKeymapAction(action, draft)) continue; let hits: number[] | null = null; if (query) { hits = wordMatch(action.title, query); diff --git a/src/features/settings/keymap/model.ts b/src/features/settings/keymap/model.ts index e5d335b24..ed7097420 100644 --- a/src/features/settings/keymap/model.ts +++ b/src/features/settings/keymap/model.ts @@ -27,6 +27,11 @@ export function resolveBindings(draft: KeymapDraft, id: string): string[] { return draft.overrides[id]?.map(normalizeBinding) ?? presetBindings(draft.preset, id); } +/** Hidden actions with shortcuts stay configurable even after their bindings are removed. */ +export function isKeymapAction(action: Action, draft: KeymapDraft): boolean { + return !action.hidden || action.id in draft.overrides || presetBindings(draft.preset, action.id).length > 0; +} + export function isModified(draft: KeymapDraft, id: string): boolean { return id in draft.overrides; }