/
/
/
1return {
2 -- LSP Configuration & Plugins
3 "neovim/nvim-lspconfig",
4 dependencies = {
5 -- Automatically install LSPs to stdpath for neovim
6 { "williamboman/mason.nvim", config = true },
7 "williamboman/mason-lspconfig.nvim",
8 "WhoIsSethDaniel/mason-tool-installer.nvim",
9 "hrsh7th/cmp-nvim-lsp",
10 "hrsh7th/cmp-buffer",
11 "hrsh7th/cmp-path",
12 "hrsh7th/cmp-cmdline",
13 "hrsh7th/nvim-cmp",
14 "L3MON4D3/LuaSnip",
15 "saadparwaiz1/cmp_luasnip",
16 "j-hui/fidget.nvim",
17
18 -- Useful status updates for LSP
19 {
20 "j-hui/fidget.nvim",
21 config = function()
22 require("fidget").setup({
23 -- Options related to notification subsystem
24 notification = {
25 -- Options related to the notification window and buffer
26 window = {
27 normal_hl = "Comment", -- Base highlight group in the notification window
28 border = "rounded", -- Border around the notification window
29 zindex = 45, -- Stacking priority of the notification window
30 max_width = 0, -- Maximum width of the notification window
31 max_height = 0, -- Maximum height of the notification window
32 x_padding = 1, -- Padding from right edge of window boundary
33 y_padding = 0, -- Padding from bottom edge of window boundary
34 align = "bottom", -- How to align the notification window
35 relative = "editor", -- What the notification window position is relative to
36 },
37 },
38 })
39 end,
40 },
41 },
42 config = function()
43 -- INFO: LSP Settings
44 -- This contains the configuration of several components related to LSPs
45 -- - luasnip
46 -- - mason
47 -- - mason-lspconfig
48 -- - nvim-cmp
49 -- - nvim-lspconfig
50
51 -- PERF:
52 -- ====================================================
53 -- LSP Keymaps
54 -- ====================================================
55
56 -- This function gets run when an LSP connects to a particular buffer.
57 vim.api.nvim_create_autocmd("LspAttach", {
58 group = vim.api.nvim_create_augroup("personal-lsp-attach", { clear = true }),
59 callback = function(event)
60 -- Create a wrapper function to simplify keymaps creation
61 local map = function(keys, func, desc)
62 vim.keymap.set("n", keys, func, { buffer = event.buf, desc = "LSP: " .. desc })
63 end
64
65 -- Jump to the definition of the word under your cursor.
66 -- This is where a variable was first declared, or where a function is defined, etc.
67 -- To jump back, press <C-T>.
68 map("gd", require("telescope.builtin").lsp_definitions, "[G]oto [D]efinition")
69 -- Find references for the word under your cursor.
70 map("gr", require("telescope.builtin").lsp_references, "[G]oto [R]eferences")
71 -- Jump to the implementation of the word under your cursor.
72 -- Useful when your language has ways of declaring types without an actual implementation.
73 map("gI", require("telescope.builtin").lsp_implementations, "[G]oto [I]mplementation")
74 -- Jump to the type of the word under your cursor.
75 -- Useful when you're not sure what type a variable is and you want to see
76 -- the definition of its *type*, not where it was *defined*.
77 map("<leader>D", require("telescope.builtin").lsp_type_definitions, "Type [D]efinition")
78 -- Fuzzy find all the symbols in your current document.
79 -- Symbols are things like variables, functions, types, etc.
80 map("<leader>ds", require("telescope.builtin").lsp_document_symbols, "[D]ocument [S]ymbols")
81 -- Fuzzy find all the symbols in your current workspace
82 -- Similar to document symbols, except searches over your whole project.
83 map(
84 "<leader>ws",
85 require("telescope.builtin").lsp_dynamic_workspace_symbols,
86 "[W]orkspace [S]ymbols"
87 )
88
89 -- Rename the variable under your cursor
90 -- Most Language Servers support renaming across files, etc.
91 map("<leader>rn", vim.lsp.buf.rename, "[R]e[n]ame")
92 -- Execute a code action, usually your cursor needs to be on top of an error
93 -- or a suggestion from your LSP for this to activate.
94 map("<leader>ca", vim.lsp.buf.code_action, "[C]ode [A]ction")
95
96 -- Opens a popup that displays documentation about the word under your cursor
97 -- See `:help K` for why this keymap
98 map("K", vim.lsp.buf.hover, "Hover Documentation")
99 map("<C-k>", vim.lsp.buf.signature_help, "Signature Documentation")
100 -- NOTE: This is not Goto Definition, this is Goto Declaration.
101 -- For example, in C this would take you to the header
102
103 -- When you move your cursor, the highlights will be cleared (the second autocommand).
104 local client = vim.lsp.get_client_by_id(event.data.client_id)
105 if client and client.server_capabilities.documentHighlightProvider then
106 vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, {
107 buffer = event.buf,
108 callback = vim.lsp.buf.document_highlight,
109 })
110
111 vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
112 buffer = event.buf,
113 callback = vim.lsp.buf.clear_references,
114 })
115 end
116 end,
117 })
118
119 -- PERF:
120 -- ====================================================
121 -- LSP Servers
122 -- ====================================================
123 local servers = {
124 clangd = {
125 cmd = {
126 -- see clangd --help-hidden
127 "clangd",
128 "--background-index",
129 -- by default, clang-tidy use -checks=clang-diagnostic-*,clang-analyzer-*
130 -- to add more checks, create .clang-tidy file in the root directory
131 -- and add Checks key, see https://clang.llvm.org/extra/clang-tidy/
132 "--clang-tidy",
133 "--completion-style=bundled",
134 "--cross-file-rename",
135 "--header-insertion=iwyu",
136 },
137 },
138 pyright = {
139 python = {
140 analysis = {
141 autoSearchPaths = true,
142 diagnosticMode = "openFilesOnly",
143 useLibraryCodeForTypes = true,
144 reportDuplicateImport = true,
145 },
146 },
147 },
148 lua_ls = {
149 settings = {
150 Lua = {
151 runtime = { version = "LuaJIT" },
152 workspace = {
153 checkThirdParty = false,
154 library = {
155 "${3rd}/luv/library",
156 unpack(vim.api.nvim_get_runtime_file("", true)),
157 },
158 },
159 completion = {
160 callSnippet = "Replace",
161 },
162 },
163 },
164 },
165 }
166
167 -- PERF:
168 -- ====================================================
169 -- capabilities Configuratioon
170 -- ====================================================
171
172 -- nvim-cmp supports additional completion capabilities, so broadcast that to servers
173 local capabilities = vim.lsp.protocol.make_client_capabilities()
174 -- capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = false
175 capabilities = require("cmp_nvim_lsp").default_capabilities(capabilities)
176 capabilities.offsetEncoding = { "utf-16" }
177 capabilities.textDocument.completion.completionItem.snippetSupport = true
178
179 -- Enable folding (for nvim-ufo)
180 capabilities.textDocument.foldingRange = {
181 dynamicRegistration = false,
182 lineFoldingOnly = true,
183 }
184
185 -- PERF:
186 -- ====================================================
187 -- Mason Configuratioon
188 -- ====================================================
189
190 -- Setup mason so it can manage external tooling
191 require("mason").setup({
192 ui = {
193 border = "rounded",
194 },
195 })
196
197 -- mason-lspconfig configurations
198 require("mason-lspconfig").setup({
199 handlers = {
200 function(server_name)
201 local server = servers[server_name] or {}
202 -- This handles overriding only values explicitly passed
203 -- by the server configuration above. Useful when disabling
204 -- certain features of an LSP (for example, turning off formatting for tsserver)
205 server.capabilities = vim.tbl_deep_extend("force", {}, capabilities, server.capabilities or {})
206 require("lspconfig")[server_name].setup(server)
207 end,
208 },
209 })
210 require("mason-tool-installer").setup({
211
212 -- List of all DAP, Linter and Formatters to install
213 ensure_installed = {
214 -- LSPs
215 "clangd",
216 "lua_ls",
217 "pyright",
218 "ruff",
219
220 -- DAP
221 "codelldb",
222 "cpptools",
223
224 -- Linter
225 "cpplint",
226
227 -- Formatters
228 "stylua",
229 "prettier",
230 },
231
232 -- if set to true this will check each tool for updates. If updates
233 -- are available the tool will be updated. This setting does not
234 -- affect :MasonToolsUpdate or :MasonToolsInstall.
235 -- Default: false
236 auto_update = false,
237
238 -- automatically install / update on startup. If set to false nothing
239 -- will happen on startup. You can use :MasonToolsInstall or
240 -- :MasonToolsUpdate to install tools and check for updates.
241 -- Default: true
242 run_on_start = true,
243
244 -- set a delay (in ms) before the installation starts. This is only
245 -- effective if run_on_start is set to true.
246 -- e.g.: 5000 = 5 second delay, 10000 = 10 second delay, etc...
247 -- Default: 0
248 start_delay = 3000, -- 3 second delay
249
250 -- Only attempt to install if 'debounce_hours' number of hours has
251 -- elapsed since the last time Neovim was started. This stores a
252 -- timestamp in a file named stdpath('data')/mason-tool-installer-debounce.
253 -- This is only relevant when you are using 'run_on_start'. It has no
254 -- effect when running manually via ':MasonToolsInstall' etc....
255 -- Default: nil
256 debounce_hours = 5, -- at least 5 hours between attempts to install/update
257 })
258
259 -- PERF:
260 -- ====================================================
261 -- nvim-cmp configuration
262 -- ====================================================
263
264 local cmp = require("cmp")
265 local luasnip = require("luasnip")
266
267 cmp.setup({
268 snippet = {
269 expand = function(args)
270 luasnip.lsp_expand(args.body)
271 end,
272 },
273 -- performance = {
274 -- max_view_entries = 20,
275 -- },
276 mapping = cmp.mapping.preset.insert({
277 ["<C-n>"] = cmp.mapping.select_next_item(),
278 ["<C-p>"] = cmp.mapping.select_prev_item(),
279 ["<C-b>"] = cmp.mapping.scroll_docs(-4),
280 ["<C-f>"] = cmp.mapping.scroll_docs(4),
281 ["<C-Space>"] = cmp.mapping.complete(),
282 ["<C-e>"] = cmp.mapping.abort(),
283 ["<CR>"] = cmp.mapping.confirm({
284 behavior = cmp.ConfirmBehavior.Replace,
285 select = true,
286 }),
287 ["<Tab>"] = cmp.mapping(function(fallback)
288 if cmp.visible() then
289 cmp.select_next_item()
290 elseif luasnip.expand_or_jumpable() then
291 luasnip.expand_or_jump()
292 else
293 fallback()
294 end
295 end, { "i", "s" }),
296 ["<S-Tab>"] = cmp.mapping(function(fallback)
297 if cmp.visible() then
298 cmp.select_prev_item()
299 elseif luasnip.jumpable(-1) then
300 luasnip.jump(-1)
301 else
302 fallback()
303 end
304 end, { "i", "s" }),
305 }),
306 sources = {
307 { name = "nvim_lsp", max_item_count = 10 },
308 { name = "luasnip" },
309 { name = "path", max_item_count = 5 },
310 { name = "buffer", max_item_count = 5 },
311 },
312 window = {
313 completion = cmp.config.window.bordered(),
314 documentation = cmp.config.window.bordered(),
315 },
316 })
317
318 -- Suggested approach to cancel snippet session after going back to normal mode
319 -- Taken from https://github.com/L3MON4D3/LuaSnip/issues/258#issuecomment-1011938524
320 function leave_snippet()
321 if
322 ((vim.v.event.old_mode == "s" and vim.v.event.new_mode == "n") or vim.v.event.old_mode == "i")
323 and require("luasnip").session.current_nodes[vim.api.nvim_get_current_buf()]
324 and not require("luasnip").session.jump_active
325 then
326 require("luasnip").unlink_current()
327 end
328 end
329
330 -- stop snippets when you leave to normal mode
331 vim.api.nvim_command([[ autocmd ModeChanged * lua leave_snippet() ]])
332
333 -- Custom command to disable completion
334 local cmp_enabled = true
335 vim.api.nvim_create_user_command("ToggleAutoComplete", function()
336 if cmp_enabled then
337 require("cmp").setup.buffer({ enabled = false })
338 cmp_enabled = false
339 else
340 require("cmp").setup.buffer({ enabled = true })
341 cmp_enabled = true
342 end
343 end, {})
344
345 -- Configure cmp-cmdline
346 cmp.setup.cmdline("/", {
347 mapping = cmp.mapping.preset.cmdline(),
348 sources = {
349 { name = "buffer" },
350 },
351 })
352 -- `:` cmdline setup.
353 cmp.setup.cmdline(":", {
354 mapping = cmp.mapping.preset.cmdline(),
355 sources = cmp.config.sources({
356 { name = "path" },
357 }, {
358 {
359 name = "cmdline",
360 option = {
361 ignore_cmds = { "Man", "!" },
362 },
363 },
364 }),
365 })
366
367 -- PERF:
368 -- ====================================================
369 -- LSP related UI Configurations
370 -- ====================================================
371
372 -- Add bordered to LSP info
373 require("lspconfig.ui.windows").default_options.border = "rounded"
374
375 -- INFO: Configure LSP textDocuments
376 vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, { border = "rounded" })
377
378 vim.lsp.handlers["textDocument/signatureHelp"] =
379 vim.lsp.with(vim.lsp.handlers.signature_help, { border = "rounded" })
380
381 -- Diagnostic signs
382 local diagnostic_signs = {
383 { name = 'DiagnosticSignError', text = 'ï¯' },
384 { name = 'DiagnosticSignWarn', text = 'ï¡' },
385 { name = 'DiagnosticSignHint', text = 'ï' },
386 { name = 'DiagnosticSignInfo', text = 'ï' },
387 }
388 for _, sign in ipairs(diagnostic_signs) do
389 vim.fn.sign_define(sign.name, { texthl = sign.name, text = sign.text, numhl = sign.name })
390 end
391
392 vim.diagnostic.config({
393 virtual_text = {
394 prefix = "â", -- Could be 'â ', 'â', 'x'
395 },
396 severity_sort = true,
397 float = {
398 source = "always", -- Or "if_many"
399 },
400 signs = true,
401 })
402 end,
403}
404