--- Title: Setup Neovim with kickstart.nvim Date: 2023-12-25 17:15 Modified: 2024-02-06 10:30 Lang: en Author: Fabrice Category: software Tags: vim, neovim, setup Slug: nvim-kickstart table-of-contents: true Header_Cover: ../images/covers/antennae.jpg Summary: How to create a sane Neovim configuration with kickstart.nvim as a starting point. --- # Introduction ## How I managed my configuration When I started using Vim, I started editing my `.vimrc` bit by bit and incrementally before it starts getting too big for me to find anything inside it and not using even half of the plugins I installed. That goes without saying, there were quite a bit on conflicting keymaps as well as I'm using [bépo](http://bepo.fr/) as my keyboard layout with [partial remaps (fr)](https://cdn.bepo.fr/Vim-bepo-066.png). Obviously, it slowly became quite a mess. To address this issue, I decided to reorganise my `$HOME/.config/vim` directory using the [vim directory structure](www.panozzaj.com/blog/2011/09/09/vim-directory-structure/) and did some cleanup at this point of time. I think it was also around this period that I discovered that Vim8 added a native package manager that I started to use. Thus, at this point, I started organising my configuration with semantic files, such as `$VIMHOME/plugin/spelling.vim` to manage my spelling configuration for instance. This approach makes debugging easier, and also checking custom keyboard shortcuts easier, as I just have to check `$VIMHOME/plugin/omnicomplete.vim` for instance to know which shortcuts I set up when I'm still getting the habits of using them. At some point of time, I moved to Neovim, and I simply moved my configuration from Vim to Neovim and continue on adding more and more plugins on top of each other depending on my hype, especially because the world of Neovim plugins opened up to me. Needless to say that less than half of these plugins were put into good use. Which leads to my first configuration big cleanup. Six months ago, I wiped my frankenconfig, and started back from scratch in [lua](https://lua.org/about.html), with the same structural approach as previously, but now wondering if the plugin would be useful or not. Since my first time using Vim, there were some big changes in the vim ecosystem, especially in language management with [tree-sitter](https://tree-sitter.github.io/tree-sitter/) and [lsp](https://en.wikipedia.org/wiki/Language_Server_Protocol). These two bring into the environment a unified way to manage languages without having to depend on language-specific plugins, henceforth I didn't need specific plugins to have nice syntax coloration for obscure languages anymore, or get frustrated with [omnicomplete](https://vim.fandom.com/wiki/Omni_completion) which decided not to work for some languages… While it's not an absolute rule (for instance, I'm using [vimtex]({filename}./nvim-latex.md) for latex, which includes a more accurate syntax coloring than tree-sitter). I also moved from the native vim way of managing plugins to use [`Lazy`](https://github.com/folke/lazy.nvim) as a proper plugin manager, which helped me synchronize my configuration between my different computers. It was working nice and well, with some weird bugs (see below) on first install, but as it was punctual… I just ignored it. ```plain E5113: Error while calling lua chunk: $VIMHOME/nvim/init.lua:13: E21: Cannot make changes, 'modifiable' is off ``` However, I was unhappy with some of my configurations, and if I managed to have something functional, there were many details that annoy me that stemmed for some configuration I wrote some times ago and of course didn't document. This leads us to today, where I just decided to use [`kickstart.nvim`](https://github.com/nvim-lua/kickstart.nvim), which is a well-documented vim starting configuration (it's not a distribution, it still requires your input to obtain something that fits your needs), which was exactly what I needed to start anew… but not fully from scratch. ## The migration To move my configuration from `kickstart.nvim`, I wanted to get the best of both world. For instance, I didn't want to have an [`init.lua`](https://github.com/nvim-lua/kickstart.nvim/blob/master/init.lua) that is over 600 lines long. I thus decided to split it into short files that manages a specific part of the configuration: completion, lsps, treesitter, mappings after reading the different configuration default from nvim-kickstart and changing what I disliked. To do that, I started with using the [`NVIM_APPNAME`](https://practical.li/neovim/configuration/) environment option in order to make the move in a non-destructive way. After installing the bare minimum to make it usable for me (as a bépo user), I exported the `NVIM_APPNAME` variable to start using my configuration to help me debug it on the fly. I also decided to write this blog post to remember the process and maybe helped some people who want to configure their text editor. Note that in the following, we will assume that the reader is already familiar with Neovim and lua. # Configuring Neovim My (in use) configuration for Neovim is available [here](https://git.epheme.re/fmouhart/nvim-config-kickstart). I started using git from the start in order to remember what I did by using its [history](https://git.epheme.re/fmouhart/nvim-config-kickstart/commits/branch/master). As it's not the easier thing to read however, here follows my rationals, and thought during this process in a chronological order. ## kickstart.nvim `kickstart.nvim` is a starting Neovim configuration file which was created by [TJ DeVries](https://github.com/tjdevries), a core developer of Neovim, author of [telescope.nvim](https://github.com/nvim-lua/telescope.nvim) and content creator about Neovim. You can see a quick presentation on his YouTube channel [[here]](https://www.youtube.com/watch?v=stqUbv-5u2s). To summarise, it is a starting configuration including a minimal set of plugins that helps to have a modern editor, that has a working and customizable LSP configuration with [Mason](https://github.com/williamboman/mason.nvim) to help install LSP servers, git helpers, completion, telescope, and a choice of shortcuts that are quite natural to learn (unlike what I used previously because I was simply adding shortcuts one after another without thinking of the compatibility between them). Note that it is made for educational purpose, and thus is not modularised as is (hence the single self-contained `init.lua` file). Which leads us to our first step after simply pasting the content of `init.lua` into the `$VIMHOME/init.lua` file. ## Modular configuration When in doubt about some shortcuts, I'm under the habit of going to read the corresponding configuration file in my `$VIMHOME/plugin` directory. `kickstart.nvim` includes `which-key`, that is a plugin that pops a helper when waiting for a command as shown hereunder. [![which-key plugin illustration](../images/nvim-which-keys.png)](../images/nvim-which-keys.png) Thanks to that, I start getting rid of this habit. However, having a modular configuration helps debugging it. Usually, when an error spawns, the filename and location of what has triggered it appears in the stack trace, and it's easier to search in a short file than a thousand-line long one. After a first read of the configuration file, I decided to split it into smaller files. Note that if you want to start directly from there a project that does exactly that exists: * [kickstart-modular.nvim](https://github.com/dam9000/kickstart-modular.nvim) However, I find it easier for educational purpose to have everything in one place to linearly read it first. The process was quite simple, as the file was already divided logically into components that make sense, I just had to take the content of those sections and move them into the `$VIMHOME/lua` folder before including them in `init.lua`. I hesitated to continue using the `$VIMHOME/plugin` directory for that, but I then realised that having it inside init.lua allows having a structure that allows using `init.lua` as an index, and I can start [jumping](https://vimhelp.org/motion.txt.html) from there to access my specific configurations. To illustrate it, let's take the example of completion. In `kickstart.nvim`, there is a section called "Configure nvim-cmp", that deleted and pasted into a file `$VIMHOME/lua/complete.lua` before adding the line `require('complete')` to load it. You can see the result in this [commit](https://git.epheme.re/fmouhart/nvim-config-kickstart/commit/7ce423fade9d6877b2e9174dd9b9dea36254ac19). ## Survival Now that it's done, I need my basic keymaps for using Vim/Neovim in bépo. I added the file `$VIMHOME/lua/bepo.lua` and simply load it with a line `require('bepo')` in my `$VIMHOME/init.lua` file. I also merged my personal remap inside the `$VIMHOME/lua/mappings.lua` file which already contained the ones imported from `kickstart.nvim` from the previous step. These mappings are convenient ones such as the following one to easily open folds to a given level. ```lua local keymap = vim.keymap.set -- z0…z9 to open folds to a certain level for i=0,9 do keymap('n', 'z' .. i , ':set fdl=' .. i .. '', {noremap = true, silent = false}) end ``` I also merged and cleaned redundant general options in the `$VIMHOME/lua/general-options.lua` file. For instance to ignore folded content when jumping between paragraphs for instance, there is the following line in the aforementioned file: ```lua -- folds vim.g.ip_skipfold=true ``` Once all of that is done, at this point of time, I started moving to use the configuration by exporting `NVIM_APPNAME=new_nvim` inside my `.zshrc`. The idea is that now I bootstrapped my Neovim config to proficiently edit the configuration, which in lua also uses LSP and many of `kickstart.nvim` features. During this process I notice what I liked and disliked to know how to edit the configuration while editing the configuration. ## Importing my former configuration After having the minimal setup top edit a configuration, I now need to make things work for other things as well. That is dependent of your workflow. For reference, I personally use Neovim to: * Write text in [markdown](https://daringfireball.net/projects/markdown/) (such as what I'm currently doing) for different purposes; * blog post, emails or more generally text blocks that will be exported in html. * Read text written in Markdown; * Try to organise my life using [neorg](https://github.com/nvim-neorg/neorg); * [LaTeX](https://en.wikipedia.org/wiki/LaTeX) documents and slideshows; * Write code in a variety of languages. To do that I went to my previous configuration (which fortunately was already using [`Lazy`](https://github.com/folke/lazy.nvim) as a plugin manager). To do that, I picked my specific plugins, for instance `Neorg`, to import the configuration. For the example of `Neorg` it has multiple steps as I had a `$VIMHOME/plugin/neorg.lua` that contained my general configuration and `$VIMHOME/ftplugin/norg.lua` which has specific configuration when editing a note in `Neorg`. After asking `Lazy` to install `Neorg`, I first imported the general configuration from my previous `plugin` folder in my new `lua` folder, as depicted by this [commit](https://git.epheme.re/fmouhart/nvim-config-kickstart/commit/0e8587b461b67082c1e34ef9cb07180ccfee9f8b). After wondering if I should create a `ftplugin` directory, I finally decided to move to autocommands to manage different filetypes. The reason behind that was that I had a limited amount of lines in total in my former `ftplugin` directory, and the subdivision with `augroup` and `autocmd` in Vim makes it reasonably readable. Which brought me to this [commit](https://git.epheme.re/fmouhart/nvim-config-kickstart/commit/c506a7ea7868ba80dc053a59cbb66c5efa666e2c). Then some sanity and cleanup have been made. For example, adding a description field to my key maps so that `which-key` nicely prints them. See this [commit](https://git.epheme.re/fmouhart/nvim-config-kickstart/commit/689bb2024d97fdc9e5e656517f00e446e95ae198). Now, rinse and repeat for each plugin/specific configuration set: `vimtex`, `vim-pandoc-syntax` (which I mainly use for the [concealer](https://vimhelp.org/syntax.txt.html#conceal)), spelling configuration, etc. All the while keeping a critical eye on what I'm moving. For instance, I have a mapping on the following command to fix typo on a misspelled word under the cursor when writing text: mz[s1z=\`z. However, the key was bound at anytime, even when spelling was not enabled. To fix that, we changed the way the binding was called: instead of being called everytime, we embedded it inside an [`autocmd`](https://vimhelp.org/autocmd.txt.html#autocommands) which [triggers when the option](https://vimhelp.org/autocmd.txt.html#OptionSet) `spell` is set. To see the resulting configuration, see these two files: [`autocommands.lua`](https://git.epheme.re/fmouhart/nvim-config-kickstart/src/commit/d0afa1066cabab3d2b0aa7e2e84a267ce0532c61/lua/autocommands.lua#L32-L37) for the autocommand setting, and [`spelling.lua`](https://git.epheme.re/fmouhart/nvim-config-kickstart/src/commit/d0afa1066cabab3d2b0aa7e2e84a267ce0532c61/lua/spelling.lua) for the utils module. Just for the record, this is what it does: 1. Store the current cursor location: `mz` [sets a mark](https://vimhelp.org/motion.txt.html#mark) z on current position; 2. [Go to the previous spelling error](https://vimhelp.org/spell.txt.html#%5Bs): `[s`; 3. [Pick the first spelling suggestion](https://vimhelp.org/spell.txt.html#z%3D): `1z=`; 4. Go back to the stored location: \`z [jumps to mark](https://vimhelp.org/motion.txt.html#%60) z at the right column. ## Adjusting the configuration While testing the default behaviour of `kickstart.nvim`, which changes quite a few things for me, I realised that some of them were quite smart, such as as the leader key, which was on `,` before because of its accessibility in bépo, disabling quite a [useful motion binding](https://vimhelp.org/motion.txt.html#%2C), some are more personal tastes, such as the default register to be the system one or not. I thus tried some of these options and decided while editing the configuration and writing this blog post which one where working nicely for me (with the help of `which-key` to help me during this whole process), and which one were not quite working (`unnamedplus` as default clipboard, I really use both separately, and I don't want to have my system clipboard polluted) Moreover, `:healthcheck which-keys` was really helpful to debug some colliding key bindings, especially because of bépo, which has been incrementally fixed while editing the configuration. I already wrote a blog post about how I handle those in [vimtex]({filename}./nvim-latex.md). My former configuration also featured LSP, but Mason was not used, which made me install my LSP server from my system package manager. However, I still prefer using the system one for some languages that features some changes between versions, which I [also configured](https://git.epheme.re/fmouhart/nvim-config-kickstart/src/commit/d0afa1066cabab3d2b0aa7e2e84a267ce0532c61/lua/lsp.lua#L120-L149) in the `$VIMHOME/lua/lsp-configure.lua` file. # Final thoughts To conclude, I would like to say thanks to the French [tuppervim](https://tuppervim.org) community, which regularly organises meetings where we can show our latest configuration file, or just exchange nice tips. I discovered both [TJ DeVries](https://www.youtube.com/c/TJDeVries) and [`kickstart.nvim`](https://github.com/nvim-lua/kickstart.nvim) there. I still have to get rid of some habits (such as the comma as a leader key) and get used to it, but I'm happy with the change so far, beside knowing exactly what is in my configuration, it also helped me fix some weird key conflicts while editing markdown, making writing this blog post quite pleasant. While trying the Git-related key bindings utilities bundled with `kickstart.nvim`, while being quite minimal, it still filled my needs (especially adding a hunk from visual selection, which is helpful to split commits into sub-blocks when `git add -p` would have been quite tedious to use). The [status bar](https://github.com/nvim-lualine/lualine.nvim) was also a pleasant surprise for me. It is exactly the kind of things I find useful but a pain to configure. I may tweak it a bit in the future, but so far it's fine as it is. The configuration will continue to evolve from this point, as my use of computer and Neovim will change as well. And to finish, I think what TJ Devries said in [this video](https://www.youtube.com/watch?v=QMVIJhC9Veg) about text editor is quite on point: you don't have to spend time in your configuration if it is not fun for you, just take something that works for you. I actually took the time to do it because I find it interesting and fun 🙂 If you also find it fun, and want to try it, I strongly encourage you to take a cup of hot cocoa, put on some relaxing music, and just dive head first!