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 |
|
Install LSP servers |
lspconfig |
|
Configure servers |
on_attach |
|
Per-buffer setup |
Capabilities |
|
Enable features |
vim.diagnostic |
|
Error display |
vim.lsp.buf |
|
LSP actions |
Native (0.11+) |
|
Built-in LSP config |
Common LSP Keymaps
| Keymap | Action |
|---|---|
|
Go to definition |
|
Find references |
|
Hover documentation |
|
Rename symbol |
|
Code action |
|
Previous/next diagnostic |
Exercises to Complete
-
[ ] Set up mason with your preferred LSP servers
-
[ ] Create on_attach with essential keymaps
-
[ ] Configure diagnostics with custom signs
-
[ ] Add format-on-save for specific filetypes
Related Resources
-
Drill 05 - Practice LSP patterns
-
:help lsp,:help vim.diagnostic
Session Log
| Timestamp | Notes |
|---|---|
Start |
<Record when you started> |
End |
<Record when you finished> |