onsdag, juni 18, 2008

Local things in Emacs

This is just a small note, since this have bugged me for a while. Basically, I have lots of extra key bindings running around in my Emacs configuration. Now, I use local-set-key for many of these. The problem is I hadn't actually read the documentation for local-set-key enough.

One example that annoyed me was this: I had some local key bindings for RSpec buffers, that differed from the regular Ruby buffers. My RSpec minor mode still uses the ruby-mode-map though. My assumption was that local-set-key did things exactly as all other things with "local" in their name, namely doing a buffer local modification only. I finally found out that this wasn't the case. Instead, when the RSpec minor mode was loaded for the first time, it ended up modifying the ruby-mode-map with its key bindings, which were then visible for all other Ruby buffers. Ouch.

So, if you use local-set-key, make sure you actually want to set that key in the current mode map, instead of only for the current buffer.

As far as I know, there is no way to set a real buffer local key binding without some acrobatics that unsets and resets the keys manually. I ended up solving my problem with the RSpec minor mode to having it clone the Ruby mode map and have its own mode map. Not an ideal solution, but it works for now.

2 kommentarer:

piyo sa...

Yes, defining key bindings in special modes or with a combination of modes is pretty hairy. I read up on (Info-goto-node "(elisp)Searching Keymaps") and though complex, there are a lot of ways to squeeze your bindings in.

For example, I wanted to redefine "h" in a *Help* buffer (help-mode, major) to help-go-back to alleviate my vi-based web-surfing habits, but View-mode (a minor mode) was active and got in the way. I perused the source elisp and found help-xref-override-view-map was used to override view-mode's bindings. Sweet:

(defkeymap help-xref-override-view-map nil
"h" help-go-back ; overwrites describe-mode
"l" help-follow-symbol
)

Aside, I have been using Emacs for years and I didn't know the *Help* buffer had a history until this year.

Unknown sa...

Probably the way to make a keymap buffer-local is via a minor-mode construct:

(defvar vi-help-map
(let ((m (make-keymap)))
(define-key m "h" 'help-go-back)
m))

(defvar vi-help-minor-mode nil
"Minor mode to override some keys in help-mode")

Make the minor-mode buffer-local:

(make-variable-buffer-local 'vi-help-minor-mode)

If you want to trigger the keymap everywhere where the minor mode is in effect, do:

(push (cons 'vi-help-minor-mode vi-help-map)
minor-mode-map-alist)

To make the keymap even "more buffer-local" you could instead use `minor-mode-overriding-map-alist', which is like `minor-mode-map-alist', only buffer-local:

(push (cons 'vi-help-minor-mode vi-help-map)
minor-mode-overriding-map-alist)

To turn it on:

(setq vi-help-minor-mode t)

...it might of course be that using the macro `define-minor-mode' is a better way to achieve the same functionality in a simpler and cleaner fashion.