A very simple simulation of kakoune inside of emacs. Most of the heavy lifting is provided by ryo-modal-mode, multiple-cursors.el, and expand-region. For extended customization, it is recommended that you install these packages and customize their interfaces directly.
straight.el users can simply call
(use-package kakoune :straight (kakoune :host github :repo "jmorag/kakoune.el"))
Once this package hopefully makes its way to MELPA, the
straight argument will be omittable. Otherwise, you can manually add kakoune.el to your load-path and call
This is not meant to be a feature-complete emulation of kakoune, like evil is to vim. Evil is a massive undertaking, which reimplements a huge number of editing primitives from scratch. By contrast, kakoune.el uses emacs’s native behavior wherever possible. In particular, it binds absolutely no keys in “insert mode,” letting you gradually adjust to “normal mode” at your own pace. To actually access normal mode, you can either call
M-x, or bind a key globally. For the true modal experience:
(global-set-key (kbd "<escape>") 'ryo-modal-mode).
Notable differences from Kakoune
- The ‘v’ family of commands is not implemented here. By default, ‘v’ is bound to
expand-region, which I personally find vastly more useful.
- Searching is very different. A cursory glance at evil’s
evil-search-forwardfunction suggests that implementing a search model where ‘n’ and ‘N’ take you forwards and backwards in the search ring while not in
isearch-modeis non-trivial. I just use emacs’s native incremental search with
C-r. If I’m wrong about getting ‘n’ and ‘N’ functionality being difficult, though, please let me know.
- This package also includes a couple bindings from
evil-exchangethat aren’t present in the original.
- ‘q’ and ‘Q’ don’t toggle and execute macros. This emacswiki article seems to suggest that it’s possible to have one key start and end a macro, but it’s been buggy in my experience, so I just use the default
C-x (and friends.
- ’;’ doesn’t shrink regions by default. This is simply my personal preference. You can get this behavior with
(ryo-modal-key ";" 'kakoune-deactivate-mark).
- ‘>’ and ‘<’ behave normally on when the region is active, but when given a count when the region is inactive, they indent that many lines instead of indenting the current line that many times. This is also my personal preference, but if anyone wants kakoune’s default behavior for this, open an issue and I’ll change it.
- I was never a full time Kakoune user, so there will certainly be many other small things that aren’t covered here. Please open issues or PR’s if you feel that including omissions would be useful to you or others.
The following is my entire configuration that I use every day with respect to overriding and extending the functionality provided by this package.
(use-package kakoune :straight (kakoune :host github :repo "jmorag/kakoune.el") ;; fd to escape insert mode is something I picked up from trying Spacemacs and can't shake :chords ("fd" . ryo-enter) ;; Having a non-chord way to escape is important, since key-chords don't work in macros :bind ("C-z" . ryo-modal-mode) :hook (after-init . my/kakoune-setup) :config (defun my/kakoune-setup () "Call kakoune-setup-keybinds and then add some personal config." (kakoune-setup-keybinds) (setq ryo-modal-cursor-type 'box) (add-hook 'prog-mode-hook #'ryo-enter) (define-key ryo-modal-mode-map (kbd "SPC h") 'help-command) ;; Access all C-x bindings easily (define-key ryo-modal-mode-map (kbd "z") ctl-x-map) (ryo-modal-keys ("," save-buffer) ("P" counsel-yank-pop) ("m" mc/mark-next-like-this) ("M" mc/skip-to-next-like-this) ("n" mc/mark-previous-like-this) ("N" mc/skip-to-previous-like-this) ("M-m" mc/edit-lines) ("*" mc/mark-all-like-this) ("v" er/expand-region) ("C-v" set-rectangular-region-anchor) ("M-s" mc/split-region) (";" (("q" delete-window) ("v" split-window-horizontally) ("s" split-window-vertically))) ("C-h" windmove-left) ("C-j" windmove-down) ("C-k" windmove-up) ("C-l" windmove-right) ("C-u" scroll-down-command :first '(deactivate-mark)) ("C-d" scroll-up-command :first '(deactivate-mark))))) ;; This overrides the default mark-in-region with a prettier-looking one, ;; and provides a couple extra commands (use-package visual-regexp :ryo ("s" vr/mc-mark) ("?" vr/replace) ("M-/" vr/query-replace)) ;; Emacs incremental search doesn't work with multiple cursors, but this fixes that (use-package phi-search :bind (("C-s" . phi-search) ("C-r" . phi-search-backward))) ;; Probably the first thing you'd miss is undo and redo, which requires an extra package ;; to work like it does in kakoune (and almost every other editor). (use-package undo-tree :config (global-undo-tree-mode) :ryo ("u" undo-tree-undo) ("U" undo-tree-redo) ("SPC u" undo-tree-visualize) :bind (:map undo-tree-visualizer-mode-map ("h" . undo-tree-visualize-switch-branch-left) ("j" . undo-tree-visualize-redo) ("k" . undo-tree-visualize-undo) ("l" . undo-tree-visualize-switch-branch-right)))
It’s kind of a lot, but it does provide a good example of how to override defaults and bind your own keys to your own commands. I use this daily, along with the rest of the commands provided by the package, and am very happy with it.
- There will be many commands that, when run for the first time with multiple active selections, will prompt you “Run <command> for all cursors? (y/n).” Hitting ‘y’ will add the command to an .mc-lists file in your .emacs.d and save your preference. There should be a way to add all the kakoune-* functions to this list by default, but because ryo adds hashes to the beginnings of commands, this isn’t as easy as it could be. This problem is usually just a one-time annoyance, but it can be irritating. I would gladly accept a PR to fix it.
- Certain commands, like
kakoune-select-to-charprompt for input once per cursor, instead of using the first input for each one. This behavior is unlike Kakoune’s but shouldn’t be. Fixing it shouldn’t be too difficult, but requires more knowledge of
multiple-cursors.el’s implementation than I have.
- When you first download this package, it is possible that non-primary cursors will have a weird color in insert mode. I have no idea why that is, but restarting emacs should take care of it.