Leveraging language server protocol is a great way to navigate large code bases, and Clangd is a popular choice for C/C++ projects. Recently, I turned to AI (LLM) for guidance in setting up Clangd in Neovim on a new platform, and I want to share my experience and the insights I gained along the way.
For example, I want to explore the OV5647 camera driver for Raspberry Pi, make modifications to the driver, and test it on actual hardware.
To start, I set up a simple toy project with the original ov5647.c driver and Makefile to build it single as a loadable kernel module object.
obj-m += ov5647.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
git clone --depth=1 https://github.com/raspberrypi/linux
bear -- make -C /lib/modules/$(uname -r)/build M=$PWD module
To enable Clangd for code indexing and navigation in Neovim, I generate a compile_commands.json file using bear.
rcam@rcam:~/Projects/ov5647-dev $ n compile_commands.json ^C
rcam@rcam:~/Projects/ov5647-dev $ bear -- make -C /lib/modules/$(uname -r)/build M=$PWD modules V=1
make: Entering directory '/usr/src/linux-headers-6.12.47+rpt-rpi-v8'
...
# (full make output omitted for brevity)
...
rcam@rcam:~/Projects/ov5647-dev $
This produces compile_commands.json that Clangd uses to understand compiler flags and include paths for the module.
With this, Neovim’s LSP features such as code navigation, autocompletion, and symbol indexing work correctly.
Even though I have configured Neovim with Clangd, Neovim LSP plugin fails to go to function definitions and shows "No definition found" error.
I have verified that the compile_commands.json file is correctly generated and includes the necessary flags and include paths for the kernel module.
I leveraged AI to guide me in finding the correct configuration for Neovim's LSP with Clangd. The process was iterative, with AI suggesting checks and adjustments at each step.
I first ensured that my LSP client was correctly set up in Neovim.
My lsp-config snippet looked like this:
use {
'neovim/nvim-lspconfig',
config = function()
local lspconfig = require('lspconfig')
local util = require('lspconfig.util')
lspconfig.clangd.setup({
cmd = { "clangd", "--background-index" },
filetypes = { "c", "cpp" },
root_dir = util.root_pattern("compile_commands.json", ".git"),
on_attach = function(client, bufnr)
local bufopts = { noremap = true, silent = true, buffer = bufnr }
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, bufopts)
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, bufopts)
vim.keymap.set('n', 'gr', vim.lsp.buf.references, bufopts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, bufopts)
end,
})
end
}
AI suggested confirming that Clangd was being attached to the buffer using :LspInfo in Neovim.
I inspected Neovim's LSP logs for detailed errors using:
cat ~/.local/state/nvim/lsp.log
From this, I could identify issues such as missing compile flags or ignored directories.
[ERROR][2026-02-22 17:09:26] ...p/_transport.lua:36 "rpc" "clangd" "stderr" "I[17:09:26.840] <-- reply(2)\n"
[ERROR][2026-02-22 17:09:26] ...p/_transport.lua:36 "rpc" "clangd" "stderr" "E[17:09:26.840] Failed to prepare a compiler instance: unknown target ABI 'lp64'"
...
To avoid indexing irrelevant files and directories, AI recommended creating a .clangd file in the project root:
CompileFlags:
Add: -I/home/rcam/Projects/ov5647-dev/linux
Index:
Ignore: .cache/
This ensured Clangd only indexed the relevant source and header files for my kernel module.
Since Linux kernel modules rely on specific headers, I generated compile_commands.json using Bear:
bear -- make -C /lib/modules/$(uname -r)/build M=$PWD module
AI highlighted that this step was crucial so Clangd could resolve include paths correctly.
After each change, I restarted Neovim, reloaded the LSP client using :LspRestart,
and checked buffer diagnostics with :lua vim.lsp.diagnostic.show_line_diagnostics().
After saving the .clangd file and restarting Neovim, I verified that:
The AI guidance helped me reach this configuration efficiently without trial-and-error across multiple kernel builds.
Using AI guidance proved very helpful in resolving toolchain-related issues with Clangd in my Neovim setup. AI directed me to the relevant logs, suggested adjustments in the .clangd configuration, and helped identify which files to ignore and which include paths to add.
This support allowed me to focus on actual development and learning, saving time and reducing frustration while configuring the LSP for my kernel module project.