Skip to content

Commit

Permalink
From Emacs to Neovim (Part 2) (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrrodriguez authored May 23, 2024
1 parent 5f4a83e commit e337549
Show file tree
Hide file tree
Showing 2 changed files with 190 additions and 10 deletions.
24 changes: 14 additions & 10 deletions _posts/2024-05-20-emacs-to-neovim.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,34 @@ date: 2024-05-20
tags: [emacs, neovim, spacemacs, vim, vscode, vscode-neovim, vi, editor, development, programming]
---

### Update: 2024-05-22

I continue this discussion into a "Part 2" post @ ["From Emacs to Neovim (Configuration)"]({% post_url 2024-05-23-emacs-to-neovim-part-2 %}) where some concrete configuration examples are provided.

# Background

I have been using emacs for around 15 years with a "traditional" configuration and key bindings setup. I’ve primarily used it for Clojure/ClojureScript development. Occasionally, I have also used it for XML, HTML, JSON, CSS, C/C++, Ruby, Python, JavaScript, and Common Lisp.
I have been using `emacs` for around 15 years with a "traditional" configuration and key bindings setup. I’ve primarily used it for Clojure/ClojureScript development. Occasionally, I have also used it for XML, HTML, JSON, CSS, C/C++, Ruby, Python, JavaScript, and Common Lisp.

About 6 years ago I started to use the [spacemacs distribution](https://www.spacemacs.org). This was done after seeing several of my coworkers using it. I thought that this would be useful due to the general knowledge sharing of common configuration advice and best practices. The defaults worked well when getting started with spacemacs to replace my existing emacs setup. My previous setup was something I had configured in an ad hoc fashion over several years with no particular setup framework in mind. Notably, I did not use `evil-mode` bindings at all. The `helm` search buffer and `magit` key sequences were the closest I had to something like vim bindings (eg. mnemonic key sequences in a "non-insert" mode).
About 6 years ago I started to use the [spacemacs distribution](https://www.spacemacs.org). This was done after seeing several of my coworkers using it. I thought that this would be useful due to the general knowledge sharing of common configuration advice and best practices. The defaults worked well when getting started with `spacemacs` to replace my existing `emacs` setup. My previous setup was something I had configured in an ad hoc fashion over several years with no particular setup framework in mind. Notably, I did not use `evil-mode` bindings at all. The `helm` search buffer and `magit` key sequences were the closest I had to something like `vim` bindings (eg. mnemonic key sequences in a "non-insert" mode).

I recently have been working more with languages such as TypeScript/JavaScript and Golang. This has made me curious if I could find better and/or more supported tooling available if I was willing to venture beyond the emacs editor. A natural fit for both seemed to be [vscode](https://code.visualstudio.com). I knew I wanted powerful editing capabilities like I was familiar with already via emacs. I tried some emacs key emulators/extensions in other IDEs before. I did not find them to be sufficient though. Without the elisp, the minibuffer, the "kill ring", among many other specifics to the emacs editor, the integration didn't seem powerful or similar enough to real emacs.
I recently have been working more with languages such as TypeScript/JavaScript and Golang. This has made me curious if I could find better and/or more supported tooling available if I was willing to venture beyond the `emacs` editor. A natural fit for both seemed to be [vscode](https://code.visualstudio.com). I knew I wanted powerful editing capabilities like I was familiar with already via `emacs`. I tried some `emacs` key emulators/extensions in other IDEs before. I did not find them to be sufficient though. Without the elisp, the minibuffer, the "kill ring", among many other specifics to the `emacs` editor, the integration didn't seem powerful or similar enough to real `emacs`.

# The idea

Lately, I've seen and heard a lot more about [neovim](https://neovim.io) as an editor. It seems to have brought a new surge in popularity vim. I have been curoius about vim for some time. I knew a little bit about how it "philosophically" differed from emacs and was intrigued.
Lately, I've seen and heard a lot more about [neovim](https://neovim.io) as an editor. It seems to have brought a new surge in popularity `vim`. I have been curoius about `vim` for some time. I knew a little bit about how it "philosophically" differed from `emacs` and was intrigued.

Since I was using spacemacs, I thought this may be an interesting opportunity to explore vim via `evil-mode`. My plan was to ease into it by using spacemacs ["hybrid editing style"](https://develop.spacemacs.org/doc/DOCUMENTATION.html#hybrid) which uses `evil-mode` for all vim modes (called "states" in spacemacs) other than "insert". This was really convenient for me to be (somewhat) efficient and productive as I learned. Along with this, I did neovim tutorials and read docs to get started learning more of the vim side. I gradually applied these ideas/concepts more to my hybrid editing style spacemacs setup.
Since I was using `spacemacs`, I thought this may be an interesting opportunity to explore `vim` via `evil-mode`. My plan was to ease into it by using `spacemacs` ["hybrid editing style"](https://develop.spacemacs.org/doc/DOCUMENTATION.html#hybrid) which uses `evil-mode` for all `vim` modes (called "states" in `spacemacs`) other than "insert". This was really convenient for me to be (somewhat) efficient and productive as I learned. Along with this, I did neovim tutorials and read docs to get started learning more of the `vim` side. I gradually applied these ideas/concepts more to my hybrid editing style `spacemacs` setup.

# Learning process

It was difficult for a week of dedicated focus on learning vim keys while doing my day to day work. This definitely made me slower for a short period. By about 3 weeks, I felt much more proficient and efficient. After that, I only had some lingering inefficient movement annoyances I knew I’d need to investigate and improve over time. Basically, the thought is to wait until the annoyance/inefficiency becomes frequent enough to be [worth the time investment to address](https://xkcd.com/1205).
It was difficult for a week of dedicated focus on learning `vim` keys while doing my day to day work. This definitely made me slower for a short period. By about 3 weeks, I felt much more proficient and efficient. After that, I only had some lingering inefficient movement annoyances I knew I’d need to investigate and improve over time. Basically, the thought is to wait until the annoyance/inefficiency becomes frequent enough to be [worth the time investment to address](https://xkcd.com/1205).

From there, I started using vscode for TypeScript with the [vscode-neovim](https://github.com/vscode-neovim/vscode-neovim) extension. I was impressed by how well this extension blended well within vscode. I was also able to find a lot of good resources available when troubleshooting and learning about the extension. The ability to use my actual neovim configuration files from within vscode in non-trivial ways was great too.
From there, I started using vscode for TypeScript with the [vscode-neovim](https://github.com/vscode-neovim/vscode-neovim) extension. I was impressed by how well this extension blended well within vscode. I was also able to find a lot of good resources available when troubleshooting and learning about the extension. The ability to use my actual `neovim` configuration files from within vscode in non-trivial ways was great too.

# Outcome

I still use spacemacs with hybrid mode bindings while doing Clojure/ClojureScript and various other data files and languages I may lightly use. I still prefer "insert" mode (aka. spacemacs "insert state") to be done with emacs traditional bindings. To me, it is a "better" insert mode experience compared to vim bindings within emacs. However, I now find myself using a lot less insert mode than I used to.
I still use `spacemacs` with hybrid mode bindings while doing Clojure/ClojureScript and various other data files and languages I may lightly use. I still prefer "insert" mode (aka. `spacemacs` "insert state") to be done with `emacs` traditional bindings. To me, it is a "better" insert mode experience compared to `vim` bindings within `emacs`. However, I now find myself using a lot less insert mode than I used to.

When I switch to vscode with the vscode-neovim extension, I can quickly adapt to that IDE environment. I can even toggle back and forth throughout the day without a struggle. Overall, the switch to vim bindings seems to be a worthwhile investment. I also use neovim now when I want to quickly open/edit text files from the terminal. My general thought is that you can get more “range”/"reach" out of vim bindings due to the extensions available in popular IDEs and other text editors (including in the terminal which is "the default" for neovim). To me, the editing efficiencies are just as powerful and efficient when compared to my traditional emacs experiences.
When I switch to `vscode` with the `vscode-neovim` extension, I can quickly adapt to that IDE environment. I can even toggle back and forth throughout the day without a struggle. Overall, the switch to `vim` bindings seems to be a worthwhile investment. I also use `neovim` now when I want to quickly open/edit text files from the terminal. My general thought is that you can get more “range”/"reach" out of `vim` bindings due to the extensions available in popular IDEs and other text editors (including in the terminal which is "the default" for `neovim`). To me, the editing efficiencies are just as powerful and efficient when compared to my traditional `emacs` experiences.

I will post a follow-up to this post to discuss some helpful resources and/or plugins/extensions/configuration that helped me transition from my traditional emacs setup concepts to vim concepts (via neovim specifically).
I have posted a follow-up to this post @ ["From Emacs to Neovim (Configuration)"]({% post_url 2024-05-23-emacs-to-neovim-part-2 %}). In this I discuss some helpful resources and/or plugins/extensions/configuration that helped me transition from my traditional `emacs` setup concepts to `vim` concepts (via `neovim` specifically).
176 changes: 176 additions & 0 deletions _posts/2024-05-23-emacs-to-neovim-part-2.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
---
layout: post
title: "From Emacs to Neovim (Configuration)"
author: Mike Rodriguez
date: 2024-05-23
tags: [emacs, neovim, spacemacs, vim, vscode, vscode-neovim, vi, editor, development, programming]
---

# Background

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.

Note that my current, primary tooling focus is:
* [spacemacs distribution](https://www.spacemacs.org) of `emacs` primarily for Clojure/ClojureScript development
* [neovim distribution](https://neovim.io) of `vim` for simple file editing in the terminal
* [vscode-neovim](https://github.com/vscode-neovim/vscode-neovim) for TypeScript/JavaScript development

# 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](#neovim-references), 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](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.

-----

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:

```vimscript
: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:

```vimscript
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:
* [This is a good post](https://builtin.com/software-engineering-perspectives/neovim-configuration) on starting a new `neovim` configuration.
* These are two good posts concerning how to mix `vimscript` and `lua` configuration files:
* ["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)
* ["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)
* [This is the neovim Lua guide](https://neovim.io/doc/user/lua-guide.html).
* [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.
* ["Insert Single Character in Vim?"](https://superuser.com/a/581669)
* This is improved upon in ["Insert a single character"](https://vim.fandom.com/wiki/Insert_a_single_character)

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).

# VSCode configuration

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.

-----

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

```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:

```sh
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:

```json
"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`:

```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:

* ["Integrate Neovim inside VSCode"](https://medium.com/@shaikzahid0713/integrate-neovim-inside-vscode-5662d8855f9d)
* ["Composite escape keys"](https://github.com/vscode-neovim/vscode-neovim/blob/02d13f0e119afbec8f68fe5add0f2c2a1072ec49/README.md#composite-escape-keys)
* ["In VSCode Neovim, press Ctrl+S to save file and switch to normal mode?"](https://stackoverflow.com/a/77769949/924604)
* Note that there is a `"command": "runCommands"` now available so the `multi-command` extension is not needed. My configuration example above utilizes this instead.
* See ["How can I run multiple commands with a single VS Code keybinding without an extension?"](https://stackoverflow.com/a/75808372/924604)

# 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](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`.

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.

-----

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:

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

-----

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`.

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

-----

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.

```lisp
(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:

* ["Single character insert for Evil"](https://www.reddit.com/r/emacs/comments/7ogu7a/comment/ds9py2s/?utm_source=reddit&utm_medium=web2x&context=3)
* ["Spacemacs : insert single character in normal mode"](https://emacs.stackexchange.com/questions/32450/spacemacs-insert-single-character-in-normal-mode)

0 comments on commit e337549

Please sign in to comment.