|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "From Emacs to Neovim (Configuration)" |
| 4 | +author: Mike Rodriguez |
| 5 | +date: 2024-05-23 |
| 6 | +tags: [emacs, neovim, spacemacs, vim, vscode, vscode-neovim, vi, editor, development, programming] |
| 7 | +--- |
| 8 | + |
| 9 | +# Background |
| 10 | + |
| 11 | +This is a continuation of my post @ ["From Emacs to Neovim"]({% post_url 2024-05-20-emacs-to-neovim %}). In this post, I am going to share some concrete configuration examples and concepts that have helped me with adapting to `vim` configuration and bindings coming from an `emacs` background. |
| 12 | + |
| 13 | +Note that my current, primary tooling focus is: |
| 14 | +* [spacemacs distribution](https://www.spacemacs.org) of `emacs` primarily for Clojure/ClojureScript development |
| 15 | +* [neovim distribution](https://neovim.io) of `vim` for simple file editing in the terminal |
| 16 | +* [vscode-neovim](https://github.com/vscode-neovim/vscode-neovim) for TypeScript/JavaScript development |
| 17 | + |
| 18 | +# Neovim configuration |
| 19 | + |
| 20 | +My public dotfiles can be found @ <https://github.com/mrrodriguez/nvim-dotfiles> |
| 21 | + |
| 22 | +So far, I have not worked much to customize `neovim` for a "standalone IDE" type of experience. I have been using it for basic file editing with minimum customization over the default terminal-based `nvim` defaults. I then utilize this same configuration via the `vscode-neovim` extension for `vscode`, which I will describe more below in its own section. [Later on below](#neovim-references), I'll enumerate the references that I used for some of this setup. |
| 23 | + |
| 24 | +I have chosen to setup the `neovim` configuration in a way where I can mix both `vimscript` configuration along with `lua` configuration. I liked this setup since I have found docs and examples are often in one or the other. I haven't spent a lot of time learning either one yet to know how to translate between the two. |
| 25 | + |
| 26 | +I am particularly a fan of `nvim-surround` to help supplement for some of the utility I am used to from using [paredit](https://paredit.org). This is a tool that I have heavily relied upon in `emacs`. `nvim-surround` isn't meant to serve the same purpose as `paredit`, but it does cover the most significant use-case I have had for `paredit` which is "slurping" and "barfing" (this is `paredit` terminology) surrounding delimiters around chunks of text/words. For the rest of the "structural navigation" parts I like from `paredit`, I've tried to adapt with the idiomatic `vim` navigation defaults and it has worked well enough so far. I'm sure there are plugins I could look into improve up on this in the future. |
| 27 | + |
| 28 | +----- |
| 29 | + |
| 30 | +I also did not want to have to always use the "escape" key (aka. `ESC`) for returning to "normal" mode. This is very common in the `vim` community. There are many possible choices that are common and there are pros and cons to read about on them. I liked using `jk` which I set via: |
| 31 | + |
| 32 | +```vimscript |
| 33 | +:inoremap jk <Esc> |
| 34 | +:inoremap kj <Esc> |
| 35 | +:vnoremap jk <Esc> |
| 36 | +:vnoremap kj <Esc> |
| 37 | +``` |
| 38 | + |
| 39 | +----- |
| 40 | + |
| 41 | +Another thing I noticed was it would often be convenient if I could go from "normal" mode to "insert" mode to insert a single character and then immediately be back in "normal" mode. This is another popularly discussed `vim` topic (which I mention in the references below). I did this via: |
| 42 | + |
| 43 | +```vimscript |
| 44 | +function! RepeatChar(char, count) |
| 45 | + return repeat(a:char, a:count) |
| 46 | +endfunction |
| 47 | +nnoremap s :<C-U>exec "normal i".RepeatChar(nr2char(getchar()), v:count1)<CR> |
| 48 | +nnoremap S :<C-U>exec "normal a".RepeatChar(nr2char(getchar()), v:count1)<CR> |
| 49 | +``` |
| 50 | + |
| 51 | +#### Neovim references |
| 52 | + |
| 53 | +I have found these sources to be valuable in getting started with this configuration: |
| 54 | +* [This is a good post](https://builtin.com/software-engineering-perspectives/neovim-configuration) on starting a new `neovim` configuration. |
| 55 | +* These are two good posts concerning how to mix `vimscript` and `lua` configuration files: |
| 56 | + * ["Is it possible to use init.vim and init.lua together?"](https://www.reddit.com/r/neovim/comments/zfimqo/is_it_possible_to_use_initvim_and_initlua_together) |
| 57 | + * ["How to handle init.lua examples if you use init.vim?"](https://www.reddit.com/r/neovim/comments/1913nyw/how_to_handle_initlua_examples_if_you_use_initvim) |
| 58 | +* [This is the neovim Lua guide](https://neovim.io/doc/user/lua-guide.html). |
| 59 | +* [This was a useful post](https://github.com/vscode-neovim/vscode-neovim/issues/819#issuecomment-1035983972) describing the difference from using `runtime` vs `source` in a `lua` script. |
| 60 | +* ["Insert Single Character in Vim?"](https://superuser.com/a/581669) |
| 61 | + * This is improved upon in ["Insert a single character"](https://vim.fandom.com/wiki/Insert_a_single_character) |
| 62 | + |
| 63 | +I haven't worked with many plugins yet, but I have followed the popular recommendations to manage them with [lazy.nvim](https://github.com/folke/lazy.nvim). In my configuration files it can be seen how to use this to install [nvim-surround](https://github.com/kylechui/nvim-surround). |
| 64 | + |
| 65 | +# VSCode configuration |
| 66 | + |
| 67 | +There were a few configuration changes to `vscode` that helped me with the `vscode-neovim` integration. [Later on below](#vscode-references), I'll enumerate the references that I used for some of these. |
| 68 | + |
| 69 | +----- |
| 70 | + |
| 71 | +To use my `neovim` configuration within `vscode-neovim` I added this to my User `settings.json`: |
| 72 | + |
| 73 | +```json |
| 74 | +"vscode-neovim.neovimInitVimPaths.darwin": "/Users/mikerod/.config/nvim/init.lua" |
| 75 | +``` |
| 76 | + |
| 77 | +Where this is my `~/.config/nvim` location of the same init file I use for `neovim`. |
| 78 | + |
| 79 | +----- |
| 80 | + |
| 81 | +Using macOS I needed to change the setting to allow for holding a key to repeat it via: |
| 82 | + |
| 83 | +```sh |
| 84 | +defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false |
| 85 | +``` |
| 86 | + |
| 87 | +----- |
| 88 | + |
| 89 | +For the same reason discussed above concerning `spacemacs` configuration, I wanted `jk` to return to "normal" mode the same way `ESC` does. To do this I added to the `vscode` User `settings.json` file: |
| 90 | + |
| 91 | +```json |
| 92 | +"vscode-neovim.compositeKeys": { |
| 93 | + "jk": { |
| 94 | + "command": "vscode-neovim.escape" |
| 95 | + } |
| 96 | + }, |
| 97 | +``` |
| 98 | + |
| 99 | +More options are supported here and it can be more complex. The docs cover this well (which I'll link below in references). |
| 100 | + |
| 101 | +----- |
| 102 | + |
| 103 | +I think it useful to have the editor change to "normal" mode on "save". This seems like a fairly common configuration people setup for `vim` style editors. This is by adding this to the User `keybindings.json`: |
| 104 | + |
| 105 | +```json |
| 106 | +{ |
| 107 | + "command": "runCommands", |
| 108 | + "key": "cmd+s", |
| 109 | + "when": "editorTextFocus && neovim.init && neovim.mode == 'insert'", |
| 110 | + "args": { |
| 111 | + "commands": ["workbench.action.files.save", "vscode-neovim.escape"] |
| 112 | + } |
| 113 | + } |
| 114 | +``` |
| 115 | + |
| 116 | +#### VSCode references |
| 117 | + |
| 118 | +I have found these sources to be valuable in updating my `vscode` configuration with a `vscode-neovim` emphasis: |
| 119 | + |
| 120 | +* ["Integrate Neovim inside VSCode"](https://medium.com/@shaikzahid0713/integrate-neovim-inside-vscode-5662d8855f9d) |
| 121 | +* ["Composite escape keys"](https://github.com/vscode-neovim/vscode-neovim/blob/02d13f0e119afbec8f68fe5add0f2c2a1072ec49/README.md#composite-escape-keys) |
| 122 | +* ["In VSCode Neovim, press Ctrl+S to save file and switch to normal mode?"](https://stackoverflow.com/a/77769949/924604) |
| 123 | + * Note that there is a `"command": "runCommands"` now available so the `multi-command` extension is not needed. My configuration example above utilizes this instead. |
| 124 | + * See ["How can I run multiple commands with a single VS Code keybinding without an extension?"](https://stackoverflow.com/a/75808372/924604) |
| 125 | + |
| 126 | +# Spacemacs configuration |
| 127 | + |
| 128 | +My public dotfiles can be found @ <https://github.com/mrrodriguez/emacs-dotfiles> |
| 129 | + |
| 130 | +`spacemacs` already comes with a lot of `vim` support via its use of [evil-mode](https://github.com/emacs-evil/evil). I haven't had to do too much configuration. There is also `spacemacs` specific features that typically involve the minibuffer and I use them the standard way. The `spacemacs` specific features are mostly expressed via a "leader key" which defaults to being the "space" key (aka. `SPC`). This works well with `dotspacemacs-editing-style` set to `hybrid` or `vim`. |
| 131 | + |
| 132 | +I'll enumerate some quick configuration conveniences here that I added to `dotspacemacs/user-config`. [Later on below](#spacemacs-references), I'll enumerate the references that I used for some of these. |
| 133 | + |
| 134 | +----- |
| 135 | + |
| 136 | +In the same way as I mentioned in the `neovim` [section above](#vscode-configuration) I wanted the editor to return to "normal" mode (aka. `evil-normal-state` in `evil-mode`) on "save". This is done via: |
| 137 | + |
| 138 | +```lisp |
| 139 | +(add-hook 'after-save-hook #'evil-normal-state) |
| 140 | +``` |
| 141 | + |
| 142 | +----- |
| 143 | + |
| 144 | +In the same way as I described in the `neovim` [section above](#neovim-configuration), I wanted to use `jk` keys to return to "normal" mode to not have to always use `ESC`. |
| 145 | + |
| 146 | +```lisp |
| 147 | +(setq-default evil-escape-key-sequence "jk") |
| 148 | +``` |
| 149 | + |
| 150 | +----- |
| 151 | + |
| 152 | +In the same way as I described in the `neovim` [section above](#neovim-configuration), I wanted to be able to "insert a single character", but remain in "normal" mode after. |
| 153 | + |
| 154 | +```lisp |
| 155 | +(evil-define-command my-evil-insert-char (count char) |
| 156 | + (interactive "<c><C>") |
| 157 | + (setq count (or count 1)) |
| 158 | + (insert (make-string count char))) |
| 159 | +
|
| 160 | +(evil-define-command my-evil-append-char (count char) |
| 161 | + (interactive "<c><C>") |
| 162 | + (setq count (or count 1)) |
| 163 | + (when (not (eolp)) |
| 164 | + (forward-char)) |
| 165 | + (insert (make-string count char))) |
| 166 | +
|
| 167 | +(define-key evil-normal-state-map (kbd "s") 'my-evil-insert-char) |
| 168 | +(define-key evil-normal-state-map (kbd "S") 'my-evil-append-char) |
| 169 | +``` |
| 170 | + |
| 171 | +#### Spacemacs references |
| 172 | + |
| 173 | +I have found these sources to be valuable in updating my `spacemacs` configuration with a `vim` emphasis: |
| 174 | + |
| 175 | +* ["Single character insert for Evil"](https://www.reddit.com/r/emacs/comments/7ogu7a/comment/ds9py2s/?utm_source=reddit&utm_medium=web2x&context=3) |
| 176 | +* ["Spacemacs : insert single character in normal mode"](https://emacs.stackexchange.com/questions/32450/spacemacs-insert-single-character-in-normal-mode) |
0 commit comments