keymap.min.js (6389B)
1 import { flipCtrlCmd, mac, presto } from "../util/browser.js" 2 import { map } from "../util/misc.js" 3 4 import { keyNames } from "./keynames.js" 5 6 export let keyMap = {} 7 8 keyMap.basic = { 9 "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", 10 "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", 11 "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", 12 "Tab": "defaultTab", "Shift-Tab": "indentAuto", 13 "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", 14 "Esc": "singleSelection" 15 } 16 // Note that the save and find-related commands aren't defined by 17 // default. User code or addons can define them. Unknown commands 18 // are simply ignored. 19 keyMap.pcDefault = { 20 "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", 21 "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", 22 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", 23 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", 24 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", 25 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", 26 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", 27 fallthrough: "basic" 28 } 29 // Very basic readline/emacs-style bindings, which are standard on Mac. 30 keyMap.emacsy = { 31 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", 32 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", 33 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", 34 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", 35 "Ctrl-O": "openLine" 36 } 37 keyMap.macDefault = { 38 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", 39 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", 40 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", 41 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", 42 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", 43 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", 44 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", 45 fallthrough: ["basic", "emacsy"] 46 } 47 keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault 48 49 // KEYMAP DISPATCH 50 51 function normalizeKeyName(name) { 52 let parts = name.split(/-(?!$)/) 53 name = parts[parts.length - 1] 54 let alt, ctrl, shift, cmd 55 for (let i = 0; i < parts.length - 1; i++) { 56 let mod = parts[i] 57 if (/^(cmd|meta|m)$/i.test(mod)) cmd = true 58 else if (/^a(lt)?$/i.test(mod)) alt = true 59 else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true 60 else if (/^s(hift)?$/i.test(mod)) shift = true 61 else throw new Error("Unrecognized modifier name: " + mod) 62 } 63 if (alt) name = "Alt-" + name 64 if (ctrl) name = "Ctrl-" + name 65 if (cmd) name = "Cmd-" + name 66 if (shift) name = "Shift-" + name 67 return name 68 } 69 70 // This is a kludge to keep keymaps mostly working as raw objects 71 // (backwards compatibility) while at the same time support features 72 // like normalization and multi-stroke key bindings. It compiles a 73 // new normalized keymap, and then updates the old object to reflect 74 // this. 75 export function normalizeKeyMap(keymap) { 76 let copy = {} 77 for (let keyname in keymap) if (keymap.hasOwnProperty(keyname)) { 78 let value = keymap[keyname] 79 if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue 80 if (value == "...") { delete keymap[keyname]; continue } 81 82 let keys = map(keyname.split(" "), normalizeKeyName) 83 for (let i = 0; i < keys.length; i++) { 84 let val, name 85 if (i == keys.length - 1) { 86 name = keys.join(" ") 87 val = value 88 } else { 89 name = keys.slice(0, i + 1).join(" ") 90 val = "..." 91 } 92 let prev = copy[name] 93 if (!prev) copy[name] = val 94 else if (prev != val) throw new Error("Inconsistent bindings for " + name) 95 } 96 delete keymap[keyname] 97 } 98 for (let prop in copy) keymap[prop] = copy[prop] 99 return keymap 100 } 101 102 export function lookupKey(key, map, handle, context) { 103 map = getKeyMap(map) 104 let found = map.call ? map.call(key, context) : map[key] 105 if (found === false) return "nothing" 106 if (found === "...") return "multi" 107 if (found != null && handle(found)) return "handled" 108 109 if (map.fallthrough) { 110 if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") 111 return lookupKey(key, map.fallthrough, handle, context) 112 for (let i = 0; i < map.fallthrough.length; i++) { 113 let result = lookupKey(key, map.fallthrough[i], handle, context) 114 if (result) return result 115 } 116 } 117 } 118 119 // Modifier key presses don't count as 'real' key presses for the 120 // purpose of keymap fallthrough. 121 export function isModifierKey(value) { 122 let name = typeof value == "string" ? value : keyNames[value.keyCode] 123 return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" 124 } 125 126 export function addModifierNames(name, event, noShift) { 127 let base = name 128 if (event.altKey && base != "Alt") name = "Alt-" + name 129 if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name 130 if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name 131 if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name 132 return name 133 } 134 135 // Look up the name of a key as indicated by an event object. 136 export function keyName(event, noShift) { 137 if (presto && event.keyCode == 34 && event["char"]) return false 138 let name = keyNames[event.keyCode] 139 if (name == null || event.altGraphKey) return false 140 return addModifierNames(name, event, noShift) 141 } 142 143 export function getKeyMap(val) { 144 return typeof val == "string" ? keyMap[val] : val 145 }