[
  {
    "path": "LICENSE",
    "content": "The MIT License\n\nCopyright (c) 2018-2021 Joseph Morag\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.org",
    "content": "* kakoune.el\nA very simple simulation of kakoune inside of emacs. Most of the heavy lifting is provided by [[https://github.com/Kungsgeten/ryo-modal][ryo-modal-mode]], [[https://github.com/magnars/multiple-cursors.el][multiple-cursors.el]], and [[https://github.com/magnars/expand-region.el][expand-region]]. For extended customization, it is recommended that you install these packages and customize their interfaces directly.\n\n** Installation\n   =kakoune.el= is on melpa, so for users of =use-package=, installation should be as simple as\n#+BEGIN_SRC elisp\n(use-package kakoune)\n#+END_SRC\n.\n** Goals\n   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 =(ryo-modal-mode)= with =M-x=, or bind a key globally. For the true modal experience: =(global-set-key (kbd \"<escape>\") 'ryo-modal-mode)=.\n** Notable differences from Kakoune\n- The 'v' family of commands is not implemented here. By default, 'v' is bound to =expand-region=, which I personally find vastly more useful.\n- Searching is very different. A cursory glance at evil's =evil-search-forward= function suggests that implementing a search model where 'n' and 'N' take you forwards and backwards in the search ring while not in =isearch-mode= is non-trivial. I just use emacs's native incremental search with =C-s= and =C-r=. If I'm wrong about getting 'n' and 'N' functionality being difficult, though, please let me know.\n- This package also includes a couple bindings from =evil-unimpaired= and =evil-exchange= that aren't present in the original.\n- 'q' and 'Q' don't toggle and execute macros. [[https://www.emacswiki.org/emacs/KeyboardMacros][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.   \n- ';' doesn't shrink regions by default. This is simply my personal preference. You can get this behavior with =(ryo-modal-key \";\" 'kakoune-deactivate-mark)=.\n- '>' 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.  \n- 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.\n** Configuration\n\nThe following is my entire configuration that I use every day with respect to overriding and extending the functionality provided by this package.\n#+BEGIN_SRC elisp\n(use-package kakoune\n  ;; Having a non-chord way to escape is important, since key-chords don't work in macros\n  :bind (\"C-z\" . ryo-modal-mode)\n  :hook (after-init . my/kakoune-setup)\n  :config\n  (defun ryo-enter () \"Enter normal mode\" (interactive) (ryo-modal-mode 1))\n  (defun my/kakoune-setup ()\n      \"Call kakoune-setup-keybinds and then add some personal config.\"\n      (kakoune-setup-keybinds)\n      (setq ryo-modal-cursor-type 'box)\n      (add-hook 'prog-mode-hook #'ryo-enter)\n      (define-key ryo-modal-mode-map (kbd \"SPC h\") 'help-command)\n      ;; Access all C-x bindings easily\n      (define-key ryo-modal-mode-map (kbd \"z\") ctl-x-map)\n      (ryo-modal-keys\n       (\",\" save-buffer)\n       (\"P\" counsel-yank-pop)\n       (\"m\" mc/mark-next-like-this)\n       (\"M\" mc/skip-to-next-like-this)\n       (\"n\" mc/mark-previous-like-this)\n       (\"N\" mc/skip-to-previous-like-this)\n       (\"M-m\" mc/edit-lines)\n       (\"*\" mc/mark-all-like-this)\n       (\"v\" er/expand-region)\n       (\"C-v\" set-rectangular-region-anchor)\n       (\"M-s\" mc/split-region)\n       (\";\" ((\"q\" delete-window)\n             (\"v\" split-window-horizontally)\n             (\"s\" split-window-vertically)))\n       (\"C-h\" windmove-left)\n       (\"C-j\" windmove-down)\n       (\"C-k\" windmove-up)\n       (\"C-l\" windmove-right)\n       (\"C-u\" scroll-down-command :first '(deactivate-mark))\n       (\"C-d\" scroll-up-command :first '(deactivate-mark)))))\n\n;; This overrides the default mark-in-region with a prettier-looking one,\n;; and provides a couple extra commands\n(use-package visual-regexp\n  :ryo\n  (\"s\" vr/mc-mark)\n  (\"?\" vr/replace)\n  (\"M-/\" vr/query-replace))\n\n;; Emacs incremental search doesn't work with multiple cursors, but this fixes that\n(use-package phi-search\n  :bind ((\"C-s\" . phi-search)\n         (\"C-r\" . phi-search-backward)))\n\n;; Probably the first thing you'd miss is undo and redo, which requires an extra package\n;; to work like it does in kakoune (and almost every other editor).\n(use-package undo-tree\n  :config\n  (global-undo-tree-mode)\n  :ryo\n  (\"u\" undo-tree-undo)\n  (\"U\" undo-tree-redo)\n  (\"SPC u\" undo-tree-visualize)\n  :bind (:map undo-tree-visualizer-mode-map\n              (\"h\" . undo-tree-visualize-switch-branch-left)\n              (\"j\" . undo-tree-visualize-redo)\n              (\"k\" . undo-tree-visualize-undo)\n              (\"l\" . undo-tree-visualize-switch-branch-right)))\n\n#+END_SRC\nIt'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.\n\n** Known bugs\n- 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.\n- Certain commands, like =kakoune-replace-char= and =kakoune-select-to-char= prompt 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.\n- 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.  \n"
  },
  {
    "path": "kakoune-exchange.el",
    "content": ";;; kakoune-exchange.el --- Exchange function for kakoune.el -*- lexical-binding: t; -*-\n;; Author: Joseph Morag <jm4157@columbia.edu>\n;;; Commentary:\n;; A ripoff of evil-exchange https://github.com/Dewdrops/evil-exchange, which is a port of Tim Pope's vim-exchange. Provides two commands,\n;; (kakoune-exchange) and (kakoune-exchange-cancel)\n\n;;; Code:\n(require 'cl-lib)\n(require 'ryo-modal)\n(require 'expand-region)\n(require 'multiple-cursors)\n\n(defcustom kakoune-exchange-highlight-face 'highlight\n  \"Face used to highlight marked area.\"\n  :type 'sexp\n  :group 'kakoune-exchange)\n\n(defvar kakoune-exchange--position nil \"Text position which will be exchanged.\")\n\n(defvar kakoune-exchange--overlays nil \"Overlays used to highlight marked area.\")\n\n(defun kakoune-exchange--highlight (beg end)\n  \"Highlight BEG to END for exchange.\"\n  (let ((o (make-overlay beg end nil t nil)))\n    (overlay-put o 'face kakoune-exchange-highlight-face)\n    (add-to-list 'kakoune-exchange--overlays o)))\n\n(defun kakoune-exchange--clean ()\n  \"Clean up after exchange.\"\n  (setq kakoune-exchange--position nil)\n  (mapc 'delete-overlay kakoune-exchange--overlays)\n  (setq kakoune-exchange--overlays nil))\n\n(defun kakoune-exchange (beg end)\n  \"Mark the region from BEG to END for exchange.\"\n  (interactive \"r\")\n  (let ((beg-marker (copy-marker beg t))\n        (end-marker (copy-marker end nil)))\n    (if (null kakoune-exchange--position)\n        ;; call without kakoune-exchange--position set: store region\n        (progn\n          (setq kakoune-exchange--position (list (current-buffer) beg-marker end-marker))\n          ;; highlight area marked to exchange\n          (kakoune-exchange--highlight beg end))\n      ;; secondary call: do exchange\n      (cl-destructuring-bind\n          (orig-buffer orig-beg orig-end) kakoune-exchange--position\n        (kakoune-exchange--do-swap (current-buffer) orig-buffer\n                                   beg-marker end-marker\n                                   orig-beg orig-end\n                                   #'delete-and-extract-region #'insert)))))\n\n(defun kakoune-exchange--do-swap (curr-buffer orig-buffer curr-beg curr-end orig-beg\n                                              orig-end extract-fn insert-fn)\n  \"This function does the real exchange work. Here's the detailed steps:\n\n1. call EXTRACT-FN with ORIG-BEG and ORIG-END to extract ORIG-TEXT\nfrom ORIG-BUFFER.\n2. call EXTRACT-FN with CURR-BEG and CURR-END to extract CURR-TEXT\nfrom CURR-BUFFER.\n3. go to ORIG-BEG and then call INSERT-FN with CURR-TEXT.\n4. go to CURR-BEG and then call INSERT-FN with ORIG-TEXT.\nAfter step 2, the two markers of the same beg/end pair (curr or orig)\nwill point to the same position. So if ORIG-BEG points to the same position\nof CURR-END initially, ORIG-BEG and CURR-BEG will point to the same position\nbefore step 3. Because CURR-BEG is a marker which moves after insertion, the\ninsertion in step 3 will push it to the end of the newly inserted text,\nthus resulting incorrect behaviour.\nTo fix this edge case, we swap two extracted texts before step 3 to\neffectively reverse the (problematic) order of two `kakoune-exchange' calls.\"\n  (if (eq curr-buffer orig-buffer)\n      ;; in buffer exchange\n      (let ((adjacent  (equal (marker-position orig-beg) (marker-position curr-end)))\n            (orig-text (funcall extract-fn orig-beg orig-end))\n            (curr-text (funcall extract-fn curr-beg curr-end)))\n        ;; swaps two texts if adjacent is set\n        (let ((orig-text (if adjacent curr-text orig-text))\n              (curr-text (if adjacent orig-text curr-text)))\n          (save-excursion\n            (goto-char orig-beg)\n            (funcall insert-fn curr-text)\n            (goto-char curr-beg)\n            (funcall insert-fn orig-text))))\n    ;; exchange across buffers\n    (let ((orig-text (with-current-buffer orig-buffer\n                       (funcall extract-fn orig-beg orig-end)))\n          (curr-text (funcall extract-fn curr-beg curr-end)))\n      (save-excursion\n        (with-current-buffer orig-buffer\n          (goto-char orig-beg)\n          (funcall insert-fn curr-text))\n        (goto-char curr-beg)\n        (funcall insert-fn orig-text))))\n  (kakoune-exchange--clean))\n\n(defun kakoune-exchange-cancel ()\n  \"Cancel current pending exchange.\"\n  (interactive)\n  (if (null kakoune-exchange--position)\n      (message \"No pending exchange\")\n    (kakoune-exchange--clean)\n    (message \"Exchange cancelled\")))\n\n(provide 'kakoune-exchange)\n;;; kakoune-exchange.el ends here\n"
  },
  {
    "path": "kakoune-shell-commands.el",
    "content": "(defun kakoune-shell-pipe ()\n  \"Run a shell command on each of the current regions separately and replace the current regions with its output.\"\n  (interactive)\n  (let ((command (read-string \"Pipe: \")))\n    (mc/for-each-cursor-ordered\n     (shell-command-on-region (mc/cursor-beg cursor)\n                              (mc/cursor-end cursor)\n                              command\n                              nil\n                              1))))\n(defun kakoune-shell-command ()\n  \"Run a shell command on each of the current regions separately and insert its output before the respective regions.\"\n  (interactive)\n  (mc/save-excursion\n   (let ((command (read-string \"Pipe: \")))\n     (mc/for-each-cursor-ordered\n      (mc/save-excursion\n       (goto-char (mc/cursor-beg cursor))\n       (insert\n        (with-output-to-string\n          (shell-command-on-region (mc/cursor-beg cursor)\n                                   (mc/cursor-end cursor)\n                                   command\n                                   standard-output))))))))\n\n(provide 'kakoune-shell-commands)\n;;; kakoune-shell-commands ends here\n"
  },
  {
    "path": "kakoune-unimpaired.el",
    "content": ";;; kakoune-unimpaired.el --- \"Unimpaired\" plugin for kakoune.el -*- lexical-binding: t; -*-\n\n;; Author: Joseph Morag <jm4157@columbia.edu>\n;;; Commentary:\n;; Provides a couple functions similar to Tim Pope's vim-unimpaired package\n\n;;; Code:\n(require 'cl-lib)\n(require 'ryo-modal)\n(require 'expand-region)\n(require 'multiple-cursors)\n\n(defun kakoune-insert-line-below (count)\n  \"Insert COUNT empty lines below the current line.\"\n  (interactive \"p\")\n  (save-excursion\n    (end-of-line)\n    (open-line count)))\n\n(defun kakoune-insert-line-above (count)\n  \"Insert COUNT empty lines above the current line.\"\n  (interactive \"p\")\n  (save-excursion\n    (end-of-line 0)\n    (open-line count)))\n\n(defun kakoune-paste-above (count)\n  \"Paste (yank) COUNT times above the current line.\"\n  (interactive \"p\")\n  (save-excursion\n    (dotimes (_ count) (end-of-line 0)\n\t     (newline)\n\t     (yank))))\n\n(defun kakoune-paste-below (count)\n  \"Paste (yank) COUNT times below the current line.\"\n  (interactive \"p\")\n  (save-excursion\n    (dotimes (_ count) (end-of-line)\n\t     (newline)\n\t     (yank))))\n\n(provide 'kakoune-unimpaired)\n;;; kakoune-unimpaired ends here\n"
  },
  {
    "path": "kakoune-utils.el",
    "content": ";;; kakoune-utils.el --- Utilities for kakoune.el -*- lexical-binding: t; -*-\n\n;; Author: Joseph Morag <jm4157@columbia.edu>\n;;; Commentary:\n;; Provides utility functions for kakoune.el\n\n;;; Code:\n(require 'cl-lib)\n(require 'ryo-modal)\n(require 'expand-region)\n(require 'multiple-cursors)\n\n(defun kakoune-insert-mode () \"Return to insert mode.\"\n       (interactive)\n       (ryo-modal-mode 0))\n\n(defun kakoune-set-mark-if-inactive () \"Set the mark if it isn't active.\"\n       (interactive)\n       (unless (use-region-p) (set-mark (point))))\n\n(defun kakoune-set-mark-here () \"Set the mark at the location of the point.\"\n       (interactive) (set-mark (point)))\n\n(defun kakoune-deactivate-mark ()\n  \"Deactivate the mark.\n\nDeactivate the mark unless mark-region-mode is active.\"\n  (interactive)\n  (unless rectangle-mark-mode (deactivate-mark)))\n\n(defun kakoune-backward-symbol (count)\n  \"Move backward COUNT times by symbol.\"\n  (interactive \"p\")\n  (forward-symbol (- count)))\n\n(defun kakoune-backward-same-syntax (count)\n  \"Move backward COUNT times by same syntax blocks.\"\n  (interactive \"p\")\n  (forward-same-syntax (- count)))\n\n(defvar kakoune-last-t-or-f ?f\n  \"Using t or f command sets this variable.\")\n\n(defvar-local kakoune-last-char-selected-to \" \"\n  \"This variable is updated by kakoune-select-to-char.\")\n\n(defun kakoune-select-up-to-char (arg char)\n  \"Select up to, but not including ARGth occurrence of CHAR.\nCase is ignored if `case-fold-search' is non-nil in the current buffer.\nGoes backward if ARG is negative; error if CHAR not found.\nIgnores CHAR at point.\"\n  (interactive \"p\\ncSelect up to char: \")\n  (setq kakoune-last-char-selected-to char)\n  (setq kakoune-last-t-or-f ?t)\n  (let ((direction (if (>= arg 0) 1 -1)))\n    (forward-char direction)\n    (unwind-protect\n\t    (search-forward (char-to-string char) nil nil arg)\n\t  (backward-char direction))\n    (point)))\n\n(defun kakoune-select-to-char (arg char)\n  \"Select up to, and including ARGth occurrence of CHAR.\nCase is ignored if `case-fold-search' is non-nil in the current buffer.\nGoes backward if ARG is negative; error if CHAR not found.\nIgnores CHAR at point.\"\n  (interactive \"p\\ncSelect to char: \")\n  (setq kakoune-last-char-selected-to char)\n  (setq kakoune-last-t-or-f ?f)\n  (let ((direction (if (>= arg 0) 1 -1)))\n    (forward-char direction)\n    (unwind-protect\n\t    (search-forward (char-to-string char) nil nil arg))\n    (point)))\n\n(defun kakoune-select-again (&optional count)\n  \"Expand the selection COUNT times to whatever the last 't' command was.\"\n  (interactive \"p\")\n  (if (eq kakoune-last-t-or-f ?t)\n      (kakoune-select-up-to-char count kakoune-last-char-selected-to)\n    (kakoune-select-to-char count kakoune-last-char-selected-to)))\n\n(defun kakoune-x (count)\n  \"Select COUNT lines from the current line.\n\nNote that kakoune's x doesn't behave exactly like this,\nbut I like this behavior better.\"\n  (interactive \"p\")\n  (beginning-of-line)\n  (set-mark (point))\n  (forward-line count))\n\n(defun kakoune-X (count)\n  \"Extend COUNT lines from the current line.\"\n  (interactive \"p\")\n  (beginning-of-line)\n  (unless (use-region-p) (set-mark (point)))\n  (forward-line count))\n\n(defun kakoune-d (count)\n  \"Kill selected text or COUNT chars.\"\n  (interactive \"p\")\n  (if (use-region-p)\n      (kill-region (region-beginning) (region-end))\n    (delete-char count t)))\n\n(defun kakoune-p (count)\n  \"Yank COUNT times after the point.\"\n  (interactive \"p\")\n  (dotimes (_ count) (save-excursion (yank))))\n\n(defun kakoune-downcase ()\n  \"Downcase region.\"\n  (interactive)\n  (if (use-region-p)\n      (downcase-region (region-beginning) (region-end))\n    (downcase-region (point) (+ 1 (point)))))\n\n(defun kakoune-upcase ()\n  \"Upcase region.\"\n  (interactive)\n  (if (use-region-p)\n      (upcase-region (region-beginning) (region-end))\n    (upcase-region (point) (1+ (point)))))\n\n(defun kakoune-replace-char (char)\n  \"Replace selection with CHAR.\"\n  (interactive \"cReplace with char: \")\n  (mc/execute-command-for-all-cursors\n   (lambda () (interactive)\n     (if (use-region-p)\n         (progn (let ((region-size (- (region-end) (region-beginning))))\n\t              (delete-region (region-beginning) (region-end))\n\t              (mc/save-excursion\n\t\t           (insert-char char region-size t))))\n       (progn (delete-region (point) (1+ (point)))\n\t          (mc/save-excursion\n\t           (insert-char char)))))))\n\n(defun kakoune-replace-selection ()\n  \"Replace selection with killed text.\"\n  (interactive)\n  (if (use-region-p)\n      (progn (delete-region (region-beginning) (region-end))\n\t         (yank))\n    (progn (delete-region (point) (1+ (point)))\n\t       (yank))))\n\n(defun kakoune-o (count)\n  \"Open COUNT lines under the cursor and go into insert mode.\"\n  (interactive \"p\")\n  (end-of-line)\n  (dotimes (_ count)\n    (electric-newline-and-maybe-indent)))\n\n(defun kakoune-O (count)\n  \"Open COUNT lines above the cursor and go into insert mode.\"\n  (interactive \"p\")\n  (beginning-of-line)\n  (dotimes (_ count)\n    (newline)\n    (forward-line -1)))\n\n(defun kakoune-join ()\n  \"Join the next line to the current one.\"\n  (interactive) (join-line 1))\n\n(defun kakoune-Y (count)\n  \"Copy to the end of COUNT lines.\"\n  (interactive \"p\")\n  (save-excursion\n    (let ((cur (point)))\n      (move-end-of-line count)\n      (kill-ring-save cur (point)))))\n\n(defun kakoune-indent-right (count)\n  \"Indent the region or COUNT lines right to tab stop.\"\n  (interactive \"p\")\n  (if (use-region-p)\n      (progn (indent-rigidly (region-beginning) (region-end) 2)\n             (setq deactivate-mark nil))\n    (let ((beg (save-excursion (beginning-of-line) (point)))\n          (end (save-excursion (forward-line count) (point))))\n      (indent-rigidly beg end 2))))\n\n(defun kakoune-indent-left (count)\n  \"Indent the region or COUNT lines left to tab stop.\"\n  (interactive \"p\")\n  (if (use-region-p)\n      (progn (indent-rigidly (region-beginning) (region-end) -2)\n             (setq deactivate-mark nil))\n    (let ((beg (save-excursion (beginning-of-line) (point)))\n          (end (save-excursion (forward-line count) (point))))\n      (indent-rigidly beg end -2))))\n\n(defun kakoune-gg (count)\n  \"Go to the beginning of the buffer or the COUNTth line.\"\n  (interactive \"p\")\n  (goto-char (point-min))\n  (when count (forward-line (1- count))))\n\n;; Until this function is accepted upstream, we inline it here\n(defun mc/split-region (beg end search)\n  \"Split region each time SEARCH occurs between BEG and END.\n\nThis can be thought of as an inverse to `mc/mark-all-in-region'.\"\n  (interactive \"r\\nsSplit on: \")\n  (let ((case-fold-search nil))\n    (if (string= search \"\")\n        (user-error \"Empty search term\")\n      (progn\n        (mc/remove-fake-cursors)\n        (goto-char beg)\n        (push-mark beg)\n        (while (search-forward search end t)\n          (save-excursion\n            (goto-char (match-beginning 0))\n            (mc/create-fake-cursor-at-point))\n          (push-mark (match-end 0)))\n        (unless (= (point) end)\n          (goto-char end))\n        (mc/maybe-multiple-cursors-mode)))))\n\n(provide 'kakoune-utils)\n;;; kakoune-utils.el ends here\n"
  },
  {
    "path": "kakoune.el",
    "content": ";;; kakoune.el --- A simulation, but not emulation, of kakoune -*- lexical-binding: t; -*-\n\n;; Author: Joseph Morag <jm4157@columbia.edu>\n;; Version: 0.1\n;; URL: https://github.com/jmorag/kakoune.el\n;; Package-Requires: ((ryo-modal \"0.45\") (multiple-cursors \"1.4\") (expand-region \"0.11.0\") (emacs \"25.1\"))\n;; MIT License\n\n;;; Commentary:\n;; This package provides many, but not all of the editing primitives in the kakoune editor.\n;; Unlike evil-mode for vim, this is very shallow emulation, and seeks to do as little\n;; work as possible, leveraging Emacs native editing commmands and the work of other\n;; packages wherever possible.\n\n;;; Code:\n(require 'kakoune-utils)\n(require 'kakoune-exchange)\n(require 'kakoune-unimpaired)\n(require 'kakoune-shell-commands)\n(require 'cl-lib)\n(require 'ryo-modal)\n(require 'expand-region)\n(require 'multiple-cursors)\n\n;;;###autoload\n(defun kakoune-setup-keybinds ()\n  \"Set up default kakoune keybindings for normal mode.\"\n  (global-subword-mode 1)\n  (ryo-modal-keys\n   (:mc-all t)\n   ;; Region selectors\n   (\"M-i\" ((\"w\" er/mark-word)\n           (\"b\" er/mark-inside-pairs)\n           (\"'\" er/mark-inside-quotes)))\n   (\"M-a\" ((\"w\" er/mark-symbol)\n           (\"b\" er/mark-outside-pairs)\n           (\"'\" er/mark-outside-quotes))))\n  ;; this works now but I've gotten used to not having it\n  ;; (ryo-modal-major-mode-keys\n  ;;  'prog-mode\n  ;;  (\"b\" kakoune-backward-same-syntax :first '(kakoune-set-mark-here) :mc-all t)\n  ;;  (\"B\" kakoune-backward-same-syntax :first '(kakoune-set-mark-if-inactive) :mc-all t)\n  ;;  (\"w\" forward-same-syntax :first '(kakoune-set-mark-here) :mc-all t)\n  ;;  (\"W\" forward-same-syntax :first '(kakoune-set-mark-if-inactive) :mc-all t))\n  (ryo-modal-keys\n   ;; Basic keybindings\n   (:mc-all t)\n   (\"a\" forward-char :exit t)\n   (\"A\" move-end-of-line :exit t)\n   (\"b\" backward-word :first '(kakoune-set-mark-here))\n   (\"B\" backward-word :first '(kakoune-set-mark-if-inactive))\n   (\"c\" kakoune-d :exit t)\n   (\"C\" kill-line :exit t)\n   (\"d\" kakoune-d)\n   (\"D\" kill-line)\n   (\"f\" kakoune-select-to-char :first '(kakoune-set-mark-here))\n   (\"F\" kakoune-select-to-char :first '(kakoune-set-mark-if-inactive))\n   (\"g\" ((\"h\" beginning-of-line)\n         (\"<left>\" beginning-of-line)\n         (\"j\" end-of-buffer)\n         (\"<down>\" end-of-buffer)\n         (\"k\" beginning-of-buffer)\n         (\"<up>\" beginning-of-buffer)\n         (\"g\" kakoune-gg)\n         (\"l\" end-of-line)\n         (\"<right>\" end-of-line)\n         (\"i\" back-to-indentation)) :first '(kakoune-deactivate-mark))\n   (\"G\" ((\"h\" beginning-of-line)\n         (\"<left>\" beginning-of-line)\n         (\"j\" end-of-buffer)\n         (\"<down>\" end-of-buffer)\n         (\"k\" beginning-of-buffer)\n         (\"<up>\" beginning-of-buffer)\n         (\"g\" kakoune-gg)\n         (\"l\" end-of-line)\n         (\"<right>\" end-of-line)\n         (\"i\" back-to-indentation)) :first '(kakoune-set-mark-if-inactive))\n   (\"g f\" find-file-at-point)\n   (\"G f\" find-file-at-point)\n   (\"g x\" kakoune-exchange)\n   (\"g X\" kakoune-exchange-cancel)\n   (\"h\" backward-char :first '(kakoune-deactivate-mark))\n   (\"H\" backward-char :first '(kakoune-set-mark-if-inactive))\n   (\"i\" kakoune-insert-mode)\n   (\"I\" back-to-indentation :exit t)\n   (\"j\" next-line :first '(kakoune-deactivate-mark))\n   (\"J\" next-line :first '(kakoune-set-mark-if-inactive))\n   (\"k\" previous-line :first '(kakoune-deactivate-mark))\n   (\"K\" previous-line :first '(kakoune-set-mark-if-inactive))\n   (\"l\" forward-char :first '(kakoune-deactivate-mark))\n   (\"L\" forward-char :first '(kakoune-set-mark-if-inactive))\n   (\"o\" kakoune-o :exit t)\n   (\"O\" kakoune-O :exit t)\n   (\"p\" kakoune-p)\n   (\"r\" kakoune-replace-char)\n   (\"R\" kakoune-replace-selection)\n   (\"t\" kakoune-select-up-to-char :first '(kakoune-set-mark-here))\n   (\"T\" kakoune-select-up-to-char :first '(kakoune-set-mark-if-inactive))\n   (\"w\" forward-word :first '(kakoune-set-mark-here))\n   (\"W\" forward-word :first '(kakoune-set-mark-if-inactive))\n   (\"M-w\" forward-symbol :first '(kakoune-set-mark-here))\n   (\"M-W\" forward-symbol :first '(kakoune-set-mark-if-inactive))\n   (\"x\" kakoune-x)\n   (\"X\" kakoune-X)\n   (\"y\" kill-ring-save)\n   (\"Y\" kakoune-Y)\n   (\".\" kakoune-select-again :first '(kakoune-set-mark-if-inactive))\n   (\"M-;\" exchange-point-and-mark)\n   (\"`\" kakoune-downcase)\n   (\"~\" kakoune-upcase)\n   (\"%\" mark-whole-buffer)\n   (\"M-j\" kakoune-join)\n   (\"[ [\" backward-paragraph :first '(kakoune-set-mark-here))\n   (\"] ]\" forward-paragraph :first '(kakoune-set-mark-here))\n   (\">\" kakoune-indent-right)\n   (\"<\" kakoune-indent-left)\n\n   ;; Treat arrow keys the same as \"hjkl\"\n   (\"<down>\" next-line :first '(kakoune-deactivate-mark))\n   (\"<S-down>\" next-line :first '(kakoune-set-mark-if-inactive))\n   (\"<up>\" previous-line :first '(kakoune-deactivate-mark))\n   (\"<S-up>\" previous-line :first '(kakoune-set-mark-if-inactive))\n   (\"<right>\" forward-char :first '(kakoune-deactivate-mark))\n   (\"<S-right>\" forward-char :first '(kakoune-set-mark-if-inactive))\n   (\"<left>\" backward-char :first '(kakoune-deactivate-mark))\n   (\"<S-left>\" backward-char :first '(kakoune-set-mark-if-inactive))\n\n   ;; Numeric arguments\n   (\"0\" \"M-0\" :norepeat t)\n   (\"1\" \"M-1\" :norepeat t)\n   (\"2\" \"M-2\" :norepeat t)\n   (\"3\" \"M-3\" :norepeat t)\n   (\"4\" \"M-4\" :norepeat t)\n   (\"5\" \"M-5\" :norepeat t)\n   (\"6\" \"M-6\" :norepeat t)\n   (\"7\" \"M-7\" :norepeat t)\n   (\"8\" \"M-8\" :norepeat t)\n   (\"9\" \"M-9\" :norepeat t)\n   (\"-\" \"M--\" :norepeat t)\n\n   ;; Unimpaired-like functionality\n   (\"[\" ((\"SPC\" kakoune-insert-line-above)\n         (\"p\" kakoune-paste-above)))\n   (\"]\" ((\"SPC\" kakoune-insert-line-below)\n         (\"p\" kakoune-paste-below)))\n\n   ;; Multiple cursors\n   (\"s\" mc/mark-all-in-region)\n   (\"S\" mc/split-region)\n\n   ;; Shell commands\n   (\"|\" kakoune-shell-pipe)\n   (\"!\" kakoune-shell-command))\n\n  ;; put these here because they shouldn't be repeated for all cursors\n  (ryo-modal-keys\n   (\"[ b\" previous-buffer)\n   (\"] b\" next-buffer)))\n\n;;;###autoload\n(defun kakoune-setup-keybinds-colemak-dh ()\n  \"Set up default kakoune keybindings for normal mode colemak-dh\nlayout to preserve muscle memory.\n\n|--------+---------|\n| QWERTY | Colemak |\n|--------+---------|\n| h      | n       |\n| j      | e       |\n| k      | i       |\n| l      | o       |\n| n      | j       |\n| e      | k       |\n| i      | l       |\n| o      | h       |\n|--------+---------|\n\"\n  (global-subword-mode 1)\n  (ryo-modal-keys\n   (:mc-all t)\n   ;; Region selectors\n   (\"M-i\" ((\"w\" er/mark-word)\n           (\"b\" er/mark-inside-pairs)\n           (\"'\" er/mark-inside-quotes)))\n   (\"M-a\" ((\"w\" er/mark-symbol)\n           (\"b\" er/mark-outside-pairs)\n           (\"'\" er/mark-outside-quotes))))\n  ;; Actually no, let's now\n  ;; new layout, let's try this again.\n  ;; (ryo-modal-major-mode-keys\n  ;;  'prog-mode\n  ;;  (\"b\" kakoune-backward-same-syntax :first '(kakoune-set-mark-here) :mc-all t)\n  ;;  (\"B\" kakoune-backward-same-syntax :first '(kakoune-set-mark-if-inactive) :mc-all t)\n  ;;  (\"w\" forward-same-syntax :first '(kakoune-set-mark-here) :mc-all t)\n  ;;  (\"W\" forward-same-syntax :first '(kakoune-set-mark-if-inactive) :mc-all t))\n  (ryo-modal-keys\n   ;; Basic keybindings\n   (:mc-all t)\n   (\"a\" forward-char :exit t)\n   (\"A\" move-end-of-line :exit t)\n   (\"b\" backward-word :first '(kakoune-set-mark-here))\n   (\"B\" backward-word :first '(kakoune-set-mark-if-inactive))\n   (\"c\" kakoune-d :exit t)\n   (\"C\" kill-line :exit t)\n   (\"d\" kakoune-d)\n   (\"D\" kill-line)\n   (\"f\" kakoune-select-to-char :first '(kakoune-set-mark-here))\n   (\"F\" kakoune-select-to-char :first '(kakoune-set-mark-if-inactive))\n   (\"g\" ((\"n\" beginning-of-line)\n         (\"<left>\" beginning-of-line)\n         (\"e\" end-of-buffer)\n         (\"<down>\" end-of-buffer)\n         (\"i\" beginning-of-buffer)\n         (\"<up>\" beginning-of-buffer)\n         (\"g\" kakoune-gg)\n         (\"o\" end-of-line)\n         (\"<right>\" end-of-line)\n         (\"l\" back-to-indentation)) :first '(kakoune-deactivate-mark))\n   (\"G\" ((\"n\" beginning-of-line)\n         (\"<left>\" beginning-of-line)\n         (\"e\" end-of-buffer)\n         (\"<down>\" end-of-buffer)\n         (\"i\" beginning-of-buffer)\n         (\"<up>\" beginning-of-buffer)\n         (\"g\" kakoune-gg)\n         (\"o\" end-of-line)\n         (\"<right>\" end-of-line)\n         (\"l\" back-to-indentation)) :first '(kakoune-set-mark-if-inactive))\n   (\"g f\" find-file-at-point)\n   (\"G f\" find-file-at-point)\n   (\"g x\" kakoune-exchange)\n   (\"g X\" kakoune-exchange-cancel)\n   (\"n\" backward-char :first '(kakoune-deactivate-mark))\n   (\"N\" backward-char :first '(kakoune-set-mark-if-inactive))\n   (\"l\" kakoune-insert-mode)\n   (\"L\" back-to-indentation :exit t)\n   (\"e\" next-line :first '(kakoune-deactivate-mark))\n   (\"E\" next-line :first '(kakoune-set-mark-if-inactive))\n   (\"i\" previous-line :first '(kakoune-deactivate-mark))\n   (\"I\" previous-line :first '(kakoune-set-mark-if-inactive))\n   (\"o\" forward-char :first '(kakoune-deactivate-mark))\n   (\"O\" forward-char :first '(kakoune-set-mark-if-inactive))\n   (\"h\" kakoune-o :exit t)\n   (\"H\" kakoune-O :exit t)\n   (\"p\" kakoune-p)\n   (\"r\" kakoune-replace-char)\n   (\"R\" kakoune-replace-selection)\n   (\"t\" kakoune-select-up-to-char :first '(kakoune-set-mark-here))\n   (\"T\" kakoune-select-up-to-char :first '(kakoune-set-mark-if-inactive))\n   (\"w\" forward-word :first '(kakoune-set-mark-here))\n   (\"W\" forward-word :first '(kakoune-set-mark-if-inactive))\n   (\"M-w\" forward-symbol :first '(kakoune-set-mark-here))\n   (\"M-W\" forward-symbol :first '(kakoune-set-mark-if-inactive))\n   (\"x\" kakoune-x)\n   (\"X\" kakoune-X)\n   (\"y\" kill-ring-save)\n   (\"Y\" kakoune-Y)\n   (\".\" kakoune-select-again :first '(kakoune-set-mark-if-inactive))\n   (\"M-;\" exchange-point-and-mark)\n   (\"`\" kakoune-downcase)\n   (\"~\" kakoune-upcase)\n   (\"%\" mark-whole-buffer)\n   (\"M-j\" kakoune-join)\n   (\"[ [\" backward-paragraph :first '(kakoune-set-mark-here))\n   (\"] ]\" forward-paragraph :first '(kakoune-set-mark-here))\n   (\">\" kakoune-indent-right)\n   (\"<\" kakoune-indent-left)\n\n   ;; Treat arrow keys the same as \"hjkl\"\n   (\"<down>\" next-line :first '(kakoune-deactivate-mark))\n   (\"<S-down>\" next-line :first '(kakoune-set-mark-if-inactive))\n   (\"<up>\" previous-line :first '(kakoune-deactivate-mark))\n   (\"<S-up>\" previous-line :first '(kakoune-set-mark-if-inactive))\n   (\"<right>\" forward-char :first '(kakoune-deactivate-mark))\n   (\"<S-right>\" forward-char :first '(kakoune-set-mark-if-inactive))\n   (\"<left>\" backward-char :first '(kakoune-deactivate-mark))\n   (\"<S-left>\" backward-char :first '(kakoune-set-mark-if-inactive))\n\n   ;; Numeric arguments\n   (\"0\" \"M-0\" :norepeat t)\n   (\"1\" \"M-1\" :norepeat t)\n   (\"2\" \"M-2\" :norepeat t)\n   (\"3\" \"M-3\" :norepeat t)\n   (\"4\" \"M-4\" :norepeat t)\n   (\"5\" \"M-5\" :norepeat t)\n   (\"6\" \"M-6\" :norepeat t)\n   (\"7\" \"M-7\" :norepeat t)\n   (\"8\" \"M-8\" :norepeat t)\n   (\"9\" \"M-9\" :norepeat t)\n   (\"-\" \"M--\" :norepeat t)\n\n   ;; Unimpaired-like functionality\n   (\"[\" ((\"SPC\" kakoune-insert-line-above)\n         (\"p\" kakoune-paste-above)))\n   (\"]\" ((\"SPC\" kakoune-insert-line-below)\n         (\"p\" kakoune-paste-below)))\n\n   ;; Multiple cursors\n   (\"s\" mc/mark-all-in-region)\n   (\"S\" mc/split-region)\n\n   ;; Shell commands\n   (\"|\" kakoune-shell-pipe)\n   (\"!\" kakoune-shell-command))\n\n  ;; put these here because they shouldn't be repeated for all cursors\n  (ryo-modal-keys\n   (\"[ b\" previous-buffer)\n   (\"] b\" next-buffer)))\n(provide 'kakoune)\n;;; kakoune.el ends here\n"
  }
]