17

Is it possible to set key bindings specific to buffer?

I have the following in one of my org files:

# Local Variables:
# eval: (local-set-key (kbd "<f10>") 'some-custom-defun-specific-to-this-buffer)
# End:

But this binding persists for other org-mode files too. If I open a file with a different major mode, then my default key binding kicks in.

It looks like local-set-key is setting bindings specific to the major-mode not specific to the buffer.

How can I have bindings that activate only in a particular buffer?

Drew
  • 75,699
  • 9
  • 109
  • 225
Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179
  • 2
    Oh wow, you're right: "The binding goes in the current buffer's local map, which in most cases is shared with all other buffers in the same major mode." I never knew that. – shosti Sep 30 '14 at 18:44
  • Now that I think about it, `local-unset-key` hooked with `org-mode-hook` might work if that is executed before the `Local Variables:` section is. – Kaushal Modi Sep 30 '14 at 18:48
  • Or you could make a minor mode with a semi sparse keymap and toggle it using `Local Variables:` – Vamsi Sep 30 '14 at 18:54
  • @Vamsi Thanks! This is an elegant solution! I have updated your solution. – Kaushal Modi Sep 30 '14 at 19:35
  • 1
    http://stackoverflow.com/questions/21486934/file-specific-key-binding-in-emacs – phils Oct 01 '14 at 05:08

2 Answers2

18

For closure, here is an answer that expands upon my comment. I define a minor mode with the required keybinding and toggle it in the required buffer using file local variables.

  (define-minor-mode my-org-buffer-local-mode
    "Minor mode to simulate buffer local keybindings."
    :init-value nil)
  (define-key my-org-buffer-local-mode-map (kbd "<f10>") 'some-custom-defun-specific-to-this-buffer)

You can toggle it in necessary files by setting the local variables section at the end of the file as follows. You can also change or add the keybinding here. You will still need to define different minor modes for each buffer that maps the same key-sequence to different commands.

 # Local Variables:
 # my-org-buffer-local-mode: t
 # eval:(define-key my-org-buffer-local-mode-map (kbd "<f10>") 'some-other-custom-defun-specific-to-this-buffer)
 # End:
Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179
Vamsi
  • 3,916
  • 22
  • 35
  • @kaushalmodi: You might want to spin off your edits into a separate answer since it is more complete. Also, you would not be able to map the same key-sequence to two different functions simultaneously unless you use a buffer specific name. – Vamsi Sep 30 '14 at 19:41
  • I get this error: `eval-buffer: Symbol's value as variable is void: my-org-buffer-local-mode-map` when I try to run this `(define-minor-mode my-org-buffer-local-mode "Minor mode to simulate buffer local keybindings." :init-value nil) (define-key my-org-buffer-local-mode-map (kbd "") 'some-custom-defun-specific-to-this-buffer) ` – incandescentman Jul 30 '15 at 01:39
  • I also get the same error. any idea? – zeltak Aug 01 '17 at 10:02
  • @incandescentman call (make-keymap) with `:keymap` keyword, check https://nullprogram.com/blog/2013/02/06/ – nichijou Dec 30 '18 at 09:47
6

Here is a generic way to set buffer-specific key bindings.

  • Create a temporary minor mode (save the below snippet to temp-mode.el and require it in your init.el.
  • Enable that temp-mode minor mode and define that minor-mode's keymap only in the buffer(s) you need.

Put the below Local Variables snippet in the buffer where you want the custom key binding. Below are examples of org-mode files.

In buffer ONE

# Local Variables:
# eval: (temp-mode 1)
# eval: (define-key temp-mode-map (kbd "<f10>") 'function-ONE)
# End:

If some other buffer redefines the same key-binding using,

In buffer TWO

# Local Variables:
# eval: (temp-mode 1)
# eval: (define-key temp-mode-map (kbd "<f10>") 'function-TWO)
# End:

then the new binding become effective on doing M-x revert-buffer.

This is what I would do if I need to switch between these two buffers very frequently and if I have to use the F10 binding in both:

  • Work in buffer ONE, C-x C-s (save) and switch to buffer TWO
  • revert-buffer (refresh bindings), work in buffer TWO, C-x C-s and switch to buffer ONE
  • revert-buffer (refresh bindings), work in buffer ONE, C-x C-s and switch to buffer TWO

But I would rather bind the different buffer-specific commands to different keys.


Temporary minor mode

;; temp-mode.el
;; Temporary minor mode
;; Main use is to enable it only in specific buffers to achieve the goal of
;; buffer-specific keymaps

(defvar temp-mode-map (make-sparse-keymap)
  "Keymap while temp-mode is active.")

;;;###autoload
(define-minor-mode temp-mode
  "A temporary minor mode to be activated only specific to a buffer."
  nil
  :lighter " Temp"
  temp-mode-map)

(provide 'temp-mode)
Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179