]> git.eshelyaron.com Git - emacs.git/commitdiff
Add new function 'kbd-valid-p'
authorLars Ingebrigtsen <larsi@gnus.org>
Sat, 16 Oct 2021 15:50:36 +0000 (17:50 +0200)
committerLars Ingebrigtsen <larsi@gnus.org>
Sat, 16 Oct 2021 15:50:40 +0000 (17:50 +0200)
* doc/lispref/keymaps.texi (Key Sequences): New function
'kbd-valid-p'.

* lisp/subr.el (kbd-valid-p): Document it.

doc/lispref/keymaps.texi
etc/NEWS
lisp/subr.el
test/lisp/subr-tests.el

index 066d8b3693ae5e2ae926a0123e8c0b7272f458c9..4277c718fea805718adf9ec3c4068fce999f51a6 100644 (file)
@@ -94,8 +94,15 @@ Manual}.
 (kbd "<f1> SPC") @result{} [f1 32]
 (kbd "C-M-<down>") @result{} [C-M-down]
 @end example
+
+@findex kbd-valid-p
+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.
 @end defun
 
+
 @node Keymap Basics
 @section Keymap Basics
 @cindex key binding
index e7d3de7798630b035fcf7fdbadcb70f41b56d2cc..fcc9b4ad32f955e2e1a5627559755c2b3b50bac5 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -209,6 +209,13 @@ This macro allows defining keymap variables more conveniently.
 ** 'kbd' can now be used in built-in, preloaded libraries.
 It no longer depends on edmacro.el and cl-lib.el.
 
++++
+** New function 'kbd-valid-p'.
+The 'kbd' function is quite permissive, and will try to return
+something usable even if the syntax of the argument isn't completely
+correct.  The 'kbd-valid-p' predicate does a stricter check of the
+syntax.
+
 \f
 * Changes in Emacs 29.1 on Non-Free Operating Systems
 
index 93ec76e290fc81a904df3979591f35780774f1fd..e55c94a9f82bb486ee7beeedecb5946d95a348f5 100644 (file)
@@ -925,6 +925,39 @@ side-effects, and the argument LIST is not modified."
 \f
 ;;;; Keymap support.
 
+(defun kbd-valid-p (keys)
+  "Say whether KEYS is a valid `kbd' sequence.
+In particular, this checks the order of the modifiers, and they
+have to be specified in this order:
+
+   A-C-H-M-S-s
+
+which is
+
+   Alt-Control-Hyper-Meta-Shift-super"
+  (declare (pure t) (side-effect-free t))
+  (and (stringp keys)
+       (string-match-p "\\`[^ ]+\\( [^ ]+\\)*\\'" keys)
+       (save-match-data
+         (seq-every-p
+          (lambda (key)
+            ;; Every key might have these modifiers, and they should be
+            ;; in this order.
+            (when (string-match
+                   "\\`\\(A-\\)?\\(C-\\)?\\(H-\\)?\\(M-\\)?\\(S-\\)?\\(s-\\)?"
+                   key)
+              (setq key (substring key (match-end 0))))
+            (or (and (= (length key) 1)
+                     ;; Don't accept control characters as keys.
+                     (not (< (aref key 0) ?\s))
+                     ;; Don't accept Meta'd characters as keys.
+                     (or (multibyte-string-p key)
+                         (not (<= 127 (aref key 0) 255))))
+                (string-match-p "\\`<[A-Za-z0-9]+>\\'" key)
+                (string-match-p
+                 "\\`\\(NUL\\|RET\\|TAB\\|LFD\\|ESC\\|SPC\\|DEL\\)\\'" key)))
+          (split-string keys " ")))))
+
 (defun kbd (keys &optional need-vector)
   "Convert KEYS to the internal Emacs key representation.
 KEYS should be a string in the format returned by commands such
index da46646d3976faa829f02d8bfc9a50f2f3c0828a..8380e8abfd3a7d6286c043f7a8f3e20121af962e 100644 (file)
   ;; These should be equivalent:
   (should (equal (kbd "\C-xf") (kbd "C-x f"))))
 
+(ert-deftest subr-test-kbd-valid-p ()
+  (should (not (kbd-valid-p "")))
+  (should (kbd-valid-p "f"))
+  (should (kbd-valid-p "X"))
+  (should (not (kbd-valid-p " X")))
+  (should (kbd-valid-p "X f"))
+  (should (not (kbd-valid-p "a  b")))
+  (should (not (kbd-valid-p "foobar")))
+  (should (not (kbd-valid-p "return")))
+
+  (should (kbd-valid-p "<F2>"))
+  (should (kbd-valid-p "<f1> <f2> TAB"))
+  (should (kbd-valid-p "<f1> RET"))
+  (should (kbd-valid-p "<f1> SPC"))
+  (should (kbd-valid-p "<f1>"))
+  (should (not (kbd-valid-p "[f1]")))
+  (should (kbd-valid-p "<return>"))
+  (should (not (kbd-valid-p "< right >")))
+
+  ;; Modifiers:
+  (should (kbd-valid-p "C-x"))
+  (should (kbd-valid-p "C-x a"))
+  (should (kbd-valid-p "C-;"))
+  (should (kbd-valid-p "C-a"))
+  (should (kbd-valid-p "C-c SPC"))
+  (should (kbd-valid-p "C-c TAB"))
+  (should (kbd-valid-p "C-c c"))
+  (should (kbd-valid-p "C-x 4 C-f"))
+  (should (kbd-valid-p "C-x C-f"))
+  (should (kbd-valid-p "C-M-<down>"))
+  (should (not (kbd-valid-p "<C-M-down>")))
+  (should (kbd-valid-p "C-RET"))
+  (should (kbd-valid-p "C-SPC"))
+  (should (kbd-valid-p "C-TAB"))
+  (should (kbd-valid-p "C-<down>"))
+  (should (kbd-valid-p "C-c C-c C-c"))
+
+  (should (kbd-valid-p "M-a"))
+  (should (kbd-valid-p "M-<DEL>"))
+  (should (not (kbd-valid-p "M-C-a")))
+  (should (kbd-valid-p "C-M-a"))
+  (should (kbd-valid-p "M-ESC"))
+  (should (kbd-valid-p "M-RET"))
+  (should (kbd-valid-p "M-SPC"))
+  (should (kbd-valid-p "M-TAB"))
+  (should (kbd-valid-p "M-x a"))
+  (should (kbd-valid-p "M-<up>"))
+  (should (kbd-valid-p "M-c M-c M-c"))
+
+  (should (kbd-valid-p "s-SPC"))
+  (should (kbd-valid-p "s-a"))
+  (should (kbd-valid-p "s-x a"))
+  (should (kbd-valid-p "s-c s-c s-c"))
+
+  (should (not (kbd-valid-p "S-H-a")))
+  (should (kbd-valid-p "S-a"))
+  (should (kbd-valid-p "S-x a"))
+  (should (kbd-valid-p "S-c S-c S-c"))
+
+  (should (kbd-valid-p "H-<RET>"))
+  (should (kbd-valid-p "H-DEL"))
+  (should (kbd-valid-p "H-a"))
+  (should (kbd-valid-p "H-x a"))
+  (should (kbd-valid-p "H-c H-c H-c"))
+
+  (should (kbd-valid-p "A-H-a"))
+  (should (kbd-valid-p "A-SPC"))
+  (should (kbd-valid-p "A-TAB"))
+  (should (kbd-valid-p "A-a"))
+  (should (kbd-valid-p "A-c A-c A-c"))
+
+  (should (kbd-valid-p "C-M-a"))
+  (should (kbd-valid-p "C-M-<up>"))
+
+  ;; Special characters.
+  (should (kbd-valid-p "DEL"))
+  (should (kbd-valid-p "ESC C-a"))
+  (should (kbd-valid-p "ESC"))
+  (should (kbd-valid-p "LFD"))
+  (should (kbd-valid-p "NUL"))
+  (should (kbd-valid-p "RET"))
+  (should (kbd-valid-p "SPC"))
+  (should (kbd-valid-p "TAB"))
+  (should (not (kbd-valid-p "\^i")))
+  (should (not (kbd-valid-p "^M")))
+
+  ;; With numbers.
+  (should (not (kbd-valid-p "\177")))
+  (should (not (kbd-valid-p "\000")))
+  (should (not (kbd-valid-p "\\177")))
+  (should (not (kbd-valid-p "\\000")))
+  (should (not (kbd-valid-p "C-x \\150")))
+
+  ;; Multibyte
+  (should (kbd-valid-p "ñ"))
+  (should (kbd-valid-p "ü"))
+  (should (kbd-valid-p "ö"))
+  (should (kbd-valid-p "ğ"))
+  (should (kbd-valid-p "ա"))
+  (should (not (kbd-valid-p "üüöö")))
+  (should (kbd-valid-p "C-ü"))
+  (should (kbd-valid-p "M-ü"))
+  (should (kbd-valid-p "H-ü"))
+
+  ;; Handle both new and old style key descriptions (bug#45536).
+  (should (kbd-valid-p "s-<return>"))
+  (should (not (kbd-valid-p "<s-return>")))
+  (should (kbd-valid-p "C-M-<return>"))
+  (should (not (kbd-valid-p "<C-M-return>")))
+
+  (should (not (kbd-valid-p "C-xx")))
+  (should (not (kbd-valid-p "M-xx")))
+  (should (not (kbd-valid-p "M-x<TAB>"))))
+
 (ert-deftest subr-test-define-prefix-command ()
   (define-prefix-command 'foo-prefix-map)
   (defvar foo-prefix-map)