Invalid Keymap error when byte compiling minor-mode key bindings - emacs

The code below from my .emacs works fine normally but gives me an "Invalid keymap my-keys-mode-map" error when I try to byte compile it.
(eval-and-compile
(defvar my-keys-mode-map (make-sparse-keymap) "my-keys-mode keymap.")
(define-minor-mode my-keys-mode
"A minor mode to override major modes keys."
t " my-keys" 'my-keys-mode-map)
(bind-key "C-;" (quote right-char) my-keys-mode-map)
(bind-key "C-j" (quote left-char) my-keys-mode-map)
)
The error is on the bind-key line. I have tried define-key instead of bind-key, or using make-keymap instead of make-sparse-map but without luck. I am not too proficient with elisp. Is there some other way to define the key-map so that it is recognized by the byte compiler?

Remove the quote in front of the keymap symbol in the define-minor-mode.
In other words, the minor-mode definition should be this:
(define-minor-mode my-keys-mode
"A minor mode to override major modes keys."
t " my-keys" my-keys-mode-map)
You need to pass a keymap, not a symbol (whose value is a keymap), to define-minor-mode.

Related

Predicate-based dynamic key binding with default fallback [duplicate]

I'm trying to write a custom tab completion implementation which tries a bunch of different completions depending on where the point is. However, if none of the conditions for completions are met I would like tab to do what ever the current mode originally intended it to do.
Something like this:
(defun my-custom-tab-completion ()
(interactive)
(cond
(some-condition
(do-something))
(some-other-condition
(do-something-else))
(t
(do-whatever-tab-is-supposed-to-do-in-the-current-mode))) ;; How do I do this?
Currently I'm checking for specific modes and doing the right thing for that mode, but I really would like a solution that just does the right thing without me having to explicitly add a condition for that specific mode.
Any ideas of how to do this?
Thanks! /Erik
BTW, here is another solution:
(define-key <map> <key>
`(menu-item "" <my-cmd> :filter ,(lambda (cmd) (if <my-predicate> cmd))))
Here is a macro I wrote based on Emacs key binding fallback to define a keybinding conditionally. It adds the keybinding to the specified minor mode but if the condition is not true, the previously assigned action is executed:
(defmacro define-key-with-fallback (keymap key def condition &optional mode)
"Define key with fallback. Binds KEY to definition DEF in keymap KEYMAP,
the binding is active when the CONDITION is true. Otherwise turns MODE off
and re-enables previous definition for KEY. If MODE is nil, tries to recover
it by stripping off \"-map\" from KEYMAP name."
`(define-key ,keymap ,key
(lambda () (interactive)
(if ,condition ,def
(let* ((,(if mode mode
(let* ((keymap-str (symbol-name keymap))
(mode-name-end (- (string-width keymap-str) 4)))
(if (string= "-map" (substring keymap-str mode-name-end))
(intern (substring keymap-str 0 mode-name-end))
(error "Could not deduce mode name from keymap name (\"-map\" missing?)"))))
nil)
(original-func (key-binding ,key)))
(call-interactively original-func))))))
Then I can do things like the following to use the special binding for TAB only when I am on a header in outline-minor-mode. Otherwise my default action (I have both indent and yasnippets) is executed:
(define-key-with-fallback outline-minor-mode-map (kbd "TAB")
(outline-cycle 1) (outline-on-heading-p))
You could use functions such as key-binding (or its more specific variants global-key-binding, minor-mode-key-binding and local-key-binding) to probe active keymaps for bindings.
For example:
(call-interactively (key-binding (kbd "TAB")))
;; in an emacs-lisp-mode buffer:
;; --> indent-for-tab-command
;;
;; in a c++-mode buffer with yas/minor-mode:
;; --> yas/expand
One way to avoid infinite loops if your command is bound to TAB could be to put your binding in a minor mode, and temporarily disable its keymap while looking for the TAB binding:
(define-minor-mode my-complete-mode
"Smart completion"
:keymap (let ((map (make-sparse-keymap)))
(define-key map (kbd "TAB") 'my-complete)
map))
(defun my-complete ()
(interactive)
(if (my-condition)
(message "my-complete")
(let ((my-complete-mode nil))
(call-interactively (key-binding (kbd "TAB"))))))
It's possible that you could achieve this without any special workarounds at all. In most modes TAB just does indentation by default, but if you set the global variable tab-always-indent to 'complete it will try to do completion first, and indent if no completion is possible. This usually works really well, although if TAB is bound to another command in one of your major modes you might be out of luck.
If that works in the modes you need, you'll just need to add your custom completion function to the front of the list completion-at-point-functions in all applicable buffers (maybe using a mode hook). The completion-at-point command calls each function listed in completion-at-point-functions until one of them returns non-nil, so all you need to do to have your custom completion function "fall through" to the existing behavior is return nil from it.
This isn't a 100% answer to the question, but if the major modes you're working with are written according to the normal guidelines it might be the cleanest way.
define-key can accept quoted string or interactive lambdas like in this example.
;Static
(define-key evil-normal-state-mapr "m" 'evil-motion-state)
;Conditional
(define-key evil-normal-state-map "m"
(lambda () (interactive) (message "%s" major-mode)))
Lambda's can be replaced with named functions like my-tab-completion and used more effectively.
From define-key's docstring (Emacs 25)
DEF is anything that can be a key's definition:
nil (means key is undefined in this keymap),
a command (a Lisp function suitable for interactive calling),
a string (treated as a keyboard macro),
a keymap (to define a prefix key),
a symbol (when the key is looked up, the symbol will stand for its
function definition, which should at that time be one of the above,
or another symbol whose function definition is used, etc.),
a cons (STRING . DEFN), meaning that DEFN is the definition
(DEFN should be a valid definition in its own right),
or a cons (MAP . CHAR), meaning use definition of CHAR in keymap MAP,
or an extended menu item definition.
(See info node `(elisp)Extended Menu Items'.)

How to define keymap for minor mode correctly

I'd like to define custom mode for improvements that suits to any program mode. And I need to define key-bindings for all this modes. I choose to use define-minor-mode with :keymap to declare key bindings with minimum effort.
I'd like to bind comment-or-uncomment-region to "C-;" The kbd macro gave me [67108923] magic number for this key sequence.
I've wrote sample that doesn't work
(define-minor-mode
my-mode
nil nil
:keymap '(
( [67108923] . comment-or-uncomment-region )
)
)
I've registered mode, toggled it on, but pressing ะก-; produces notifications that the key sequence is not defined
After that I've wrote in the scratch buffer and evaluate simple global-set-key that performed in expected way.
(global-set-key [67108923] 'comment-or-uncomment-region )
Now pressing C-; produces expected comment-or-oncomment-region behavior.
I've tried to debug the issue with searching to function info via C-h f. It produces strange output, comment-or-oncomment-region is bound twice to different key sequences:
It is bound to C - ;, C-;
First one appears and disappears with the minor mode toggling, other emerge from global-set-key invocation.
How can it be, if I've used the same key definition for both maps? What details I have missed?
Don't use the magic number. IOW use [?\C-\;], so it can be understood by humans.
And I agree with Drew:
(defvar my-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [?\C-\;] 'comment-or-uncomment-region)
map))
(define-minor-mode my-mode
"blabla"
nil nil nil
<add code, if any>)
Oh, and one more thing: why would you prefer C-; over the standard M-; binding?
Just create a keymap normally, using make-sparse-keymap, and name it my-mode-map --- you're done. No need for :keymap arg to define-minor-mode.
Or use the keymap you create using make-sparse-keymap as the value of :keymap, if you like. (But no need to, since it is named as the minor mode expects: my-mode-map.)
But why not just use a global binding, via global-set-key? Why do you even need this to be a minor-mode binding?

Elisp: Conditionally change keybinding

I'm trying to write a custom tab completion implementation which tries a bunch of different completions depending on where the point is. However, if none of the conditions for completions are met I would like tab to do what ever the current mode originally intended it to do.
Something like this:
(defun my-custom-tab-completion ()
(interactive)
(cond
(some-condition
(do-something))
(some-other-condition
(do-something-else))
(t
(do-whatever-tab-is-supposed-to-do-in-the-current-mode))) ;; How do I do this?
Currently I'm checking for specific modes and doing the right thing for that mode, but I really would like a solution that just does the right thing without me having to explicitly add a condition for that specific mode.
Any ideas of how to do this?
Thanks! /Erik
BTW, here is another solution:
(define-key <map> <key>
`(menu-item "" <my-cmd> :filter ,(lambda (cmd) (if <my-predicate> cmd))))
Here is a macro I wrote based on Emacs key binding fallback to define a keybinding conditionally. It adds the keybinding to the specified minor mode but if the condition is not true, the previously assigned action is executed:
(defmacro define-key-with-fallback (keymap key def condition &optional mode)
"Define key with fallback. Binds KEY to definition DEF in keymap KEYMAP,
the binding is active when the CONDITION is true. Otherwise turns MODE off
and re-enables previous definition for KEY. If MODE is nil, tries to recover
it by stripping off \"-map\" from KEYMAP name."
`(define-key ,keymap ,key
(lambda () (interactive)
(if ,condition ,def
(let* ((,(if mode mode
(let* ((keymap-str (symbol-name keymap))
(mode-name-end (- (string-width keymap-str) 4)))
(if (string= "-map" (substring keymap-str mode-name-end))
(intern (substring keymap-str 0 mode-name-end))
(error "Could not deduce mode name from keymap name (\"-map\" missing?)"))))
nil)
(original-func (key-binding ,key)))
(call-interactively original-func))))))
Then I can do things like the following to use the special binding for TAB only when I am on a header in outline-minor-mode. Otherwise my default action (I have both indent and yasnippets) is executed:
(define-key-with-fallback outline-minor-mode-map (kbd "TAB")
(outline-cycle 1) (outline-on-heading-p))
You could use functions such as key-binding (or its more specific variants global-key-binding, minor-mode-key-binding and local-key-binding) to probe active keymaps for bindings.
For example:
(call-interactively (key-binding (kbd "TAB")))
;; in an emacs-lisp-mode buffer:
;; --> indent-for-tab-command
;;
;; in a c++-mode buffer with yas/minor-mode:
;; --> yas/expand
One way to avoid infinite loops if your command is bound to TAB could be to put your binding in a minor mode, and temporarily disable its keymap while looking for the TAB binding:
(define-minor-mode my-complete-mode
"Smart completion"
:keymap (let ((map (make-sparse-keymap)))
(define-key map (kbd "TAB") 'my-complete)
map))
(defun my-complete ()
(interactive)
(if (my-condition)
(message "my-complete")
(let ((my-complete-mode nil))
(call-interactively (key-binding (kbd "TAB"))))))
It's possible that you could achieve this without any special workarounds at all. In most modes TAB just does indentation by default, but if you set the global variable tab-always-indent to 'complete it will try to do completion first, and indent if no completion is possible. This usually works really well, although if TAB is bound to another command in one of your major modes you might be out of luck.
If that works in the modes you need, you'll just need to add your custom completion function to the front of the list completion-at-point-functions in all applicable buffers (maybe using a mode hook). The completion-at-point command calls each function listed in completion-at-point-functions until one of them returns non-nil, so all you need to do to have your custom completion function "fall through" to the existing behavior is return nil from it.
This isn't a 100% answer to the question, but if the major modes you're working with are written according to the normal guidelines it might be the cleanest way.
define-key can accept quoted string or interactive lambdas like in this example.
;Static
(define-key evil-normal-state-mapr "m" 'evil-motion-state)
;Conditional
(define-key evil-normal-state-map "m"
(lambda () (interactive) (message "%s" major-mode)))
Lambda's can be replaced with named functions like my-tab-completion and used more effectively.
From define-key's docstring (Emacs 25)
DEF is anything that can be a key's definition:
nil (means key is undefined in this keymap),
a command (a Lisp function suitable for interactive calling),
a string (treated as a keyboard macro),
a keymap (to define a prefix key),
a symbol (when the key is looked up, the symbol will stand for its
function definition, which should at that time be one of the above,
or another symbol whose function definition is used, etc.),
a cons (STRING . DEFN), meaning that DEFN is the definition
(DEFN should be a valid definition in its own right),
or a cons (MAP . CHAR), meaning use definition of CHAR in keymap MAP,
or an extended menu item definition.
(See info node `(elisp)Extended Menu Items'.)

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.

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.