Override (globally override key binding in emacs)

2019-09-10 18:16发布

问题:

I use the method stated here : globally-override-key-binding-in-emacs

(defvar my-keys-minor-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "C-i") 'some-function)
    map)
  "my-keys-minor-mode keymap.")

(define-minor-mode my-keys-minor-mode
  "A minor mode so that my key settings override annoying major modes."
  ;; Define-minor-mode will use `my-keys-minor-mode-map' for us.
  :init-value t
  :lighter " my-keys")

(defun my-keys-keep-on-top (&rest _)
  "Try to ensure that my keybindings always have priority."
  (unless (eq (caar minor-mode-map-alist) 'my-keys-minor-mode)
    (let ((mykeys (assq 'my-keys-minor-mode minor-mode-map-alist)))
      (assq-delete-all 'my-keys-minor-mode minor-mode-map-alist)
      (add-to-list 'minor-mode-map-alist mykeys))))
(add-hook 'after-load-functions #'my-keys-keep-on-top)

(my-keys-minor-mode 1)

How to I explicitly override this? The methods in the link is not working to me.

EDIT2 :

Issue is not only applied to read only mode. The eval-after-load is not a good method since it makes change to my-minor-mode-keymap in long term.

I put another example here:

I use program mode for most of the time. Suppose I want to have C-q bind to syntax-check in program mode. However, I jot notes in text mode sometime. It makes no sense to have syntax check in text mode. So I decide to have C-q bind to spell-check.

Under normal circumstances, I would do this

(global-set-key (kbd "C-q") 'syntax-check)
(define-key text-mode-map (kbd "C-q") 'spell-check)

How to have such effect if I define my-keys-minor-mode-map? Assume I put this in my keymap:

(define-key 'my-keys-minor-mode-map (kbd "C-q") 'syntax-check)

(define-key text-mode-map (kbd "C-q") 'spell-check) doesn't work since my keymap always has precedence over other minor mode mapping.

Neither

(eval-after-load "text-mode"
  (define-key my-keys-minor-mode-map (kbd "C-q") 'spell-check))

nor

(add-hook 'text-mode-map (lambda () 
  (define-key my-keys-minor-mode-map (kbd "C-q") 'spell-check)))

do a good job because they change the my-keys-minor-mode-map in long term. That means when I switch back from text mode to program mode, I lose syntax check function.

EDIT :

For example,

(define-key my-keys-minor-mode-map (kbd "<return>") 'newline-and-indent)

this works 99% of time and is good to prevent any other minor modes or major modes to modify this.

However, the key map has problem in read only mode. So I need to have "super priority" function to remap this key. I want something like

(defun super-priority-map()
  (define-key super-my-keys-minor-mode-map (kbd "<return>") 'something-else))
(add-hook 'some-modes 'super-priority-map)

And more important, the super-priority-map should take off as long as that specified mode is left.

回答1:

You're talking about read-only functionality? You can't override that with a keymap. There's no "read-only" keymap capturing all keystrokes and issuing errors.

To override read-only-mode, just toggle off read-only-mode!

In code, set the buffer-read-only variable.

You can also let-bind the inhibit-read-only variable in code to ignore all read-only mechanisms (including properties on specific regions of text -- see levels 7 and 3 in the list below).


Read-only aside, it is entirely possible for other keymaps to have precedence over your minor mode map, but I'm inclined to say that for any rare conflicts with your minor mode keymap, you should directly modify the keymap which is conflicting with your own.

Your minor mode keymap can in fact be superseded by many others, but you probably don't want to be working at a higher level of priority in general.

I recommend reading Mastering Key Bindings in Emacs which is a very readable explanation of how this stuff works, and from which I've nabbed the following keymap priority list:

  1. overriding-terminal-local-map for terminal-specific key binds.
  2. overriding-local-map for keys that should override all other local keymaps. Be VERY careful if you use this!
  3. Keymap char property at point for keymaps that are local to the character point is at. This is used for stuff like fields in yasnippet and the customize dialog.
  4. emulation-mode-map-alists for advanced multi-mode keymap management
  5. minor-mode-overriding-map-alist for overriding the keymaps used by minor modes in major modes.
  6. minor-mode-map-alist is exactly like the overriding version above, but the preferred means of specifying the keymaps for minor modes.
  7. Keymap text property at point is like the one above for char properties but is for text properties only.
  8. current-local-map for keymaps defined in the buffers’ current local map
  9. current-global-map is the last place Emacs will look for key binds and it is for the global ones.

Note that your minor mode is working at level 6, and everything above that could take precedence.

A typical pattern to unbind a conflict would be:

(eval-after-load "NAME-OF-LIBRARY"
  '(define-key NAME-OF-KEYMAP (kbd "KEY") nil)) ;; unbind KEY in KEYMAP implemented by LIBRARY

Most likely, you would also add a replacement (non-conflicting) binding for the original command.



回答2:

buffer-locally-overriding-minor-mode-key-bindings-in-emacs

Combining everything togather

(defvar my-keys-minor-mode-map (make-keymap) "my-keys-minor-mode keymap.")

(define-minor-mode my-keys-minor-mode
  "A minor mode so that my key settings override annoying major modes."
  t " my-keys" 'my-keys-minor-mode-map)

(defadvice load (after give-my-keybindings-priority)
  "Try to ensure that my keybindings always have priority."
  (unless (eq (caar minor-mode-map-alist) 'my-keys-minor-mode)
    (let ((mykeys (assq 'my-keys-minor-mode minor-mode-map-alist)))
      (assq-delete-all 'my-keys-minor-mode minor-mode-map-alist)
          (add-to-list 'minor-mode-map-alist mykeys))))
    (ad-activate 'load)

(defun my-keys-minor-mode-override (key def)
  "Overrides a minor mode keybinding for the local
   buffer, by creating or altering keymaps stored in buffer-local
   `minor-mode-overriding-map-alist'."
    (let* ((oldmap (cdr (assoc 'my-keys-minor-mode minor-mode-map-alist)))
           (newmap (or (cdr (assoc 'my-keys-minor-mode minor-mode-overriding-map-alist))
                     (let ((map (make-sparse-keymap)))
                       (set-keymap-parent map oldmap)
                       (push `(my-keys-minor-mode . ,map) minor-mode-overriding-map-alist) 
                       map))))
  ;; (make-local-variable 'minor-mode-overriding-map-alist)
  (define-key newmap key def)))

(my-keys-minor-mode 1)

What this can do is:

(define-key my-keys-minor-mode-map (kbd "C-i") 'backward-char)

to define a high precedence keybind over other minor mode.

(add-hook 'text-mode-hook (lambda ()
    (my-keys-minor-mode-override (kbd "C-i") 'forward-char)))

to override the high precedence keybind temporarily.

Sweet