binding to "cp" without breaking "c"

Frank Fischer frank-fischer at shadow-soft.de
Sat Jun 7 12:09:12 CEST 2014


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




More information about the implementations-list mailing list