How to
June 14, 2026 · View on GitHub
Automatically set your compile_commands.json
There are three ways:
- Use softlink: Firstly, you should set
cmake_compile_commands_options.actiontosoft_link, then, this plugin will automatically make a softlink to compile_commands.json after generation. - Copy compile_commands.json: Set
cmake_compile_commands_options.actiontocopythen the plugin will automatically copy compile_commands.json to directory specified bycmake_compile_commands_options.targetafter generation. - Use lsp: If you're using clangd or ccls configured through lspconfig you can
set your compilation database directory to your active build directory by calling a hook in your on_new_config callback provided by lspconfig. You need to set
cmake_compile_commands_options.actiontononeto disable softlink and copy.
require('lspconfig').clangd.setup{
on_new_config = function(new_config, new_cwd)
local status, cmake = pcall(require, "cmake-tools")
if status then
cmake.clangd_on_new_config(new_config)
end
end,
}
Mimic UI of cmake-tools toolbar in visual-studio-code
We provide a list of getters for you.
cmake.is_cmake_project() -- return if current project is a cmake project
cmake.has_cmake_preset() -- return if there exists cmake presets configuration
cmake.get_build_target() -- return current build target
cmake.get_build_target_path() -- reurn full path of current build target
cmake.get_launch_target() -- return current launch target
cmake.get_launch_target_path() -- return full path of current launch target
cmake.get_launch_args() -- return args used by current launch target
cmake.get_build_type() -- return current build type
cmake.get_kit() -- return current using kit
cmake.get_configure_preset() -- return current using configure preset
cmake.get_build_preset() -- return current using build preset
cmake.get_build_directory() -- return current using build directory
cmake.get_launch_targets() -- return all launch targets
cmake.get_build_targets() -- return all build targets
cmake.get_generate_options() -- return generate options used by cmake
cmake.get_build_options() -- return build options used by cmake
With these helper functions, I've mimic the UI using lualine.
Full Configuration is as follows: (click to expand)
return {
"nvim-lualine/lualine.nvim", -- status line
config = function()
local lualine = require("lualine")
local cmake = require("cmake-tools")
-- you can find the icons from https://github.com/Civitasv/runvim/blob/master/lua/config/icons.lua
local icons = require("config.icons")
-- Credited to [evil_lualine](https://github.com/nvim-lualine/lualine.nvim/blob/master/examples/evil_lualine.lua)
local conditions = {
buffer_not_empty = function()
return vim.fn.empty(vim.fn.expand("%:t")) ~= 1
end,
hide_in_width = function()
return vim.fn.winwidth(0) > 80
end,
check_git_workspace = function()
local filepath = vim.fn.expand("%:p:h")
local gitdir = vim.fn.finddir(".git", filepath .. ";")
return gitdir and #gitdir > 0 and #gitdir < #filepath
end,
}
local colors = {
normal = {
bg = "#202328",
fg = "#bbc2cf",
yellow = "#ECBE7B",
cyan = "#008080",
darkblue = "#081633",
green = "#98be65",
orange = "#FF8800",
violet = "#a9a1e1",
magenta = "#c678dd",
blue = "#51afef",
red = "#ec5f67",
},
nightfly = {
bg = "#011627",
fg = "#acb4c2",
yellow = "#ecc48d",
cyan = "#7fdbca",
darkblue = "#82aaff",
green = "#21c7a8",
orange = "#e3d18a",
violet = "#a9a1e1",
magenta = "#ae81ff",
blue = "#82aaff ",
red = "#ff5874",
},
light = {
bg = "#f6f2ee",
fg = "#3d2b5a",
yellow = "#ac5402",
cyan = "#287980",
darkblue = "#2848a9",
green = "#396847",
orange = "#a5222f",
violet = "#8452d5",
magenta = "#6e33ce",
blue = "#2848a9",
red = "#b3434e",
},
catppuccin_mocha = {
bg = "#1E1E2E",
fg = "#CDD6F4",
yellow = "#F9E2AF",
cyan = "#7fdbca",
darkblue = "#89B4FA",
green = "#A6E3A1",
orange = "#e3d18a",
violet = "#a9a1e1",
magenta = "#ae81ff",
blue = "#89B4FA",
red = "#F38BA8",
}
}
colors = colors.light;
local config = {
options = {
icons_enabled = true,
component_separators = "",
section_separators = "",
disabled_filetypes = { "alpha", "dashboard", "Outline" },
always_divide_middle = true,
theme = {
-- We are going to use lualine_c an lualine_x as left and
-- right section. Both are highlighted by c theme . So we
-- are just setting default looks o statusline
normal = { c = { fg = colors.fg, bg = colors.bg } },
inactive = { c = { fg = colors.fg, bg = colors.bg } },
},
},
sections = {
lualine_a = {},
lualine_b = {},
lualine_y = {},
lualine_z = {},
-- c for left
lualine_c = {},
-- x for right
lualine_x = {},
},
inactive_sections = {
lualine_a = {},
lualine_b = {},
lualine_y = {},
lualine_z = {},
lualine_c = { "filename" },
lualine_x = { "location" },
},
tabline = {},
extensions = {},
}
-- Inserts a component in lualine_c at left section
local function ins_left(component)
table.insert(config.sections.lualine_c, component)
end
-- Inserts a component in lualine_x ot right section
local function ins_right(component)
table.insert(config.sections.lualine_x, component)
end
ins_left {
function()
return icons.ui.Line
end,
color = { fg = colors.blue }, -- Sets highlighting of component
padding = { left = 0, right = 1 }, -- We don't need space before this
}
ins_left {
-- mode component
function()
return icons.ui.Evil
end,
color = function()
-- auto change color according to neovims mode
local mode_color = {
n = colors.red,
i = colors.green,
v = colors.blue,
[""] = colors.blue,
V = colors.blue,
c = colors.magenta,
no = colors.red,
s = colors.orange,
S = colors.orange,
[""] = colors.orange,
ic = colors.yellow,
R = colors.violet,
Rv = colors.violet,
cv = colors.red,
ce = colors.red,
r = colors.cyan,
rm = colors.cyan,
["r?"] = colors.cyan,
["!"] = colors.red,
t = colors.red,
}
return { fg = mode_color[vim.fn.mode()] }
end,
padding = { right = 1 },
}
ins_left {
-- filesize component
"filesize",
cond = conditions.buffer_not_empty,
}
ins_left {
"filename",
cond = conditions.buffer_not_empty,
color = { fg = colors.magenta, gui = "bold" },
}
ins_left { "location" }
ins_left {
"diagnostics",
sources = { "nvim_diagnostic" },
symbols = { error = icons.diagnostics.Error, warn = icons.diagnostics.Warning, info = icons.diagnostics.Information },
diagnostics_color = {
color_error = { fg = colors.red },
color_warn = { fg = colors.yellow },
color_info = { fg = colors.cyan },
},
}
ins_left {
function()
local c_preset = cmake.get_configure_preset()
return "CMake: [" .. (c_preset and c_preset or "X") .. "]"
end,
icon = icons.ui.Search,
cond = function()
return cmake.is_cmake_project() and cmake.has_cmake_preset()
end,
on_click = function(n, mouse)
if (n == 1) then
if (mouse == "l") then
vim.cmd("CMakeSelectConfigurePreset")
end
end
end
}
ins_left {
function()
local type = cmake.get_build_type()
return "CMake: [" .. (type and type or "") .. "]"
end,
icon = icons.ui.Search,
cond = function()
return cmake.is_cmake_project() and not cmake.has_cmake_preset()
end,
on_click = function(n, mouse)
if (n == 1) then
if (mouse == "l") then
vim.cmd("CMakeSelectBuildType")
end
end
end
}
ins_left {
function()
local kit = cmake.get_kit()
return "[" .. (kit and kit or "X") .. "]"
end,
icon = icons.ui.Pencil,
cond = function()
return cmake.is_cmake_project() and not cmake.has_cmake_preset()
end,
on_click = function(n, mouse)
if (n == 1) then
if (mouse == "l") then
vim.cmd("CMakeSelectKit")
end
end
end
}
ins_left {
function()
return "Build"
end,
icon = icons.ui.Gear,
cond = cmake.is_cmake_project,
on_click = function(n, mouse)
if (n == 1) then
if (mouse == "l") then
vim.cmd("CMakeBuild")
end
end
end
}
ins_left {
function()
local b_preset = cmake.get_build_preset()
return "[" .. (b_preset and b_preset or "X") .. "]"
end,
icon = icons.ui.Search,
cond = function()
return cmake.is_cmake_project() and cmake.has_cmake_preset()
end,
on_click = function(n, mouse)
if (n == 1) then
if (mouse == "l") then
vim.cmd("CMakeSelectBuildPreset")
end
end
end
}
ins_left {
function()
local b_target = cmake.get_build_target()
return "[" .. (b_target and b_target or "X") .. "]"
end,
cond = cmake.is_cmake_project,
on_click = function(n, mouse)
if (n == 1) then
if (mouse == "l") then
vim.cmd("CMakeSelectBuildTarget")
end
end
end
}
ins_left {
function()
return icons.ui.Debug
end,
cond = cmake.is_cmake_project,
on_click = function(n, mouse)
if (n == 1) then
if (mouse == "l") then
vim.cmd("CMakeDebug")
end
end
end
}
ins_left {
function()
return icons.ui.Run
end,
cond = cmake.is_cmake_project,
on_click = function(n, mouse)
if (n == 1) then
if (mouse == "l") then
vim.cmd("CMakeRun")
end
end
end
}
ins_left {
function()
local l_target = cmake.get_launch_target()
return "[" .. (l_target and l_target or "X") .. "]"
end,
cond = cmake.is_cmake_project,
on_click = function(n, mouse)
if (n == 1) then
if (mouse == "l") then
vim.cmd("CMakeSelectLaunchTarget")
end
end
end
}
-- Insert mid section. You can make any number of sections in neovim :)
-- for lualine it's any number greater then 2
ins_left {
function()
return "%="
end,
}
-- Add components to right sections
ins_right {
"o:encoding", -- option component same as &encoding in viml
fmt = string.upper, -- I'm not sure why it's upper case either ;)
cond = conditions.hide_in_width,
color = { fg = colors.green, gui = "bold" },
}
ins_right {
"fileformat",
fmt = string.upper,
icons_enabled = false,
color = { fg = colors.green, gui = "bold" },
}
ins_right {
function()
return vim.api.nvim_buf_get_option(0, "shiftwidth")
end,
icons_enabled = false,
color = { fg = colors.green, gui = "bold" },
}
ins_right {
"branch",
icon = icons.git.Branch,
color = { fg = colors.violet, gui = "bold" },
}
ins_right {
"diff",
-- Is it me or the symbol for modified us really weird
symbols = { added = icons.git.Add, modified = icons.git.Mod, removed = icons.git.Remove },
diff_color = {
added = { fg = colors.green },
modified = { fg = colors.orange },
removed = { fg = colors.red },
},
cond = conditions.hide_in_width,
}
ins_right {
function()
local current_line = vim.fn.line(".")
local total_lines = vim.fn.line("$")
local chars = { "__", "▁▁", "▂▂", "▃▃", "▄▄", "▅▅", "▆▆", "▇▇", "██" }
local line_ratio = current_line / total_lines
local index = math.ceil(line_ratio * #chars)
return chars[index]
end,
color = { fg = colors.orange, gui = "bold" }
}
ins_right {
function()
return "▊"
end,
color = { fg = colors.blue },
padding = { left = 1 },
}
-- Now don't forget to initialize lualine
lualine.setup(config)
end
}
it looks like:

Calling :lua RunPerf() will then run perf record --call-graph dwarf {target} {launch_args}.
Fix errors in quickfix list (Windows/msvc)
If the errors and warnings are not being parsed correctly from the build output then navigation via the quickfix list will not function. It is possible that additional errorformats (:h errorformat) are required. Adding the following to your init.lua, or other suitable lua file, will enable quickfix support for MSBuild and cl.exe:
-- MSBuild:
vim.opt.errorformat:append([[\ %#%f(%l\,%c):\ %m]])
-- cl.exe:
vim.opt.errorformat:append([[\ %#%f(%l)\ :\ %#%t%[A-z]%#\ %m]])
Experimental: Additional command runners (executors)
By default, this plugin uses quickfix console for generate, build, clean, install, and others about cmake, and only uses terminal for run specific target. But if you want you can use specific executors.
Use overseer
If you want to use overseer to run cmake operations, set cmake_executor={name="overseer", opts={}} where opts is the overseer specific arguments as presented in the readme.
Always use terminal
If you want to always use terminal(for example, you want to record all commands and corresponding output), there is a way. You need set cmake_executor to {name="terminal"}, then, all commands will be executed in the terminal.
Wrapping run/build/install calls with external tools
cmake.run, cmake.build and cmake.install support a wrap_call option that prepends an external tool and its arguments to the command. This is useful for running your executables under profiling or analysis tools like perf, valgrind or strace or for installing with elevated permissions via sudo.
The wrap_call option takes a table of strings where the first element is the tool executable and the remaining elements are its arguments.
Examples
Run the current launch target under perf:
function RunPerf()
local cmake = require("cmake-tools")
cmake.run({ wrap_call = { "perf", "record", "--call-graph", "dwarf" } })
end
Calling :lua RunPerf() will run: perf record --call-graph dwarf <target> <launch_args>
Run under valgrind:
function RunValgrind()
local cmake = require("cmake-tools")
cmake.run({ wrap_call = { "valgrind", "--leak-check=full" } })
end
Time the build process:
function TimeBuild()
local cmake = require("cmake-tools")
cmake.build({ wrap_call = { "time" } })
end
Install with sudo:
function SudoInstall()
local cmake = require("cmake-tools")
cmake.install({ wrap_call = { "sudo" } })
end
Integrations
NvimTree
For users that are using nvim-tree, to keep the size of terminal constant, you should add the following configuration for nvim-tree.
require("nvim-tree").setup {
--
view = {
preserve_window_proportions = true,
---
},
}
Terminal type buffer filtering (HardTime.nvim)
When focused on a terminal buffer (not for quickfix-lists, except run terminal), you can check the filetype with :set ft. It should display cmake_tools_terminal.
This can be useful for users of plugins like hardtime.nvim you can specify the CMake terminal buffers like:
(Scrolling becomes much easier without having to resort to vim motions like CTRL H + zz or CTRL L + zz) in order to scroll the buffer
{
"m4xshen/hardtime.nvim",
event = "VeryLazy",
opts = { disabled_filetypes = { "cmake_tools_terminal" },
}
},
Telescope
cmake-tools provides Telescope integration.
Telescope cmake_tools shows files associated with the cmake project. Ignoring some files such as objects files or cmake rules. (Combines both sources and cmake_files).
Telescope cmake_tools sources shows only source files and files directly added to targets.
Telescope cmake_tools cmake_files shows files associated with the cmake-model (CMakeLists files and similar).
Additionaly CMakeShowTargetFiles can be used to only show files associated with a specific target.