My nvim configuration
refactor: robust clangd lsp config
1 file changed, +102 -3
+102-3lsp/clangd.lua
| @@ -1,6 +1,105 @@ | ||
| 1 | +---@brief | |
| 2 | +--- | |
| 3 | +--- https://clangd.llvm.org/installation.html | |
| 4 | +--- | |
| 5 | +--- - **NOTE:** Clang >= 11 is recommended! See [#23](https://github.com/neovim/nvim-lspconfig/issues/23). | |
| 6 | +--- - If `compile_commands.json` lives in a build directory, you should | |
| 7 | +--- symlink it to the root of your source tree. | |
| 8 | +--- ``` | |
| 9 | +--- ln -s /path/to/myproject/build/compile_commands.json /path/to/myproject/ | |
| 10 | +--- ``` | |
| 11 | +--- - clangd relies on a [JSON compilation database](https://clang.llvm.org/docs/JSONCompilationDatabase.html) | |
| 12 | +--- specified as compile_commands.json, see https://clangd.llvm.org/installation#compile_commandsjson | |
| 13 | + | |
| 14 | +-- https://clangd.llvm.org/extensions.html#switch-between-sourceheader | |
| 15 | +local function switch_source_header(bufnr, client) | |
| 16 | + local method_name = 'textDocument/switchSourceHeader' | |
| 17 | + ---@diagnostic disable-next-line:param-type-mismatch | |
| 18 | + if not client or not client:supports_method(method_name) then | |
| 19 | + return vim.notify(('method %s is not supported by any servers active on the current buffer'):format(method_name)) | |
| 20 | + end | |
| 21 | + local params = vim.lsp.util.make_text_document_params(bufnr) | |
| 22 | + ---@diagnostic disable-next-line:param-type-mismatch | |
| 23 | + client:request(method_name, params, function(err, result) | |
| 24 | + if err then | |
| 25 | + error(tostring(err)) | |
| 26 | + end | |
| 27 | + if not result then | |
| 28 | + vim.notify('corresponding file cannot be determined') | |
| 29 | + return | |
| 30 | + end | |
| 31 | + vim.cmd.edit(vim.uri_to_fname(result)) | |
| 32 | + end, bufnr) | |
| 33 | +end | |
| 34 | + | |
| 35 | +local function symbol_info(bufnr, client) | |
| 36 | + local method_name = 'textDocument/symbolInfo' | |
| 37 | + ---@diagnostic disable-next-line:param-type-mismatch | |
| 38 | + if not client or not client:supports_method(method_name) then | |
| 39 | + return vim.notify('Clangd client not found', vim.log.levels.ERROR) | |
| 40 | + end | |
| 41 | + local win = vim.api.nvim_get_current_win() | |
| 42 | + local params = vim.lsp.util.make_position_params(win, client.offset_encoding) | |
| 43 | + ---@diagnostic disable-next-line:param-type-mismatch | |
| 44 | + client:request(method_name, params, function(err, res) | |
| 45 | + if err or #res == 0 then | |
| 46 | + -- Clangd always returns an error, there is no reason to parse it | |
| 47 | + return | |
| 48 | + end | |
| 49 | + local container = string.format('container: %s', res[1].containerName) ---@type string | |
| 50 | + local name = string.format('name: %s', res[1].name) ---@type string | |
| 51 | + vim.lsp.util.open_floating_preview({ name, container }, '', { | |
| 52 | + height = 2, | |
| 53 | + width = math.max(string.len(name), string.len(container)), | |
| 54 | + focusable = false, | |
| 55 | + focus = false, | |
| 56 | + title = 'Symbol Info', | |
| 57 | + }) | |
| 58 | + end, bufnr) | |
| 59 | +end | |
| 60 | + | |
| 61 | +---@class ClangdInitializeResult: lsp.InitializeResult | |
| 62 | +---@field offsetEncoding? string | |
| 63 | + | |
| 1 | 64 | ---@type vim.lsp.Config |
| 2 | 65 | return { |
| 3 | - cmd = { "clangd" }, | |
| 4 | - filetypes = { "c" }, | |
| 5 | - root_markers = { "Makefile", "CMakeLists.txt" }, | |
| 66 | + cmd = { 'clangd' }, | |
| 67 | + filetypes = { 'c', 'cpp', 'objc', 'objcpp', 'cuda' }, | |
| 68 | + root_markers = { | |
| 69 | + '.clangd', | |
| 70 | + '.clang-tidy', | |
| 71 | + '.clang-format', | |
| 72 | + 'Makefile', | |
| 73 | + 'compile_commands.json', | |
| 74 | + 'compile_flags.txt', | |
| 75 | + 'configure.ac', -- AutoTools | |
| 76 | + '.git', | |
| 77 | + }, | |
| 78 | + get_language_id = function(_, ftype) | |
| 79 | + local t = { objc = 'objective-c', objcpp = 'objective-cpp', cuda = 'cuda-cpp' } | |
| 80 | + return t[ftype] or ftype | |
| 81 | + end, | |
| 82 | + capabilities = { | |
| 83 | + textDocument = { | |
| 84 | + completion = { | |
| 85 | + editsNearCursor = true, | |
| 86 | + }, | |
| 87 | + }, | |
| 88 | + offsetEncoding = { 'utf-8', 'utf-16' }, | |
| 89 | + }, | |
| 90 | + ---@param init_result ClangdInitializeResult | |
| 91 | + on_init = function(client, init_result) | |
| 92 | + if init_result.offsetEncoding then | |
| 93 | + client.offset_encoding = init_result.offsetEncoding | |
| 94 | + end | |
| 95 | + end, | |
| 96 | + on_attach = function(client, bufnr) | |
| 97 | + vim.api.nvim_buf_create_user_command(bufnr, 'LspClangdSwitchSourceHeader', function() | |
| 98 | + switch_source_header(bufnr, client) | |
| 99 | + end, { desc = 'Switch between source/header' }) | |
| 100 | + | |
| 101 | + vim.api.nvim_buf_create_user_command(bufnr, 'LspClangdShowSymbolInfo', function() | |
| 102 | + symbol_info(bufnr, client) | |
| 103 | + end, { desc = 'Show symbol info' }) | |
| 104 | + end, | |
| 6 | 105 | } |