From Scratch: Set LSP on neovim 0.11

Zhenbo Li August 03, 2025

In this guide, I walk through setting up a fresh Neovim configuration from scratch, using the latest features provided by Neovim 0.11.

Step 0: Clean my old Neovim config

I created a new git branch for my brand new neovim config.

$ nvim --version
NVIM v0.11.2

Step 1: Install Package Manager (lazy.nvim)

In Neovim 0.11, the built-in plugin manager vim.pack is still in early stage. (Development News) I'm going with lazy.nvim by folke.

Following the official guide, I create ~/.config/nvim/lua/config/lazy.lua and add require("config.lazy") to the brand new ~/.config/nvim/init.lua

lazy.nvim fails to load when we have no plugins. So I add johnfrankmorgan/whitespace to my nvim. Create ~/.config/nvim/lua/plugins/whitespace.lua

return {
    'johnfrankmorgan/whitespace.nvim',
    config = function ()
        require('whitespace-nvim').setup({
            highlight = 'DiffDelete',
            ignored_filetypes = { 'TelescopePrompt', 'Trouble', 'help', 'dashboard' },
            ignore_terminal = true,
            return_cursor = true,
        })
        -- remove trailing whitespace with a keybinding
        -- vim.keymap.set('n', '<Leader>t', require('whitespace-nvim').trim)
    end
}

Restart nvim, this plugin is automatically installed. Executing :checkhealth lazy should say OK.

Move vim key binding to a new config file

Personally I prefer to gather my vim keybind settings. I create a new file ~/.config/nvim/lua/config/key_binding.lua

vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

I also remove them from ~/.config/nvim/lua/config/lazy.lua

My init.lua now changes to

require("config.key_binding")
require("config.lazy")

Step 2: Install Mason

Mason.nvim is the most popular neovim plugin for LSP servers. I only use lazy.nvim as package manager, so I don't follow the best practice on readme. I create ~/.config/nvim/lua/plugins/mason.lua

return {
    {"mason-org/mason.nvim"},
    {"mason-org/mason-lspconfig.nvim"},
}

I put my lsp config in a separate file ~/.config/nvim/lua/config/lsp_config.lua

require("mason").setup()
require("mason-lspconfig").setup()

I also add require("config.lsp_config") to init.lua

Step 3: Install LSP Server via Mason

I use mason to manage my installed LSP servers, so my life could be easier when I'm migrating my neovim config between multiple computers. mason-registry shows all the plugins managed by mason. For now, I install clangd. Modify ~/.config/nvim/lua/config/lsp_config.lua

require("mason").setup()
require("mason-lspconfig").setup({
    ensure_installed = { "clangd" },
})
vim.lsp.enable({
    "clangd"
})

neovim/nvim-lspconfig collects basic config files for most LSP servers. I prefer to only copy files needed instead of cloning the whole repo.

mkdir -p ~/.config/nvim/lsp && cd ~/.config/nvim/lsp
wget https://raw.githubusercontent.com/neovim/nvim-lspconfig/refs/heads/master/lsp/clangd.lua

Restart neovim, mason automatically downloads clangd.

~/.local/share/nvim/mason/bin/clangd --version
clangd version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
Features: linux+grpc
Platform: x86_64-unknown-linux-gnu

Try clangd in a real C/C++ project

I use rizsotto/Bear by László Nagy to generate compile_commands.json. Neovim 0.11 provides a few default lsp key mappings.

Step 4: Show inline error message

As Heiker says in their blog Virtual text is an option to show the error message inline. We add it to ~/.config/nvim/lua/config/lsp_config.lua

vim.diagnostic.config({
  virtual_text = true,
})

Epilogue

The step-by-step code is hosted at https://github.com/Endle/my-neovim-config/commits/nvim_11_demo/

Heiker has written several articles about nvim, including built-in treesitter, LSP guide, simple config