Say, I want to bind a key to turn some mode on/off. I guess, the function I need should look something like
(defun toggle-some-mode ()
(if (some-mode-is-active)
(some-mode 0)
(some-mode 1)))
(global-set-key (kbd "some key") 'toggle-some-mode)
The question is what does this some-mode-is-active look like? How could I know mode is active/inactive? I don't believe there's no standard function for such a useful case.
Any ideas?
Just use some-mode to toggle, i.e., (global-set-key (kbd "some key") 'some-mode). The argument of some-mode should be optional and if the argument is not present in the function call the function toggles the mode by default.
About your question how to detect whether mode is active: If some-mode is defined by define-minor-mode it exists as function and as variable. The variable some-mode evaluates to t if the mode is active and nil otherwise.
Related
I'm using emacs with evil-mode, I want to map <leader>tt to the function projectile-dired however if a dired buffer is being shown then it should be mapped to the evil-delete-buffer, so in essence creating a map to a toggle function.
After learning the basics of emacs lisp I came up with this solution:
(defun toggle-projectile-dired ()
"Toggles projectile-dired buffer."
(interactive)
(or
(when (derived-mode-p 'dired-mode)
(evil-delete-buffer (current-buffer)))
(projectile-dired)))
;; This is how the mapping is done
(evil-leader/set-key "tt" 'toggle-projectile-dired)
But what I did with this solution was to create a map to a function that in the end calls to another function.
While my solution works (and I'm fine with it) what I could not do was to return the function to be called (instead of calling it, as I did) how such approach should be written?
Or in other words, how to return a function name and make that mapping call the returning function?.
PD: This question is just for the sake of learn some elisp. Thanks!
EDIT:
Here is some pseudo code (javascript) of what I want to achieve:
function toggleProjectileDired() {
if (derivedModeP == 'dired-mode') {
// We're in dired buffer
return 'evilDeleteBuffer';
} else {
return 'projectileDired';
}
}
evilLeaderSetKey("tt", toggleProjectileDired());
My solution in pseudo code is:
function toggleProjectileDired() {
if (derivedModeP == 'dired-mode') {
// We're in dired buffer
evilDeleteBuffer();
} else {
projectileDired();
}
}
evilLeaderSetKey("tt", toggleProjectileDired);
As you can see, one returns the function name to be called while the other calls the function. How to return a function name to be called in elisp?
(Caveat: I don't use evil, and am not familiar with its custom keybinding functions.)
The canonical approach to making a key do one thing in dired-mode and another thing elsewhere is to define one binding in dired's keymap, and another binding in the global keymap (or whatever is appropriate). I would recommend that you try to follow this approach in most circumstances, because it makes it much simpler to see what's happening.
However, there is a way to do what you're asking for. These pages demonstrate some variations on the approach:
https://stackoverflow.com/a/22863701
http://endlessparentheses.com/define-context-aware-keys-in-emacs.html
http://paste.lisp.org/display/304865
In essence you use the :filter facility of menu items (n.b. menus are actually fancy keymaps in Emacs) to determine the command at run-time. Note that if the filter function returns nil, Emacs treats it as if no binding exists in that keymap, and continues looking for a binding in the remaining keymaps; so this feature facilitates bindings which are only conditionally active.
A non-evil version of your example might look like this:
(define-key global-map (kbd "<f6>")
`(menu-item "" projectile-dired
:filter ,(lambda (default)
(if (derived-mode-p 'dired-mode)
'evil-delete-buffer
default))))
Again, this would be more usual:
(global-set-key (kbd "<f6>") 'projectile-dired)
(eval-after-load "dired"
'(define-key dired-mode-map (kbd "<f6>") 'evil-delete-buffer))
FWIW, I actually think the general approach you started with is probably the best one in this instance. If typing KEY should always toggle dired in that window, then binding it to a toggle-dired command seems like the most self-explanatory implementation.
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'.)
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?
Question:
How do I create custom keybindings for minor modes? Something like this.
Here is what I have so far. I'm trying to get just one custom keybinding to work:
(define-minor-mode num-mode
"Toggle the Num pad keys.
With no argument, this command toggles the mode.
Non-null prefix argument turns on the mode.
Null prefix argument turns off the mode.
When Num mode is enabled, the num pad inserts the
keys you see on the pad. This may over ried existing
definitions, and is probably only usefule if you aref
running Emacs through a terminal."
;; The initial value.
nil
;; The indicator for the mode line.
" NumPad"
;; The minor mode bindings.
;; This doesn't work right now!!!!
'(((kbd "<kp-1>") . "a"))
:global 1
)
When I hit "1" on the num pad after calling my custom minor mode, "num-mode" and verifying it is on in the mini buffer, I get the error <kp-1> is undefined. What I want to happen is a is printed out where the pointer is when I hit <kp-1>. (just a test)
Context:
So, I usually use my num pad to move between buffers (the arrow keys move me the appropriate directions). This is set globally. I want to create a minor mode that I can call when I want to use my num-pad to simply enter numbers.
By default, the keys on my num-pad are undefined. I use <kp-0> to <kp-9> to define the keybindings for the numpad keys.
I can create a minor mode that I can call, but I can't attach any keybindings. This is true for all keybindings, including one not defined anywhere else.
Thanks for any help!
Short Answer
Problem line:
'(((kbd "<kp-1>") . "a"))
Solution (force the evaluation of the macro):
;; Single quote changed to back-quote and added a comma
`((,(kbd "<kp-1>") . "a"))
Long Answer
The define-minor-mode macro allows you to create minor modes relatively easily (as far as Emacs) goes.
First I'll show how it's done, then I'll explain how it works:
In general form:
(define-minor-mode NAME-mode
"DOCUMENTATION"
INIT-VALUE
"LIGHTER"
;; keymap
'(
(KEY-SEQUENCE . DEFINITION)
(KEY-SEQUENCE . DEFINITION)
... ETC ...
)
;; other options
:KEYWORD-ARG VALUE
:KEYWORD-ARG VALUE
... ETC ...
)
Specific example with forced evaluation of macros in the alist:
;; Custom Minor Mode
(define-minor-mode custom-mode
"Doc description, yada yada yada."
;; The initial value - Set to 1 to enable by default
nil
;; The indicator for the mode line.
" CustomMode"
;; The minor mode keymap
`(
(,(kbd "C-c C-a") . some-command)
(,(kbd "C-c C-b") . other-command)
("\C-c\C-c" . "This works too")
)
;; Make mode global rather than buffer local
:global 1
)
An alternative way, if you wish to use a variable for the keymap is to define the keymap variable and the keymap before the minor mode declaration something like this:
(defvar custom-mode-keymap (make-keymap) "num-mode keymap.")
(define-key custom-mode-keymap (kbd "C-c C-a") 'some-command)
And then, in your minor mode definition, simple list the variable name for your keymap, instead of the alist
(define-key custom-mode-keymap (kbd "C-c C-b") 'other-command)
;; Num pad enable
(define-minor-mode custom-mode
...
;; The minor mode bindings.
custom-mode-keymap
How it all works
From top to bottom, right after define-minor-mode we define a command name to toggle the minor mode. custom-mode in this case (M-x custom-mode to toggle the mode). This also defines a variable of the same name.
Right after the command name, we list the documentation string for the minor mode in quotes. This can pretty much be as long as you want.
Next we have several choices. The simplest choice is to simply list three things and then any additional options. The three things have to be listed in the order below. These three things are:
The initilization value for the minor mode variable. nil will have the mode off by default. Something other than nil will have it on by default.
The lighter. The lighter is simply what is display in the mode line at the bottom when your minor mode is on. This should be short, and it'll often help, in terms of formatting, to start it with a space.
The keymap. The keymap can either be defined as a variable or an alist (association list). Since using an alist is simpler and shorter, that's what I used in the example. The alist should be in the form (key-sequence . definition).
If you define the keymap as an alist, there's a few things to watch out for, especially if you're used to defining global keybindings. First, command names are not quoted. Second, if you want to use a macro, you have to force it to evaluate (and on SO). This is done with a combination of the back-quote (not single quote) and comma. You can see how this is done in the example with the kbd macro. I also included a keystroke definition if you don't use the kbd macro. If you simply quote a string in your keymap, that'll print out when the defined key combination is pressed (just like for defining global key bindings).
The keymap will not work with kbd macros if you do not force the evaluation of the kbd macros with a combination of the back quote and comma. Like this:
`((,(kbd "C-c C-a") . some-command))
Finally, you can add additional options using keyword-args of the form :blah. In the example I used :global. We could have defined the entire mode with keyword-args, but it's shorter to just list the init value, lighter, and keymap in the right order.
You need to have code like this in your minor mode definition:
(defvar your-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map parent-mode-shared-map)
(define-key map "\C-c\C-a" 'some-defun)
(define-key map "\C-c\C-b" 'some-other-sexp)
map)
(use-local-map your-mode-map)
You could have a look at the many modes available in EmacsWiki for reference.
Have a look at this (just for information about keybindings):
http://www.gnu.org/software/emacs/manual/html_node/emacs/Key-Bindings.html#Key-Bindings
http://www.gnu.org/software/emacs/manual/html_node/emacs/Local-Keymaps.html#Local-Keymaps
From Link:
Well written major modes will run hook at the end. So, you can use a hook to define your keybinding. Minor modes usually do not have hooks. In that case, you can call “(require ‹minor mode feature symbol›)” first, then define your keybinding.
Also you might have a look at:
http://www.cs.utah.edu/dept/old/texinfo/emacs19/emacs_35.html#SEC347
Maybe the accepted answer here also helps you.
I have a minor mode. If that mode is active and the user hits DEL, I
want to do some action, but only if some condition holds. If the
condition holds and the action is executed I want to do nothing more
after that. But if the condition fails, I don't want to do anything
and let the default DEL action execute.
Not sure how I could solve this. But I guess I could do it in two ways:
1)
I could rebind the DEL key to a function in the minor mode and then
check if the conditions holds ot not. But then how do I know what the
default command to DEL is?
2)
I could add a pre command hook like this. Execute the command and then
break the chain. But how do I break the chain?
(add-hook 'pre-command-hook
(lambda()
(when (equal last-input-event 'backspace)
;; Do something and then stop (do not execute the
;; command that backspace is bound to)
)))
In what way would you solve it? Thanks!
The way to do this is to temporarily disable your minor mode, then look up the key binding.
Pretend that you've bound 'do-thingy to DEL. Then this would do the trick (assuming the condition you want to trigger off is (equal last-input-event 'backspace):
(defun do-thingy ()
"Do something, unless last event was backspace."
(interactive)
(if (equal last-input-event 'backspace)
(let* ((my-minor-mode nil)
(original-func (key-binding (kbd "DEL"))))
;; original-func is whatever DEL would be if
;; my-minor-mode were disabled
(call-interactively original-func))
(message "Here's my minor mode behavior!")))
Note: This behavior assumes you have set up your key bindings the standard way a minor-mode would. Specifically, you should add your keymap to the variable minor-mode-map-alist by adding an element (my-minor-mode . my-minor-mode-keymap). That's how the above let statement works, it looks up the binding you want with your mode temporarily disabled.
If you use define-minor-mode to define your minor mode, the keymap gets set up the "right way" automatically.
This is what I use for my smart-tab package which does exactly that.
(defun smart-tab-default ()
"Indents region if mark is active, or current line otherwise."
(interactive)
(if mark-active
(indent-region (region-beginning)
(region-end))
(call-interactively
(or
;; Minor mode maps for tab (without smart-tab-mode)
(cdar (assq-delete-all 'smart-tab-mode (minor-mode-key-binding "\t")))
(cdar (assq-delete-all 'smart-tab-mode (minor-mode-key-binding [(tab)])))
(local-key-binding "\t")
(local-key-binding [(tab)])
(global-key-binding "\t")
(global-key-binding [(tab)])))))
And in the command smart-tab (which is the one bound to tab in the minor
mode), it has the following:
(if (smart-tab-must-expand prefix)
;; use smart tab
(smart-tab-default))
It first checks if there are any minor mode bindings for tab (not including
smart-tab-mode), then local, and finally global keybindings.
There doesn't seem to be a way to do what you want reliably. If your new command is bound to DEL, then whatever was bound to DEL before in the current keymap isn't there anymore. The other approach you proposed won't work because pre-command-hooks don't prevent the following action from taking place. You might also think to interrupt further execution with ^G (Keyboard-Quit), but that's an uncontrolled interrupt that might stop more things than you want.
Even if you make the process of setting up the new binding a little more sophisticated than just re-bind, and remember what was bound there before, so you can call it afterwards, you don't really have what your looking for. If someone wants to rebind the "default" action, they have to do it by modifying to your function rather than replacing the key binding.
What you want to do doesn't fit Emacs' model of how key binding works.