Taking back control of hjkl with modifiers keys

The arrow cluster

One of the most famous lore about vim and its derivative like kakoune is the use of h j k l as arrow keys.
This keys placement is often lauded by the users of these editors as a power-feature because the fingers barely have to move from the home-row of the keyboard.

Strangely, this kind of spatial considerations is almost unique to the h j k l cluster. Other keys tend to be placed on the keyboard according to some mnemonics related to their name.
Non exhaustive examples using the “first letter”:

  • w for Word or Window
  • f for Find
  • b for Backward or Before
  • n for Next
  • p for Previous or Paste

More extreme examples, coming from regexp notation:

  • ^ for Goto first non-blank char of line
  • $ for Goto end of the line

So as we can see the rules to affect a command to a key are quite arbitrary.
But let’s focus back on the arrow cluster and on kakoune.

  • h, j, k, l move the selection by 1 char
  • shift + h, j, k, l extend the selection by 1 char (or line)

These 2 sets of keys above are coherent.
The discrepancy appears when the alt modifier is involved.

Good:

  • alt + h, alt + l select to line begin / end

Less good:

  • alt + j Join lines (legacy from vim)
  • alt + k Keep matching selections (in vim K opens the keywordprg)

Suddenly the “spatial trait” of j and k are exchanged for the “mnemonic trait”.
Arguably, joining lines is a pretty common operation, but filtering selections on a regex is less common (at least in my usage of kakoune, your mileage may vary).

This got me thinking:

Should these 2 commands move somewhere else? If so, which commands should take their place?

The ctrl modifier

But before diving into these questions, we should address the elephant in the room: what about the ctrl modifier?
Because of well-known historical reasons dating back to the prehistory of computers, terminal generate ambiguous keys for the following scenarios:

  • ctrl + itab
  • ctrl + mret

More painfully related to our present discussion:

  • ctrl + hbackspace
  • ctrl + jret

Which means that in a regular setup, our mighty arrow cluster is amputated of the h and j keys when used with the ctrl modifier.

One could argue that vim and kakoune try to minimize the use of actions involving modifiers. (a great counter example in vim world is the u / ctrl + r combo).
I agree that commands involving 2 modifiers like shift + alt + k are indeed cumbersome to type. But on the contrary, commands where the left ctrl (pinky because remapped on caps-lock) and left alt (under the thumb) is pressed
in combination with a right-handed letter (like our h, j, k, l candidates here) are quite comfortable (for my hands at least).

Therefore, what would happen if we were able to disambiguate the ctrl + h and ctrl + j, to claim back the arrow cluster? Along the years, attempts have been made to fix this problem, most notably libtermkey. Kitty, also offers an interesting fullkbd mode but kakoune CSI parser needs to be adapted to take this protocol into account (in the future maybe?). But right now, we can nevertheless use kitty to build our own “hacky” solution.

~/.config/kitty/kitty.conf

map ctrl+h send_text all \u24D7
map ctrl+i send_text all \u24D8
map ctrl+j send_text all \u24D9
map ctrl+m send_text all \u24DC

These unicode code-points have been chosen because they are rare in the wild and their representation is a circled letter like this: ⓘ (\u24D8).
Thanks to this “hack” (which can be adapted to other terminal emulators having remapping capabilities), we can now use these chars in kakoune mapping, like I will show in the rest of this post.

Adapting the layout

Without further ado, let’s discover my current experiment resuming all of the above observations. (I stress out the word experiment as it’s by no means a definitive stance)

Let’s free the alt + k (and alt + K) key, by moving it to the D (and alt + D) key. The mnemonic kind of work. Regular d “delete the content of selections”, whereas this new D “delete the selections themselves”.
Let’s free the alt + j (and alt + J) key, by moving it in place of C (and alt + C). This time the mnemonic can be “Combine lines”.

Now that they are free, let’s affect them a duo of commands that are often requested in the GitHub issues. Kakoune offers the X key to extend down, but has no equivalent to extend up.
Hopefully, many users provided dedicated commands like these ones:

define-command -hidden -params 1 extend-line-down %{
  execute-keys "<a-:>%arg{1}X"
}

define-command -hidden  -params 1 extend-line-up %{
  execute-keys "<a-:><a-;>%arg{1}K<a-;>"
  try %{
    execute-keys -draft ';<a-K>\n<ret>'
    execute-keys X
  }
  execute-keys '<a-;><a-X>'
}

So we can map alt + k to extend-line-up and alt + j to extend-line-down.

But “what about the C and alt + C key you sacrificed earlier?” you may asked.

Here’s where hacking the ctrl key becomes very handy. Because we can now map C (copy selection to next line) to ctrl + j and alt + C to ctrl + k!

map global normal ⓙ 'C'         -docstring 'copy selection on next line'
map global normal <c-k> '<a-C>'  -docstring 'copy selection on previous line'

I’m still thinking about what would be a great choice for ctrl + h and ctrl + l to complement this arrow. It could be to generate arbitrary selections on the left/right of the same line. (WIP)

Conclusion

Overall it feels very natural to gain back the “spatial attributes” of h j k l with the shift, alt or ctrl modifiers.

I also try to apply the same philosophy when designing user-modes, privileging this arrow for commands having a before / after or next / previous behaviors.

14 Likes