Neovim again

· geekery · software · coding ·
On the left, a terminal window running Neovim, shows a Markdown source file with coloured syntax highlighting and a fancy modeline. On the right, Marked shows a preview of the Markdown file rendered to HTML.

Neovim in iTerm and Marked previewing the same file

There are many things that I enjoy about both Emacs and Vim, and I have used both for extended periods. I remain resolutely neutral in the Editor Wars! Most recently, I have been an enthusiastic Emacs user. I used to use org-mode for planning and managing my tasks, but I moved back to Things for that a while ago for pragmatic reasons, and let Emacs deal with all my other plain text needs. However, I kept a very minimal Neovim setup in my Nix configuration that I would use for quick edits to files in iTerm.

Perhaps I’m on one of my periodic ‘minimalist’ adventures (which would seem to be borne out by my keyboard choice), but Emacs has recently felt a bit much to me, and made me curious about the current state of the art in Neovim.

It turns out that Neovim has improved in leaps and bounds since I last delved into it. There is a thriving plugin ecosystem (facilitated I am sure by the ability to write scripts and modules in Lua rather than Vimscript), and there is even a thriving plugin manager ecosystem. The I ended up using the LazyVim starter setup, which builds on top of the lazy.nvim plugin manager. It is quite similar to Doom for Emacs, but is perhaps a bit less opinionated and monolithic (not that there is anything inherently wrong with either of those things). LazyVim sets up sensible configurations for most things that you are likely to want for coding or writing in Neovim, but it is easy to disable what you don’t want to use, and to configure or add the plugins you prefer.

I don’t really know Lua, but it is a fairly simple scripting language to understand, and it didn’t take me too long to learn how to configure and add plugins using lazy.nvim. It’s a nicely structured system, as you can create one file per plugin, or group several together as a module for handling one feature (like completion). LazyVim gives you a spacebar leader key and which-key guides like Doom, as well as a Dashboard page, and the whole setup feels quite familiar. Updating packages is really easy, and since precise git sha details of all the installed packages are stored in a JSON lockfile which you can commit to a repository, you can make sure that your setup works the same on several machines, and rollback to a previous version if an update breaks something.

As I have not been keeping an eye on the Neovim scene, I don’t think I had seen Telescope before. It is a universal fuzzy finder, a bit like orderless or consult on Emacs. Many plugins provide integration with Telescope, or you can add it yourself fairly easily, so that it becomes a nice universal interface for finding and selecting stuff. Combined with which-key, you can set up easy file finding, buffer or window switching or whatever you like, and you don’t need to think too much about how to accomplish things. LazyVim also sets up attractive formatting for messages and notifications and the command line, so even if you use Neovim exclusively in the terminal as I do, the UI feels quite slick.

Speaking of the terminal UI, I remember when I last used Vim/Neovim extensively that one of the slightly awkward things was running code in a terminal (such as a REPL, or a few lines of code, rather than a one-line shell command). People tended to either suspend Vim and drop back to the shell to run another command before foregrounding the Vim process again, or they used Vim in tmux. This has improved enormously, and it is now possible to have an integrated shell, or a floating terminal window very easily. This lead to me discover an amazing terminal UI for managing git: LazyGit (do you see a lazy theme?). I think that little can displace magit in the top spot for power and ease of managing git repositories, but LazyGit gets very close. It is powerful but also very easy to use, and has a nice discoverable UI. I have set up LazyVim to pop up LazyGit in a floating terminal when I hit <space>gg, and in a couple of seconds I can stage, commit and push to a remote, before hitting q to be back in my files again. Even better, once learned, you have exactly the same setup outside of Neovim in the terminal.

One of the things that I found comparatively tricky with Emacs was working on different projects in an isolated way. Using projectile helped a lot with that, but it always felt a little clunky that you needed different commands to filter a file or buffer search to the current project rather than the files/buffers open in Emacs. Admittedly, that setup has other advantages, but my projects tend to be standalone, and I would prefer to focus on one at a time. When I started configuring Neovim, I started looking for a ‘project’ plugin. There is such a plugin, but as I worked more in Neovim, I realised that you can naturally isolate projects just by opening the editor in your project directory: no plugins needed, and no special conditions needed to filter to project files. In iTerm, I can easily open different projects in different tabs of the terminal, and switch between them with a keystroke. Neovim sessions are also stored by directory, so if you restore a session in a particular directory, you restore the state of the editor when you were last in that project directory. It’s pretty neat. LazyVim starts up so fast (120 ms with my config, which involves no deliberate optimisation on my part), that it seems instantaneous, so there is no friction involved in opening or closing instances of Neovim for each project.

Both Emacs and Neovim are excellent and capable editors, and both are enjoyable to use in their different ways. I suppose what I am enjoying about Neovim this time around is its streamlined modularity. It lacks some of the power and sophistication of Emacs (you can’t alter editor features in real time as you use the editor, for example), but Neovim is a very good *nix citizen and plays nicely with all the other command line tools that you already know how to use. To give an example, I am writing this article in Neovim. There is a Hugo plugin, but I haven’t yet installed it. Hugo has a command to create a new post, so I can just use Neovim to issue a shell command like this, and I’m editing the new file:

:! hugo new archives/2023-02-12-neovim-again.md

Similarly, I already have Marked that can accept a file to preview at the command line, so the command below opens a preview of the current buffer in Marked and updates it on each save.

:! open -a Marked %

Similarly, I can pop open a floating terminal and start running hugo server to preview my post in the browser, then dismiss it again while it runs in the background. You get the idea… The point is that while I could easily add a keybinding to issue those commands with one keystroke if I found myself using them frequently, I don’t have to, and I don’t have to install a plugin to work easily and conveniently with an existing command line tool. I’m enjoying that minimalism.