@pierre/diffs 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import { EditorChange, EditorChangeEvent, Position, Range, TextEdit } from "../types.js";
2
+ import { EditorCommand, EditorKeymap, EditorShortcut, KeyboardKey, KeyboardModifier } from "../editor/command.js";
2
3
  import { TextDocument, TextDocumentChange } from "../editor/textDocument.js";
3
4
  import { IStateStorage, PersistStateStorage } from "../editor/stateStorage.js";
4
5
  import { Editor, EditorFocusOptions, EditorOptions } from "../editor/editor.js";
5
- export { Editor, type EditorChange, type EditorChangeEvent, EditorFocusOptions, EditorOptions, type IStateStorage, type PersistStateStorage, type Position, type Range, TextDocument, TextDocumentChange, type TextEdit };
6
+ export { Editor, type EditorChange, type EditorChangeEvent, type EditorCommand, EditorFocusOptions, type EditorKeymap, EditorOptions, type EditorShortcut, type IStateStorage, type KeyboardKey, type KeyboardModifier, type PersistStateStorage, type Position, type Range, TextDocument, TextDocumentChange, type TextEdit };
@@ -1,7 +1,15 @@
1
1
  //#region src/editor/command.d.ts
2
2
  type EditorCommand = 'indent' | 'outdent' | 'indentLess' | 'indentMore' | 'undo' | 'redo' | 'selectAll' | 'findNextMatch' | 'openSearchPanel' | 'openSearchReplacePanel' | 'moveLineUp' | 'moveLineDown' | 'copyLineUp' | 'copyLineDown' | 'simplifySelection' | 'insertBlankLine' | 'deleteHardLineForward' | 'toggleComment' | 'toggleBlockComment' | 'moveCursorToDocStart' | 'moveCursorToDocEnd' | 'expandSelectionDocStart' | 'expandSelectionDocEnd';
3
- declare function resolveEditorCommandFromKeyboardEvent(event: KeyboardEvent, isMac?: boolean): EditorCommand | undefined;
3
+ type KeyboardKey = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '`' | '-' | '=' | ',' | '.' | '/' | ';' | "'" | '[' | ']' | '\\' | 'Space' | 'Tab' | 'Enter' | 'Escape' | 'Backspace' | 'Delete' | 'ArrowUp' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight' | 'Home' | 'End' | 'PageUp' | 'PageDown' | 'F1' | 'F2' | 'F3' | 'F4' | 'F5' | 'F6' | 'F7' | 'F8' | 'F9' | 'F10' | 'F11' | 'F12';
4
+ type KeyboardModifier = 'cmdOrCtrl' | 'shift' | 'alt' | 'ctrl' | 'cmd';
5
+ type EditorShortcut = KeyboardKey | `${KeyboardModifier}+${KeyboardKey}` | `${KeyboardModifier}+${KeyboardModifier}+${KeyboardKey}`;
6
+ /** Later groups take precedence when bindings overlap. */
7
+ type EditorKeymap = ReadonlyArray<{
8
+ /** Undefined applies on every platform. */readonly platform?: 'mac' | 'windows' | 'linux';
9
+ readonly bindings: Readonly<Partial<Record<EditorShortcut, EditorCommand>>>;
10
+ }>;
11
+ declare function resolveEditorCommandFromKeyboardEvent(event: KeyboardEvent, keymap?: EditorKeymap, isMac?: boolean): EditorCommand | undefined;
4
12
  declare function resolveFindAgainShortcut(event: KeyboardEvent, isMac?: boolean): 'next' | 'previous' | undefined;
5
13
  //#endregion
6
- export { EditorCommand, resolveEditorCommandFromKeyboardEvent, resolveFindAgainShortcut };
14
+ export { EditorCommand, EditorKeymap, EditorShortcut, KeyboardKey, KeyboardModifier, resolveEditorCommandFromKeyboardEvent, resolveFindAgainShortcut };
7
15
  //# sourceMappingURL=command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"command.d.ts","names":[],"sources":["../../src/editor/command.ts"],"mappings":";KAEY,aAAA;AAAA,iBA8BI,qCAAA,CACd,KAAA,EAAO,aAAA,EACP,KAAA,aACC,aAAa;AAAA,iBAqIA,wBAAA,CACd,KAAA,EAAO,aAAa,EACpB,KAAA"}
1
+ {"version":3,"file":"command.d.ts","names":[],"sources":["../../src/editor/command.ts"],"mappings":";KAEY,aAAA;AAAA,KAyBA,WAAA;AAAA,KA2EA,gBAAA;AAAA,KAEA,cAAA,GACR,WAAA,MACG,gBAAA,IAAoB,WAAA,QACpB,gBAAA,IAAoB,gBAAA,IAAoB,WAAA;;KAGnC,YAAA,GAAe,aAAA;EAnFf,oDAqFD,QAAA;EAAA,SACA,QAAA,EAAU,QAAA,CAAS,OAAA,CAAQ,MAAA,CAAO,cAAA,EAAgB,aAAA;AAAA;AAAA,iBAsI7C,qCAAA,CACd,KAAA,EAAO,aAAA,EACP,MAAA,GAAS,YAAA,EACT,KAAA,aACC,aAAA;AAAA,iBA8Ca,wBAAA,CACd,KAAA,EAAO,aAAa,EACpB,KAAA"}
@@ -1,48 +1,121 @@
1
1
  import { isMacLike } from "../utils/platform.js";
2
2
  import { isPrimaryModifier } from "./platform.js";
3
3
  //#region src/editor/command.ts
4
- const SHORTCUTS = {
5
- a: "selectAll",
6
- d: "findNextMatch"
7
- };
8
- function resolveEditorCommandFromKeyboardEvent(event, isMac = isMacLike()) {
9
- const hasPrimaryModifier = isPrimaryModifier(event, isMac);
10
- const { shiftKey, altKey, key } = event;
11
- const normalizedKey = key.length === 1 ? key.toLowerCase() : key;
12
- if (isMac && event.ctrlKey && !event.metaKey && !shiftKey && !altKey && (normalizedKey === "k" || event.code === "KeyK")) return "deleteHardLineForward";
13
- if (!event.metaKey && !event.ctrlKey && shiftKey && altKey && (normalizedKey === "a" || event.code === "KeyA")) return "toggleBlockComment";
14
- if (!event.metaKey && !event.ctrlKey && shiftKey && altKey) {
15
- if (normalizedKey === "ArrowUp") return "copyLineUp";
16
- if (normalizedKey === "ArrowDown") return "copyLineDown";
17
- }
18
- if (!event.metaKey && !event.ctrlKey && !shiftKey && !altKey && normalizedKey === "Escape") return "simplifySelection";
19
- if (!event.metaKey && !shiftKey) {
20
- if (!event.ctrlKey && altKey && normalizedKey === "ArrowUp") return "moveLineUp";
21
- if (!event.ctrlKey && altKey && normalizedKey === "ArrowDown") return "moveLineDown";
22
- if ((isMac || navigator.platform.includes("Linux")) && event.ctrlKey && altKey) {
23
- if (normalizedKey === "p" || event.code === "KeyP") return "moveLineUp";
24
- if (normalizedKey === "n" || event.code === "KeyN") return "moveLineDown";
4
+ const compiledEditorKeymaps = /* @__PURE__ */ new WeakMap();
5
+ const compiledDefaultKeymap = getCompiledEditorKeymap([
6
+ { bindings: {
7
+ Tab: "indent",
8
+ "shift+Tab": "outdent",
9
+ "cmdOrCtrl+[": "indentLess",
10
+ "cmdOrCtrl+]": "indentMore",
11
+ "cmdOrCtrl+z": "undo",
12
+ "cmdOrCtrl+shift+z": "redo",
13
+ "cmdOrCtrl+a": "selectAll",
14
+ "cmdOrCtrl+d": "findNextMatch",
15
+ "cmdOrCtrl+f": "openSearchPanel",
16
+ "cmdOrCtrl+alt+f": "openSearchReplacePanel",
17
+ "alt+ArrowUp": "moveLineUp",
18
+ "alt+ArrowDown": "moveLineDown",
19
+ "shift+alt+ArrowUp": "copyLineUp",
20
+ "shift+alt+ArrowDown": "copyLineDown",
21
+ Escape: "simplifySelection",
22
+ "cmdOrCtrl+Enter": "insertBlankLine",
23
+ "cmdOrCtrl+/": "toggleComment",
24
+ "shift+alt+a": "toggleBlockComment",
25
+ "cmdOrCtrl+Home": "moveCursorToDocStart",
26
+ "cmdOrCtrl+End": "moveCursorToDocEnd",
27
+ "cmdOrCtrl+shift+Home": "expandSelectionDocStart",
28
+ "cmdOrCtrl+shift+End": "expandSelectionDocEnd"
29
+ } },
30
+ {
31
+ platform: "mac",
32
+ bindings: {
33
+ "ctrl+k": "deleteHardLineForward",
34
+ "ctrl+alt+p": "moveLineUp",
35
+ "ctrl+alt+n": "moveLineDown",
36
+ "cmd+ArrowUp": "moveCursorToDocStart",
37
+ "cmd+ArrowDown": "moveCursorToDocEnd",
38
+ "cmd+shift+ArrowUp": "expandSelectionDocStart",
39
+ "cmd+shift+ArrowDown": "expandSelectionDocEnd"
40
+ }
41
+ },
42
+ {
43
+ platform: "windows",
44
+ bindings: { "ctrl+y": "redo" }
45
+ },
46
+ {
47
+ platform: "linux",
48
+ bindings: {
49
+ "ctrl+y": "redo",
50
+ "ctrl+alt+p": "moveLineUp",
51
+ "ctrl+alt+n": "moveLineDown"
25
52
  }
26
53
  }
27
- if (hasPrimaryModifier && (normalizedKey === "f" || event.code === "KeyF")) return altKey ? "openSearchReplacePanel" : "openSearchPanel";
28
- if (altKey) return;
29
- if (!hasPrimaryModifier && normalizedKey === "Tab") return shiftKey ? "outdent" : "indent";
30
- if (!hasPrimaryModifier) return;
31
- if (!shiftKey && normalizedKey === "Enter") return "insertBlankLine";
32
- if (!shiftKey && (normalizedKey === "/" || event.code === "Slash")) return "toggleComment";
33
- if (!shiftKey && normalizedKey === "[") return "indentLess";
34
- if (!shiftKey && normalizedKey === "]") return "indentMore";
35
- if (normalizedKey === "z") return shiftKey ? "redo" : "undo";
36
- if (!isMac && normalizedKey === "y") return "redo";
37
- if (normalizedKey === "Home" || isMac && normalizedKey === "ArrowUp") {
38
- if (shiftKey) return "expandSelectionDocStart";
39
- return "moveCursorToDocStart";
54
+ ]);
55
+ const keyboardCodeKeys = {
56
+ Backquote: "`",
57
+ Minus: "-",
58
+ Equal: "=",
59
+ Comma: ",",
60
+ Period: ".",
61
+ Slash: "/",
62
+ Semicolon: ";",
63
+ Quote: "'",
64
+ BracketLeft: "[",
65
+ BracketRight: "]",
66
+ Backslash: "\\",
67
+ Space: "Space"
68
+ };
69
+ function getCompiledEditorKeymap(keymap) {
70
+ let compiledKeymap = compiledEditorKeymaps.get(keymap);
71
+ if (compiledKeymap === void 0) {
72
+ compiledKeymap = {
73
+ mac: /* @__PURE__ */ new Map(),
74
+ windows: /* @__PURE__ */ new Map(),
75
+ linux: /* @__PURE__ */ new Map()
76
+ };
77
+ for (const entry of keymap) for (const shortcut of Object.keys(entry.bindings)) {
78
+ const command = entry.bindings[shortcut];
79
+ if (command === void 0) continue;
80
+ const parts = shortcut.split("+");
81
+ const shortcutKey = parts.pop();
82
+ for (const platform of [
83
+ "mac",
84
+ "windows",
85
+ "linux"
86
+ ]) {
87
+ if (entry.platform !== void 0 && entry.platform !== platform) continue;
88
+ let modifiers = 0;
89
+ for (const modifier of parts) if (modifier === "alt") modifiers |= 1;
90
+ else if (modifier === "ctrl") modifiers |= 2;
91
+ else if (modifier === "cmd") modifiers |= 4;
92
+ else if (modifier === "shift") modifiers |= 8;
93
+ else if (modifier === "cmdOrCtrl") modifiers |= platform === "mac" ? 4 : 2;
94
+ let bindings = compiledKeymap[platform].get(modifiers);
95
+ if (bindings === void 0) {
96
+ bindings = /* @__PURE__ */ new Map();
97
+ compiledKeymap[platform].set(modifiers, bindings);
98
+ }
99
+ bindings.set(shortcutKey, command);
100
+ }
101
+ }
102
+ compiledEditorKeymaps.set(keymap, compiledKeymap);
40
103
  }
41
- if (normalizedKey === "End" || isMac && normalizedKey === "ArrowDown") {
42
- if (shiftKey) return "expandSelectionDocEnd";
43
- return "moveCursorToDocEnd";
104
+ return compiledKeymap;
105
+ }
106
+ function resolveEditorCommandFromKeyboardEvent(event, keymap, isMac = isMacLike()) {
107
+ const eventKey = event.key === " " ? "Space" : event.key.length === 1 ? event.key.toLowerCase() : event.key;
108
+ const code = event.code ?? "";
109
+ const codeKey = code.startsWith("Key") && code.length === 4 ? code.slice(3).toLowerCase() : code.startsWith("Digit") && code.length === 6 ? code.slice(5) : keyboardCodeKeys[code];
110
+ const platform = isMac ? "mac" : globalThis.navigator?.platform.includes("Linux") === true ? "linux" : "windows";
111
+ const modifiers = (event.altKey ? 1 : 0) | (event.ctrlKey ? 2 : 0) | (event.metaKey ? 4 : 0) | (event.shiftKey ? 8 : 0);
112
+ if (keymap !== void 0) {
113
+ const bindings = getCompiledEditorKeymap(keymap)[platform].get(modifiers);
114
+ const command = bindings?.get(eventKey) ?? (codeKey === void 0 ? void 0 : bindings?.get(codeKey));
115
+ if (command !== void 0) return command;
44
116
  }
45
- return SHORTCUTS[normalizedKey];
117
+ const bindings = compiledDefaultKeymap[platform].get(modifiers);
118
+ return bindings?.get(eventKey) ?? (codeKey === void 0 ? void 0 : bindings?.get(codeKey));
46
119
  }
47
120
  function resolveFindAgainShortcut(event, isMac = isMacLike()) {
48
121
  if (event.altKey) return;
@@ -1 +1 @@
1
- {"version":3,"file":"command.js","names":[],"sources":["../../src/editor/command.ts"],"sourcesContent":["import { isMacLike, isPrimaryModifier } from './platform';\n\nexport type EditorCommand =\n | 'indent'\n | 'outdent'\n | 'indentLess'\n | 'indentMore'\n | 'undo'\n | 'redo'\n | 'selectAll'\n | 'findNextMatch'\n | 'openSearchPanel'\n | 'openSearchReplacePanel'\n | 'moveLineUp'\n | 'moveLineDown'\n | 'copyLineUp'\n | 'copyLineDown'\n | 'simplifySelection'\n | 'insertBlankLine'\n | 'deleteHardLineForward'\n | 'toggleComment'\n | 'toggleBlockComment'\n | 'moveCursorToDocStart'\n | 'moveCursorToDocEnd'\n | 'expandSelectionDocStart'\n | 'expandSelectionDocEnd';\n\nconst SHORTCUTS: Partial<Record<string, EditorCommand>> = {\n a: 'selectAll',\n d: 'findNextMatch',\n};\n\nexport function resolveEditorCommandFromKeyboardEvent(\n event: KeyboardEvent,\n isMac: boolean = isMacLike()\n): EditorCommand | undefined {\n const hasPrimaryModifier = isPrimaryModifier(event, isMac);\n const { shiftKey, altKey, key } = event;\n\n const normalizedKey = key.length === 1 ? key.toLowerCase() : key;\n\n // Safari and Firefox do not report macOS control+k as a beforeinput action,\n // so resolve the native delete-to-line-end shortcut from keydown instead.\n if (\n isMac &&\n event.ctrlKey &&\n !event.metaKey &&\n !shiftKey &&\n !altKey &&\n (normalizedKey === 'k' || event.code === 'KeyK')\n ) {\n return 'deleteHardLineForward';\n }\n\n if (\n !event.metaKey &&\n !event.ctrlKey &&\n shiftKey &&\n altKey &&\n (normalizedKey === 'a' || event.code === 'KeyA')\n ) {\n return 'toggleBlockComment';\n }\n\n if (!event.metaKey && !event.ctrlKey && shiftKey && altKey) {\n if (normalizedKey === 'ArrowUp') {\n return 'copyLineUp';\n }\n if (normalizedKey === 'ArrowDown') {\n return 'copyLineDown';\n }\n }\n\n if (\n !event.metaKey &&\n !event.ctrlKey &&\n !shiftKey &&\n !altKey &&\n normalizedKey === 'Escape'\n ) {\n return 'simplifySelection';\n }\n\n if (!event.metaKey && !shiftKey) {\n if (!event.ctrlKey && altKey && normalizedKey === 'ArrowUp') {\n return 'moveLineUp';\n }\n if (!event.ctrlKey && altKey && normalizedKey === 'ArrowDown') {\n return 'moveLineDown';\n }\n if (\n (isMac || navigator.platform.includes('Linux')) &&\n event.ctrlKey &&\n altKey\n ) {\n if (normalizedKey === 'p' || event.code === 'KeyP') {\n return 'moveLineUp';\n }\n if (normalizedKey === 'n' || event.code === 'KeyN') {\n return 'moveLineDown';\n }\n }\n }\n\n // cmd/ctrl+f opens the search panel in find mode; adding alt opens it in\n // find/replace mode. macOS emits a dead key for Option+F (key === 'ƒ'), so\n // fall back to event.code to detect the physical F key.\n if (hasPrimaryModifier && (normalizedKey === 'f' || event.code === 'KeyF')) {\n return altKey ? 'openSearchReplacePanel' : 'openSearchPanel';\n }\n\n if (altKey) {\n return undefined;\n }\n\n if (!hasPrimaryModifier && normalizedKey === 'Tab') {\n return shiftKey ? 'outdent' : 'indent';\n }\n\n if (!hasPrimaryModifier) {\n return undefined;\n }\n\n if (!shiftKey && normalizedKey === 'Enter') {\n return 'insertBlankLine';\n }\n\n if (!shiftKey && (normalizedKey === '/' || event.code === 'Slash')) {\n return 'toggleComment';\n }\n\n if (!shiftKey && normalizedKey === '[') {\n return 'indentLess';\n }\n\n if (!shiftKey && normalizedKey === ']') {\n return 'indentMore';\n }\n\n if (normalizedKey === 'z') {\n return shiftKey ? 'redo' : 'undo';\n }\n\n if (!isMac && normalizedKey === 'y') {\n return 'redo';\n }\n\n if (normalizedKey === 'Home' || (isMac && normalizedKey === 'ArrowUp')) {\n if (shiftKey) {\n return 'expandSelectionDocStart';\n }\n return 'moveCursorToDocStart';\n }\n\n if (normalizedKey === 'End' || (isMac && normalizedKey === 'ArrowDown')) {\n if (shiftKey) {\n return 'expandSelectionDocEnd';\n }\n return 'moveCursorToDocEnd';\n }\n\n return SHORTCUTS[normalizedKey];\n}\n\n// Detects the native \"find again\" shortcut (cmd/ctrl+g for next, adding shift\n// for previous) so the find/replace panel can override the browser default and\n// step through matches. macOS can emit a dead key for the physical G, so fall\n// back to event.code like the cmd+f handling above.\nexport function resolveFindAgainShortcut(\n event: KeyboardEvent,\n isMac: boolean = isMacLike()\n): 'next' | 'previous' | undefined {\n if (event.altKey) {\n return undefined;\n }\n if (!isPrimaryModifier(event, isMac)) {\n return undefined;\n }\n const normalizedKey =\n event.key.length === 1 ? event.key.toLowerCase() : event.key;\n if (normalizedKey === 'g' || event.code === 'KeyG') {\n return event.shiftKey ? 'previous' : 'next';\n }\n return undefined;\n}\n"],"mappings":";;;AA2BA,MAAM,YAAoD;CACxD,GAAG;CACH,GAAG;AACL;AAEA,SAAgB,sCACd,OACA,QAAiB,UAAU,GACA;CAC3B,MAAM,qBAAqB,kBAAkB,OAAO,KAAK;CACzD,MAAM,EAAE,UAAU,QAAQ,QAAQ;CAElC,MAAM,gBAAgB,IAAI,WAAW,IAAI,IAAI,YAAY,IAAI;CAI7D,IACE,SACA,MAAM,WACN,CAAC,MAAM,WACP,CAAC,YACD,CAAC,WACA,kBAAkB,OAAO,MAAM,SAAS,SAEzC,OAAO;CAGT,IACE,CAAC,MAAM,WACP,CAAC,MAAM,WACP,YACA,WACC,kBAAkB,OAAO,MAAM,SAAS,SAEzC,OAAO;CAGT,IAAI,CAAC,MAAM,WAAW,CAAC,MAAM,WAAW,YAAY,QAAQ;EAC1D,IAAI,kBAAkB,WACpB,OAAO;EAET,IAAI,kBAAkB,aACpB,OAAO;CAEX;CAEA,IACE,CAAC,MAAM,WACP,CAAC,MAAM,WACP,CAAC,YACD,CAAC,UACD,kBAAkB,UAElB,OAAO;CAGT,IAAI,CAAC,MAAM,WAAW,CAAC,UAAU;EAC/B,IAAI,CAAC,MAAM,WAAW,UAAU,kBAAkB,WAChD,OAAO;EAET,IAAI,CAAC,MAAM,WAAW,UAAU,kBAAkB,aAChD,OAAO;EAET,KACG,SAAS,UAAU,SAAS,SAAS,OAAO,MAC7C,MAAM,WACN,QACA;GACA,IAAI,kBAAkB,OAAO,MAAM,SAAS,QAC1C,OAAO;GAET,IAAI,kBAAkB,OAAO,MAAM,SAAS,QAC1C,OAAO;EAEX;CACF;CAKA,IAAI,uBAAuB,kBAAkB,OAAO,MAAM,SAAS,SACjE,OAAO,SAAS,2BAA2B;CAG7C,IAAI,QACF;CAGF,IAAI,CAAC,sBAAsB,kBAAkB,OAC3C,OAAO,WAAW,YAAY;CAGhC,IAAI,CAAC,oBACH;CAGF,IAAI,CAAC,YAAY,kBAAkB,SACjC,OAAO;CAGT,IAAI,CAAC,aAAa,kBAAkB,OAAO,MAAM,SAAS,UACxD,OAAO;CAGT,IAAI,CAAC,YAAY,kBAAkB,KACjC,OAAO;CAGT,IAAI,CAAC,YAAY,kBAAkB,KACjC,OAAO;CAGT,IAAI,kBAAkB,KACpB,OAAO,WAAW,SAAS;CAG7B,IAAI,CAAC,SAAS,kBAAkB,KAC9B,OAAO;CAGT,IAAI,kBAAkB,UAAW,SAAS,kBAAkB,WAAY;EACtE,IAAI,UACF,OAAO;EAET,OAAO;CACT;CAEA,IAAI,kBAAkB,SAAU,SAAS,kBAAkB,aAAc;EACvE,IAAI,UACF,OAAO;EAET,OAAO;CACT;CAEA,OAAO,UAAU;AACnB;AAMA,SAAgB,yBACd,OACA,QAAiB,UAAU,GACM;CACjC,IAAI,MAAM,QACR;CAEF,IAAI,CAAC,kBAAkB,OAAO,KAAK,GACjC;CAIF,KADE,MAAM,IAAI,WAAW,IAAI,MAAM,IAAI,YAAY,IAAI,MAAM,SACrC,OAAO,MAAM,SAAS,QAC1C,OAAO,MAAM,WAAW,aAAa;AAGzC"}
1
+ {"version":3,"file":"command.js","names":[],"sources":["../../src/editor/command.ts"],"sourcesContent":["import { isMacLike, isPrimaryModifier } from './platform';\n\nexport type EditorCommand =\n | 'indent'\n | 'outdent'\n | 'indentLess'\n | 'indentMore'\n | 'undo'\n | 'redo'\n | 'selectAll'\n | 'findNextMatch'\n | 'openSearchPanel'\n | 'openSearchReplacePanel'\n | 'moveLineUp'\n | 'moveLineDown'\n | 'copyLineUp'\n | 'copyLineDown'\n | 'simplifySelection'\n | 'insertBlankLine'\n | 'deleteHardLineForward'\n | 'toggleComment'\n | 'toggleBlockComment'\n | 'moveCursorToDocStart'\n | 'moveCursorToDocEnd'\n | 'expandSelectionDocStart'\n | 'expandSelectionDocEnd';\n\nexport type KeyboardKey =\n | 'a'\n | 'b'\n | 'c'\n | 'd'\n | 'e'\n | 'f'\n | 'g'\n | 'h'\n | 'i'\n | 'j'\n | 'k'\n | 'l'\n | 'm'\n | 'n'\n | 'o'\n | 'p'\n | 'q'\n | 'r'\n | 's'\n | 't'\n | 'u'\n | 'v'\n | 'w'\n | 'x'\n | 'y'\n | 'z'\n | '0'\n | '1'\n | '2'\n | '3'\n | '4'\n | '5'\n | '6'\n | '7'\n | '8'\n | '9'\n | '`'\n | '-'\n | '='\n | ','\n | '.'\n | '/'\n | ';'\n | \"'\"\n | '['\n | ']'\n | '\\\\'\n | 'Space'\n | 'Tab'\n | 'Enter'\n | 'Escape'\n | 'Backspace'\n | 'Delete'\n | 'ArrowUp'\n | 'ArrowDown'\n | 'ArrowLeft'\n | 'ArrowRight'\n | 'Home'\n | 'End'\n | 'PageUp'\n | 'PageDown'\n | 'F1'\n | 'F2'\n | 'F3'\n | 'F4'\n | 'F5'\n | 'F6'\n | 'F7'\n | 'F8'\n | 'F9'\n | 'F10'\n | 'F11'\n | 'F12';\n\nexport type KeyboardModifier = 'cmdOrCtrl' | 'shift' | 'alt' | 'ctrl' | 'cmd';\n\nexport type EditorShortcut =\n | KeyboardKey\n | `${KeyboardModifier}+${KeyboardKey}`\n | `${KeyboardModifier}+${KeyboardModifier}+${KeyboardKey}`;\n\n/** Later groups take precedence when bindings overlap. */\nexport type EditorKeymap = ReadonlyArray<{\n /** Undefined applies on every platform. */\n readonly platform?: 'mac' | 'windows' | 'linux';\n readonly bindings: Readonly<Partial<Record<EditorShortcut, EditorCommand>>>;\n}>;\n\ntype CompiledEditorKeymap = Record<\n 'mac' | 'windows' | 'linux',\n Map<number, Map<KeyboardKey, EditorCommand>>\n>;\n\nconst compiledEditorKeymaps = new WeakMap<EditorKeymap, CompiledEditorKeymap>();\n\nconst defaultKeymap = [\n {\n bindings: {\n Tab: 'indent',\n 'shift+Tab': 'outdent',\n 'cmdOrCtrl+[': 'indentLess',\n 'cmdOrCtrl+]': 'indentMore',\n 'cmdOrCtrl+z': 'undo',\n 'cmdOrCtrl+shift+z': 'redo',\n 'cmdOrCtrl+a': 'selectAll',\n 'cmdOrCtrl+d': 'findNextMatch',\n 'cmdOrCtrl+f': 'openSearchPanel',\n 'cmdOrCtrl+alt+f': 'openSearchReplacePanel',\n 'alt+ArrowUp': 'moveLineUp',\n 'alt+ArrowDown': 'moveLineDown',\n 'shift+alt+ArrowUp': 'copyLineUp',\n 'shift+alt+ArrowDown': 'copyLineDown',\n Escape: 'simplifySelection',\n 'cmdOrCtrl+Enter': 'insertBlankLine',\n 'cmdOrCtrl+/': 'toggleComment',\n 'shift+alt+a': 'toggleBlockComment',\n 'cmdOrCtrl+Home': 'moveCursorToDocStart',\n 'cmdOrCtrl+End': 'moveCursorToDocEnd',\n 'cmdOrCtrl+shift+Home': 'expandSelectionDocStart',\n 'cmdOrCtrl+shift+End': 'expandSelectionDocEnd',\n },\n },\n {\n platform: 'mac',\n bindings: {\n 'ctrl+k': 'deleteHardLineForward',\n 'ctrl+alt+p': 'moveLineUp',\n 'ctrl+alt+n': 'moveLineDown',\n 'cmd+ArrowUp': 'moveCursorToDocStart',\n 'cmd+ArrowDown': 'moveCursorToDocEnd',\n 'cmd+shift+ArrowUp': 'expandSelectionDocStart',\n 'cmd+shift+ArrowDown': 'expandSelectionDocEnd',\n },\n },\n {\n platform: 'windows',\n bindings: {\n 'ctrl+y': 'redo',\n },\n },\n {\n platform: 'linux',\n bindings: {\n 'ctrl+y': 'redo',\n 'ctrl+alt+p': 'moveLineUp',\n 'ctrl+alt+n': 'moveLineDown',\n },\n },\n] satisfies EditorKeymap;\n\nconst compiledDefaultKeymap = getCompiledEditorKeymap(defaultKeymap);\n\nconst keyboardCodeKeys: Partial<Record<string, KeyboardKey>> = {\n Backquote: '`',\n Minus: '-',\n Equal: '=',\n Comma: ',',\n Period: '.',\n Slash: '/',\n Semicolon: ';',\n Quote: \"'\",\n BracketLeft: '[',\n BracketRight: ']',\n Backslash: '\\\\',\n Space: 'Space',\n};\n\nfunction getCompiledEditorKeymap(keymap: EditorKeymap): CompiledEditorKeymap {\n let compiledKeymap = compiledEditorKeymaps.get(keymap);\n if (compiledKeymap === undefined) {\n compiledKeymap = {\n mac: new Map(),\n windows: new Map(),\n linux: new Map(),\n };\n\n // Compile once so keydown resolves by modifier mask and key without\n // scanning or splitting every configured shortcut.\n for (const entry of keymap) {\n for (const shortcut of Object.keys(entry.bindings) as EditorShortcut[]) {\n const command = entry.bindings[shortcut];\n if (command === undefined) {\n continue;\n }\n const parts = shortcut.split('+');\n const shortcutKey = parts.pop() as KeyboardKey;\n\n for (const platform of ['mac', 'windows', 'linux'] as const) {\n if (entry.platform !== undefined && entry.platform !== platform) {\n continue;\n }\n let modifiers = 0;\n for (const modifier of parts as KeyboardModifier[]) {\n if (modifier === 'alt') {\n modifiers |= 1;\n } else if (modifier === 'ctrl') {\n modifiers |= 2;\n } else if (modifier === 'cmd') {\n modifiers |= 4;\n } else if (modifier === 'shift') {\n modifiers |= 8;\n } else if (modifier === 'cmdOrCtrl') {\n modifiers |= platform === 'mac' ? 4 : 2;\n }\n }\n let bindings = compiledKeymap[platform].get(modifiers);\n if (bindings === undefined) {\n bindings = new Map();\n compiledKeymap[platform].set(modifiers, bindings);\n }\n bindings.set(shortcutKey, command);\n }\n }\n }\n compiledEditorKeymaps.set(keymap, compiledKeymap);\n }\n return compiledKeymap;\n}\n\nexport function resolveEditorCommandFromKeyboardEvent(\n event: KeyboardEvent,\n keymap?: EditorKeymap,\n isMac: boolean = isMacLike()\n): EditorCommand | undefined {\n const eventKey =\n event.key === ' '\n ? 'Space'\n : event.key.length === 1\n ? event.key.toLowerCase()\n : event.key;\n const code = event.code ?? '';\n const codeKey =\n code.startsWith('Key') && code.length === 4\n ? (code.slice(3).toLowerCase() as KeyboardKey)\n : code.startsWith('Digit') && code.length === 6\n ? (code.slice(5) as KeyboardKey)\n : keyboardCodeKeys[code];\n const platform = isMac\n ? 'mac'\n : globalThis.navigator?.platform.includes('Linux') === true\n ? 'linux'\n : 'windows';\n const modifiers =\n (event.altKey ? 1 : 0) |\n (event.ctrlKey ? 2 : 0) |\n (event.metaKey ? 4 : 0) |\n (event.shiftKey ? 8 : 0);\n\n if (keymap !== undefined) {\n const bindings = getCompiledEditorKeymap(keymap)[platform].get(modifiers);\n const command =\n bindings?.get(eventKey as KeyboardKey) ??\n (codeKey === undefined ? undefined : bindings?.get(codeKey));\n if (command !== undefined) {\n return command;\n }\n }\n\n const bindings = compiledDefaultKeymap[platform].get(modifiers);\n return (\n bindings?.get(eventKey as KeyboardKey) ??\n (codeKey === undefined ? undefined : bindings?.get(codeKey))\n );\n}\n\n// Detects the native \"find again\" shortcut (cmd/ctrl+g for next, adding shift\n// for previous) so the find/replace panel can override the browser default and\n// step through matches. macOS can emit a dead key for the physical G, so fall\n// back to event.code like the cmd+f handling above.\nexport function resolveFindAgainShortcut(\n event: KeyboardEvent,\n isMac: boolean = isMacLike()\n): 'next' | 'previous' | undefined {\n if (event.altKey) {\n return undefined;\n }\n if (!isPrimaryModifier(event, isMac)) {\n return undefined;\n }\n const normalizedKey =\n event.key.length === 1 ? event.key.toLowerCase() : event.key;\n if (normalizedKey === 'g' || event.code === 'KeyG') {\n return event.shiftKey ? 'previous' : 'next';\n }\n return undefined;\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { DiffLineAnnotation, DiffsEditableComponent, DiffsEditor, EditableInstance, EditorChangeEvent, EditorState, FileContents, LineAnnotation, Range, TextEdit } from "../types.js";
2
+ import { EditorKeymap } from "./command.js";
2
3
  import { LanguageConfigMap } from "./languages.js";
3
4
  import { Marker } from "./marker.js";
4
5
  import { AutoSurround } from "./selection.js";
@@ -9,6 +10,8 @@ import { PersistStateStorage } from "./stateStorage.js";
9
10
  interface EditorOptions<LAnnotation> {
10
11
  /** The maximum number of entries to keep in the undo stack. */
11
12
  historyMaxEntries?: number;
13
+ /** Custom keymap groups checked before defaults; later groups take precedence. */
14
+ keymap?: EditorKeymap;
12
15
  /**
13
16
  * Preserve each file's document and item-local editor state when switching files.
14
17
  * Every editable file must provide a unique, stable `cacheKey`.
@@ -1 +1 @@
1
- {"version":3,"file":"editor.d.ts","names":[],"sources":["../../src/editor/editor.ts"],"mappings":";;;;;;;;UAuLiB,aAAA;;EAEf,iBAAA;EAF4B;;;;EAO5B,YAAA;EA4B+B;;;;EAvB/B,mBAAA,GAAsB,mBAAA;EA+BZ;EA7BV,gBAAA;EA8BgB;EA5BhB,aAAA;EAkCqB;;;;EA7BrB,YAAA,GAAe,YAAA;EAgCN;EA9BT,qBAAA,GAAwB,iBAAA;EA8BE;;;;EAzB1B,sBAAA;EAhBsB;;;;;EAsBtB,SAAA;IACE,QAAA,GAAW,IAAA,cAAkB,OAAA;EAAA;EAD/B;EAIA,qBAAA,IACE,OAAA,EAAS,sBAAA,CAAuB,WAAA,MAC7B,WAAA;EALQ;EAOb,QAAA,IACE,MAAA,EAAQ,MAAA,CAAO,WAAA,GACf,YAAA,EAAc,sBAAA,CAAuB,WAAA;EANvC;EASA,QAAA,IACE,IAAA,EAAM,YAAA,EACN,eAAA,EACI,cAAA,CAAe,WAAA,MACf,kBAAA,CAAmB,WAAA,iBAEvB,KAAA,EAAO,iBAAA,CAAkB,WAAA;EAdO;EAiBlC,OAAA;EAhBK;EAkBL,MAAA;EAEA,OAAA;AAAA;AAAA,UAGe,kBAAA,SAA2B,YAAY;EAnBtC;EAqBhB,UAAA;EArBE;EAuBF,SAAA;EAnBQ;EAqBR,MAAA;AAAA;AAAA,cAmBW,MAAA,yBAA+B,WAAA,CAAY,WAAA;EAAA;EAkJtD,WAAA,CAAY,OAAA,GAAS,aAAA,CAAc,WAAA;EAInC,UAAA,CAAW,OAAA,EAAS,aAAA,CAAc,WAAA;EAkClC,IAAA,WAAe,sBAAA,CAAuB,WAAA,GACpC,YAAA,EAAc,gBAAA,CAAiB,CAAA;EA5NN;;;;;;AAOpB;AAGT;;;EA4OE,UAAA,CAAW,KAAA,EAAO,QAAA,IAAY,aAAA;EA5OY;EAAA,IA+StC,OAAA;EA3SJ;EAAA,IAgTI,OAAA;EA9SE;EAmTN,IAAA;EAhSW;EAqSX,IAAA;EAIA,OAAA,IAAW,YAAA;EAcX,OAAA;EAIA,QAAA,IAAY,WAAA;EAaZ,QAAA;IAAW,UAAA;IAAY;EAAA,GAAQ,WAAA;EAgB/B,aAAA,CACE,UAAA,GAAa,KAAA;IAAU,SAAA;EAAA;EAoCzB,UAAA,CAAW,OAAA,EAAS,MAAA;EA6BpB,KAAA,CAAM,OAAA,GAAU,kBAAA;EAmChB,IAAA;EAOA,OAAA,CAAQ,OAAA;EA3JG;EA8OX,aAAA,CAAc,IAAA,EAAM,YAAA,GAAe,YAAA;EA/MxB;EA2OX,+BAAA;EA3O+B;EAsP/B,+BAAA;EAjMoB;EAgNpB,gBAAA,EAAkB,WAAA,CAAY,WAAA;AAAA"}
1
+ {"version":3,"file":"editor.d.ts","names":[],"sources":["../../src/editor/editor.ts"],"mappings":";;;;;;;;;UAwLiB,aAAA;;EAEf,iBAAA;EAF4B;EAI5B,MAAA,GAAS,YAAA;EAAA;;;;EAKT,YAAA;EAgCkC;;;;EA3BlC,mBAAA,GAAsB,mBAAA;EAgCiB;EA9BvC,gBAAA;EAkCQ;EAhCR,aAAA;EAkCM;;;;EA7BN,YAAA,GAAe,YAAA;EAgCW;EA9B1B,qBAAA,GAAwB,iBAAA;EAzBK;;;;EA8B7B,sBAAA;EAhBA;;;;;EAsBA,SAAA;IACE,QAAA,GAAW,IAAA,cAAkB,OAAA;EAAA;EAP/B;EAUA,qBAAA,IACE,OAAA,EAAS,sBAAA,CAAuB,WAAA,MAC7B,WAAA;EALH;EAOF,QAAA,IACE,MAAA,EAAQ,MAAA,CAAO,WAAA,GACf,YAAA,EAAc,sBAAA,CAAuB,WAAA;EATR;EAY/B,QAAA,IACE,IAAA,EAAM,YAAA,EACN,eAAA,EACI,cAAA,CAAe,WAAA,MACf,kBAAA,CAAmB,WAAA,iBAEvB,KAAA,EAAO,iBAAA,CAAkB,WAAA;EAdhB;EAiBX,OAAA;EAjBE;EAmBF,MAAA;EAEA,OAAA;AAAA;AAAA,UAGe,kBAAA,SAA2B,YAAY;EApBpD;EAsBF,UAAA;EArBuC;EAuBvC,SAAA;EApBA;EAsBA,MAAA;AAAA;AAAA,cAmBW,MAAA,yBAA+B,WAAA,CAAY,WAAA;EAAA;EAkJtD,WAAA,CAAY,OAAA,GAAS,aAAA,CAAc,WAAA;EAInC,UAAA,CAAW,OAAA,EAAS,aAAA,CAAc,WAAA;EAkClC,IAAA,WAAe,sBAAA,CAAuB,WAAA,GACpC,YAAA,EAAc,gBAAA,CAAiB,CAAA;EA5NxB;;;;;;;AAOF;AAGT;;EA4OE,UAAA,CAAW,KAAA,EAAO,QAAA,IAAY,aAAA;EA5OwB;EAAA,IA+SlD,OAAA;EA7SJ;EAAA,IAkTI,OAAA;EA9SJ;EAmTA,IAAA;EAnTM;EAwTN,IAAA;EAIA,OAAA,IAAW,YAAA;EAcX,OAAA;EAIA,QAAA,IAAY,WAAA;EAaZ,QAAA;IAAW,UAAA;IAAY;EAAA,GAAQ,WAAA;EAgB/B,aAAA,CACE,UAAA,GAAa,KAAA;IAAU,SAAA;EAAA;EAoCzB,UAAA,CAAW,OAAA,EAAS,MAAA;EA6BpB,KAAA,CAAM,OAAA,GAAU,kBAAA;EAmChB,IAAA;EAOA,OAAA,CAAQ,OAAA;EAjPU;EAoUlB,aAAA,CAAc,IAAA,EAAM,YAAA,GAAe,YAAA;EA5NvB;EAwPZ,+BAAA;EA3OuB;EAsPvB,+BAAA;EArOe;EAoPf,gBAAA,EAAkB,WAAA,CAAY,WAAA;AAAA"}
@@ -865,19 +865,22 @@ var Editor = class {
865
865
  }
866
866
  }, { passive: true }),
867
867
  addEventListener(contentEl, "keydown", (e) => {
868
- if (e.key === "Escape") {
869
- e.preventDefault();
870
- this.#searchPanel?.close();
871
- this.#searchPanel = void 0;
872
- this.#retainSearchPanelFocus = false;
873
- this.#selectionAction?.cleanup();
874
- this.#selectionAction = void 0;
875
- this.#runCommand("simplifySelection");
876
- return;
877
- }
878
868
  if (!targetIsContentElement(e)) return;
879
869
  this.#canMountSelectionAction = true;
880
870
  this.#suppressNativeSelectionSync = false;
871
+ const command = resolveEditorCommandFromKeyboardEvent(e, this.#options.keymap);
872
+ if (command !== void 0) {
873
+ e.preventDefault();
874
+ if (command === "simplifySelection") {
875
+ this.#searchPanel?.close();
876
+ this.#searchPanel = void 0;
877
+ this.#retainSearchPanelFocus = false;
878
+ this.#selectionAction?.cleanup();
879
+ this.#selectionAction = void 0;
880
+ }
881
+ this.#runCommand(command);
882
+ return;
883
+ }
881
884
  const mvShortcut = isMoveCursorShortcut(e);
882
885
  const textDocument = this.#textDocument;
883
886
  if (this.#selections !== void 0 && this.#selections.length > 0 && mvShortcut !== void 0 && textDocument !== void 0) {
@@ -910,11 +913,6 @@ var Editor = class {
910
913
  return;
911
914
  }
912
915
  }
913
- const command = resolveEditorCommandFromKeyboardEvent(e);
914
- if (command !== void 0) {
915
- e.preventDefault();
916
- this.#runCommand(command);
917
- }
918
916
  }),
919
917
  addEventListener(contentEl, "copy", (e) => {
920
918
  if (!targetIsContentElement(e)) return;
@@ -2343,17 +2341,17 @@ var Editor = class {
2343
2341
  top,
2344
2342
  bottom: top + popoverHeight,
2345
2343
  left,
2346
- anchorTop
2344
+ anchorTop,
2345
+ rowTop
2347
2346
  };
2348
2347
  };
2349
2348
  const preferredGeometry = candidateGeometry(preferred);
2350
- const fallbackGeometry = candidateGeometry(fallback);
2351
2349
  const lineCount = textDocument.lineCount;
2352
2350
  const atDocumentEdge = isBackward ? head.line < 3 : head.line >= lineCount - 3;
2353
- const canUseFallback = this.#isLineVisible(fallback.anchor.line);
2351
+ const fallbackGeometry = this.#isLineVisible(fallback.anchor.line) ? candidateGeometry(fallback) : void 0;
2354
2352
  const popoverManager = this.#getPopoverManager();
2355
2353
  const viewport = popoverManager.getPlacementBounds();
2356
- const useFallback = canUseFallback && popoverManager.choosePlacement({
2354
+ const useFallback = fallbackGeometry !== void 0 && popoverManager.choosePlacement({
2357
2355
  preferred: preferredGeometry,
2358
2356
  fallback: fallbackGeometry,
2359
2357
  viewport,
@@ -2361,10 +2359,13 @@ var Editor = class {
2361
2359
  atDocumentEdge,
2362
2360
  placementKey: SELECTION_ACTION_POPOVER_PLACEMENT_KEY
2363
2361
  }) === "fallback";
2364
- if (!canUseFallback) popoverManager.setPlacement("preferred", SELECTION_ACTION_POPOVER_PLACEMENT_KEY);
2362
+ if (fallbackGeometry === void 0) popoverManager.setPlacement("preferred", SELECTION_ACTION_POPOVER_PLACEMENT_KEY);
2365
2363
  const { placeAbove } = useFallback ? fallback : preferred;
2366
- const { left, anchorTop } = useFallback ? fallbackGeometry : preferredGeometry;
2367
- this.#selectionAction.reposition(left, anchorTop, this.#getGutterWidth(), placeAbove, viewport);
2364
+ const { left, anchorTop } = useFallback && fallbackGeometry !== void 0 ? fallbackGeometry : preferredGeometry;
2365
+ const selectionTop = isBackward ? preferredGeometry.rowTop : fallbackGeometry?.rowTop ?? -Infinity;
2366
+ const selectionBottom = (isBackward ? fallbackGeometry?.rowTop ?? Infinity : preferredGeometry.rowTop) + (primarySelection.end.line > primarySelection.start.line && primarySelection.end.character === 0 ? 0 : lineHeight);
2367
+ const isSelectionVisible = viewport === void 0 || selectionBottom > viewport.top && selectionTop < viewport.bottom;
2368
+ this.#selectionAction.reposition(left, anchorTop, this.#getGutterWidth(), placeAbove, isSelectionVisible, viewport);
2368
2369
  }
2369
2370
  #openSearchPanel(mode) {
2370
2371
  if (this.#searchPanel !== void 0) {