prism-code-editor
Version:
Lightweight, extensible code editor component for the web using Prism
258 lines (257 loc) • 7.32 kB
JavaScript
import { l as preventDefault } from "./core-C0HOUBwg.js";
import { h as addTextareaListener, s as getModifierCode, u as isMac } from "./utils-Cmdnutew.js";
//#region src/extensions/commands/utils.ts
/**
* Normalizes modifier keys to a bitmask that's used by {@link getKeysFromEvent}.
* All keys are case insensitive.
*
* Rules:
* - `alt` → `1`
* - `ctrl` or `control` → `2`
* - `meta` or `cmd` → `4`
* - `shift` → `8`
* - `mod` → `4` on Mac, `2` otherwise
*
* @param key Key to normalize.
* @returns Normalized and lowercase key.
* @example
* normalizeKey("a") // "0+a"
* normalizeKey("+") // "0++"
* normalizeKey("ctrl+alt+a") // "3+a"
* normalizeKey("10+ALT+a") // "11+a"
* normalizeKey("shift+alt+12+a") // "13+a"
*/
var normalizeKey = (key) => {
let code = 0;
let parts = key.toLowerCase().split("+");
let last = parts.pop() || "+";
for (const part of parts) code |= +part || modifierMap[part] || 0;
return code + "+" + last;
};
/**
* Gets a `Set` of keys that match a keyboard event.
* @param e Keyboard event to get keys for.
*/
var getKeysFromEvent = (e) => {
if (!keyCodeMap[48]) {
for (let i = 48; i < 91; i++) keyCodeMap[i] = String.fromCharCode(i > 64 ? i + 32 : i);
for (let i = 96; i < 112; i++) keyCodeMap[i] = String.fromCharCode(i < 106 ? i - 48 : i - 64);
}
const key = e.key || "";
const code = getModifierCode(e);
const keyBase = keyCodeMap[e.keyCode];
const keyShift = keyCodeMapShift[e.keyCode];
const hasShift = code > 7;
const keyName = (isMac && code == 12 || key == "Dead" || key == "Unidentified") && (hasShift && keyShift || keyBase) || key.toLowerCase();
const isNotChar = /\w\w| /.test(keyName.slice(0, 2));
const noShift = code & 7;
const commands = /* @__PURE__ */ new Set([`${isNotChar || /\p{L}/u.test(keyName) ? code : noShift}+${keyName}`]);
if (!isNotChar) {
if (noShift && !(isMac && noShift == 1) && keyBase) {
commands.add(code + "+" + keyBase);
if (hasShift && keyShift) commands.add(noShift + "+" + keyShift);
} else if (hasShift) commands.add(code + "+" + keyName);
}
return commands;
};
var editorMap = /* @__PURE__ */ new WeakMap();
/**
* Utility that runs all commands whose key matches the keyboard event until a command
* returns a truthy value. In that case `preventDefault` and `stopImmediatePropagation`
* are called.
*
* @param e Keyboard event to run commands for.
* @param map Record mapping keys to a list of commands for that key.
* @param data Argument to pass to the commands.
* @returns `true` if a command returned a truthy value.
*
* @example
* addEventListener("keydown", e => {
* const handled = !!runHotkeys(e, map, data)
* })
*/
var runHotkeys = (e, map, data) => {
let seen = /* @__PURE__ */ new Set();
let firstKey;
try {
for (const key of getKeysFromEvent(e)) {
firstKey ||= key;
for (const set of map[key] || []) for (const cmd of set || []) if (!seen.has(cmd)) {
if (cmd(data, e, key)) {
preventDefault(e);
return true;
}
seen.add(cmd);
}
}
} finally {
map.any?.forEach((set) => set?.forEach((cmd) => cmd(data, e, firstKey)));
}
};
var getMap = (editor) => {
let map = editorMap.get(editor);
if (!map) {
editorMap.set(editor, map = {});
addTextareaListener(editor, "keydown", (e) => {
runHotkeys(e, map, editor);
});
}
return map;
};
/**
* Registers a command for the specified key.
*
* @param editor Editor to add the command to.
* @param key Key the command will run for.
* @param command Command for the specified key.
* @param precedence Positive integer denoting the precedence of the command where `0`
* is the highest precedence. Defaults to `2`.
* @returns Function to remove the hotkey.
*/
var addEditorHotkey = (editor, key, command, precedence) => {
return addHotkey(getMap(editor), key, command, precedence);
};
/**
* Registers a command for the specified key to the hotkey map.
*
* @param map Map to add the command to.
* @param key Key the command will run for.
* @param command Command for the specified key.
* @param precedence Positive integer denoting the precedence of the command where `0`
* is the highest precedence. Defaults to `2`.
* @returns Function to remove the hotkey.
*/
var addHotkey = (map, key, command, precedence = 2) => {
let set = (map[key == "any" ? key : normalizeKey(key)] ||= [])[precedence] ||= /* @__PURE__ */ new Set();
set.add(command);
return () => {
set.delete(command);
};
};
/**
* Registers a sequential hotkey to the specifed editor.
*
* @param editor Editor to add the sequence to.
* @param sequence Sequence of keys to press for the command to be executed.
* @param command Command to execute when the sequence is pressed.
* @returns Function to remove the hotkey.
*/
var addEditorHotkeySequence = (editor, sequence, command, options) => {
return addHotkeySequence(getMap(editor), sequence, command, options);
};
/**
* Registers a sequential hotkey to the specifed hotkey map.
*
* @param map Map to add the sequence to.
* @param sequence Sequence of keys to press for the command to be executed.
* @param command Command to execute when the sequence is pressed.
* @returns Function to remove the hotkey.
*/
var addHotkeySequence = (map, sequence, command, options = {}) => {
let lastTimestamp;
let handledEvent;
let current = 0;
let last = sequence.length - 1;
let removeHandler;
if (last < 0) throw Error("Sequence must have a least one key");
const { timeout = 2e3, precedence, preventDefault = true } = options;
const handleStep = (data, e, key) => {
if (current) removeHandler();
if (lastTimestamp + timeout < (lastTimestamp = Date.now())) return;
handledEvent = e;
if (current == last) {
current = 0;
return command(data, e, key);
}
if (preventDefault) e.preventDefault();
removeHandler = addHotkey(map, sequence[++current], handleStep, 0);
};
const removeStart = addHotkey(map, sequence[0], (data, e, key) => {
if (handledEvent != e) {
lastTimestamp = Date.now();
current = 0;
handleStep(data, e, key);
}
}, precedence);
const removeAny = addHotkey(map, "any", (_, e) => {
if (current && handledEvent != e && !isModifierKey(e)) {
current = 0;
removeHandler();
}
});
return () => {
removeStart();
removeAny();
removeHandler?.();
};
};
/**
* Returns whether the key pressed is Alt, Shift, Control, or Meta.
* @param e Keyboard event to check.
*/
var isModifierKey = (e) => {
return modifierCodes.has(e.keyCode);
};
var modifierCodes = /* @__PURE__ */ new Set([
16,
17,
18,
91,
92,
224,
225
]);
var mod = isMac ? 4 : 2;
var keyCodeMap = {
32: " ",
173: "-",
186: ";",
187: "=",
188: ",",
189: "-",
190: ".",
191: "/",
192: "`",
219: "[",
220: "\\",
221: "]",
222: "'"
};
var keyCodeMapShift = {
48: ")",
49: "!",
50: "@",
51: "#",
52: "$",
53: "%",
54: "^",
55: "&",
56: "*",
57: "(",
59: ":",
61: "+",
173: "_",
186: ":",
187: "+",
188: "<",
189: "_",
190: ">",
191: "?",
192: "~",
219: "{",
220: "|",
221: "}",
222: "\""
};
var modifierMap = {
alt: 1,
control: 2,
ctrl: 2,
meta: 4,
cmd: 4,
shift: 8,
mod: isMac ? 4 : 2
};
//#endregion
export { getKeysFromEvent as a, normalizeKey as c, addHotkeySequence as i, runHotkeys as l, addEditorHotkeySequence as n, isModifierKey as o, addHotkey as r, mod as s, addEditorHotkey as t };
//# sourceMappingURL=utils-ChVWFtO9.js.map