aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Pluim2022-08-02 14:22:32 +0200
committerRobert Pluim2022-08-02 14:29:43 +0200
commitbf47851e0817abcf17eeb4a10d27cf18de2c68a2 (patch)
tree7fc3cb4b2dd1726eaa6d1e8807cee27155ba485a
parente5e840168c039f3daf9cce05e0b8ac4c49a450ec (diff)
downloademacs-bf47851e0817abcf17eeb4a10d27cf18de2c68a2.tar.gz
emacs-bf47851e0817abcf17eeb4a10d27cf18de2c68a2.zip
Signal error on duplicate key definitions
* lisp/keymap.el (define-keymap, defvar-keymap): Signal error if the same key is specified twice. (Bug#56873) * doc/lispref/keymaps.texi (Creating Keymaps): Document error signaling behaviour. * test/src/keymap-tests.el (keymap-test-duplicate-definitions): Test duplicate definition detection.
-rw-r--r--doc/lispref/keymaps.texi6
-rw-r--r--lisp/keymap.el16
-rw-r--r--test/src/keymap-tests.el12
3 files changed, 31 insertions, 3 deletions
diff --git a/doc/lispref/keymaps.texi b/doc/lispref/keymaps.texi
index 5cb5367bc0e..2be31d63a62 100644
--- a/doc/lispref/keymaps.texi
+++ b/doc/lispref/keymaps.texi
@@ -374,7 +374,8 @@ number of keys. Here's a very basic example:
374@end lisp 374@end lisp
375 375
376This function creates a new sparse keymap, defines the keystrokes in 376This function creates a new sparse keymap, defines the keystrokes in
377@var{pairs}, and returns the new keymap. 377@var{pairs}, and returns the new keymap. It signals an error if there
378are duplicate key bindings in @var{pairs}.
378 379
379@var{pairs} is a list of alternating key bindings and key definitions, 380@var{pairs} is a list of alternating key bindings and key definitions,
380as accepted by @code{keymap-set}. In addition, the key can be the 381as accepted by @code{keymap-set}. In addition, the key can be the
@@ -438,7 +439,8 @@ variable. This is what virtually all modes do---a mode called
438 439
439This macro defines @var{name} as a variable, passes @var{options} 440This macro defines @var{name} as a variable, passes @var{options}
440and @var{pairs} to @code{define-keymap}, and uses the result as the 441and @var{pairs} to @code{define-keymap}, and uses the result as the
441default value for the variable. 442default value for the variable. It signals an error if there are
443duplicate key bindings in @var{pairs}.
442 444
443@var{options} is like the keywords in @code{define-keymap}, but 445@var{options} is like the keywords in @code{define-keymap}, but
444there's an additional @code{:doc} keyword that provides the doc 446there's an additional @code{:doc} keyword that provides the doc
diff --git a/lisp/keymap.el b/lisp/keymap.el
index 376a30f1065..107565590c1 100644
--- a/lisp/keymap.el
+++ b/lisp/keymap.el
@@ -530,7 +530,8 @@ should be a MENU form as accepted by `easy-menu-define'.
530 (keymap keymap) 530 (keymap keymap)
531 (prefix (define-prefix-command prefix nil name)) 531 (prefix (define-prefix-command prefix nil name))
532 (full (make-keymap name)) 532 (full (make-keymap name))
533 (t (make-sparse-keymap name))))) 533 (t (make-sparse-keymap name))))
534 seen-keys)
534 (when suppress 535 (when suppress
535 (suppress-keymap keymap (eq suppress 'nodigits))) 536 (suppress-keymap keymap (eq suppress 'nodigits)))
536 (when parent 537 (when parent
@@ -544,6 +545,9 @@ should be a MENU form as accepted by `easy-menu-define'.
544 (let ((def (pop definitions))) 545 (let ((def (pop definitions)))
545 (if (eq key :menu) 546 (if (eq key :menu)
546 (easy-menu-define nil keymap "" def) 547 (easy-menu-define nil keymap "" def)
548 (if (member key seen-keys)
549 (error "Duplicate definition for key: %S %s" key keymap)
550 (push key seen-keys))
547 (keymap-set keymap key def))))) 551 (keymap-set keymap key def)))))
548 keymap))) 552 keymap)))
549 553
@@ -571,6 +575,16 @@ as the variable documentation string.
571 (push (pop defs) opts)))) 575 (push (pop defs) opts))))
572 (unless (zerop (% (length defs) 2)) 576 (unless (zerop (% (length defs) 2))
573 (error "Uneven number of key/definition pairs: %s" defs)) 577 (error "Uneven number of key/definition pairs: %s" defs))
578 (let ((defs defs)
579 key seen-keys)
580 (while defs
581 (setq key (pop defs))
582 (pop defs)
583 (when (not (eq key :menu))
584 (if (member key seen-keys)
585 (error "Duplicate definition for key '%s' in keymap '%s'"
586 key variable-name)
587 (push key seen-keys)))))
574 `(defvar ,variable-name 588 `(defvar ,variable-name
575 (define-keymap ,@(nreverse opts) ,@defs) 589 (define-keymap ,@(nreverse opts) ,@defs)
576 ,@(and doc (list doc))))) 590 ,@(and doc (list doc)))))
diff --git a/test/src/keymap-tests.el b/test/src/keymap-tests.el
index b0876664ed1..ce96be6869e 100644
--- a/test/src/keymap-tests.el
+++ b/test/src/keymap-tests.el
@@ -430,6 +430,18 @@ g .. h foo
430 (make-non-key-event 'keymap-tests-event) 430 (make-non-key-event 'keymap-tests-event)
431 (should (equal (where-is-internal 'keymap-tests-command) '([3 103])))) 431 (should (equal (where-is-internal 'keymap-tests-command) '([3 103]))))
432 432
433(ert-deftest keymap-test-duplicate-definitions ()
434 "Check that defvar-keymap rejects duplicate key definitions."
435 (should-error
436 (defvar-keymap
437 ert-keymap-duplicate
438 "a" #'next-line
439 "a" #'previous-line))
440 (should-error
441 (define-keymap
442 "a" #'next-line
443 "a" #'previous-line)))
444
433(provide 'keymap-tests) 445(provide 'keymap-tests)
434 446
435;;; keymap-tests.el ends here 447;;; keymap-tests.el ends here