branch: elpa/bind-map
commit 75aac732c10d97bc8dc49196c6623a09faf30d37
Merge: f23cfc13222 5913a860c56
Author: Justin Burkett <[email protected]>
Commit: GitHub <[email protected]>
Merge pull request #13 from aaronjensen/evil-override
Alternative technique for Evil binding
---
bind-map.el | 128 +++++++++++++++++++++++++++++++++++++-----------------------
1 file changed, 79 insertions(+), 49 deletions(-)
diff --git a/bind-map.el b/bind-map.el
index 41e6e5b5f03..fd7cb053300 100644
--- a/bind-map.el
+++ b/bind-map.el
@@ -170,23 +170,6 @@ be activated.")
(put map-sym (pop properties)
(when properties (pop properties)))))
-(defun bind-map-evil-local-mode-hook ()
- "Called to activate local state maps in a buffer."
- ;; format is (OVERRIDE-MODE STATE KEY DEF)
- (dolist (entry bind-map-evil-local-bindings)
- (let* ((map (intern (format "evil-%s-state-local-map" (nth 1 entry))))
- (mode (nth 0 entry))
- (global-mode (intern (format "global-%s" (nth 0 entry))))
- (set-explicitly (intern (format "%s-set-explicitly" mode))))
- (when (and (boundp global-mode) (boundp mode)
- (boundp set-explicitly) (boundp map)
- (keymapp (symbol-value map))
- (symbol-value global-mode)
- (not (and (symbol-value set-explicitly)
- (null (symbol-value mode)))))
- (define-key (symbol-value map) (nth 2 entry) (nth 3 entry))))))
-(add-hook 'evil-local-mode-hook 'bind-map-evil-local-mode-hook)
-
(defvar bind-map-major-modes-alist '()
"Each element takes the form (MAP-ACTIVE (MAJOR-MODE1
MAJOR-MODE2 ...)). The car is the variable used to activate a map
@@ -198,12 +181,10 @@ when the major mode is an element of the cdr. See
;; format is (ACTIVATE-VAR MAJOR-MODES-LIST)
(dolist (entry bind-map-major-modes-alist)
(if (boundp (car entry))
- (setf (symbol-value (car entry))
- (not
- (null
- (member major-mode
- (mapcan
- #'bind-map--lookup-major-modes (cdr entry))))))
+ (set (make-local-variable (car entry))
+ (let* ((modes (cdr entry))
+ (expanded (mapcan #'bind-map--lookup-major-modes modes)))
+ (and (memq major-mode expanded) t)))
(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)
@@ -241,7 +222,20 @@ then append MAJOR-MODE-LIST to the existing cdr."
(setcdr current (append (cdr current)
major-mode-list))
(push (cons activate-var major-mode-list)
- bind-map-major-modes-alist))))
+ bind-map-major-modes-alist)))
+ ;; Recompute activation immediately for existing buffers so the change takes
effect
+ ;; without requiring a mode change.
+ (let* ((entry (assq activate-var bind-map-major-modes-alist))
+ (modes (and entry (cdr entry)))
+ (expanded (when modes (mapcan #'bind-map--lookup-major-modes modes))))
+ (when expanded
+ (dolist (buf (buffer-list))
+ (with-current-buffer buf
+ (when (boundp activate-var)
+ (set (make-local-variable activate-var)
+ (and (memq major-mode expanded) t))
+ (when (featurep 'evil)
+ (evil-normalize-keymaps))))))))
(defun bind-map-kbd-keys (keys)
"Apply `kbd' to KEYS filtering out nil and empty strings."
@@ -341,6 +335,8 @@ mode maps. Set up by bind-map.el." map))
(turn-on-override-mode (intern (format "turn-on-%s" override-mode)))
(turn-on-override-mode-doc (format "Enable `%s' except in minibuffer"
override-mode))
+ (ensure-override-mode-func (intern (format "bind-map--ensure-%s"
override-mode)))
+ (ensure-override-mode-doc (format "Ensure %s is enabled in
appropriate buffers." override-mode))
(evil-keys (or (plist-get args :evil-keys)
bind-map-default-evil-keys))
(evil-states (or (plist-get args :evil-states)
@@ -379,8 +375,16 @@ mode maps. Set up by bind-map.el." map))
`((with-no-warnings (defvar-local ,active-var nil))
(add-to-list 'minor-mode-map-alist (cons ',active-var ,root-map))
(bind-map-add-to-major-mode-list ',active-var ',major-modes)
- ;; call once in case we are already in the relevant major mode
- (bind-map-change-major-mode-after-body-hook)))
+ ;; Normalize activation across existing buffers immediately
+ (let* ((modes ',major-modes)
+ (expanded (mapcan #'bind-map--lookup-major-modes modes)))
+ (dolist (buf (buffer-list))
+ (with-current-buffer buf
+ (when (boundp ',active-var)
+ (set (make-local-variable ',active-var)
+ (and (memq major-mode expanded) t))
+ (when (featurep 'evil)
+ (evil-normalize-keymaps))))))))
(when (and override-minor-modes
(null major-modes)
@@ -395,7 +399,24 @@ mode maps. Set up by bind-map.el." map))
,override-mode-doc)
(,global-override-mode 1))
(add-to-list 'emulation-mode-map-alists
- (list (cons ',override-mode ,root-map)))))
+ (list (cons ',override-mode ,root-map)))
+ ;; Ensure Evil includes this map by also adding to minor-mode maps
+ (add-to-list 'minor-mode-map-alist (cons ',override-mode ,root-map))
+ ;; Normalize Evil keymaps when the override mode toggles
+ (with-eval-after-load 'evil
+ (add-hook ',(intern (format "%s-hook" override-mode))
+ #'evil-normalize-keymaps))
+ ;; Defensive hook to ensure the mode is enabled in buffers that may
+ ;; have been created in unusual ways (e.g., programmatically via
+ ;; get-buffer-create). This catches cases where
after-change-major-mode-hook
+ ;; might not fire or fires before the global mode is enabled.
+ (defun ,ensure-override-mode-func ()
+ ,ensure-override-mode-doc
+ (when (and ,global-override-mode
+ (not ,override-mode)
+ (not (minibufferp)))
+ (,override-mode 1)))
+ (add-hook 'buffer-list-update-hook #',ensure-override-mode-func)))
(if (or minor-modes major-modes)
;; only bind keys in root-map
@@ -409,28 +430,37 @@ mode maps. Set up by bind-map.el." map))
(when evil-keys
(if (or minor-modes major-modes)
- `((eval-after-load 'evil
- '(progn
- (dolist (key (bind-map-kbd-keys (list ,@evil-keys)))
- (dolist (state ',evil-states)
- (when ',major-modes
- (define-key
- (evil-get-auxiliary-keymap ,root-map state t)
- key ',prefix-cmd))
- (dolist (mode ',minor-modes)
- (when (fboundp 'evil-define-minor-mode-key)
- (evil-define-minor-mode-key
- state mode key ',prefix-cmd)))))
- (evil-normalize-keymaps))))
- `((eval-after-load 'evil
- '(progn
- (dolist (key (bind-map-kbd-keys (list ,@evil-keys)))
- (dolist (state ',evil-states)
- (when ,override-minor-modes
- (push (list ',override-mode state key ',prefix-cmd)
- bind-map-evil-local-bindings))
- (evil-global-set-key state key ',prefix-cmd)))
- (evil-normalize-keymaps))))))
+ `((eval-after-load 'evil
+ '(progn
+ ;; Bind per-state leaders in the auxiliary keymaps of
root-map
+ (dolist (key (bind-map-kbd-keys (list ,@evil-keys)))
+ (dolist (state ',evil-states)
+ (when ',major-modes
+ (define-key (evil-get-auxiliary-keymap ,root-map state
t)
+ key ',prefix-cmd))
+ (dolist (mode ',minor-modes)
+ (evil-define-minor-mode-key ',evil-states mode key
',prefix-cmd))))
+ (evil-normalize-keymaps))))
+ `((eval-after-load 'evil
+ '(progn
+ (dolist (state ',evil-states)
+ ;; Mark the root-map overriding for precedence
+ (evil-make-overriding-map ,root-map state)
+ ;; Bind keys in the per-state auxiliary keymaps
+ (dolist (key (bind-map-kbd-keys (list ,@evil-keys)))
+ (define-key (evil-get-auxiliary-keymap ,root-map state t)
+ key ',prefix-cmd)))
+ ;; Also associate bindings with the override minor mode so they
+ ;; are scoped to buffers where it is enabled
+ (when ,override-minor-modes
+ (dolist (key (bind-map-kbd-keys (list ,@evil-keys)))
+ (evil-define-minor-mode-key ',evil-states ',override-mode
key ',prefix-cmd)))
+ ;; Fall back to global bindings when not overriding minor modes
+ (unless ,override-minor-modes
+ (dolist (key (bind-map-kbd-keys (list ,@evil-keys)))
+ (dolist (state ',evil-states)
+ (evil-global-set-key state key ',prefix-cmd))))
+ (evil-normalize-keymaps))))))
(when bindings
`((bind-map-set-keys ,map