I'm in the early stages of creating a major mode for Emacs for browsing and interacting with the Stack Exchange Network.
Involved in it are several major modes, all with one-key keybindings similar to dired. I looked at the source for dired, and extracted what I thought would work:
(defvar stack-network-mode-map
(let ((map (make-keymap)))
(define-key map "n" 'stack-network-next-site)
(define-key map "p" 'stack-network-previous-site)
(define-key map "," 'stack-network-move-site-up)
(define-key map "." 'stack-network-move-site-down)
(define-key map "j" 'stack-network-jump-to-bookmarks)
(define-key map "\C-m" 'stack-network-do-enter-site) ; ret
(define-key map "o" 'stack-network-do-enter-site)
(define-key map "u" 'stack-network-do-profile-summary)
(define-key map "\C-uu" 'stack-network-do-profile-summary-for-user)
(define-key map "i" 'stack-network-do-inbox)
(define-key map "b" 'stack-network-toggle-bookmark)
(define-key map "?" 'stack-network-list-functions) ; [1]
(define-key map "\C-i" 'stack-network-display-details) ; tab
map)
"Keymap for Stack Exchange: Network Browser major mode")
but unfortunately this seems to have absolutely no effect; the buffer is simply edited just as any other normal buffer would be. How can I achieve single-key keybindings if this isn't they way? (Which, by the way, I'm sure it is. There has to be something else going on here.)
You want to define stack-network-mode using define-derived-mode (and make it derive from special-mode, for example).
Other comments about your code:
use () rather than nil for empty argument lists.
stack-network-next-site needs to have (interactive) right after the docstring to make it an interactive command otherwise it won't work as a key-binding.
If you don't want to use special-mode, you can call supress-keymap right after creating your make-keymap.
Move the definition of stack-network-mode-map before the definition of the mode. Otherwise define-derived-mode implicitly defines this variable, and defvar does not change the value of non-nil variables, so the map will be empty actually.
See Derived Modes:
The new mode has its own sparse keymap, named variant-map. define-derived-mode makes the parent mode's keymap the parent of the new map, unless variant-map is already set and already has a parent.
You have defined a keymap but you have not used it. The variable exists but does not capture any key events.
(use-local-map stack-network-mode-map)
Related
Sometimes I need to redefined the whole map of a mode. That means that I am not interested in the default bindings, it is undesired to accidentally use some default keybinding when no remapping was assigned.
For example, I define dired-mode-map as
(evil-define-key 'normal dired-mode-map
....
How can I clear all the default keybindings before mapping my own?
This seems to work:
(setcdr dired-mode-map (cdr (make-keymap)))
(set-keymap-parent dired-mode-map special-mode-map)
That is, it creates a new empty keymap and replaces the contents of dired-mode-map with that. It happens to work because a keymap is a list whose car is just the symbol keymap, so the cdr is all that needs to be changed.
Is there a way to advise a keymap or otherwise run a function after a keymap prefix, but before commands in the keymap?
Say I have a keymap with bindings for hideshow, but these bindings are only useful after hs-minor-mode is activated. How can I run (hs-minor-mode) after the prefix is entered, but before the functions in the map are called?
I thought making a prefix command and advising it might work, but that is an error (below).
Example:
(let ((map (define-prefix-command 'my-activate-fold 'my-fold-map)))
(define-key map "a" #'hs-hide-all)
(define-key map "s" #'hs-show-all)
(define-key map "l" #'hs-hide-level)
(global-set-key (kbd "<f6>") 'my-activate-fold))
;; error: wrong-type-argument commandp my-activate-fold
(define-advice my-activate-fold (:before (fn &rest r) "activate-hideshow")
(hs-minor-mode)
(apply fn r))
If I understand correctly, I think you may be approaching this incorrectly. For your specific question, no, there is no way to define a function which will run after a prefix key is called but before the command (an interactive function) which is bound to the key is run. However, I'm not sure that is really what your after. You can of course define commands which can wrap another command and do whatever you want. However, I'm not sure that is what you want either. You might want to state exactly what you want rather than part of what you believe is the solution to what you want.
Normal practice is for a minor mode to define a keymap and you add your mode specific key bindings to that map in a mode initialisation hook. In the case of hs-minor-mode, that is called hs-minor-mode-map. This map only exists inside buffers running hs-minro-mode and it takes precedence over the global map. So this is where you want to place your mode specific bindings. To do this, create a function which adds the bindings to the map and attach that to the hs-minor-mode-hook, which is run when hs-minor-mode is loaded.
(add-hook 'hs-minor-mode-hook (lambda ()
(define-key 'hs-minor-mode-map "a" #'hs-hide-all)
...))
The key bindings defined above will only exist if hs-minor-mode is active in the buffer. If it is not active, the keys used in the binding will either be bound to whatever the next highest map in the mode is or the global map or nothing (see the section on keymaps in the elsip manual for full details).
If what you want is to have specific hs-minor-mode bindings only exist in specific modes, then you can define those bindings in the keymap for that mode. For example, if you wanted hs-minor-mode bindings that only exist when you run hs-minor-mode in js2-mode, but not when you run hs-minor-mode in c-mode, then you can add the bindings to the js2-mode-map and load hs-minor-mode as part of the js2-mode-hook
If it is something else you are after, you need to clarify.
FWIW, you could simply advise the commands themselves:
(defun my-ensure-hs-minor-mode (&rest _args)
"Ensure `hs-minor-mode' is active."
(unless (bound-and-true-p hs-minor-mode)
(hs-minor-mode 1)))
(advice-add 'hs-hide-all :before #'my-ensure-hs-minor-mode)
(advice-add 'hs-show-all :before #'my-ensure-hs-minor-mode)
(advice-add 'hs-hide-level :before #'my-ensure-hs-minor-mode)
Your keymap then just works:
(require 'hideshow)
(let ((map (define-prefix-command 'my-activate-fold 'my-fold-map)))
(define-key map "a" #'hs-hide-all)
(define-key map "s" #'hs-show-all)
(define-key map "l" #'hs-hide-level))
(global-set-key (kbd "<f6>") 'my-activate-fold)
I'm trying to redefine the keys used to navigate the history when inside several commands accepting regexps and offering C-p / C-n history navigation. I'd like to use other keys, in addition to C-p / C-n. For example when using occur or replace-regexp, C-p and C-n can be used to go to previous and next elements.
I've tried several things but can't make it work. I think I'm missing the "big picture" here.
Which mode-map do I need to modify, when and how? Everything I tried failed.
P.S: Note that I've got my own minor mode with all my keymaps as adviced here.
I'm assuming you just needed minibuffer-local-map. Subsequent definitions using keys previously assigned to that key map will trump the prior definitions. To disable a prior key assignment, then just create a new definition and set the last portion to nil instead of 'function-name.
(define-key minibuffer-local-map (kbd "<f6>") 'help-for-help)
Here is an excerpt from Emacs Trunk .../lisp/bindings.el:
(let ((map minibuffer-local-map))
(define-key map "\en" 'next-history-element)
(define-key map [next] 'next-history-element)
(define-key map [down] 'next-history-element)
(define-key map [XF86Forward] 'next-history-element)
(define-key map "\ep" 'previous-history-element)
(define-key map [prior] 'previous-history-element)
(define-key map [up] 'previous-history-element)
(define-key map [XF86Back] 'previous-history-element)
(define-key map "\es" 'next-matching-history-element)
(define-key map "\er" 'previous-matching-history-element)
;; Override the global binding (which calls indent-relative via
;; indent-for-tab-command). The alignment that indent-relative tries to
;; do doesn't make much sense here since the prompt messes it up.
(define-key map "\t" 'self-insert-command)
(define-key map [C-tab] 'file-cache-minibuffer-complete))
To add to what #lawlist said (which was to bind the key in minibuffer-local-map):
There are multiple minibuffer keymaps, depending on what is being read in the minibuffer, and how. And which of those keymaps you might want to use can depend on which Emacs version you are using.
In addition, there is also the keymap for interaction with buffer *Completions*: completion-list-mode-map.
For completion in the minibuffer, the main keymap is minibuffer-local-completion-map.
Here is a list of the minibuffer keymaps. Some of these might not be available (used) in your Emacs version.
minibuffer-local-map
minibuffer-local-ns-map
minibuffer-local-isearch-map
minibuffer-local-completion-map
minibuffer-local-must-match-map
minibuffer-local-filename-completion-map
minibuffer-local-filename-must-match-map
minibuffer-local-must-match-filename-map
In addition, you can use minibuffer-with-setup-hook (or minibuffer-setup-hook directly) to add key bindings on the fly, for the duration of a single minibuffer reading.
I will add this info, since it can be really helpful when you are manipulating minibuffer keymaps: You can use C-h M-k (command describe-keymap), from library help-fns+.el, to see all of the bindings of a given minibuffer keymap in human-readable form.
Q: in Emacs, how does one make mode-specific key bindings for text objects in evil?
One may bind a key in a specific state (normal, insert, etc.) in a specific mode as the following example demonstrates:
(evil-define-key 'normal org-mode "a" 'some-command)
However, it's not clear to me how to bind a key in a mode-specific way to evil-outer-text-objects-map (or its -inner- counterpart). As an alternative, it's also not clear how one might bind keys in these maps in a buffer-local way through a mode hook.
It doesn't look like evil-local-set-key will do it, because it expects a state (normal, insert, etc.) as its first argument, and that's not relevant to this task.
It's also not clear how to use local-set-key in this instance, because it expects a key and a command as arguments, but does not take a map as an argument.
This is kinda late but for the first part of your question you can use local maps like:
(defun my-elisp-mode-configuration ()
(with-eval-after-load 'evil
(define-key evil-visual-state-local-map "ie" 'sp-evil-i-sexp)
(define-key evil-operator-state-local-map "ie" 'sp-evil-i-sexp)))
(add-hook 'emacs-lisp-mode-hook #'my-elisp-mode-configuration)
In this example I bind 'inner' e operator to a custom sp-evil-i-sexp text object for elisp mode only.
Coming to your second question; evil overrides local maps so using local-set-key won't suffice. Instead you can use:
local evil maps with hooks; like evil-normal-state-local-map as in the previous example
use evil-define-key; ie: (evil-define-key 'normal emacs-lisp-mode-map (kbd " ") 'my-leader)
Note that you can not override Evil's bindings this way but those unemployed or employed at a global or local level. Use the first method in case you want to override Evil bindings.
And there is evil-make-overriding-map which causes local-map to override Evil's bindings but this is rarely what you want because you want hjkl to work at least but useful for modes like dired which evil makes less sense.
Footnote: There is nothing special about Evil's operators or text objects from Emacs's perspective. They're just bound keymaps. Eg: i key is bound to the evil-inner-text-objects-map which includes text objects like w as in:
(define-key evil-visual-state-map "i" evil-inner-text-objects-map)
(define-key evil-inner-text-objects-map "w" 'evil-inner-word)
You can find these lines in evil-maps.el
When reading the mailing list someone mentioned it is better to put keybindings in eval-after-load instead of hooks, so here it is:
(eval-after-load "<mode>"
'(progn
<object-definition>))
As for defining new text objects, I must recommend this function from #gordon-gustafson:
(defmacro define-and-bind-text-object (key start-regex end-regex)
(let ((inner-name (make-symbol "inner-name"))
(outer-name (make-symbol "outer-name")))
`(progn
(evil-define-text-object ,inner-name (count &optional beg end type)
(evil-select-paren ,start-regex ,end-regex beg end type count t))
(evil-define-text-object ,outer-name (count &optional beg end type)
(evil-select-paren ,start-regex ,end-regex beg end type count nil))
(define-key evil-inner-text-objects-map ,key (quote ,inner-name))
(define-key evil-outer-text-objects-map ,key (quote ,outer-name)))))
So the <object-definitions> part would become:
(define-and-bind-text-object "<key>" "<start-regex>" "<end-regex>")
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.