Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
822b317
Add basic implementation for kit scanner
drook207 Oct 25, 2025
09afee7
Fix call to make directory
drook207 Oct 25, 2025
206038a
Add global config dir for kit file
drook207 Oct 27, 2025
e4f2c2d
Fix version parsing
drook207 Oct 27, 2025
52f1efc
Add linker and toolchain to global kit file
drook207 Oct 27, 2025
4481b50
Add automatic kit scanning
drook207 Oct 27, 2025
e58c87d
Add user command for kit scanning
drook207 Oct 27, 2025
a1bc307
Use path from const table
drook207 Nov 12, 2025
adeb9e5
Use vim mkdir builtin
drook207 Nov 12, 2025
e0bd683
Fix: Config path for cmake kits
drook207 Jan 20, 2026
a6602cd
Feature: Use vim builtin json functions
drook207 Jan 20, 2026
b71824b
Feature: Add tests
drook207 Jan 20, 2026
49f717d
Merge pull request #1 from Civitasv/master
drook207 Jan 20, 2026
6f259b5
Merge branch 'master' into feature-add-kit-scanner
drook207 Jan 20, 2026
3022b5c
Fix: Make regex more simple
drook207 Jan 20, 2026
e547e0d
Fix: Use built in functions
drook207 Jan 20, 2026
e44aae1
Refactor: Improve kit detection
drook207 Apr 10, 2026
dd64e52
Merge branch 'Civitasv:master' into master
drook207 Apr 11, 2026
9bf4e1b
Refactor: Reimplement scanner function
drook207 Apr 11, 2026
8d06f2d
Merge branch 'master' into feature-add-kit-scanner
drook207 Apr 11, 2026
ea837cc
Refactor: Remove Debug logs
drook207 Apr 11, 2026
8975ae3
feat(constants): Add cmake-tools config path
drook207 Apr 11, 2026
12d1018
fix(scanner): Create config dir if not exists
drook207 Apr 11, 2026
9f1c58b
fix(scanner):Move initial scan for kits to setup
drook207 Apr 11, 2026
47b60a8
fix(autocommands): Add missing implementation for scan for kits
drook207 Apr 11, 2026
e3723fe
refactor: Use stdpath(config) for path expansion
drook207 Apr 16, 2026
9d46a1e
refactor: Reduce noise while scanning for kits
drook207 Apr 16, 2026
75c8279
refactor: name loop variable more meaningfull
drook207 Apr 16, 2026
684d9eb
refactor: Remove unused files
drook207 Apr 16, 2026
2898fd0
fix: make path separator system dependant
drook207 Apr 16, 2026
02a80c1
refactor: return boolean as the function name suggests
drook207 Apr 16, 2026
b83a051
refactor: move executable variable to global scope
drook207 Apr 16, 2026
757cd4a
refactor: remove unnecessary conditions
drook207 Apr 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lua/cmake-tools/const.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ local const = {
-- none: this will make this option ignored
target = vim.loop.cwd(), -- path to directory, this is used only if action == "soft_link" or action == "copy"
},
cmake_kits_path = nil, -- this is used to specify global cmake kits path, see CMakeKits for detailed usage
cmake_kits_path = "~/.config/cmake-tools/", -- this is used to specify global cmake kits path, see CMakeKits for detailed usage
cmake_variants_message = {
short = { show = true }, -- whether to show short message
long = { show = true, max_length = 40 }, -- whether to show long message
Expand Down
27 changes: 27 additions & 0 deletions lua/cmake-tools/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ local const = require("cmake-tools.const")
local Config = require("cmake-tools.config")
local variants = require("cmake-tools.variants")
local kits = require("cmake-tools.kits")
local scanner = require("cmake-tools.scanner")
local Presets = require("cmake-tools.presets")
local log = require("cmake-tools.log")
local hints = require("cmake-tools.hints")
Expand Down Expand Up @@ -205,6 +206,9 @@ function cmake.generate(opt, callback)
-- if exists cmake-kits.json, kit is used to set
-- environmental variables and args.
local kits_config = kits.parse(const.cmake_kits_path, config.cwd)
if not kits_config then
kits_config = scanner.scan_for_kits()
end
if kits_config and not config.kit then
return cmake.select_kit(function(result)
if not result:is_ok() then
Expand Down Expand Up @@ -742,6 +746,29 @@ function cmake.select_build_type(callback)
)
end

function cmake.scan_for_kits(callback)
callback = type(callback) == "function" and callback
or function(result)
if result:is_ok() then
cmake.generate({ bang = false, fargs = {} }, nil)
end
end
if check_active_job_and_notify(callback) then
return
end

if get_cmake_configuration_or_notify(callback) == nil then
return
end

local kits = scanner.scan_for_kits()
if kits then
callback(Result:new(Types.SUCCESS, nil, nil))
else
callback(Result:new_error(Types.CANNOT_FIND_CMAKE_KITS, "Cannot find CMakeKits file"))
end
end

function cmake.select_kit(callback)
callback = type(callback) == "function" and callback
or function(result)
Expand Down
211 changes: 211 additions & 0 deletions lua/cmake-tools/scanner.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
local scanner = {}
Comment thread
drook207 marked this conversation as resolved.
-- Configuration
scanner.HOME = os.getenv("HOME") or os.getenv("USERPROFILE")
scanner.KITS_FILE = scanner.HOME .. "/.config/cmake-tools/cmake-kits.json"
Comment thread
drook207 marked this conversation as resolved.
Outdated

--Helper functions
-- Simple JSON encoder
function scanner.json_encode(obj, indent)
Comment thread
drook207 marked this conversation as resolved.
Outdated
indent = indent or 0
local spaces = string.rep(" ", indent)

if type(obj) == "table" then
local is_array = #obj > 0
local result = "{\n"
local first = true

for k, v in pairs(obj) do
if not first then
result = result .. ",\n"
end
first = false

if is_array then
result = result .. spaces .. " " .. scanner.json_encode(v, indent + 1)
else
result = result .. spaces .. ' "' .. k .. '": ' .. scanner.json_encode(v, indent + 1)
end
end

return result .. "\n" .. spaces .. "}"
elseif type(obj) == "string" then
return '"' .. obj:gsub("\\", "\\\\"):gsub('"', '\\"') .. '"'
elseif type(obj) == "number" or type(obj) == "boolean" then
return tostring(obj)
elseif obj == nil then
return "null"
end
end

function scanner.execute_command(cmd)
Comment thread
drook207 marked this conversation as resolved.
Outdated
local handle = io.popen(cmd .. " 2>&1")
if handle == nil then
return false, -1, ""
end
local result = handle:read("*a")
if result == nil then
result = ""
end
local success, exit_type, exit_code = handle:close()
-- io.popen's close() returns: true on success, or nil, "exit", code on failure
if success == nil then
return false, exit_code or -1, result
end
return true, 0, result
end

function scanner.file_exists(path)
Comment thread
drook207 marked this conversation as resolved.
Outdated
local file = io.open(path, "r")
if file then
file:close()
return true
else
return false
end
end

function scanner.split_path(path_env)
local paths = {}
local sep = package.config:sub(1, 1) == "\\" and ";" or ":"
for path in string.gmatch(path_env, "([^" .. sep .. "]+)") do
table.insert(paths, path)
end
return paths
end

function scanner.get_gcc_version(gcc_path)
local success, exit_code, output = scanner.execute_command('"' .. gcc_path .. '" --version')
if output == nil then
return nil
end
-- Try multiple patterns to match different gcc output formats
local version = output:match("gcc%s+%(GCC%)%s+([%d%.]+)") -- "gcc (GCC) 15.2.1"
Comment thread
drook207 marked this conversation as resolved.
Outdated
or output:match("gcc version ([%d%.]+)") -- "gcc version 11.4.0"
return version
end

function scanner.get_clang_version(clang_path)
Comment thread
drook207 marked this conversation as resolved.
Outdated
local success, exit_code, output = scanner.execute_command('"' .. clang_path .. '" --version ')
if output == nil then
return nil
Comment thread
drook207 marked this conversation as resolved.
Outdated
end
local version_line = output:match("clang version ([%d%.]+)")
return version_line
end

function scanner.find_compiler_pair(dir, c_compiler)
Comment thread
drook207 marked this conversation as resolved.
Outdated
local base_name = c_compiler:match("([^/\\]+)$")
local cxx_name
if base_name:match("gcc") then
cxx_name = base_name:gsub("gcc", "g++")
elseif base_name:match("clang") then
cxx_name = base_name:gsub("clang", "clang++")
else
return nil
end
local cxx_path = dir .. "/" .. cxx_name
Comment thread
drook207 marked this conversation as resolved.
Outdated
if scanner.file_exists(cxx_path) then
return cxx_path
end
return nil
end

function scanner.find_linker_pair(dir, linker_name)
Comment thread
drook207 marked this conversation as resolved.
Outdated
if not linker_name then
return nil
end
local linker_path = dir .. "/" .. linker_name
if scanner.file_exists(linker_path) then
return linker_path
end
return nil
end

function scanner.get_toolchain_file()
local toolchainFile = os.getenv("CMAKE_TOOLCHAIN_FILE")
if toolchainFile and scanner.file_exists(toolchainFile) then
return toolchainFile
end
return nil
end

function scanner.ensure_directory(path)
local pattern = "(.*/)"
local dir = path:match(pattern)
if dir then
os.execute('mkdir -p "' .. dir .. '"')
Comment thread
drook207 marked this conversation as resolved.
Outdated
end
end

function scanner.save_kits(kits, filepath)
scanner.ensure_directory(filepath)
local file = io.open(filepath, "w")
if not file then
error("Failed to open file for writing: " .. filepath)
return false
end
local json_content = scanner.json_encode(kits)
file:write(json_content)
file:close()
return true
end

-- Main fucntion to scan for kits
function scanner.scan_for_kits()
local kits = {}

local path_env = os.getenv("PATH") or ""
local paths = scanner.split_path(path_env)

for _, dir in ipairs(paths) do
local linker_path = scanner.find_linker_pair(dir, "lld")
if linker_path == nil then
linker_path = scanner.find_linker_pair(dir, "ld")
end
local toolchainFile = scanner.get_toolchain_file()
local gcc_path = dir .. "/gcc"
if scanner.file_exists(gcc_path) then
local gcc_version = scanner.get_gcc_version(gcc_path)
local gxx_path = scanner.find_compiler_pair(dir, gcc_path)
if gxx_path then
local kit = {
name = "GCC-" .. (gcc_version or "unknown"),
compilers = {
C = gcc_path,
CXX = gxx_path,
},
linker = (linker_path or ""),
toolchainFile = (toolchainFile or ""),
}
table.insert(kits, kit)
end
end

local clang_path = dir .. "/clang"
if scanner.file_exists(clang_path) then
local clang_version = scanner.get_clang_version(clang_path)
local clangxx_path = scanner.find_compiler_pair(dir, clang_path)
if clangxx_path then
local kit = {
name = "Clang-" .. (clang_version or "unknown"),
compilers = {
C = clang_path,
CXX = clangxx_path,
},
linker = (linker_path or ""),
toolchainFile = (toolchainFile or ""),
}
table.insert(kits, kit)
end
end
end

if #kits == 0 then
print("No compilers found in PATH.")
return {}
Comment thread
drook207 marked this conversation as resolved.
Outdated
end
scanner.save_kits(kits, scanner.KITS_FILE)
return kits
end

return scanner
1 change: 1 addition & 0 deletions lua/cmake-tools/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ local Types = {
"ANOTHER_JOB_RUNNING",
"CMAKE_RUN_FAILED",
"SETTINGS_ALREADY_OPENED",
"CANNOT_FIND_CMAKE_KITS",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this return type is not used, is it?

}

Types[0] = "SUCCESS"
Expand Down
9 changes: 9 additions & 0 deletions plugin/cmake-tools.lua
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,15 @@ vim.api.nvim_create_user_command(
desc = "CMake select cmake kit",
}
)
--- CMake scan for kits
vim.api.nvim_create_user_command(
"CMakeScanForKits", -- name
Comment thread
drook207 marked this conversation as resolved.
cmake_tools.scan_for_kits, -- command
{ -- opts
nargs = 0,
desc = "CMake scan for cmake kits",
}
)

--- CMake select configure preset
vim.api.nvim_create_user_command(
Expand Down