diff --git a/frontend/src/core/codemirror/keymaps/__tests__/keymaps.test.ts b/frontend/src/core/codemirror/keymaps/__tests__/keymaps.test.ts index 456aeaa56a4..a4f9e6e673d 100644 --- a/frontend/src/core/codemirror/keymaps/__tests__/keymaps.test.ts +++ b/frontend/src/core/codemirror/keymaps/__tests__/keymaps.test.ts @@ -1,8 +1,15 @@ /* Copyright 2026 Marimo. All rights reserved. */ -import { defaultKeymap as originalDefaultKeymap } from "@codemirror/commands"; +import { + copyLineDown, + copyLineUp, + defaultKeymap as originalDefaultKeymap, +} from "@codemirror/commands"; import { describe, expect, it } from "vitest"; -import { HotkeyProvider } from "@/core/hotkeys/hotkeys"; +import { + HotkeyProvider, + OverridingHotkeyProvider, +} from "@/core/hotkeys/hotkeys"; import { visibleForTesting } from "../keymaps"; const { defaultKeymap, defaultVimKeymap, overrideKeymap, OVERRIDDEN_COMMANDS } = @@ -40,4 +47,26 @@ describe("keymaps", () => { expect(keys.some((k) => k.run === command)).toBe(true); } }); + + it.each([ + ["cell.copyLineUp", copyLineUp, "Alt-Shift-ArrowUp"], + ["cell.copyLineDown", copyLineDown, "Alt-Shift-ArrowDown"], + ] as const)( + "%s is bound to the configured hotkey, not CodeMirror's default", + (action, command, defaultKey) => { + // Without an override, the binding matches CodeMirror's default key. + expect( + overrideKeymap(HotkeyProvider.create()).find((k) => k.run === command) + ?.key, + ).toBe(defaultKey); + + // With an override, the binding follows the user's configured key. + // This is what `editable: true` on these hotkeys promises. + expect( + overrideKeymap( + new OverridingHotkeyProvider({ [action]: "Ctrl-d" }), + ).find((k) => k.run === command)?.key, + ).toBe("Ctrl-d"); + }, + ); }); diff --git a/frontend/src/core/codemirror/keymaps/keymaps.ts b/frontend/src/core/codemirror/keymaps/keymaps.ts index 956e40648f4..4c02504b5c8 100644 --- a/frontend/src/core/codemirror/keymaps/keymaps.ts +++ b/frontend/src/core/codemirror/keymaps/keymaps.ts @@ -1,6 +1,8 @@ /* Copyright 2026 Marimo. All rights reserved. */ import { + copyLineDown, + copyLineUp, cursorCharLeft, cursorCharRight, cursorLineDown, @@ -94,6 +96,8 @@ export function keymapBundle( const OVERRIDDEN_COMMANDS = new Set([ toggleComment, toggleBlockComment, + copyLineUp, + copyLineDown, ]); const defaultKeymap = once(() => { @@ -111,6 +115,14 @@ const overrideKeymap = (keymap: HotkeyProvider): readonly KeyBinding[] => { key: keymap.getHotkey("cell.toggleBlockComment").key, run: toggleBlockComment, }, + { + key: keymap.getHotkey("cell.copyLineUp").key, + run: copyLineUp, + }, + { + key: keymap.getHotkey("cell.copyLineDown").key, + run: copyLineDown, + }, ]; }; diff --git a/frontend/src/core/hotkeys/hotkeys.ts b/frontend/src/core/hotkeys/hotkeys.ts index 5d5fbabc721..ed5ee98599c 100644 --- a/frontend/src/core/hotkeys/hotkeys.ts +++ b/frontend/src/core/hotkeys/hotkeys.ts @@ -256,13 +256,11 @@ const DEFAULT_HOT_KEY = { name: "Copy line(s) up", group: "Editing", key: "Alt-Shift-ArrowUp", - editable: false, }, "cell.copyLineDown": { name: "Copy line(s) down", group: "Editing", key: "Alt-Shift-ArrowDown", - editable: false, }, // Markdown