Jakub Mazur

CS Student & Full Stack Developer

Maastricht - Netherlands

Back

Minimalistic Modern LSP Setup in Neovim with lazy.nvim

March 20, 2025 (about 1 month ago)

-

100 views

If you're customizing Neovim and want a more powerful coding experience - real-time diagnostics, go-to-definition, smart completion, code actions - you'll want LSP.

This post walks you through a minimal LSP setup using lazy.nvim for plugin management.

What is LSP?

LSP stands for Language Server Protocol — it defines a standard way for editors and language-specific servers to communicate.

By connecting Neovim to language servers, you get:

  • Syntax checking (errors, warnings)
  • Auto-completion
  • Go-to-definition, references
  • Hover documentation
  • Code actions (like quick-fixes, rename, refactor)
  • Formatting

Plugins Used

We’ll use the following Neovim plugins:

  • mason.nvim
    Easily install and manage LSP servers, formatters, linters, and debuggers from inside Neovim.

  • mason-lspconfig.nvim
    A bridge between Mason and Neovim’s built-in LSP client. It ensures the correct servers are installed and makes them easy to configure.

  • nvim-lspconfig
    Official plugin for configuring and launching language servers. It handles the actual connection between Neovim and each server.

Step 1: Create lsp-config.lua

To keep things organized, start by creating a new file for LSP-related plugins:

~/.config/nvim/lua/plugins/lsp-config.lua

If you're using a plugin manager like lazy.nvim or packer.nvim, this is where you’ll define and configure your LSP setup.

Step 2: Install mason.nvim — A Package Manager for LSPs

First, we need a way to install and manage language servers without dealing with npm, pip, or system package managers. That’s exactly what mason.nvim does.

What is Mason?

mason.nvim is a plugin that downloads and manages:

  • LSP servers
  • Formatters (like prettier, black, clang-format)
  • Linters (like eslint, flake8)
  • Debuggers

All directly from Neovim — cross-platform and without leaving your config.

Add this to your lsp-config.lua:

{
  'williamboman/mason.nvim',
  opts = {},
}

Now What?

Restart Neovim and run:

:Mason

This opens an UI where you can:

  • Browse all available LSP servers, linters, and formatters
  • Install or uninstall them with a keypress
  • See what's already installed

At this point, Mason can manage tools — but Neovim doesn’t yet know which language servers you want or how to talk to them. Let’s fix that next.

Step 3: Install mason-lspconfig.nvim — Connecting Mason to Neovim

Now that mason.nvim is installed, you might be wondering:

“How does Neovim know which LSP servers to actually set up and connect to my code?”

That’s where mason-lspconfig.nvim comes in.

Why We Use mason-lspconfig.nvim

This plugin acts as a bridge between Mason and Neovim's built-in LSP client. It:

  • Automatically installs LSP servers listed in ensure_installed
  • Helps match Mason’s internal server names to the config names expected by Neovim
  • Makes setup smoother and less error-prone

Add this to your lsp-config.lua:

{
  "williamboman/mason-lspconfig.nvim",
  config = function()
    require("mason-lspconfig").setup({
      ensure_installed = {
        "lua_ls",
        "pyright",
        "tsserver",
        "rust_analyzer",
        "clangd"
      }
    })
  end
}

This tells Mason to automatically install these popular LSP servers on startup:

  • lua_ls: for Lua (great for configuring Neovim)
  • pyright: Python
  • tsserver: JavaScript & TypeScript
  • rust_analyzer: Rust
  • clangd: C and C++

You can find the full list of supported servers here.

After this step:
Neovim now knows:

  • Which LSP servers to install
  • Where to find them (via Mason)
  • That these servers are available for connection

But we still haven’t actually hooked up Neovim to the servers. That’s what we’ll handle next using nvim-lspconfig.

Step 4: Connect to LSP Servers with nvim-lspconfig

So far, we've told Mason which language servers to install and manage. But installing the servers is only half of the setup — we still need to tell Neovim how to connect to each server and actually start using them.

That's exactly what nvim-lspconfig is for.

What is nvim-lspconfig?

nvim-lspconfig is the official plugin maintained by the Neovim team that provides easy configurations for connecting to LSP servers. Each language server needs to be started with some settings, and this plugin handles the boilerplate for you.

In short, it’s the final link in the chain:

  • Mason installs the servers
  • mason-lspconfig tells us which ones we want
  • nvim-lspconfig starts them and connects them to Neovim

Without this, the servers would be installed on your system, but Neovim wouldn’t use them.

Add this to your lsp-config.lua:

{
  "neovim/nvim-lspconfig",
  config = function()
    local lspconfig = require("lspconfig")

    lspconfig.lua_ls.setup({})
    lspconfig.rust_analyzer.setup({})
    lspconfig.clangd.setup({})
    lspconfig.eslint.setup({})
    lspconfig.rome.setup({})
    lspconfig.pyre.setup({})
  end
}

This code:

  • Loads the lspconfig module
  • Calls .setup({}) for each installed server
  • Initializes each language server so it attaches to your files when you open them

You can customize the {} part for each server to pass in specific settings, root directories, or formatting options — but keeping it empty works as a good starting point.

How to Check if It's Working

Once you've saved your config and restarted Neovim, open a file in one of the supported languages (like .lua, .py, or .rs), and run:

:LspInfo

This will show you:

  • Which servers are currently installed
  • Which ones are attached to your current buffer
  • Whether your LSP setup is working properly

You should also see things like:

  • Function names and types on hover
  • Inline diagnostics under problematic code
  • Completion suggestions from the language server

At this point, you're connected — LSP is up and running.

(Optional) Telescope UI for Code Actions

Create telescope.lua under lua/plugins/:

return {
  {
    'nvim-telescope/telescope.nvim', tag = '0.1.8',
    dependencies = { 'nvim-lua/plenary.nvim' },
    config = function()
      local builtin = require("telescope.builtin")
      vim.keymap.set('n', '<C-p>', builtin.find_files, {})
      vim.keymap.set('n', '<leader>fg', builtin.live_grep, {})
    end
  },
  {
    'nvim-telescope/telescope-ui-select.nvim',
    config = function()
      require("telescope").setup {
        extensions = {
          ["ui-select"] = {
            require("telescope.themes").get_dropdown {}
          }
        }
      }
      require("telescope").load_extension("ui-select")
    end
  }
}

Explanation:

  • telescope-ui-select replaces boring vim selection prompts with Telescope dropdowns.
  • Now when you press <space>ca, you'll get a clean dropdown for code actions.

Result

With this setup:

  • LSP servers are installed automatically.
  • Neovim is connected to the servers and offers full IDE functionality.
  • Keymaps work out-of-the-box when a server is active.
  • Code actions look great thanks to Telescope.

It’s minimal, modular, and fully powered by lazy.nvim.

© 2025 - Jakub Mazur