kickstart.nvim/lua/plugins/nvim-lspconfig.lua

258 lines
9.5 KiB
Lua

return {
-- Main LSP Configuration
'neovim/nvim-lspconfig',
dependencies = {
-- Automatically install LSPs and related tools to stdpath for Neovim
{ 'williamboman/mason.nvim', config = true }, -- NOTE: Must be loaded before dependants
'williamboman/mason-lspconfig.nvim',
-- 'folke/neodev.nvim', -- Adds support for Neovim Lua API -- No longer needed with lazydev
{
'WhoIsSethDaniel/mason-tool-installer.nvim',
config = function()
require('mason-tool-installer').setup {
ensure_installed = {
'lua-language-server',
'marksman',
-- Go tools
'gopls', -- Go LSP
'gofumpt', -- Stricter Go formatter
'goimports', -- Go import manager
'golangci-lint', -- Go linter
'delve', -- Go debugger
-- Zig tools
'zls', -- Zig LSP
-- C tools
'clangd', -- C/C++ LSP
'clang-format', -- C/C++ formatter
'codelldb', -- Native code debugger
-- Python tools
'pyright', -- Python LSP
'black', -- Python formatter
'ruff', -- Python linter
'debugpy', -- Python debugger
-- SQL tools
'sqls', -- Advanced SQL LSP
},
auto_update = true,
run_on_start = true,
}
end,
},
-- Useful status updates for LSP.
-- NOTE: `opts = {}` is the same as calling `require('fidget').setup({})`
{ 'j-hui/fidget.nvim', opts = {} },
-- Allows extra capabilities provided by nvim-cmp
'hrsh7th/cmp-nvim-lsp',
},
config = function()
-- Brief aside: **What is LSP?**
--
-- LSP is an initialism you've probably heard, but might not understand what it is.
--
-- LSP stands for Language Server Protocol. It's a protocol that helps editors
-- and language tooling communicate in a standardized fashion.
--
-- See `:help lsp` for more details.
-- Enable the following language servers
-- Feel free to add/remove any LSPs that you want here. They will automatically be installed.
--
-- Add any additional override configuration in the following tables. Available keys are:
-- - cmd (table): Override the default command used to start the server
-- - filetypes (table): Override the default list of associated filetypes for the server
-- - capabilities (table): Override fields in capabilities. Can be used to disable certain LSP features.
-- - settings (table): Override the default settings passed to the server. Can be used to disable diagnostics.
local servers = {
-- Python
pyright = {},
-- Go
gopls = {
settings = {
gopls = {
analyses = {
unusedparams = true,
},
staticcheck = false, -- Let golangci-lint handle this
gofumpt = false, -- Let null-ls handle this
hints = {
assignVariableTypes = false, -- Disable hints for better performance
compositeLiteralFields = false,
compositeLiteralTypes = false,
constantValues = false,
functionTypeParameters = false,
parameterNames = false,
rangeVariableTypes = false,
},
vulncheck = "Off", -- Disable vulnerability checking
completionBudget = "100ms", -- Limit completion time
symbolMatcher = "FastFuzzy", -- Faster symbol matching
symbolStyle = "Dynamic",
usePlaceholders = false, -- Disable placeholders for better performance
matcher = "Fuzzy", -- Faster matching algorithm
diagnosticsDelay = "500ms", -- Add slight delay to batch diagnostics
},
},
},
-- Lua
lua_ls = {
settings = {
Lua = {
diagnostics = {
globals = { 'vim' },
},
workspace = {
library = {
[vim.fn.expand('$VIMRUNTIME/lua')] = true,
[vim.fn.stdpath('config') .. '/lua'] = true,
},
},
},
},
},
-- SQL - Advanced SQL language server with better completion
sqls = {
settings = {
sqls = {
connections = {}, -- Will be populated by dadbod
lowercaseKeywords = true, -- Format keywords to lowercase
},
},
},
}
-- nvim-cmp supports additional completion capabilities, so broadcast that to servers
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities = require('cmp_nvim_lsp').default_capabilities(capabilities)
-- Single LSP attach handler for all functionality
vim.api.nvim_create_autocmd('LspAttach', {
group = vim.api.nvim_create_augroup('kickstart-lsp-attach', { clear = true }),
callback = function(args)
-- Remove any diagnostic keymaps
pcall(vim.keymap.del, 'n', '<leader>e', { buffer = args.buf })
-- Get the client
local client = vim.lsp.get_client_by_id(args.data.client_id)
if not client then return end
-- Disable semantic tokens for gopls
if client.name == "gopls" then
client.server_capabilities.semanticTokensProvider = nil
end
-- Only set up document formatting for null-ls
if client.name ~= "null-ls" then
client.server_capabilities.documentFormattingProvider = false
end
-- Set up LSP keymaps
require('core.keymaps').setup_lsp_keymaps(args.buf)
-- Override the built-in LSP handler for references to use telescope
vim.lsp.handlers['textDocument/references'] = function(_, result, ctx)
if not result or vim.tbl_isempty(result) then
vim.notify('No references found')
else
require('telescope.builtin').lsp_references()
end
end
-- Set up document highlight on hover only if the server supports it
if client.server_capabilities.documentHighlightProvider then
local highlight_group = vim.api.nvim_create_augroup('lsp_document_highlight_' .. args.buf, { clear = true })
vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, {
group = highlight_group,
buffer = args.buf,
callback = function()
-- Check if the client is still attached and valid
if vim.lsp.buf_get_clients(args.buf)[1] then
vim.lsp.buf.document_highlight()
end
end,
})
vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
group = highlight_group,
buffer = args.buf,
callback = function()
-- Check if the client is still attached and valid
if vim.lsp.buf_get_clients(args.buf)[1] then
vim.lsp.buf.clear_references()
end
end,
})
end
end,
})
-- Ensure the servers above are installed
local mason_lspconfig = require 'mason-lspconfig'
mason_lspconfig.setup {
ensure_installed = vim.tbl_keys(servers),
}
-- Custom semantic tokens handler for gopls
local semantic_tokens_handler = function(err, result, ctx, config)
local client = vim.lsp.get_client_by_id(ctx.client_id)
if not client then return end
-- Check if client has the required semantic tokens capabilities
local semantic_tokens = client.server_capabilities.semanticTokensProvider
if not semantic_tokens or not semantic_tokens.legend then
-- If no legend is provided, disable semantic tokens for this client
client.server_capabilities.semanticTokensProvider = nil
return
end
-- If we have a valid legend, proceed with default handler
vim.lsp.semantic_tokens.on_full(err, result, ctx, config)
end
mason_lspconfig.setup_handlers {
function(server_name)
local server_config = servers[server_name] or {}
-- For gopls, add debug logging
if server_name == "gopls" then
-- Create a custom on_attach that disables semantic tokens
local orig_on_attach = server_config.on_attach
server_config.on_attach = function(client, bufnr)
-- Disable semantic tokens for this client
client.server_capabilities.semanticTokensProvider = nil
-- Call original on_attach if it exists
if orig_on_attach then
orig_on_attach(client, bufnr)
end
end
end
server_config.capabilities = capabilities
require('lspconfig')[server_name].setup(server_config)
end,
}
-- Override the semantic tokens handler to be more resilient
vim.lsp.handlers['textDocument/semanticTokens/full'] = function(err, result, ctx, config)
-- If there's an error or no result, just return
if err or not result then return end
local client = vim.lsp.get_client_by_id(ctx.client_id)
if not client then return end
local bufnr = ctx.bufnr
if not bufnr then return end
-- Get the highlighter safely
local highlighter = vim.lsp.semantic_tokens.create_highlighter(bufnr, client)
if not highlighter then return end
-- Process the response safely
pcall(function()
highlighter:process_response(result, client, ctx.request.version)
end)
end
end,
}