I was a vim user for a while. The :wq, "+P, y, g, gg, =g and all the cryptic shortcuts invented ten years before I was born… They are just hard wired into my brain.

A while ago I decided to use neovim. I was most impressed the fact that neovim turned out to be a healthy, good community project. So I did my little bit at opencollective and I contribute a few bucks monthly to the awesome neovim community back.

Lazy neovim configuration

hyperextensible Vim-based text editor

As a long time vimer, I am amazed and frightened by the hyperextensible nature of a neovim. On the one hand neovim can do much more than vim, due all the new features it has. The lua for plugins, async, builtin LSP, treesitter integration and so. On the other hand, all this super power comes at a cost. I went from a few plugins I used with vim to 24 ones.

That is a lot of a configuration and integration.

Especially if you are new to neovim and its plugins and don’t have a no idea where to start.

Kickstart

I can’t recommend the kickstart.nvim enough. Unlike a full featured neovim distribution this is designed to (kick)start your neovim journey and you’re expected to maintain your own configuration.

There is a video by TJ de Vries about it. In which he explains the whole configuration, for example the reason why C-y is used to accept the completion instead of the more traditional Enter. There are a few other interesting bits, like letting neovim communicate more capabilities to LSP.

-- LSP servers and clients are able to communicate to each
--  other what features they support. By default, Neovim
--  doesn't support everything that is in the LSP Specification.
--  When you add nvim-cmp, luasnip, etc. Neovim now has *more*
--  capabilities. So, we create new capabilities with nvim cmp, and
--  then broadcast that to the servers.
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = vim.tbl_deep_extend(
    'force',
    capabilities,
    require('cmp_nvim_lsp').default_capabilities())

It uses folke/lazy.nvim, which is where the title of this post comes from.

Distributions

Another approach is to go and use a neovim distro. Personally, I found such enhanced editor too intimidating for vim veterans like me. Also, there are too many distributions out there. So many there is a tool that allows you to switch between them https://lazyman.dev. I counted 14 of them on a page.

Give the kickstart.nvim a try, really.

My plugins

I was a stubborn guy who was proud to call a git clone to install his plugins. This does not work well for advanced neovim and I found folke/lazy.nvim to be good and well known option. It supports the lockfile, so you never have need to upgrade any plugin.

Theme

ellisonleao/gruvbox.nvim is my favourite theme. Great dark one and a great light one. Colors in this blog’s CSS are from gruvbox itself.

nvim-tree/nvim-web-devicons supports all the tiny icons that make the editor more aesthetically pleasing.

Misc

Not everything falls into a category 5 plugins you need for your $DAYJOB. Here are not non-essential but useful plugins that integrates neovim with tmux or git. Did you know that you can create and present slides from neovim?

LSP

I used the dense-analysis/ale plugin with vim. And it is an excellent plugin that I miss a bit. It is a single solution for a LSP, linting and a formatting. And it required almost no configuration. If the required tool was installed, ale was ready to use it. And unlike coc.vim it don’t need a node.js installed.

The neovim story is a bit more complicated. This is where the hyperextensibility comes into the play. Or a Cambrian explosion of a neovim plugins.

Let us start with VonHeikemen/lsp-zero.nvim

Out of the box it will help you integrate nvim-cmp (an autocompletion plugin) and nvim-lspconfig (a collection of configurations for various language servers)

This is not really needed and kickstart does not use it. However since ThePrimeagen’s video “Neovim: 0 to LSP” recommends it, I use it too. It introduces a few more plugins and plugin dependencies and dependencies of a plugin dependencies. This is where lazy.nvim enters the chat.

  • neovim/nvim-lspconfig provides a configuration for LSP servers. Each must be enabled in a config require('lspconfig').gopls.setup({})
  • hrsh7th/nvim-cmp is a completion plugin which that displays completion hints from all various sources. LSP beeing one of them.
  • j-hui/fidget.nvim provides a nice progress notifications. Like when the gopls loaded a workspace.
{
    'neovim/nvim-lspconfig',
    dependencies = {
        {'hrsh7th/nvim-cmp'},
        { 'j-hui/fidget.nvim', opts = {} },
    }
},

Kickstart integrates with williamboman/mason.nvim. Since I don’t want my editor to install programs behind my back I do not use it. Which is fine and in a line with the neovim philosophy.

Lint

The native solution for linting is mfussenegger/nvim-lint, which is used by the lazyvim distribution. It took a bit of fiddling to figure out the setup. But I found a configuration I am happy with.

-- linting - nvim native, complementary to LSP
{
    "mfussenegger/nvim-lint",
    event = { "BufWritePost", "BufReadPost", "InsertLeave" },
    config = function()
        local lint = require("lint")
        lint.linters_by_ft = {
            go = {"golangcilint"},
            sh = {"shellcheck"},
            proto = {"buf_lint"},
        }
        local lint_augroup = vim.api.nvim_create_augroup("lint", { clear = true })

        vim.api.nvim_create_autocmd({ "BufEnter", "BufWritePost", "InsertLeave" }, {
            group = lint_augroup,
            callback = function()
                lint.try_lint()
            end,
        })
        vim.keymap.set("n", "<leader>l", function()
            lint.try_lint()
        end, { desc = "Trigger linting for current file" })
    end,
},

Treesitter

Treesitter is one of the features of a neovim that I was skeptical about. I thought the vim’s regexp based parsing was good enough. I could not have been more wrong. Colors provided via treesitter gives me more information about a semantics helping the readability. It also does not break in the middle of a big file. And it has powerful queries that can give you a context of source file like what is the function name you are currently editing.

It is definitely something to explore more.

Fuzzy search

I have been using vim-fzf for an eternity and can’t work without a fuzzy search. nvim-telescope/telescope.nvim is a native plugin for neovim. You can fuzzy search - files, open buffers, LSP output and even a list of vim config files. Kickstart does an amazing job of showing the possibilities and how to integrate the LSP and telescope.

   map('gd', require('telescope.builtin').lsp_definitions, '[G]oto [D]efinition')

Autoformatting

stevearc/conform.nvim fixes some missbehaving LSPs by

Conform calculates minimal diffs and applies them using the built-in LSP format utilities.

So the marks and others won’t get removed by LSP reformat.

Snippets

L3MON4D3/LuaSnip is something I would love to explore more. I have a plan to watch TJ de Vries’s video about snippets for Go.

{
    'L3MON4D3/LuaSnip',
    dependencies = {
        {'rafamadriz/friendly-snippets'},
    },
    run = "make install_jsregexp",
},

Completion

The mighty hrsh7th/nvim-cmp has taken the code completion to whole new level. This is a typical plugin for neovim. Provides a core functionality itself and requires dependent plugins to integrate with other components.

-- Autocompletion
{
    'hrsh7th/nvim-cmp',
    dependencies = {
        {'L3MON4D3/LuaSnip'},
        {'hrsh7th/cmp-buffer'},
        {'hrsh7th/cmp-nvim-lsp'},
        {'saadparwaiz1/cmp_luasnip'},
    },
},

Which are called sources and must be enabled as well.

sources = {
  { name = "nvim_lsp" },
  { name = "path" },
  { name = "luasnip" },
  {
      name = 'buffer',
      option = {
          get_bufnrs = function()
              return vim.api.nvim_list_bufs()
          end
      }
  }
},

Conclusion

Configuring the neovim can be a lot of a work. Starting with a kickstart makes it much easier. You can get quite advanced configuration that is meant to be adapted. There are popular plugins like which-key used in most of distributions and I never learned to like it.

At the moment I have mixed feelings about this. Configuring neovim requires a lot of Lua code a lot of different plugins to be installed. On the one hand this gives you the flexibility to configure everything exactly the way you want to. On the other hand I hope that the existing state is just a Cambrian explosion will ends eventually and some of the functionality will finds its way into the core neovim.