Advice that calls transpose-chars instead of evil-shift-right-line (C-t)

Frank Fischer frank-fischer at shadow-soft.de
Thu Feb 21 10:06:41 CET 2013


On 2013-02-20, Christoph LANGE <langec at web.de> wrote:
> Hi all,
>
> on the one hand I welcome the new commands evil-shift-right-line (C-t)
> and evil-shift-left-line (C-d) in insert mode, as this has been one of
> the features I had always missed in evil.
>
> And it is good that evil-shift-right-line is bound to C-t instead of C-i
> as in vim, as C-i does other useful things in Emacs.

C-i should never be bound, because it's exactly the same key-event as
TAB in the terminal.

> However, one of my most frequently used Emacs functions in insert mode
> has so far been transpose-chars, which is what Emacs binds to C-t.
> (Transposing two characters in vim is a PITA; you'd have to do "_xp in
> command mode.)

This was exactly what I've been afraid of and why I was reluctant to
add this binding. I asked in #evil and we came to the conclusion that
it's easier to remove a binding than to add a new one, so if one
doesn't like C-t in its current form then

    (define-key evil-insert-state-map "\C-t" nil)

is sufficient to get rid of it.

But of course, the question is what to do if you have two potential
commands, both candidates for the same key, and you can't live without
one of them. I don't know. Probably the best is to find an alternative
binding for one of the two commands.

>
> So I came up with the following for my .emacs:
>
> (defadvice evil-shift-right-line (around evil activate)
>   "call transpose-chars if universal argument given"
>   (if (equal current-prefix-arg '(4))
>       (transpose-chars 1)
>     ad-do-it))

I would never use an advice. If the advised function is called
somewhere else, e.g. as a subroutine (which is currently not the case
for `evil-shift-right-line`, but who knows the future ...) this could
easily introduce strange bugs. It's better to write a new command and
bind it to \C-t.

> This gives me transpose-chars with C-u C-t, and evil-shift-right-line
> with C-t.
>
> The only drawback is that it unconditionally calls transpose-chars with
> an argument of 1, but so far I have never used it with different
> arguments anyway.

You could check whether the prefix argument is a number and pass that
number as count to `transpose-char`. I would do

    (defun shift-right-or-transpose (count)
	  "Some really descriptive doc should go here, but I'm too lazy ..."
      (interactive "P")
      (cond
       ((null count) (evil-shift-right-line 1))
       ((numberp count) (transpose-chars count))
       (t (transpose-chars 1))))
    
    (define-key evil-insert-state-map "\C-t" 'shift-right-or-transpose)

Then you can use, in addition to C-t for right shift or C-u C-t for a
single transpose, C-u 5 C-t or M-5 C-t to transpose 5 characters.

If we get the possibility to have C-t and C-t C-t bound both in insert
state one could, e.g., use C-t for indent and C-t C-t for transpose,
which could also be a solution. Unfortunately, this does not work yet,
until we find someone who implements this ... ;)

Best regards,
Frank




More information about the implementations-list mailing list