Support a new ["..."] key binding syntax
* doc/lispref/keymaps.texi (Key Sequences): (Changing Key Bindings): Document the various key syntaxes. * lisp/emacs-lisp/byte-opt.el (byte-optimize-define-key) (byte-optimize-define-keymap) (byte-optimize-define-keymap--define): New functions to check and expand ["..."] syntax at compile time. * src/keymap.c (Fdefine_key): Understand the ["..."] syntax. (syms_of_keymap): Define `kbd' symbols.
This commit is contained in:
parent
8122501fca
commit
e36d3fc452
4 changed files with 142 additions and 22 deletions
|
@ -100,6 +100,16 @@ The @code{kbd} function is very permissive, and will try to return
|
|||
something sensible even if the syntax used isn't completely
|
||||
conforming. To check whether the syntax is actually valid, use the
|
||||
@code{kbd-valid-p} function.
|
||||
|
||||
@code{define-key} also supports using the shorthand syntax
|
||||
@samp{["..."]} syntax to define a key. The string has to be a
|
||||
strictly valid @code{kbd} sequence, and if it's not valid, an error
|
||||
will be signalled. For instance, to bind @key{C-c f}, you can say:
|
||||
|
||||
@lisp
|
||||
(define-key global-map ["C-c f"] #'find-file-literally)
|
||||
@end lisp
|
||||
|
||||
@end defun
|
||||
|
||||
|
||||
|
@ -1285,24 +1295,46 @@ Binding Conventions}).
|
|||
|
||||
@cindex meta character key constants
|
||||
@cindex control character key constants
|
||||
In writing the key sequence to rebind, it is good to use the special
|
||||
escape sequences for control and meta characters (@pxref{String Type}).
|
||||
The syntax @samp{\C-} means that the following character is a control
|
||||
character and @samp{\M-} means that the following character is a meta
|
||||
character. Thus, the string @code{"\M-x"} is read as containing a
|
||||
single @kbd{M-x}, @code{"\C-f"} is read as containing a single
|
||||
@kbd{C-f}, and @code{"\M-\C-x"} and @code{"\C-\M-x"} are both read as
|
||||
containing a single @kbd{C-M-x}. You can also use this escape syntax in
|
||||
vectors, as well as others that aren't allowed in strings; one example
|
||||
is @samp{[?\C-\H-x home]}. @xref{Character Type}.
|
||||
@code{define-key} (and other functions that are used to rebind keys)
|
||||
understand a number of different syntaxes for the keys.
|
||||
|
||||
The key definition and lookup functions accept an alternate syntax for
|
||||
event types in a key sequence that is a vector: you can use a list
|
||||
containing modifier names plus one base event (a character or function
|
||||
key name). For example, @code{(control ?a)} is equivalent to
|
||||
@code{?\C-a} and @code{(hyper control left)} is equivalent to
|
||||
@code{C-H-left}. One advantage of such lists is that the precise
|
||||
numeric codes for the modifier bits don't appear in compiled files.
|
||||
@table @asis
|
||||
@item A vector containing a single string.
|
||||
This is the preferred way to represent a key sequence. Here's a
|
||||
couple of examples:
|
||||
|
||||
@example
|
||||
["C-c M-f"]
|
||||
["S-<home>"]
|
||||
@end example
|
||||
|
||||
The syntax is the same as the one used by Emacs when displaying key
|
||||
bindings, for instance in @samp{*Help*} buffers and help texts.
|
||||
|
||||
If the syntax isn't valid, an error will be raised when running
|
||||
@code{define-key}, or when byte-compiling code that has these calls.
|
||||
|
||||
@item A vector containing lists of keys.
|
||||
You can use a list containing modifier names plus one base event (a
|
||||
character or function key name). For example, @code{[(control ?a)
|
||||
(meta b)]} is equivalent to @kbd{C-a M-b} and @code{[(hyper control
|
||||
left)]} is equivalent to @kbd{C-H-left}.
|
||||
|
||||
@item A string with control and meta characters.
|
||||
Internally, key sequences are often represented as strings using the
|
||||
special escape sequences for control and meta characters
|
||||
(@pxref{String Type}), but this representation can also be used by
|
||||
users when rebinding keys. A string like @code{"\M-x"} is read as
|
||||
containing a single @kbd{M-x}, @code{"\C-f"} is read as containing a
|
||||
single @kbd{C-f}, and @code{"\M-\C-x"} and @code{"\C-\M-x"} are both
|
||||
read as containing a single @kbd{C-M-x}.
|
||||
|
||||
@item a vector of characters.
|
||||
This is the other internal representation of key sequences, and
|
||||
supports a fuller range of modifiers than the string representation.
|
||||
One example is @samp{[?\C-\H-x home]}, which represents the @kbd{C-H-x
|
||||
home} key sequence. @xref{Character Type}.
|
||||
@end table
|
||||
|
||||
The functions below signal an error if @var{keymap} is not a keymap,
|
||||
or if @var{key} is not a string or vector representing a key sequence.
|
||||
|
@ -1344,7 +1376,7 @@ bindings in it:
|
|||
@result{} (keymap)
|
||||
@end group
|
||||
@group
|
||||
(define-key map "\C-f" 'forward-char)
|
||||
(define-key map ["C-f"] 'forward-char)
|
||||
@result{} forward-char
|
||||
@end group
|
||||
@group
|
||||
|
@ -1354,7 +1386,7 @@ map
|
|||
|
||||
@group
|
||||
;; @r{Build sparse submap for @kbd{C-x} and bind @kbd{f} in that.}
|
||||
(define-key map (kbd "C-x f") 'forward-word)
|
||||
(define-key map ["C-x f"] 'forward-word)
|
||||
@result{} forward-word
|
||||
@end group
|
||||
@group
|
||||
|
@ -1367,14 +1399,14 @@ map
|
|||
|
||||
@group
|
||||
;; @r{Bind @kbd{C-p} to the @code{ctl-x-map}.}
|
||||
(define-key map (kbd "C-p") ctl-x-map)
|
||||
(define-key map ["C-p"] ctl-x-map)
|
||||
;; @code{ctl-x-map}
|
||||
@result{} [nil @dots{} find-file @dots{} backward-kill-sentence]
|
||||
@end group
|
||||
|
||||
@group
|
||||
;; @r{Bind @kbd{C-f} to @code{foo} in the @code{ctl-x-map}.}
|
||||
(define-key map (kbd "C-p C-f") 'foo)
|
||||
(define-key map ["C-p C-f"] 'foo)
|
||||
@result{} 'foo
|
||||
@end group
|
||||
@group
|
||||
|
@ -1404,7 +1436,8 @@ keys. Here's a very basic example:
|
|||
@lisp
|
||||
(define-keymap
|
||||
"n" #'forward-line
|
||||
"f" #'previous-line)
|
||||
"f" #'previous-line
|
||||
["C-c C-c"] #'quit-window)
|
||||
@end lisp
|
||||
|
||||
This function creates a new sparse keymap, defines the two keystrokes
|
||||
|
|
7
etc/NEWS
7
etc/NEWS
|
@ -173,6 +173,13 @@ with recent versions of Firefox.
|
|||
|
||||
* Lisp Changes in Emacs 29.1
|
||||
|
||||
+++
|
||||
** 'define-key' now understands a new strict 'kbd' representation for keys.
|
||||
The (define-key map ["C-c M-f"] #'some-command) syntax is now
|
||||
supported, and is like the 'kbd' representation, but is stricter. If
|
||||
the string doesn't represent a valid key sequence, an error is
|
||||
signalled (both when evaluating and byte compiling).
|
||||
|
||||
+++
|
||||
** :keys in 'menu-item' can now be a function.
|
||||
If so, it is called whenever the menu is computed, and can be used to
|
||||
|
|
|
@ -1186,6 +1186,67 @@ See Info node `(elisp) Integer Basics'."
|
|||
|
||||
(put 'concat 'byte-optimizer #'byte-optimize-concat)
|
||||
|
||||
(defun byte-optimize-define-key (form)
|
||||
"Expand key bindings in FORM."
|
||||
(let ((key (nth 2 form)))
|
||||
(if (and (vectorp key)
|
||||
(= (length key) 1)
|
||||
(stringp (aref key 0)))
|
||||
;; We have key on the form ["C-c C-c"].
|
||||
(if (not (kbd-valid-p (aref key 0)))
|
||||
(error "Invalid `kbd' syntax: %S" key)
|
||||
(list (nth 0 form) (nth 1 form)
|
||||
(kbd (aref key 0)) (nth 4 form)))
|
||||
;; No improvement.
|
||||
form)))
|
||||
|
||||
(put 'define-key 'byte-optimizer #'byte-optimize-define-key)
|
||||
|
||||
(defun byte-optimize-define-keymap (form)
|
||||
"Expand key bindings in FORM."
|
||||
(let ((result nil)
|
||||
(orig-form form)
|
||||
improved)
|
||||
(push (pop form) result)
|
||||
(while (and form
|
||||
(keywordp (car form))
|
||||
(not (eq (car form) :menu)))
|
||||
(push (pop form) result)
|
||||
(when (null form)
|
||||
(error "Uneven number of keywords in %S" form))
|
||||
(push (pop form) result))
|
||||
;; Bindings.
|
||||
(while form
|
||||
(let ((key (pop form)))
|
||||
(if (and (vectorp key)
|
||||
(= (length key) 1)
|
||||
(stringp (aref key 0)))
|
||||
(progn
|
||||
(unless (kbd-valid-p (aref key 0))
|
||||
(error "Invalid `kbd' syntax: %S" key))
|
||||
(push (kbd (aref key 0)) result)
|
||||
(setq improved t))
|
||||
;; No improvement.
|
||||
(push key result)))
|
||||
(when (null form)
|
||||
(error "Uneven number of key bindings in %S" form))
|
||||
(push (pop form) result))
|
||||
(if improved
|
||||
(nreverse result)
|
||||
orig-form)))
|
||||
|
||||
(defun byte-optimize-define-keymap--define (form)
|
||||
"Expand key bindings in FORM."
|
||||
(let ((optimized (byte-optimize-define-keymap (nth 1 form))))
|
||||
(if (eq optimized (nth 1 form))
|
||||
;; No improvement.
|
||||
form
|
||||
(list (car form) optimized))))
|
||||
|
||||
(put 'define-keymap 'byte-optimizer #'byte-optimize-define-keymap)
|
||||
(put 'define-keymap--define 'byte-optimizer
|
||||
#'byte-optimize-define-keymap--define)
|
||||
|
||||
;; I'm not convinced that this is necessary. Doesn't the optimizer loop
|
||||
;; take care of this? - Jamie
|
||||
;; I think this may some times be necessary to reduce ie (quote 5) to 5,
|
||||
|
|
19
src/keymap.c
19
src/keymap.c
|
@ -1084,6 +1084,22 @@ binding KEY to DEF is added at the front of KEYMAP. */)
|
|||
def = tmp;
|
||||
}
|
||||
|
||||
if (VECTORP (key) && ASIZE (key) == 1 && STRINGP (AREF (key, 0)))
|
||||
{
|
||||
/* KEY is on the ["C-c"] format, so translate to internal
|
||||
format. */
|
||||
if (NILP (Ffboundp (Qkbd_valid_p)))
|
||||
xsignal2 (Qerror,
|
||||
build_string ("`kbd-valid-p' is not defined, so this syntax can't be used: %s"),
|
||||
key);
|
||||
if (NILP (call1 (Qkbd_valid_p, AREF (key, 0))))
|
||||
xsignal2 (Qerror, build_string ("Invalid `kbd' syntax: %S"), key);
|
||||
key = call1 (Qkbd, AREF (key, 0));
|
||||
length = CHECK_VECTOR_OR_STRING (key);
|
||||
if (length == 0)
|
||||
xsignal2 (Qerror, build_string ("Invalid `kbd' syntax: %S"), key);
|
||||
}
|
||||
|
||||
ptrdiff_t idx = 0;
|
||||
while (1)
|
||||
{
|
||||
|
@ -3263,4 +3279,7 @@ that describe key bindings. That is why the default is nil. */);
|
|||
defsubr (&Stext_char_description);
|
||||
defsubr (&Swhere_is_internal);
|
||||
defsubr (&Sdescribe_buffer_bindings);
|
||||
|
||||
DEFSYM (Qkbd, "kbd");
|
||||
DEFSYM (Qkbd_valid_p, "kbd-valid-p");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue