Why don't my <c-w> and <c-u> bindings work?

I have the following in my kakrc:

# delete previous word
map global insert <c-w> %{<a-;>: execute-keys -draft h<a-i>wd<ret>}

# delete to beginning of line
map global insert <c-u> %{<a-;>: execute-keys -draft h<a-h>d<ret>}

Neither one achieves the desired effect. The <c-w> binding deletes the word in front of the cursor. That is, if the cursor is positioned on the space after This in the following line:

This is a test.

Then <c-w> will delete the space. If it positioned on the i in is, it will delete the word is. On the other hand, if I manually execute the same sequence of characters (<esc>h<a-i>wd) with the cursor on the space after This, it does what I want.

Similarly, the binding for <c-u> simply deletes the character to the left of the cursor, but the same sequence of keys, when typed in manually, extends the selection to the beginning of the line (<a-h>) and then deletes it (d).

What’s going on here?

You need to take special care to handle <> characters on the right hand side of a mapping. When you have <a-i> in the delete word mapping, it is interpreted as literally pressing alt+i rather than a sequence of characters starting with <. So instead of above, you should use this:

map global insert <c-w> %{<a-;>: execute-keys -draft h<lt>a-i<gt>wd<ret>}

<lt> expands to a literal < character typed and prevents <a-i> from being interpreted as alt+i. (This is mentioned in :doc mapping mappable-keys.)

As an aside, if you don’t care about the draft context, you could remove the prompt mode+execute-keys indirection and use

map global insert <c-w> h<a-i>wd
1 Like

Thanks, that’s helps! It looks like I need to tweak my mapping a bit, since it doesn’t quite match the behavior I expect from c-w, but at least it does what it should.

I actually tried this previously, and it doesn’t seem to work. With this mapping in place, typing c-w inserts the literal string hwd.

This seems closer to what I want:

map global insert <c-w> %{<a-;>: execute-keys -draft Bd<ret>}

Of course, I forgot you need to switch to normal mode first (wih your new version):

map global insert <c-w> <esc>Bdi

(or maybe a instead of i at the end)

The most reliable implementation I have is this one:

define-command erase_characters_before_cursor_to_line_begin %{
  evaluate-commands -draft %{
    execute-keys '<a-h>'
    evaluate-commands -draft -itersel -verbatim -- try %{
      execute-keys '<a-k>^.\z<ret>'
    } catch %{
      execute-keys '<a-k>^\h+.\z<ret><a-:>Hd'
    } catch %{
      execute-keys '<a-k>^\h+\H<ret>WL<a-:>Hd'
    } catch %{
      execute-keys '<a-:>Hd'
    } catch %{}
  }
  execute-keys '<a-;><a-:><a-;><a-;>'
}

define-command erase_word_before_cursor %{
  evaluate-commands -draft %{
    execute-keys ';<a-_>'
    evaluate-commands -draft -itersel -verbatim -- try %{
      execute-keys -draft '<a-k>^.\z<ret>'
    } catch %{
      execute-keys -draft 'h<a-k>^.\z<ret>d'
    } catch %{
      execute-keys -draft 'h<a-k>\b\w|\B[^\w\h]<ret>d'
    } catch %{
      execute-keys -draft 'hBd'
    } catch %{}
  }
}

map global insert <c-u> '<a-;>:erase_characters_before_cursor_to_line_begin<ret>'
map global insert <c-w> '<a-;>:erase_word_before_cursor<ret>'
1 Like