File-specific key-binding in emacs - emacs

Is it possible to define file specific key-bindings in emacs?
I suppose it would be possible to create a minor mode and have it loaded when the particular file is open but for only one key-binding that seems overkill.

If you combine the code to local-set-key and Buffer-locally overriding minor-mode key bindings in Emacs then you could end up with something like this:
(defun my-buffer-local-set-key (key command)
(interactive "KSet key buffer-locally: \nCSet key %s buffer-locally to command: ")
(let ((oldmap (current-local-map))
(newmap (make-sparse-keymap)))
(when oldmap
(set-keymap-parent newmap oldmap))
(define-key newmap key command)
(use-local-map newmap)))
and then, as per Barmar's answer:
;; Local Variables:
;; eval: (my-buffer-local-set-key (kbd "C-c C-c") 'foo)
;; End:
Note that minor mode maps take precedence over the local map.

Use eval: in the File Local Variables section:
;;; Local Variables:
;;; eval: (local-set-key ...)

It smells like you are doing things wrong --- that's my guess. If you have a particular file buffer for which a given key binding is appropriate, then define a mode especially for it and bind the key in that mode's keymap. Let the mode inherit from any other mode you like.
For example:
(define-derived-mode my-file-mode org-mode "My file mode")
(define-key 'my-file-mode-map (kbd "SPC") #'org-toggle-checkbox)
You don't really describe anything about your context: how do you access this file (C-x C-f something else)?, why only this file -- what is special about it? what is the key used for? So it is hard to give you any helpful advice.
If you really want to have some key act differently for this particular file, then maybe define a command that visits the file (however you want to visit it) and then creates an overlay over all of its text, and uses the overlay property keymap to add your binding everywhere. This of course sounds pretty silly, but as it stands now, so does your question.
Emacs works with buffers. Buffers are in modes. A file has little meaning in this context. Once the file is visited, its buffer is what you want to work with.
That's why #Barmar tried to answer in terms of a buffer and its mode. You apparently don't want this to be for a given mode, unless, I guess, the mode is specific to that one file. In that case, define a mode that applies (only) to that file.
Clarify your question and perhaps we will be able to help you more.
(Sounds like this might be an XY problem.)

Setting up a minor mode and loading it automatically when opening the specific file is actually simpler than I thought.
The mode file is something along these lines:
(define-minor-mode magic-mode
"Provide functions to do magic."
:lighter " !!!"
:keymap (let ((map (make-sparse-keymap)))
(define-key map (kbd "M-z") 'xyzzy)
map)
)
(defun xyzzy()
"Use at your own risk"
(message "Nothing happens.")
)
(provide 'magic-mode)
It has to be put somewhere the .emacs will look into and the following line is to be added to the .emacs:
(require magic-mode)
Finally, the followingblock should be added at the end of the file that should use the specific commands:
;; Local Variables:
;; eval: (magic-mode)
;; End:

Related

Binding similar commands from different modes to the same key

I use emacs in multiple modes (ESS, Auctex, Slime, elisp, etc...) all using evil-mode key-bindings. Each of the interaction modes have similar functions for evaluating regions, lines or buffers that I have bound to shortcuts using spacebar as a prefix.
;; bind slime's eval and elisp eval to the key sequence "<SPC>e"
(evil-define-key 'normal lisp-mode-map (kbd "<SPC>e") 'slime-eval-last-expression)
(evil-define-key 'normal lisp-interaction-mode-map (kbd "<SPC>e") 'eval-last-sexp)
I would like to set a default key for a "type" of function, so that I don't need to have an entry like the above for every interaction mode I use and for every command. This would hopefully give a more readable .emacs init file and make it easier to change my key-bindings in the future.
I'm fairly sure that I could do this myself using a series of hooks, but I wonder if there is any existing or built-in support for this?
Thanks
tensorproduct
I don't know anything about Evil, so I'll give the normal Emacs solution:
(global-set-key [?\s ?e] #'my-eval-last-sexp)
(defvar my-eval-last-sexp-command #'undefined)
(defun my-eval-last-sexp ()
(interactive)
(call-interactively my-eval-last-sexp-command))
(add-hook 'emacs-lisp-mode-hook
(lambda () (set (make-local-variable 'my-eval-last-sexp-command) #'eval-last-sexp))
(add-hook 'lisp-mode-hook
(lambda () (set (make-local-variable 'my-eval-last-sexp-command) #'slime-eval-last-expression))
...
As you can see, there's only one mention of the key you want (in this case [?\s ?e]). But you don't save much on the amount of code you have to write. You might improve it by making my-eval-last-sexp a bit more complex (e.g. it could try to guess the command name from the major mode name), or by replacing the hook function with a global alist.
Hopefully, in some future Emacs, all such source-code modes that interact with some interpreter/compiler will share more of their code so that your problem will simply disappear.

emacs, python-mode.el define-key map assignment

When I load the python-mode.el file in emacs, I don't get any of the key bindings specified.
I started playing with the file and noticed that when I change:
(define-key map [(control c)(\#)] 'py-comment-region)
to:
(define-key global-map [(control c)(\#)] 'py-comment-region)
it works fine.
I went to look where the define the map variable and saw:
(defvar py-shell-map nil
"Keymap used in *Python* shell buffers.")
;; used by py-completion-at-point, the way of python.el
(defvar python-shell-map
(let ((map (copy-keymap comint-mode-map)))
(define-key map [tab] 'py-shell-complete)
(define-key map "\C-c-" 'py-up-exception)
(define-key map "\C-c=" 'py-down-exception)
map)
"Keymap used in *Python* shell buffers.")
Is the 'map' variable defined alright? Should I do some changes to my init file? I assume that this file works to everybody else, so why I need to change the 'map' variable to 'global-map' for it to work in my computer?
I'm running in a virtual machine if that's of any help.
The code you quoted in which you believe map is defined is not actually the relevant portion of the code. It is a different keymap used for a python shell, and it's not the one used when you edit a python file in Emacs.
The line you're editing appears in python-mode inside the following code block:
(defvar python-mode-map)
(setq python-mode-map
(let ((map (make-sparse-keymap)))
;; electric keys
(define-key map [(:)] 'py-electric-colon)
(define-key map [(\#)] 'py-electric-comment)
...
As you can see the variable map is first initialized as a "sparse keymap", then certain key-bindings get defined in that map, and finally the map is set as the value of python-mode-map. The latter is the keymap used in a buffer that is in python-mode.
So the keybindings should work - but of course only in a buffer that is in python-mode. To activate python-mode in a buffer, type M-x python-mode. This works only after the file python-mode.el has been loaded.
You can check if your current buffer is in python-mode in two ways:
your mode line should display the letters "Py"
type M-: ENTER major-mode ENTER -> this should print "python-mode" to the minibuffer
Each major mode and some minor modes have their own keymap, which is overlaid on the global keymap (which is global-map). When you press a key, Emacs tries to find a binding for that key in the overlaid keymaps, falling back to "more global" ones until it gets to the global-map. This is why global-map works and map doesn't.
In lisp, let is used to bind local variables. The map variable doesn't exist outside of the let (or maybe it does, but it's probably not the one you want). Read the documentation for let and defvar (e.g. C-h f defvar).
You need to figure out which keymap is being used in the major mode, and use define-key on that. In this case, (define-key python-mode-map (kbd "C-c #") 'py-comment-region) will probably work. N.B. I do not use python-mode.el, but looking at the source it seems like it uses python-mode-map as the keymap variable. The other keymaps are for auxiliary buffers.

Set custom keybinding for specific Emacs mode

Though I know how to set a global key-binding in Emacs, I find it hard to even Google out the code for a local (minor-mode specific) key-binding. For instance, I have this code in my .emacs:
;; PDFLaTeX from AucTeX
(global-set-key (kbd "C-c M-p")
(lambda ()
(interactive)
(shell-command (concat "pdflatex " buffer-file-name))))
I don't want to set it globally. Is there a function like local-set-key?
I use the following:
(add-hook 'LaTeX-mode-hook
(lambda () (local-set-key (kbd "C-0") #'run-latexmk)))
to have a bind defined for LaTeX mode alone.
To bind a key in a mode, you need to wait for the mode to be loaded before defining the key. One could require the mode, or use eval-after-load
(eval-after-load 'latex
'(define-key LaTeX-mode-map [(tab)] 'outline-cycle))
Don't forget either '—eval-after-load is not a macro, so it needs them.
You need to identify the key map for that mode (for example, LaTeX-mode-map) and use the function define-key. As an example, along with activating outline-minor-mode within LaTeX mode, I have:
(define-key LaTeX-mode-map [(tab)] 'outline-cycle))
In this case the major mode (LaTeX) holds the key binding, but there is also an outline-minor-mode-map.
None of the other answers satisfied my needs. So this may help other people. I wanted Tab to jump to the beginning of the line if I'm in Evil's normal mode (basically this means everywhere in Emacs), but I instead wanted it to cycle between org item states if I am in an org-mode document.
One option was to mess around with separate bindings and constant binding-rebinding whenever I switched buffers (because evil allows only one binding per key in its normal state).
But a more efficient option was to make Tab run my own code which runs the required function based on which major mode the current buffer uses. So if I am in a org buffer, this code runs org-cycle, and otherwise it runs evil-first-non-blank (go to the first non-whitespace character on the line).
The technique I used here can also be used by calling your custom function via global-set-key instead, for people who use regular non-evil Emacs.
For those who don't know Emacs lisp, the first line after the "if" statement is the true-action, and the line after that is the false-action. So if major-mode equals org-mode, we run org-cycle, otherwise we run evil-first-non-blank in all other modes:
(defun my/tab-jump-or-org-cycle ()
"jumps to beginning of line in all modes except org mode, where it cycles"
(interactive)
(if (equal major-mode 'org-mode)
(org-cycle)
(evil-first-non-blank))
)
(define-key evil-normal-state-map (kbd "<tab>") 'my/tab-jump-or-org-cycle)

Assign multiple Emacs keybindings to a single command?

I'm giving ErgoEmacs mode a try to see if I can use Emacs more comfortably. Some of its keybindings are fairly intuitive, but in many cases I don't want to outright replace the defaults.
For example, in the context of ErgoEmacs' navigation shortcut structure, M-h makes sense as a replacement for C-a--but I want to be able to use both, not just M-h. I tried simply duplicating the commands:
;; Move to beginning/ending of line
(defconst ergoemacs-move-beginning-of-line-key (kbd "C-a")) ; original
(defconst ergoemacs-move-end-of-line-key (kbd "C-e")) ; original
(defconst ergoemacs-move-beginning-of-line-key (kbd "M-h")) ; ergoemacs
(defconst ergoemacs-move-end-of-line-key (kbd "M-H")) ; ergoemacs
But Emacs simply overwrites the first keybinding with the second. What's the best way to address this?
To re-post reply from ergo-emacs mailing list:
Xah Lee said:
that's very easy.
in the
ergoemacs-mode.el file, there's this
line (load "ergoemacs-unbind") just
comment it out. That should be all
you need to do. However, note that
ErgoEmacs keybinding defines those
common shortcuts such as Open, Close,
New, Save... with keys Ctrl+o,
Ctrl+w, Ctrl+n, Ctrl+s etc. About 7 of
them or so. So, i think some of these
will hit on emacs traditional
bindings with Ctrl. if you are new to
ErgoEmacs and trying to explore it,
you might just try starting with few
keys. this page might have some
useful info:
http://code.google.com/p/ergoemacs/wiki/adoption
thanks for checking out ErgoEmacs!
Xah ∑ http://xahlee.org/
As it turns out, ErgoEmacs uses two files to define the keybinding. One is the main ergoemacs-mode.el file, and the other is the specific keyboard layout you select (e.g. ergoemacs-layout-us.el). The latter document creates a constant, which the former uses to create the keybinding. So while I thought I was duplicating the keybinding, I was actually changing the constant which was subsequently used for that purpose.
Solution:
In ergomacs-mode.el:
;; Move to beginning/ending of line
(define-key ergoemacs-keymap ergoemacs-move-beginning-of-line-key 'move-beginning-of-line)
(define-key ergoemacs-keymap ergoemacs-move-end-of-line-key 'move-end-of-line)
(define-key ergoemacs-keymap ergoemacs-move-beginning-of-line-key2 'move-beginning-of-line) ; new
(define-key ergoemacs-keymap ergoemacs-move-end-of-line-key2 'move-end-of-line) ; new
In ergoemacs-layout-us.el:
;; Move to beginning/ending of line
(defconst ergoemacs-move-beginning-of-line-key (kbd "M-h"))
(defconst ergoemacs-move-end-of-line-key (kbd "M-H"))
(defconst ergoemacs-move-beginning-of-line-key2 (kbd "C-a")) ; new
(defconst ergoemacs-move-end-of-line-key2 (kbd "C-e")) ; new
Huh? Is having one and only one way for every function some golden principle of ErgoEmacs? Because normal keybinding works exactly the opposite way: you name one key at a time and specify what it should do. If a mode defines a global variable to mean "the key that end-of-line is bound to", then of course there can be only one value, but with the normal binding commands you can bind the same function to as many combinations as you like. In fact, every keybinding I have ever seen used looked either like this
(global-set-key [(meta space)] 'just-one-space)
or like this
(add-hook 'c-mode-hook 'my-c-mode-hook)
(defun my-c-mode-hook ()
(define-key c-mode-map [(control c) b] 'c-insert-block))
if it's only for a specific mode.

Globally override key binding in Emacs

How can I set a key binding that globally overrides and takes precedence over all other bindings for that key? I want to override all major/minor mode maps and make sure my binding is always in effect.
This of course doesn't work:
(global-set-key "\C-i" 'some-function)
It works in text-mode, but when I use lisp-mode, C-i is rebound to lisp-indent-line.
I can go through and override this binding in lisp-mode and in every other mode individually, but there must be an easier way. Every time I install a new mode for a new file type, I'd have to go back and check to make sure that all of my key bindings aren't being overridden by the new mode.
I want to do this because I want to emulate bindings I've already learned and ingrained from other editors.
I use a minor mode for all my "override" key bindings:
(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."
:init-value t
:lighter " my-keys")
(my-keys-minor-mode 1)
This has the added benefit of being able to turn off all my modifications in one fell swoop (just disable the minor mode) in case someone else is driving the keyboard or if I need to see what a default key binding does.
Note that you may need to turn this off in the minibuffer:
(defun my-minibuffer-setup-hook ()
(my-keys-minor-mode 0))
(add-hook 'minibuffer-setup-hook 'my-minibuffer-setup-hook)
As an addition to scottfrazer's answer, I've written the following so that my keybindings retain precedence, even if subsequently-loaded libraries bring in new keymaps of their own.
Because keymaps can be generated at compile time, load seemed like the best place to do this.
(add-hook 'after-load-functions 'my-keys-have-priority)
(defun my-keys-have-priority (_file)
"Try to ensure that my keybindings retain priority over other minor modes.
Called via the `after-load-functions' special hook."
(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))))
Install use-package, eval and you're done:
(require 'bind-key)
(bind-key* "C-i" 'some-function)
I found this question while searching for "emacs undefine org mode keybindings", because I wanted to unbind the existing C-c C-b behavior to allow my global map to bury-buffer to work in an org buffer.
This ended up being the simplest solution for me:
(add-hook 'org-mode-hook
(lambda ()
(local-unset-key (kbd "C-c C-b"))))
Although scottfrazer's answer is exactly what you asked for, I will mention for posterity another solution.
From The Emacs Manual:
"Don't define C-c letter as a key in Lisp programs. Sequences consisting of C-c and a letter (either upper or lower case) are reserved for users; they are the only sequences reserved for users, so do not block them."
If you bind your personal global bindings to C-c plus a letter, then you "should" be safe. However, this is merely a convention, and any mode is still able to override your bindings.
If you want to "always use the keybinds in the map, unless I explicitly override them for a specific mode-map", and assuming you are using scottfrazier's approach, you want:
(defun locally-override (key cmd)
(unless (local-variable-p 'my-keys-minor-mode-map)
(set (make-variable-buffer-local 'my-keys-minor-mode-map)
(make-sparse-keymap))
(set-keymap-parent my-keys-minor-mode-map
(default-value 'my-keys-minor-mode-map)))
(define-key my-keys-minor-mode-map key cmd))
So
(locally-override "\C-i" nil)
should remove the "\C-i" binding from the minor mode in the current buffer only. Warning: this is completely untested, but seems like the right approach. The point of setting the parent rather than just coping the global value of my-keys-minor-mode-map is so any later changes to the global value are automatically reflected in the local value.
I don't think you can. That is roughly equivalent to saying that you want to define a global variable that cannot be hidden by local variable declarations in functions. Scope just doesn't work that way.
However, there might be a way to write an elisp function to go through the mode list and reassign it in every single one for you.
Unless you really want to do this yourself, you should check around and see if anyone else already has done it.
There is a package for Emacs which gives your windows-like keybindings. You should be able to find it through google.