From Emacs to Neovim (Configuration)

May 23, 2024

Background

This is a continuation of my post @ “From 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.

Note that my current, primary tooling focus is:

Neovim configuration

My public dotfiles can be found @ https://github.com/mrrodriguez/nvim-dotfiles

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, I’ll enumerate the references that I used for some of this setup.

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.

I am particularly a fan of nvim-surround to help supplement for some of the utility I am used to from using paredit. 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.


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:

:inoremap jk <Esc>
:inoremap kj <Esc>
:vnoremap jk <Esc>
:vnoremap kj <Esc>

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:

function! RepeatChar(char, count)
  return repeat(a:char, a:count)
endfunction
nnoremap s :<C-U>exec "normal i".RepeatChar(nr2char(getchar()), v:count1)<CR>
nnoremap S :<C-U>exec "normal a".RepeatChar(nr2char(getchar()), v:count1)<CR>

Neovim references

I have found these sources to be valuable in getting started with this configuration:

I haven’t worked with many plugins yet, but I have followed the popular recommendations to manage them with lazy.nvim. In my configuration files it can be seen how to use this to install nvim-surround.

VSCode configuration

There were a few configuration changes to vscode that helped me with the vscode-neovim integration. Later on below, I’ll enumerate the references that I used for some of these.


To use my neovim configuration within vscode-neovim I added this to my User settings.json:

"vscode-neovim.neovimInitVimPaths.darwin": "/Users/mikerod/.config/nvim/init.lua"

Where this is my ~/.config/nvim location of the same init file I use for neovim.


Using macOS I needed to change the setting to allow for holding a key to repeat it via:

defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false

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:

"vscode-neovim.compositeKeys": {
    "jk": {
      "command": "vscode-neovim.escape"
    }
  },

More options are supported here and it can be more complex. The docs cover this well (which I’ll link below in references).


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:

{
    "command": "runCommands",
    "key": "cmd+s",
    "when": "editorTextFocus && neovim.init && neovim.mode == 'insert'",
    "args": {
      "commands": ["workbench.action.files.save", "vscode-neovim.escape"]
    }
  }

VSCode references

I have found these sources to be valuable in updating my vscode configuration with a vscode-neovim emphasis:

Spacemacs configuration

My public dotfiles can be found @ https://github.com/mrrodriguez/emacs-dotfiles

spacemacs already comes with a lot of vim support via its use of evil-mode. 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.

I’ll enumerate some quick configuration conveniences here that I added to dotspacemacs/user-config. Later on below, I’ll enumerate the references that I used for some of these.


In the same way as I mentioned in the neovim section above I wanted the editor to return to “normal” mode (aka. evil-normal-state in evil-mode) on “save”. This is done via:

(add-hook 'after-save-hook #'evil-normal-state)

In the same way as I described in the neovim section above, I wanted to use jk keys to return to “normal” mode to not have to always use ESC.

(setq-default evil-escape-key-sequence "jk")

In the same way as I described in the neovim section above, I wanted to be able to “insert a single character”, but remain in “normal” mode after.

(evil-define-command my-evil-insert-char (count char)
  (interactive "<c><C>")
  (setq count (or count 1))
  (insert (make-string count char)))

(evil-define-command my-evil-append-char (count char)
  (interactive "<c><C>")
  (setq count (or count 1))
  (when (not (eolp))
    (forward-char))
  (insert (make-string count char)))

(define-key evil-normal-state-map (kbd "s") 'my-evil-insert-char)
(define-key evil-normal-state-map (kbd "S") 'my-evil-append-char)

Spacemacs references

I have found these sources to be valuable in updating my spacemacs configuration with a vim emphasis: