Motivation
Modal editing is great, but when people say they just want to enter their editor and start typing I think I understand what they mean. I don’t think the deal is pressing i
. Rather, I’d say is the lack of support for insert mode. Just look at the number of mappings of the rest of the modes and the number of mappings of insert mode. Even writing in the prompt is easier (for those that know they keybindings, which is not my case ). Kakoune forces mode switching even for the simplest tasks, like moving a word behind to change something.
In my opinion, Kakoune is sublime for editing code projects where you basically make small changes everywhere, but sometimes you want to write a lot of new text/code.
I will write here my customization to tackle some of this issues. Feel free to comment with additions and reviews (criticism is accepted).
Also, a lot of the code is actually not mine. I’ll try to link to the original source.
General Shortcuts
Return to normal mode
Most of us find the esc
key to be far way from our fingers, specially if you keep your hands more close to the lower keys to reach ctrl
and alt
. That’s why people tend to map key combinations like jd
to escape insert mode. I feel it’s better to map a ctrl-key
combination and use it everywhere: to escape insert mode, to escape prompt mode, to toogle the completition menu off when it’s disturbing, etc.
I’ve started to use <c-q>
but I’m looking to change it because you get used to pressing it and it closes most programs.
TODO: looking for a new combination
Edit file
For me, editing files or changing between them feels more like an action than a command I want to type (that’s why we have ga
). That’s why it makes sense to map a combination. I’d use <c-o>
because that’s what most programs use, but it’s already used in Kakoune. That’s why I decided to map <c-e>
(ctrl-edit
) to the edit command and <c-E>
to the buffer command.
Close buffer
The same thing applies for closing buffers, specially draft buffers. I’d rather not type : db
and press enter
. To maintain compatibility with browsers and most text editors, I mapped <c-w>
to this functionality.
Save file
What about saving a file? It’s <c-s>
in most programs, but in Kakoune <c-s>
is saving selections. I decided I rarely need to save my selections manually (even less by using phantom-selections). Therefore, I mapped <c-s>
to save and created a shortcut in user mode to save selections.
Open prompt in insert
<c-p>
(ctrl-prompt
) instead of <a-;>:
Comment Lines
Other editors like Visual Studio Code use the mapping <c-/>
for this. Unfortunately, we cannot map it in terminals. For the moment, I’m using #
in normal mode but I’d be glad to have a key combination that could also be used in insert mode.
TODO: Look for a new key combination
<tab>
/<s-tab>
for completition
Then again, we do use tab
everywhere for completition, so I think we should use it here too. Additionally, I like to be able to close the completition menu with esc
or the mapping that you usually use for this purpose.
Check @mawww’s kakrc, from where I took the implementation for this.
Chart and code
Combination | Mode(s) | Action |
---|---|---|
<c-q> |
Not normal | Return to normal mode (esc ) |
<c-e> |
Insert/Normal | Edit File |
<c-b> |
Insert/Normal | Change to buffer |
<c-w> |
Insert/Normal | Close buffer |
<c-s> |
Insert/Normal | Save file |
<c-p> |
Insert | Open Prompt |
# |
Normal | Comment Lines |
<tab>/<s-tab> |
Insert | Completition selection |
<esc>/<c-q> |
Insert | Toggle completition menu |
# General Short-cuts and custom mappings
# ––––––––––––––––––––––––––––––––––––––
# <c-q> - Return to normal mode
map global insert <c-q> <esc>
map global prompt <c-q> <esc>
# <c-e> - Edit file, <c-w> - Close buffer
map global normal <c-e> ': edit '
map global insert <c-e> '<esc>: edit '
map global normal <c-E> ': buffer '
map global insert <c-E> '<esc>: buffer '
map global normal <c-w> ': db<ret>'
map global insert <c-w> '<esc>: db<ret>'
# <c-s> - Save file
map global normal <c-s> ': w<ret>'
map global insert <c-s> '<a-;>: w<ret>'
map global user <c-s> '<c-s>'
# <c-p> - Open prompt in insert
map global insert <c-p> '<a-;>:'
# <#> Comments lines
map global normal '#' ': comment-line<ret>'
# <tab>/<s-tab> for completion selection
hook global InsertCompletionShow .* %{
map window insert <tab> '<c-n>'
map window insert <s-tab> '<c-p>'
map window insert <esc> '<c-o>'
map window insert <c-q> '<c-o>'
}
hook global InsertCompletionHide .* %{
unmap window insert <tab> '<c-n>'
unmap window insert <s-tab> '<c-p>'
unmap window insert <esc> '<c-o>'
unmap window insert <c-q> '<c-o>'
}
Improved Movement
Better home movement
When coding, I feel like most of the time we’d rather move to the the first non-blank character than to the beginning of the line (gi
), but sometimes you do want to move to the beginning of the line. My approach is, <home>
behaves as gi
unless you are already in the first non-blank character, in which case it behaves as gh
. Same functionality for <s-home>
.
Ctrl-arrow
to move between words and paragraphs
This is something most browsers and apps allow in their text fields. In Kakoune we shouldn’t need them because we have w
/b
and ]p
/[p
, but in reality, I believe it is very comfortable to be able to do it without leaving insert mode.
Chart and Code
Combination | Mode(s) | Action |
---|---|---|
<home> /<s-home> |
Insert/Normal | Improved home movement |
<c-left> /<c-right> |
Insert/Normal | Move around a word |
<c-down> /<c-up > |
Insert/Normal | Move around paragraph |
# Movement
# ––––––––––––––––––––––––––––––––––––––
# Home moves/expand to the begining of line/non blank depending on position
define-command -override -hidden home-expansion %{
# Run each selection independently so that the
# test does not just remove failing selections.
eval -itersel %{
try %{
exec -draft %{ <a-h><a-k>\A\h+.\z<ret> } # check that the preceeding characters are horizontal whitespaces
exec Gh # if the previous line does not fail, go to begining of line
} catch %{
exec Gi # if it failed, go to first non blank character
}
}
}
map global insert <home> '<esc>: home-expansion<ret>;i'
# If you want to implement the following section, use this instead:
# map global insert <s-home> '<esc>: home-expansion<ret>: enter-user-mode fixsel'
map global insert <s-home> '<esc>: home-expansion<ret>i'
map global normal <home> ': home-expansion<ret>;'
map global normal <s-home> ': home-expansion<ret>'
# Ctrl for moving objects in insert mode
map global insert <c-left> '<a-;>b<a-;>;'
map global insert <c-right> '<a-;>w<a-;>;'
map global insert <c-up> '<a-;>[p<a-;>;'
map global insert <c-down> '<a-;>]p<a-;>;'
map global normal <c-left> 'b;'
map global normal <c-right> 'w;'
map global normal <c-up> '[p;'
map global normal <c-down> ']p;'
Insert Mode Selections
Just as much as we want to move inside insert mode, we’d like to be able to select things and do quick fixes. It’s pretty easy to implement <c-s-left>
to select a word to the left, but we cannot do anything to selections while we are in insert mode. For me, quick fixes means deleting, yanking, changing or surrounding text.
I decided I could achieve that functionality via a new user mode. I’ve called it fixsel
(fix selection) because I like mode names with 6 letters for my scripts (like insert and normal).
Entering fixsel
mode
The combinations <ctrl-s-arrow>
behave just like <ctrl-arrow>
but they add a word/paragraph to the current selection and enter fixsel
mode (which falls back to normal mode for complex tasks).
The same applies for <s-arrow>
but with a single character.
It is also possible to continue expanding the selection in fixsel
mode.
fixsel
mappings
In the new mode, you can insert(i
), append(a
), change(c
), yank(y
) and delete(d
) as usual. There’s also a mapping that will result familiar for newcomers: <backspace>
behaves as c
. Deleting a word you mistyped is as “natural” (ofc, this is subjective) as pressing <c-s-left>
to select it and pressing <backspace>
, after which you can continue editing.
In addition, we have some already mentioned mapping to surround the selection: "
, '
, (
and {
. They depend on the plugin kakoune-surround by @h-youhei.
Chart and code
Combination | Mode(s) | Action |
---|---|---|
<home> /<s-home> |
Insert/Normal | Improved home movement |
<c-s-left> /<c-s-right> |
Insert/Normal | Select around a word and enter quickfix |
<c-down> /<c-up > |
Insert/Normal | Select around paragraph and enter quickfix |
<backspace> |
Fixsel | Delete selection and edit (c ) |
" /' /( /{ |
Fixsel | Surround selection |
# Selection
# ––––––––––––––––––––––––––––––––––––––
declare-user-mode fixsel
map global fixsel i 'i'
map global fixsel a 'a'
map global fixsel c 'c'
map global fixsel <backspace> 'c'
map global fixsel y 'y'
map global fixsel d 'd'
map global fixsel m 'm'
# map global fixsel H 'H'
# map global fixsel L 'L'
# map global fixsel K 'K'
# map global fixsel J 'J'
map global fixsel <s-left> 'H: enter-user-mode fixsel<ret>'
map global fixsel <s-right> 'L: enter-user-mode fixsel<ret>'
map global fixsel <s-up> 'K: enter-user-mode fixsel<ret>'
map global fixsel <s-down> 'J: enter-user-mode fixsel<ret>'
# map global fixsel B 'B'
# map global fixsel W 'W'
# map global fixsel { '{'
# map global fixsel } '}'
map global fixsel <c-s-left> 'B: enter-user-mode fixsel<ret>'
map global fixsel <c-s-right> 'W: enter-user-mode fixsel<ret>'
map global fixsel <c-s-up> '{p: enter-user-mode fixsel<ret>'
map global fixsel <c-s-down> '}p: enter-user-mode fixsel<ret>'
map global fixsel <s-home> ': home-expansion<ret>: enter-user-mode fixsel<ret>'
map global fixsel <s-end> 'Gl: enter-user-mode fixsel<ret>'
map global fixsel <"> ': surround<ret>"' -docstring 'surround with ""'
map global fixsel <'> ": surround<ret>'" -docstring "surround with ''"
map global fixsel <(> ': surround<ret>(' -docstring 'surround with ()'
map global fixsel <{> ': surround<ret>{' -docstring 'surround with {}'
map global insert <s-left> '<esc>H: enter-user-mode fixsel<ret>'
map global insert <s-right> '<esc>L: enter-user-mode fixsel<ret>'
map global insert <s-up> '<esc>K: enter-user-mode fixsel<ret>'
map global insert <s-down> '<esc>J: enter-user-mode fixsel<ret>'
map global insert <c-s-left> '<esc>B: enter-user-mode fixsel<ret>'
map global insert <c-s-right> '<esc>W: enter-user-mode fixsel<ret>'
map global insert <c-s-up> '<esc>{p: enter-user-mode fixsel<ret>'
map global insert <c-s-down> '<esc>}p: enter-user-mode fixsel<ret>'
map global normal <c-s-left> 'B: enter-user-mode fixsel<ret>'
map global normal <c-s-right> 'W: enter-user-mode fixsel<ret>'
map global normal <c-s-up> '{p: enter-user-mode fixsel<ret>'
map global normal <c-s-down> '}p: enter-user-mode fixsel<ret>'
Alternative Implementation
If you’d prefer to select things and jump to normal mode instead of entering a special mode, it’s very easy to adapt the code above.
Powerful Functionality
Sublime’s and Visual Studio Code <ctrl-d>
Thanks to @andreyorst, we can enjoy the functionality in Kakoune as well. Have a look.
Move line’s above or below
Check @alexherbo2’s plugin. I suggest you map the command to <a-down>
and <a-up>
.
Kakoune phantom selections
Sublime’s <c-d>
mapping became very popular. I think @occivink’s phantom selections plugin can make Kakoune very appealing.
I’ve found very useful to loop and modify individual selections with <tab>
. We already discussed about it in another blog post. <c-g>
is my new favourite mapping along with <c-d>
.
Automatically grouping insert modifications in undo groups
TODO: I’d like to implement a hook that would group modifications into a single undo group after some time (every 5s?). This would also favor big inserts.
Conclusion
I think these modifications make Kakoune feel more similar to other programs without compromising modal editing and Kakoune’s view. I would also claim that it makes easier to work in insert mode, but everyone has their own tastes.
The part I am the least comfortable with is the new mode fixsel
. I continue to think in ways of making this behave better.