From fe57aa57ccc23ecb5387bc321c7564f1a7ee904e Mon Sep 17 00:00:00 2001 From: Gregory Heytings Date: Mon, 18 Oct 2021 11:45:43 +0000 Subject: [PATCH] Make it possible to use the kbd key binding syntax in more places. * src/keymap.c (Fstring_key_binding_p, maybe_convert_string_key_binding): New functions. (Fdefine_key, Flookup_key, Fkey_binding, Fminor_mode_key_binding): Use the new functions, and document the change. (syms_of_keymap): Add symbols. --- src/keymap.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/keymap.c b/src/keymap.c index 5324f7f021..90efe41827 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -1026,6 +1026,113 @@ DEFUN ("copy-keymap", Fcopy_keymap, Scopy_keymap, 1, 1, 0, /* GC is possible in this function if it autoloads a keymap. */ +DEFUN ("string-key-binding-p", Fstring_key_binding_p, Sstring_key_binding_p, + 1, 1, 0, + doc: /* Whether STRING contains a valid key binding sequence. + +A valid key binding sequence is a sequence of valid key bindings separated +by single spaces. +A valid key binding starts with a sequence of zero or more modifiers, each +represented with one of the letters ACMSHs, and each followed by a single +dash (-), in which no modifier appears more than once, and ends with a +valid key or a valid event. +A valid key is either a single visible ASCII or Unicode character, or one +of the following symbolic characters: NUL (representing the ASCII character +0 or C-@), TAB (9 or C-i), LFD (10 or C-j), RET (13 or C-m), ESC (27 or C-[), +SPC (32 or space), DEL (127 or C-?). +A valid event is a sequence of two or more characters enclosed by angle +brackets (<>), which can only contain ASCII letters and digits, dashes (-) +and underscores (_), which must start with a letter, which must end with a +letter or a digit, and in which the first dash cannot appear before the +second character. +If STRING is not a string, returns nil. */) + (Lisp_Object string) +{ + bool mod[6]; + bool space = false; + int chars, bytes; + char *key; + + if (!STRINGP (string)) + return Qnil; + + key = SSDATA (string); + if (strlen (key) == 0) + return Qnil; + + while (*key) + if (*key == ' ' || space) + { + if (*key == ' ' && !*(key + 1)) + return Qnil; + if ((*key == ' ') ^ space) + return Qnil; + space = false; + key++; + } + else + { + space = true; + mod[0] = mod[1] = mod[2] = mod[3] = mod[4] = mod[5] = false; + while (((*key == 'A' && !mod[0] && (mod[0] = true)) || + (*key == 'C' && !mod[1] && (mod[1] = true)) || + (*key == 'H' && !mod[2] && (mod[2] = true)) || + (*key == 'M' && !mod[3] && (mod[3] = true)) || + (*key == 's' && !mod[4] && (mod[4] = true)) || + (*key == 'S' && !mod[5] && (mod[5] = true))) && + *(key + 1) == '-') + key += 2; + if (*key == '<' && + ((*(key + 1) >= 'a' && *(key + 1) <= 'z') || + (*(key + 1) >= 'A' && *(key + 1) <= 'Z'))) + { + key++; + chars = 0; + while (*key && *key != '>') + if ((*key >= 'a' && *key <= 'z') || + (*key >= 'A' && *key <= 'Z') || + (*key >= '0' && *key <= '9') || + (*key == '-' && chars > 1 && *(key + 1) != '>') || + (*key == '_' && *(key + 1) != '>')) + chars++, key++; + else + return Qnil; + if ((*key != '>') || (*key == '>' && chars < 2)) + return Qnil; + key++; + } + else if (!strncmp (key, "NUL", 3) || !strncmp (key, "RET", 3) || + !strncmp (key, "LFD", 3) || !strncmp (key, "TAB", 3) || + !strncmp (key, "ESC", 3) || !strncmp (key, "SPC", 3) || + !strncmp (key, "DEL", 3)) + key += 3; + else if (*key >= '!' && *key <= '~') + key++; + else if (STRING_MULTIBYTE (string) && LEADING_CODE_P (*key)) + { + bytes = BYTES_BY_CHAR_HEAD (*key++) - 1; + while (bytes && *key && TRAILING_CODE_P (*key)) + bytes--, key++; + if (bytes) + return Qnil; + } + else + return Qnil; + } + return Qt; +} + +static Lisp_Object +maybe_convert_string_key_binding (Lisp_Object key) +{ + Lisp_Object string_key_binding; + if (!NILP (Fstring_key_binding_p (key)) && + !NILP (Ffboundp (Qkbd)) && + !NILP ((string_key_binding = safe_call1 (Qkbd, key)))) + key = string_key_binding; + return key; +} + DEFUN ("define-key", Fdefine_key, Sdefine_key, 3, 3, 0, doc: /* In KEYMAP, define key sequence KEY as DEF. KEYMAP is a keymap. @@ -1037,6 +1144,10 @@ DEFUN ("define-key", Fdefine_key, Sdefine_key, 3, 3, 0, [remap COMMAND] remaps any key binding for COMMAND. [t] creates a default definition, which applies to any event with no other definition in KEYMAP. +When KEY is in the format of the `string-key-binding-p' predicate, +it is first processed by `kbd', which see. Note: in Emacs 30, a warning +will be displayed for strings returning nil with `string-key-binding-p'; +in Emacs 31, they will raise an error. DEF is anything that can be a key's definition: nil (means key is undefined in this keymap), @@ -1061,6 +1172,8 @@ DEFUN ("define-key", Fdefine_key, Sdefine_key, 3, 3, 0, { bool metized = false; + key = maybe_convert_string_key_binding (key); + keymap = get_keymap (keymap, 1, 1); ptrdiff_t length = CHECK_VECTOR_OR_STRING (key); @@ -1206,6 +1319,8 @@ DEFUN ("lookup-key", Flookup_key, Slookup_key, 2, 3, 0, { bool t_ok = !NILP (accept_default); + key = maybe_convert_string_key_binding (key); + if (!CONSP (keymap) && !NILP (keymap)) keymap = get_keymap (keymap, true, true); @@ -1579,6 +1694,10 @@ DEFUN ("current-active-maps", Fcurrent_active_maps, Scurrent_active_maps, DEFUN ("key-binding", Fkey_binding, Skey_binding, 1, 4, 0, doc: /* Return the binding for command KEY in current keymaps. KEY is a string or vector, a sequence of keystrokes. +When KEY is in the format of the `string-key-binding-p' predicate, +it is first processed by `kbd', which see. Note: in Emacs 30, a warning +will be displayed for strings returning nil with `string-key-binding-p'; +in Emacs 31, they will raise an error. The binding is probably a symbol with a function definition. Normally, `key-binding' ignores bindings for t, which act as default @@ -1604,6 +1723,8 @@ DEFUN ("key-binding", Fkey_binding, Skey_binding, 1, 4, 0, */) (Lisp_Object key, Lisp_Object accept_default, Lisp_Object no_remap, Lisp_Object position) { + key = maybe_convert_string_key_binding (key); + if (NILP (position) && VECTORP (key)) { if (ASIZE (key) == 0) @@ -1663,6 +1784,8 @@ DEFUN ("minor-mode-key-binding", Fminor_mode_key_binding, Sminor_mode_key_bindin int nmaps = current_minor_maps (&modes, &maps); Lisp_Object binding = Qnil; + key = maybe_convert_string_key_binding (key); + int j; for (int i = j = 0; i < nmaps; i++) if (!NILP (maps[i]) @@ -3263,4 +3386,7 @@ syms_of_keymap (void) defsubr (&Stext_char_description); defsubr (&Swhere_is_internal); defsubr (&Sdescribe_buffer_bindings); + defsubr (&Sstring_key_binding_p); + + DEFSYM (Qkbd, "kbd"); } -- 2.33.0