Hi, I'm trying to bind a command to the "unused namespace" that happens to start with "c". By "unused namespace" I mean things like "cp" or "co" for which there is no valid text object or motion. In Vim, you can bind "cow" and it does not affect the behavior of "cdw", "ciw", etc, because "o" is not defined as a text object. For example, in unimpaired.vim[1], "cow" toggles word-wrap. I would like to do this in emacs evil, so I tried: (define-key evil-normal-state-map (kbd "c o w") 'toggle-truncate-lines) It reports "eval-region: Key sequence c p w starts with non-prefix key c". But I don't want to unbind "c" because then I can't use the evil-change operator. I tried defining a custom operator, but after looking at the evil source code I couldn't figure out how to hook into evil-change to preserve its behavior while listening for "o" and then routing it somewhere else. It seems like I'm missing something, I would think this is a common task, but I haven't found any posts that address this. Thanks for evil, it's superb! [1] https://github.com/tpope/vim-unimpaired Justin M. Keyes
Am 07.06.2014 09:45, schrieb Justin M. Keyes:
Hi,
I'm trying to bind a command to the "unused namespace" that happens to start with "c". By "unused namespace" I mean things like "cp" or "co" for which there is no valid text object or motion. In Vim, you can bind "cow" and it does not affect the behavior of "cdw", "ciw", etc, because "o" is not defined as a text object. For example, in unimpaired.vim[1], "cow" toggles word-wrap. I would like to do this in emacs evil, so I tried:
(define-key evil-normal-state-map (kbd "c o w") 'toggle-truncate-lines)
It reports "eval-region: Key sequence c p w starts with non-prefix key c". But I don't want to unbind "c" because then I can't use the evil-change operator.
Something like this is currently not possible. That's because of the way Emacs key-sequences and Evil operators work: The parsing of a key-sequence does not happen when the sequence is complete but in steps. "c" is bound to the command `evil-change`, so when "c" is typed Emacs immediately executes `evil-change` without waiting for further keys (i.e., the decision which command has to be executed is taken as soon as "c" is typed--that's also the reason why you cannot define a command "c" and another "cp" in the same keymap: a keymap is a lookup-table and "c" can either be mapped to a command or to another keymap, not both). What Evil does is to manually wait for further keys before the body of `evil-change` is executed. This happens in the `interactive` form of `evil-change` (and each other operator--in fact, the magic behind `evil-define-operator` is to automatically generate an appropriate interactive form). This second key-sequence is looked up in `evil-operator-state-map` (and inherited keymaps like `evil-motion-state-map`). If the sequence completely defines a (motion) command, this command is executed and used to build the range parameter for the operator. Then the body of the operator is executed (with that range). So there is a real limitation of key sequence in Evil. What you could to, however, is to provide a "fake" motion, that executes whatever you want but does not execute the operator's body: ``` (defun my-command () (interactive) (do-what-you-want) (setq evil-inhibit-operator t)) (define-key evil-operator-state-map "p" 'my-command) ``` The important thing is the last line, which causes Evil to not execute the operators body. However, this would cause "p" to work with *any* operator. But you're free to do whatever you want in your command, so you could dispatch depending on the current operator: ``` (defun my-command () (interactive) (case evil-this-operator (evil-change (do-what-you-want)) (evil-delete (do-something-else)) (t (keyboard-quit))) (setq evil-inhibit-operator t)) ```
I tried defining a custom operator, but after looking at the evil source code I couldn't figure out how to hook into evil-change to preserve its behavior while listening for "o" and then routing it somewhere else.
Maybe you can do something with advice tricks, too, or some temporary keymaps together with `unread-command-events`. The suggestion above is only one possibility. But as I said, there is no direct support for what you want because of Emacs keymaps and/or the current implementation. Best regards, Frank
and another "cp" in the same keymap: a keymap is a lookup-table and "c" can either be mapped to a command or to another keymap, not both). What Evil does is to manually wait for further keys before the body of `evil-change` is executed. This happens in the `interactive` form of
I'm very much not familiar with Evil, so this may be completely inapplicable, but an option might be to do the following: - don't bind `c' to `evil-change'. - instead, add various `c <foo>' bindings, to `evil-change' and change it so it uses last-command-event or this-single-command-keys to know which binding triggered it (instead of using something like `read-event' to wait for the next event). -- Stefan
On 13.06.2014 21:54, Stefan Monnier wrote:
I'm very much not familiar with Evil, so this may be completely inapplicable, but an option might be to do the following: - don't bind `c' to `evil-change'. - instead, add various `c <foo>' bindings, to `evil-change' and change it so it uses last-command-event or this-single-command-keys to know which binding triggered it (instead of using something like `read-event' to wait for the next event).
This approach has some disadvantages: * a count after the operator but before the motion would not work anymore (something like c2w) * the same has to be done for all other operators and for every new operator to be defined and possibly more. And it would be a big change, maybe not (yet) worth the effort. Best regards, Frank
It seems like the better fix would be to add a helper function that lets you easily define key sequences that begin with an action. Something like the "fake-motion" suggested, but generalized for any leader key. -Jason On 17:35 Wed 18 Jun , Frank Fischer wrote:
On 13.06.2014 21:54, Stefan Monnier wrote:
I'm very much not familiar with Evil, so this may be completely inapplicable, but an option might be to do the following: - don't bind `c' to `evil-change'. - instead, add various `c <foo>' bindings, to `evil-change' and change it so it uses last-command-event or this-single-command-keys to know which binding triggered it (instead of using something like `read-event' to wait for the next event).
This approach has some disadvantages:
* a count after the operator but before the motion would not work anymore (something like c2w) * the same has to be done for all other operators and for every new operator to be defined
and possibly more. And it would be a big change, maybe not (yet) worth the effort.
Best regards, Frank
_______________________________________________ implementations-list mailing list [email protected] https://lists.ourproject.org/cgi-bin/mailman/listinfo/implementations-list
>> I'm very much not familiar with Evil, so this may be completely
>> inapplicable, but an option might be to do the following:
>> - don't bind `c' to `evil-change'.
>> - instead, add various `c <foo>' bindings, to `evil-change' and change
>> it so it uses last-command-event or this-single-command-keys to know
>> which binding triggered it (instead of using something like
>> `read-event' to wait for the next event).
> This approach has some disadvantages:
> * a count after the operator but before the motion would not work
> anymore (something like c2w)
Not sure how ugly it would end up, indeed.
But you could very well handle it with something like:
(define-key map "c" operator-arg-map)
(defvar operator-arg-map
(let ((map (make-sparse-keymap)))
(dotimes (i 10)
(define-key map (vector (+ i ?0)) map))
(define-key map [?w] 'my-command)
map))
(defun my-command (keys)
(interactive (list (this-single-command-keys)))
... analyze keys to extract the count and the motion...
...)
It would probably introduce other changes to the behavior that you
didn't intend, and such "recursive maps" are sufficiently rare that you
might bump into Emacs bugs, so yes... not necessarily a good idea.
OK, going back to lurk mode before I start rambling about set-transient-map.
Stefan
Am 19.06.2014 04:12, schrieb Stefan Monnier:
Not sure how ugly it would end up, indeed.
But you could very well handle it with something like:
(define-key map "c" operator-arg-map) (defvar operator-arg-map (let ((map (make-sparse-keymap))) (dotimes (i 10) (define-key map (vector (+ i ?0)) map)) (define-key map [?w] 'my-command) map))
(defun my-command (keys) (interactive (list (this-single-command-keys))) ... analyze keys to extract the count and the motion... ...)
It would probably introduce other changes to the behavior that you didn't intend, and such "recursive maps" are sufficiently rare that you might bump into Emacs bugs, so yes... not necessarily a good idea.
Another idea would be to multiplex operators (they are defined via `evil-define-operator` anyway, so this should not be a problem). If an operator is called (interactively), it does not execute its body but simply activates a new minor mode (i.e., operator-state) in which other keymaps are active (and possibly some prefix specific ones, e.g., one that is only active if the invoking key-sequence has been "c"). If some (motion) command is executed in that state, the operator's body is finally called with the range defined by the motion. If the command to be called in operator-state is one in the special keymap, then that command is executed normally and the body of the operator is not. (the special keymap could be built automatically if the bindings are defined via `evil-define-key`). But again, this requires some work and I'm not sure if it pays of. Best regards, Frank
participants (4)
-
Frank Fischer -
Jason Miller -
Justin M. Keyes -
Stefan Monnier