From 771488148daf095c03ef5b61ce0778886dde8134 Mon Sep 17 00:00:00 2001 From: Dan Kessler Date: Fri, 16 Feb 2024 18:56:57 -0800 Subject: [PATCH] add support for remapped and aliased modes This patch tries to make `bind-map` aware of two ways in which major modes can be remapped: via aliasing or via `major-mode-remap-alist`. The motivating scenario is AUCTeX, which as of version 14.0.1 has renamed several major modes, e.g., latex-mode has been renamed as LaTeX-mode. AUCTeX attempts to offer backwards compatibility using a mix of remapping and aliasing. For users of emacs < 29, `latex-mode` is redefined as an alias for `LaTeX-mode`, whereas for users of emacs 29+, `latex-mode` is remapped to `LaTeX-mode` using `major-mode-remap-alist`. However, if a user had configured keymaps for `latex-mode` using `bind-map`, they will not be available in `LaTeX-mode`. This is especially problematic for distributions like `spacemacs` which need to support users of both old and new AUCTeX (see https://github.com/syl20bnr/spacemacs/issues/16282 for more discussion). This patch introduces two new customizable options, `bind-map-use-remapped-modes` and `bind-map-use-aliased-modes`. Both options default to `t`, in which case when `bind-map` is deciding whether to activate a given keymap, it will compare the value of `major-mode` to not only the symbol for which the keymap was configured configured (e.g., `latex-mode`), but any remapped or aliased modes (e.g., `'(latex-mode LaTeX-mode)`. This logic is implemented in the new (private) function `bind-map--lookup-major-modes` to facilitate lookup of applicable major-modes on the fly. As a result, if a user adjusts `major-mode-remap-alist` or adjusts aliased, `bind-map`'s behavior will change accordingly. --- bind-map.el | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/bind-map.el b/bind-map.el index dee50aa..76b9435 100644 --- a/bind-map.el +++ b/bind-map.el @@ -128,6 +128,32 @@ :group 'bind-map :type 'string) +(defcustom bind-map-use-remapped-modes t + "If non-nil, bind-map will be aware of remapped modes. For +example, suppose you used `bind-map' to define a keymap for +`foo-mode', but use `major-mode-remap-alist' to remap `foo-mode' +to `bar-mode'. If `bind-map-use-remapped-modes' is non-nil, then, +when `bar-mode' is activated + (either directly or by activating `foo-mode'), the keymap for + `foo-mode' will be active. If you define separate keymaps for + `foo-mode' and `bar-mode' yet leave this option non-nil, chaos + may ensue." + :group 'bind-map + :type 'boolean) + +(defcustom bind-map-use-aliased-modes t + "If non-nil, bind-map will be aware of aliased modes. For example, +suppose you used `bind-map' to define a keymap for `foo-mode', +but `foo-mode' is actually an alias for `bar-mode'. If +`bind-map-use-aliased-modes' is non-nil, then, when `bar-mode' is +activated (either directly or by activating `foo-mode'), the +keymap for `foo-mode' will be active. If you define separate +keymaps for `foo-mode' and `bar-mode' yet leave this option +non-nil, chaos may ensue." + :group 'bind-map + :type 'boolean) + + (defvar bind-map-evil-local-bindings '() "Each element takes the form (OVERRIDE-MODE STATE KEY DEF) and corresponds to a binding for an evil local state map. @@ -173,11 +199,30 @@ when the major mode is an element of the cdr. See (dolist (entry bind-map-major-modes-alist) (if (boundp (car entry)) (setf (symbol-value (car entry)) - (not (null (member major-mode (cdr entry))))) + (not + (null + (member major-mode + (mapcar + #'bind-map--lookup-major-modes (cdr entry)))))) (message "bind-map: %s is void in change major mode hook" (car entry))))) (add-hook 'change-major-mode-after-body-hook 'bind-map-change-major-mode-after-body-hook) +(defun bind-map--lookup-major-modes (mode) + "Return a list of implicated modes depending on the values of +`bind-map-use-remapped-modes' and `bind-map-use-aliased-modes'. +If both are nil, just return `mode'. If +`bind-map-use-remapped-modes' is non-nil, also return mode to +which it has been remapped in `major-mode-remap-alist' (if +applicable). If `bind-map-use-aliased-modes' is non-nil, also +return any modes for which `mode' is an alias (if applicable)." + (let ((r-mode + (or (and bind-map-use-remapped-modes + (boundp 'major-mode-remap-alist) + (alist-get mode major-mode-remap-alist)))) + (a-modes (and bind-map-use-aliased-modes (function-alias-p mode)))) + (delq nil (append (list mode r-mode) a-modes)))) + (defun bind-map-add-to-major-mode-list (activate-var major-mode-list) "Add (ACTIVATE-VAR . MAJOR-MODE-LIST) to `bind-map-major-modes-alist'. If ACTIVATE-VAR is already a key,