Lua Session 05: LSP Configuration

Language intelligence. This session covers LSP server configuration, mason for installation, and diagnostic handling.

Pre-Session State

  • Understand lazy.nvim plugin specs

  • Know vim.keymap patterns

  • Can use autocommands

Lesson 1: Mason for LSP Installation

Concept: Mason installs and manages LSP servers.

Exercise 1.1: Mason setup

-- lua/plugins/mason.lua
return {
    "williamboman/mason.nvim",
    cmd = "Mason",
    build = ":MasonUpdate",
    opts = {
        ui = {
            border = "rounded",
            icons = {
                package_installed = "✓",
                package_pending = "➜",
                package_uninstalled = "✗"
            }
        }
    }
}

Exercise 1.2: mason-lspconfig bridge

return {
    "williamboman/mason-lspconfig.nvim",
    dependencies = {
        "williamboman/mason.nvim",
        "neovim/nvim-lspconfig",
    },
    opts = {
        ensure_installed = {
            "lua_ls",      -- Lua
            "pyright",     -- Python
            "bashls",      -- Bash
            "yamlls",      -- YAML
        },
        automatic_installation = true,
    }
}

Lesson 2: nvim-lspconfig Setup

Concept: Configure LSP servers with nvim-lspconfig.

Exercise 2.1: Basic server setup

-- lua/plugins/lsp.lua
return {
    "neovim/nvim-lspconfig",
    event = { "BufReadPre", "BufNewFile" },
    dependencies = {
        "williamboman/mason.nvim",
        "williamboman/mason-lspconfig.nvim",
    },
    config = function()
        local lspconfig = require("lspconfig")

        -- Simple servers (no special config)
        lspconfig.bashls.setup({})
        lspconfig.pyright.setup({})
        lspconfig.yamlls.setup({})

        -- Lua with special settings
        lspconfig.lua_ls.setup({
            settings = {
                Lua = {
                    diagnostics = { globals = { "vim" } },
                    workspace = { checkThirdParty = false },
                }
            }
        })
    end
}

Exercise 2.2: Shared on_attach function

config = function()
    local lspconfig = require("lspconfig")

    local on_attach = function(client, bufnr)
        -- Buffer-local keymaps
        local opts = { buffer = bufnr, silent = true }

        vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
        vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
        vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
        vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts)
        vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, opts)

        print("LSP attached: " .. client.name)
    end

    -- Apply to all servers
    lspconfig.lua_ls.setup({ on_attach = on_attach })
    lspconfig.pyright.setup({ on_attach = on_attach })
    lspconfig.bashls.setup({ on_attach = on_attach })
end

Exercise 2.3: Neovim 0.11+ native config

-- Neovim 0.11+ has vim.lsp.config
-- No lspconfig required for basic setup

vim.lsp.config('lua_ls', {
    settings = {
        Lua = {
            diagnostics = { globals = { 'vim' } }
        }
    }
})

vim.lsp.config('pyright', {})
vim.lsp.config('bashls', {})

-- Enable for filetypes
vim.lsp.enable({'lua_ls', 'pyright', 'bashls'})

Lesson 3: Diagnostics

Concept: Configure how LSP errors/warnings display.

Exercise 3.1: Diagnostic config

vim.diagnostic.config({
    virtual_text = {
        prefix = '●',
        source = "if_many",
    },
    signs = true,
    underline = true,
    update_in_insert = false,
    severity_sort = true,
    float = {
        border = "rounded",
        source = "always",
    },
})

Exercise 3.2: Diagnostic signs

local signs = {
    Error = " ",
    Warn = " ",
    Hint = " ",
    Info = " ",
}

for type, icon in pairs(signs) do
    local hl = "DiagnosticSign" .. type
    vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = "" })
end

Exercise 3.3: Diagnostic keymaps

-- Navigate diagnostics
vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, { desc = "Previous diagnostic" })
vim.keymap.set('n', ']d', vim.diagnostic.goto_next, { desc = "Next diagnostic" })
vim.keymap.set('n', '<leader>e', vim.diagnostic.open_float, { desc = "Show diagnostic" })
vim.keymap.set('n', '<leader>q', vim.diagnostic.setloclist, { desc = "Diagnostic list" })

Lesson 4: Capabilities (Completion)

Concept: Advertise completion capabilities to LSP.

Exercise 4.1: With nvim-cmp

config = function()
    local lspconfig = require("lspconfig")
    local cmp_lsp = require("cmp_nvim_lsp")

    -- Extended capabilities for completion
    local capabilities = cmp_lsp.default_capabilities()

    local on_attach = function(client, bufnr)
        -- keymaps here
    end

    local servers = { "lua_ls", "pyright", "bashls", "yamlls" }
    for _, server in ipairs(servers) do
        lspconfig[server].setup({
            on_attach = on_attach,
            capabilities = capabilities,
        })
    end
end

Exercise 4.2: Conditional formatting

local on_attach = function(client, bufnr)
    -- Disable formatting for specific servers
    if client.name == "tsserver" then
        client.server_capabilities.documentFormattingProvider = false
    end

    -- Format on save
    if client.supports_method("textDocument/formatting") then
        vim.api.nvim_create_autocmd("BufWritePre", {
            buffer = bufnr,
            callback = function()
                vim.lsp.buf.format({ bufnr = bufnr })
            end
        })
    end
end

Summary: What You Learned

Concept Syntax Example

Mason

require("mason").setup()

Install LSP servers

lspconfig

lspconfig.server.setup({})

Configure servers

on_attach

on_attach = function(client, bufnr)

Per-buffer setup

Capabilities

capabilities = …​

Enable features

vim.diagnostic

vim.diagnostic.config({})

Error display

vim.lsp.buf

vim.lsp.buf.definition()

LSP actions

Native (0.11+)

vim.lsp.config('name', {})

Built-in LSP config

Common LSP Keymaps

Keymap Action

gd

Go to definition

gr

Find references

K

Hover documentation

<leader>rn

Rename symbol

<leader>ca

Code action

[d / ]d

Previous/next diagnostic

Exercises to Complete

  1. [ ] Set up mason with your preferred LSP servers

  2. [ ] Create on_attach with essential keymaps

  3. [ ] Configure diagnostics with custom signs

  4. [ ] Add format-on-save for specific filetypes

  • Drill 05 - Practice LSP patterns

  • :help lsp, :help vim.diagnostic

Session Log

Timestamp Notes

Start

<Record when you started>

End

<Record when you finished>