From 822d397635407384d9bd22d6d746e8d9ef2f026b Mon Sep 17 00:00:00 2001 From: Martin Larsson Date: Sat, 21 Sep 2024 16:45:34 +0200 Subject: [PATCH] Update clang from lspconfig, fix keybinds for header/source switching and symbol info. Also start clang with more and different options for better handling in larger projects: + "--background-index", -- Enables background indexing + "--clang-tidy", -- Enables clang-tidy diagnostics + "--completion-style=bundled", -- Simpler completions for faster performance + "--rename-file-limit=0", -- No limit on renaming files + "--header-insertion=iwyu", -- Suggest missing includes based on IWYU + "--inlay-hints", -- Enable inlay hints for parameter and type information + "--limit-results=70", -- Limit autocompletion and symbol results + "--suggest-missing-includes", -- Still show missing includes suggestions + "--pch-storage=disk", -- Stores precompiled headers on disk (fixes the issue where system ran out of memory when indexing large projects, not a huge performance hit on fast m2 ssds) + "--log=error", -- Log only errors --- .../nvim/lua/language_servers/clangd.lua | 147 +++++++++++++----- 1 file changed, 108 insertions(+), 39 deletions(-) diff --git a/home/.config/nvim/lua/language_servers/clangd.lua b/home/.config/nvim/lua/language_servers/clangd.lua index a6327b8..369372d 100644 --- a/home/.config/nvim/lua/language_servers/clangd.lua +++ b/home/.config/nvim/lua/language_servers/clangd.lua @@ -1,54 +1,123 @@ -local function switch_between_header_and_source(bufnr) - local util = require("lspconfig/util") +local util = require "lspconfig.util" + +-- https://clangd.llvm.org/extensions.html#switch-between-sourceheader +local function switch_source_header(bufnr) bufnr = util.validate_bufnr(bufnr) local clangd_client = util.get_active_client_by_name(bufnr, "clangd") local params = { uri = vim.uri_from_bufnr(bufnr) } - -- invert this with an early return - if not clangd_client then + if clangd_client then + clangd_client.request("textDocument/switchSourceHeader", params, function(err, result) + if err then + error(tostring(err)) + end + if not result then + print "Corresponding file cannot be determined" + return + end + vim.api.nvim_command("edit " .. vim.uri_to_fname(result)) + end, bufnr) + else print "method textDocument/switchSourceHeader is not supported by any servers active on the current buffer" - return end +end - clangd_client.request("textDocument/switchSourceHeader", params, function(err, result) - if err then - error(tostring(err)) - end - if not result then - print("Corresponding file cannot be determined") +local function symbol_info() + local bufnr = vim.api.nvim_get_current_buf() + local clangd_client = util.get_active_client_by_name(bufnr, "clangd") + if not clangd_client or not clangd_client.supports_method "textDocument/symbolInfo" then + return vim.notify("Clangd client not found", vim.log.levels.ERROR) + end + local params = vim.lsp.util.make_position_params() + clangd_client.request("textDocument/symbolInfo", params, function(err, res) + if err or #res == 0 then + -- Clangd always returns an error, there is not reason to parse it return end - vim.api.nvim_command("drop " .. vim.uri_to_fname(result)) + local container = string.format("container: %s", res[1].containerName) ---@type string + local name = string.format("name: %s", res[1].name) ---@type string + vim.lsp.util.open_floating_preview({ name, container }, "", { + height = 2, + width = math.max(string.len(name), string.len(container)), + focusable = false, + focus = false, + border = require("lspconfig.ui.windows").default_options.border or "single", + title = "Symbol Info", + }) end, bufnr) end -local M = { - cmd = { - "clangd", - "--background-index", - "--clang-tidy", - "--completion-style=bundled", - -- "--cross-file-rename", // This has been deprecated - "--rename-file-limit=0", - "--header-insertion=iwyu", - "--inlay-hints", - -- "--compile-commands-dir=build/", +local lsp_maps = { + { + "ko", + function() switch_source_header(0) end, }, - commands = { - ClangdSwitchSourceHeader = { - function() - switch_between_header_and_source(0) - end, - description = "Switch between source/header", + { + "K", + symbol_info, + } +} +local keymaps = { n = {} } +for i, _ in ipairs(lsp_maps) do + local binding, cmd = unpack(lsp_maps[i]) + keymaps.n[binding] = { cmd = cmd } +end +require("utils").add_keymaps(keymaps) + +local root_files = { + ".clangd", + ".clang-tidy", + ".clang-format", + "compile_commands.json", + "compile_flags.txt", + "configure.ac", -- AutoTools +} + +local default_capabilities = { + textDocument = { + completion = { + editsNearCursor = true, + }, + }, + offsetEncoding = { "utf-8", "utf-16" }, +} + +return { + default_config = { + cmd = { + "clangd", + "--background-index", -- Enables background indexing + "--clang-tidy", -- Enables clang-tidy diagnostics + "--completion-style=bundled", -- Simpler completions for faster performance + "--rename-file-limit=0", -- No limit on renaming files + "--header-insertion=iwyu", -- Suggest missing includes based on IWYU + "--inlay-hints", -- Enable inlay hints for parameter and type information + "--limit-results=70", -- Limit autocompletion and symbol results + "--suggest-missing-includes", -- Still show missing includes suggestions + "--pch-storage=disk", -- Stores precompiled headers on disk (fixes the issue where system ran out of memory when indexing large projects, not a huge performance hit on fast m2 ssds) + "--log=error", -- Log only errors + }, + filetypes = { "c", "cpp", "objc", "objcpp", "cuda", "proto" }, + root_dir = function(fname) + return util.root_pattern(unpack(root_files))(fname) or util.find_git_ancestor(fname) + end, + single_file_support = true, + capabilities = default_capabilities, + }, + docs = { + description = [[ +https://clangd.llvm.org/installation.html + +- **NOTE:** Clang >= 11 is recommended! See [#23](https://github.com/neovim/nvim-lsp/issues/23). +- If `compile_commands.json` lives in a build directory, you should + symlink it to the root of your source tree. + ln -s /path/to/myproject/build/compile_commands.json /path/to/myproject/ +- clangd relies on a [JSON compilation database](https://clang.llvm.org/docs/JSONCompilationDatabase.html) +specified as compile_commands.json, see https://clangd.llvm.org/installation#compile_commandsjson +]], + default_config = { + root_dir = + [[ root_pattern( ".clangd", ".clang-tidy", ".clang-format", "compile_commands.json", "compile_flags.txt", "configure.ac", ".git" ) ]], + capabilities = [[default capabilities, with offsetEncoding utf-8]], }, }, } - -function M.post_setup() - require("utils").add_keymaps({ - n = { - ["ko"] = { cmd = ":ClangdSwitchSourceHeader" } - }, - }) -end - -return M