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

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.

Related

Run function before keymap commands

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)

How to make Emacs key-bindings work in minibuffer? [duplicate]

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.

Emacs global-set-key in find-file

In my init.el file I have this:
(global-set-key "\M-n" (lambda () (interactive) (insert "~")))
This allows me to use Alt-n on my Mac to produce the ~ character. It works fine in buffers etc. but when I try to use it in find-file I get
"End of history no default available".
C-h k reveals that M-n actually calls:
(lambda nil (interactive) (insert "~"))
Why doesn't this work with find-file?
Global key bindings are overridden by local (i.e., major-mode) key bindings, which are overridden by minor-mode bindings, which are overridden by... IOW, there are lots of levels of key binding.
In this case, your global binding is overridden by a minibuffer keymap binding.
In the minibuffer completion keymaps, which are local maps, M-n is bound to next-history-element. If you want M-n in such a map to be bound to something else, then you need to bind it. For example:
(define-key minibuffer-local-completion-map "\M-n" 'your-command)
There are several minibuffer completion keymaps, depending on your Emacs version. The two main ones are minibuffer-local-completion-map and minibuffer-local-must-match-map.

File-specific key-binding in 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:

Emacs keybinding not working in custom major mode

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)