Files
openidea/src/editor/core/cm/baseKeymap.ts
T
l0ng-ai c6621c5c36 fix(editor): Count wide characters as two columns in column editing (#172)
Column-based editing computed columns with countColumn / findColumn, which count every grapheme as one column, and up/down and Clone Caret moved by pixel x, so block selections and cloned carets drifted on lines with CJK, fullwidth punctuation, Hangul, kana or emoji. A shared visible-column helper (UI side only, UTF-16 offsets in and out) now counts East Asian wide/fullwidth characters and emoji (ZWJ sequences, modifiers, flags, VS16) as 2, combining marks and format characters as 0, and expands tabs to the next tab stop.

It drives Alt+drag block selection, drag and Shift+arrows in Column Selection Mode, middle-drag block selection (new; not on Linux, where middle-click pastes the primary selection), the up/down goal column (kept across short lines; soft-wrapped and folded lines still move visually), Clone Caret Above/Below, the caret move after Comment Line, Tab padding to the next indent stop, and macro playback of arrow keys.

Claude-Session: https://claude.ai/code/session_01Dq6jQQn8ZhRfkkyj3PRutA
2026-10-29 08:32:15 +08:00

55 lines
1.9 KiB
TypeScript

/**
* CodeMirror's default keymap minus the bindings that clash with IntelliJ IDEA.
* Those keys belong to IDE actions instead (keymaps in `platform/keymaps/`):
* ⌘/Ctrl+Enter is Split Line, Ctrl+M / ⌃L Scroll to Center, Ctrl+[ / ] code
* block navigation, ⌥⌘↑↓ / Ctrl+Alt+↑↓ Previous / Next Occurrence.
* Line up / down (with ⇧ too) keep the caret's visible column (`verticalMotion`).
*/
import type { Command, KeyBinding } from "@codemirror/view";
import {
addCursorAbove,
addCursorBelow,
cursorLineDown as cmCursorLineDown,
cursorLineUp as cmCursorLineUp,
defaultKeymap,
historyKeymap,
indentLess,
indentMore,
insertBlankLine,
redo,
selectLine,
selectLineDown as cmSelectLineDown,
selectLineUp as cmSelectLineUp,
toggleTabFocusMode,
} from "@codemirror/commands";
import { cursorLineDown, cursorLineUp, selectLineDown, selectLineUp } from "./verticalMotion";
const DROPPED = new Set<unknown>([
insertBlankLine, // Mod-Enter
toggleTabFocusMode, // Ctrl-m / Shift-Alt-m
selectLine, // Alt-l / Ctrl-l
addCursorAbove, // Mod-Alt-ArrowUp
addCursorBelow, // Mod-Alt-ArrowDown
indentLess, // Mod-[
indentMore, // Mod-]
]);
const VERTICAL = new Map<Command | undefined, Command>([
[cmCursorLineUp, cursorLineUp],
[cmCursorLineDown, cursorLineDown],
[cmSelectLineUp, selectLineUp],
[cmSelectLineDown, selectLineDown],
]);
const vertical = (b: KeyBinding): KeyBinding =>
VERTICAL.has(b.run) || VERTICAL.has(b.shift)
? { ...b, run: VERTICAL.get(b.run) ?? b.run, shift: VERTICAL.get(b.shift) ?? b.shift }
: b;
export const editorBaseKeymap: readonly KeyBinding[] = [
...defaultKeymap.filter((b) => !DROPPED.has(b.run)).map(vertical),
...historyKeymap,
// IDEA's Windows Redo is Ctrl+Shift+Z (Ctrl+Y is Delete Line); CodeMirror only binds it on macOS and Linux.
{ win: "Ctrl-Shift-z", run: redo, preventDefault: true },
];