ESP32.nvim provides a clangd configuration; your Neovim setup must register and enable it.
Add this alongside the ESP32.nvim plugin spec:
{
"neovim/nvim-lspconfig",
opts = function(_, opts)
opts.servers = opts.servers or {}
opts.servers.clangd = require("esp32").lsp_config()
end,
}LazyVim will register and enable the resulting server configuration.
After ESP32.nvim is available on the runtime path:
vim.lsp.config("clangd", require("esp32").lsp_config())
vim.lsp.enable("clangd")vim.lsp.config() and vim.lsp.enable() are the native Neovim 0.11 APIs. The
Neovim LSP documentation describes the
same registration flow.
require("esp32").lsp_config():
- resolves the configured build directory against the ESP-IDF project root
- starts Espressif's clangd with that compilation database
- prefers
sdkconfigandCMakeLists.txtas project root markers - avoids falling back to a parent
.gitdirectory for nested projects - uses current LSP position-encoding capabilities
- appends any configured
clangd_args
The plugin searches for Espressif's clangd in this order:
clangdonPATH, ifclangd --versionidentifies an Espressif buildIDF_TOOLS_PATH, supporting EIM and classic ESP-IDF layouts~/.espressif/tools/esp-clangC:\Espressif\tools\esp-clangon Windows
If it cannot find Espressif's build when the server starts, it warns and falls
back to clangd on PATH. :ESPInfo reports the exact command without
triggering that warning.
clangd needs the flags, target, definitions, and include paths from
compile_commands.json. ESP32.nvim points it at build.clang by default.
Run :ESPReconfigure when the database is missing or was produced by GCC. The
command:
- runs
idf.py -B <build_dir> -D IDF_TOOLCHAIN=clang reconfigure - waits for a successful exit
- restarts clangd clients attached to that project
- reattaches their buffers
The restart is intentional. clangd reads its compilation database at startup, and its own troubleshooting guide notes that it must be restarted after the database changes.
:ESPSetTarget performs the same restart after ESP-IDF regenerates the project
for the selected chip.
ESP-IDF commands do not depend on Neovim's current working directory. The plugin
uses the root of the attached clangd client, or searches upward from the current
buffer for sdkconfig or CMakeLists.txt, then passes that root to idf.py.
This is useful for an ESP-IDF project nested inside a monorepo. Use :ESPInfo
and :checkhealth vim.lsp to verify the resolved root if commands affect the
wrong directory.
The snippets above replace the enabled clangd configuration with the
ESP32.nvim configuration. CMakeLists.txt is also common outside ESP-IDF, so a
single global setup can use Espressif's clangd for other CMake projects.
If you regularly work on both ESP-IDF and non-ESP projects, use separate Neovim profiles or choose the clangd configuration conditionally in your own config. Avoid enabling two clangd configurations for the same buffer.
Cross-toolchain projects sometimes need clangd to query the compiler named in
compile_commands.json for its built-in include paths. Add a narrow allowlist
through clangd_args:
{
"Aietes/esp32.nvim",
opts = {
clangd_args = {
"--query-driver=/absolute/path/to/*-gcc,/absolute/path/to/*-g++",
},
},
}Match the actual compiler paths found in build.clang/compile_commands.json.
The clangd system-header guide
recommends this over hard-coding include directories.
--query-driver permits clangd to execute matching compiler binaries to inspect
their defaults. --query-driver=** is convenient, but it permits every compiler
path supplied by a compilation database. Use it only when you trust the
projects and generated databases you open.
The default command resolution is:
- configured
idf_cmd - an executable
idf.pyonPATH $IDF_PYTHON_ENV_PATH's Python running$IDF_PATH/tools/idf.py
The third form supports EIM shells where idf.py is a shell function. It
resolves to:
$IDF_PYTHON_ENV_PATH/bin/python $IDF_PATH/tools/idf.pyon macOS and Linux%IDF_PYTHON_ENV_PATH%\Scripts\python.exe %IDF_PATH%\tools\idf.pyon Windows
For a custom environment manager or wrapper:
opts = {
idf_cmd = { "mise", "exec", "--", "idf.py" },
}An argv list is preferred when a launcher needs multiple arguments. A string is also accepted for a single executable path.