From 1f9333c9d0e800b17d76e2a7aee42a6d20abe025 Mon Sep 17 00:00:00 2001 From: Denzel <> Date: Sat, 14 Mar 2026 09:21:32 +0100 Subject: [PATCH 1/8] feat(ctest): add option to include labels into test selection --- README.md | 1 + lua/cmake-tools/config.lua | 6 +++++ lua/cmake-tools/const.lua | 1 + lua/cmake-tools/init.lua | 46 +++++++++++++++++++++++++++++++--- lua/cmake-tools/test/ctest.lua | 38 +++++++++++++++++++++++++--- 5 files changed, 85 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6e1186f2..f636cdf1 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ local osys = require("cmake-tools.osys") require("cmake-tools").setup { cmake_command = "cmake", -- this is used to specify cmake command path ctest_command = "ctest", -- this is used to specify ctest command path + ctest_show_labels = false, -- also show labels in the test picker cmake_use_preset = true, cmake_regenerate_on_save = true, -- auto generate when save CMakeLists.txt cmake_generate_options = { "-DCMAKE_EXPORT_COMPILE_COMMANDS=1" }, -- this will be passed when invoke `CMakeGenerate` diff --git a/lua/cmake-tools/config.lua b/lua/cmake-tools/config.lua index b744893e..b77a1fd7 100644 --- a/lua/cmake-tools/config.lua +++ b/lua/cmake-tools/config.lua @@ -26,6 +26,7 @@ local Config = { generate_options = {}, build_options = {}, show_disabled_build_presets = true, + ctest_show_labels = false, }, -- general config target_settings = {}, -- target specific config executor = nil, @@ -45,6 +46,7 @@ function Config:new(const) obj.base_settings.use_preset = const.cmake_use_preset obj.base_settings.show_disabled_build_presets = const.cmake_show_disabled_build_presets + obj.base_settings.ctest_show_labels = const.ctest_show_labels obj.executor = const.cmake_executor obj.runner = const.cmake_runner @@ -148,6 +150,10 @@ function Config:show_disabled_build_presets() return self.base_settings.show_disabled_build_presets end +function Config:ctest_show_labels() + return self.base_settings.ctest_show_labels +end + function Config:generate_build_directory() local build_directory = Path:new(self.build_directory) diff --git a/lua/cmake-tools/const.lua b/lua/cmake-tools/const.lua index 7528ffdf..999cde5f 100644 --- a/lua/cmake-tools/const.lua +++ b/lua/cmake-tools/const.lua @@ -2,6 +2,7 @@ local osys = require("cmake-tools.osys") local const = { cmake_command = "cmake", -- this is used to specify cmake command path ctest_command = "ctest", -- this is used to specify ctest command path + ctest_show_labels = false, -- show test labels in the test picker (when true, labels from ctest are shown as filterable entries) cmake_use_preset = true, -- when `false`, this is used to define if the `--preset` option should be use on cmake commands cmake_regenerate_on_save = true, -- auto generate when save CMakeLists.txt cmake_generate_options = { "-DCMAKE_EXPORT_COMPILE_COMMANDS=1" }, -- this will be passed when invoke `CMakeGenerate` diff --git a/lua/cmake-tools/init.lua b/lua/cmake-tools/init.lua index d9f41d96..8a985cf6 100644 --- a/lua/cmake-tools/init.lua +++ b/lua/cmake-tools/init.lua @@ -1177,17 +1177,55 @@ function cmake.run_test(opt, callback) if #all_tests == 0 then return end - table.insert(all_tests, 1, "all") - vim.ui.select(all_tests, { prompt = "select test to run" }, function(_, idx) + + local items = {} + local display = {} + + local Type = { + ALL = 1, + LABEL = 2, + TEST = 3, + } + + table.insert(items, { type = Type.ALL }) + table.insert(display, "all") + + if config:ctest_show_labels() then + local labels = ctest.get_all_labels(all_tests) + for _, entry in ipairs(labels) do + table.insert(items, { type = Type.LABEL, label = entry.label }) + table.insert( + display, + table.concat({ "[label]", entry.label, "(" .. entry.count, "tests)" }, " ") + ) + end + end + + for _, test in ipairs(all_tests) do + table.insert(items, { type = Type.TEST, name = test.name }) + table.insert(display, test.name) + end + + vim.ui.select(display, { prompt = "select test to run" }, function(_, idx) if not idx then return end - if idx == 1 then + local selected = items[idx] + if selected.type == Type.ALL then ctest.run(const.ctest_command, nil, config:build_directory_path(), env, config, opt) + elseif selected.type == Type.LABEL then + ctest.run( + const.ctest_command, + nil, + config:build_directory_path(), + env, + config, + vim.tbl_extend("force", opt, { label = selected.label }) + ) else ctest.run( const.ctest_command, - all_tests[idx], + selected.name, config:build_directory_path(), env, config, diff --git a/lua/cmake-tools/test/ctest.lua b/lua/cmake-tools/test/ctest.lua index a5fdf8a2..10c00df9 100644 --- a/lua/cmake-tools/test/ctest.lua +++ b/lua/cmake-tools/test/ctest.lua @@ -1,6 +1,5 @@ local Job = require("plenary.job") local utils = require("cmake-tools.utils") -local const = require("cmake-tools.const") local ctest = { job = nil, @@ -24,7 +23,16 @@ function ctest.list_all_tests(build_dir, callback) local tests = {} for _, item in ipairs(result.tests) do - table.insert(tests, item["name"]) + local labels = {} + if item["properties"] then + for _, prop in ipairs(item["properties"]) do + if prop["name"] == "LABELS" then + labels = prop["value"] or {} + break + end + end + end + table.insert(tests, { name = item["name"], labels = labels }) end callback(tests) end) @@ -34,12 +42,36 @@ function ctest.list_all_tests(build_dir, callback) ctest.job:start() end +--- Collect all labels from test objects with counts +--- @param tests { name: string, labels: string[] }[] +--- @return { label: string, count: number }[] sorted list of labels with their test counts +function ctest.get_all_labels(tests) + local label_counts = {} + for _, test in ipairs(tests) do + for _, label in ipairs(test.labels) do + label_counts[label] = (label_counts[label] or 0) + 1 + end + end + + local labels = {} + for label, count in pairs(label_counts) do + table.insert(labels, { label = label, count = count }) + end + table.sort(labels, function(a, b) + return a.label < b.label + end) + return labels +end + function ctest.run(ctest_command, test_name, build_dir, env, config, opt) local cmd = ctest_command opt = opt or {} local args = { "--test-dir", utils.transform_path(build_dir) } - if test_name then + if opt.label then + table.insert(args, "-L") + table.insert(args, opt.label) + elseif test_name then table.insert(args, "-R") table.insert(args, test_name) end From 9cf64792b0ed4fc10dac09df832af93dd44e0a1f Mon Sep 17 00:00:00 2001 From: Denzel <> Date: Sat, 14 Mar 2026 11:12:06 +0100 Subject: [PATCH 2/8] feat(presets): support testPresets --- lua/cmake-tools/config.lua | 1 + lua/cmake-tools/init.lua | 95 ++++++++++++++++++++------ lua/cmake-tools/presets.lua | 26 +++++-- lua/cmake-tools/test/ctest.lua | 34 ++++++--- lua/cmake-tools/test_preset.lua | 21 ++++++ lua/types/cmake-preset/condition.lua | 12 ++++ lua/types/cmake-preset/test_preset.lua | 79 +++++++++++++++++++++ plugin/cmake-tools.lua | 15 +++- 8 files changed, 242 insertions(+), 41 deletions(-) create mode 100644 lua/cmake-tools/test_preset.lua create mode 100644 lua/types/cmake-preset/condition.lua create mode 100644 lua/types/cmake-preset/test_preset.lua diff --git a/lua/cmake-tools/config.lua b/lua/cmake-tools/config.lua index b77a1fd7..ccd3418d 100644 --- a/lua/cmake-tools/config.lua +++ b/lua/cmake-tools/config.lua @@ -18,6 +18,7 @@ local Config = { kit = nil, configure_preset = nil, build_preset = nil, + test_preset = nil, base_settings = { env = {}, build_dir = "", diff --git a/lua/cmake-tools/init.lua b/lua/cmake-tools/init.lua index 8a985cf6..f3a5494b 100644 --- a/lua/cmake-tools/init.lua +++ b/lua/cmake-tools/init.lua @@ -838,6 +838,54 @@ function cmake.select_configure_preset(callback) end end +function cmake.select_test_preset(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 + + -- if exists presets + if Presets.exists(config.cwd) then + local presets = Presets:parse(config.cwd) + local test_preset_names = presets:get_test_preset_names() + local format_preset_name = function(p_name) + local p = presets:get_test_preset(p_name) + return p.displayName or p.name + end + vim.ui.select( + test_preset_names, + { + prompt = "Select cmake test preset", + format_item = format_preset_name, + }, + vim.schedule_wrap(function(choice) + if not choice then + callback(Result:new_error(Types.NOT_SELECT_PRESET, "No test preset selected")) + return + end + if config.test_preset ~= choice then + config.test_preset = choice + end + callback(Result:new(Types.SUCCESS, nil, nil)) + end) + ) + else + callback( + Result:new_error(Types.CANNOT_FIND_PRESETS_FILE, "Cannot find CMake[User]Presets file") + ) + log.error("Cannot find CMake[User]Presets.json at Root (" .. config.cwd .. ") !!") + end +end + function cmake.select_build_preset(callback) callback = type(callback) == "function" and callback or function(result) @@ -1173,7 +1221,17 @@ function cmake.run_test(opt, callback) end local env = environment.get_build_environment(config) - ctest.list_all_tests(config:build_directory_path(), function(all_tests) + local preset_name = nil + if config.test_preset and Presets.exists(config.cwd) then + local presets = Presets:parse(config.cwd) + local test_preset = presets:get_test_preset(config.test_preset) + if test_preset and test_preset:isValid() then + preset_name = config.test_preset + end + end + local build_dir = config:build_directory_path() + + ctest.list_all_tests(build_dir, preset_name, function(all_tests) if #all_tests == 0 then return end @@ -1211,27 +1269,18 @@ function cmake.run_test(opt, callback) return end local selected = items[idx] - if selected.type == Type.ALL then - ctest.run(const.ctest_command, nil, config:build_directory_path(), env, config, opt) - elseif selected.type == Type.LABEL then - ctest.run( - const.ctest_command, - nil, - config:build_directory_path(), - env, - config, - vim.tbl_extend("force", opt, { label = selected.label }) - ) - else - ctest.run( - const.ctest_command, - selected.name, - config:build_directory_path(), - env, - config, - opt - ) + local run_opt = vim.tbl_extend("force", opt, { + preset = preset_name, + build_dir = build_dir, + }) + + if selected.type == Type.LABEL then + run_opt.label = selected.label + elseif selected.type == Type.TEST then + run_opt.test_name = selected.name end + + ctest.run(const.ctest_command, env, config, run_opt) end) end) end @@ -1367,6 +1416,10 @@ function cmake.get_build_preset() return config.build_preset end +function cmake.get_test_preset() + return config.test_preset +end + function cmake.get_build_directory() return config.build_directory end diff --git a/lua/cmake-tools/presets.lua b/lua/cmake-tools/presets.lua index fffb80c0..d222d998 100644 --- a/lua/cmake-tools/presets.lua +++ b/lua/cmake-tools/presets.lua @@ -1,6 +1,7 @@ local Path = require("plenary.path") local Preset = require("cmake-tools.preset") local BuildPreset = require("cmake-tools.build_preset") +local TestPreset = require("cmake-tools.test_preset") -- Extends (or creates a new) key-value pair in [dest] in which the -- key is [key] and the value is the resulting list table of merging @@ -138,23 +139,26 @@ function Presets:parse(cwd) local function getPreset(name) return instance:get_configure_preset(name, { include_hidden = true, include_disabled = true }) end - return Preset:new(cwd, obj, getPreset) + Preset:new(cwd, obj, getPreset) end - local function createBuildPreset(obj) - return BuildPreset:new(cwd, obj) + for _, preset in ipairs(instance.configurePresets) do + createPreset(preset) end - for _, preset in ipairs(instance.configurePresets) do - preset = createPreset(preset) + instance.testPresets = instance.testPresets or {} + for _, test_preset in ipairs(instance.testPresets) do + TestPreset.new(cwd, test_preset) end + table.insert(instance.testPresets, TestPreset.new(cwd, { name = "None", valid = false })) + instance.buildPresets = instance.buildPresets or {} for _, build_preset in ipairs(instance.buildPresets) do - build_preset = createBuildPreset(build_preset) + BuildPreset:new(cwd, build_preset) end - table.insert(instance.buildPresets, createBuildPreset({ name = "None", valid = false })) + table.insert(instance.buildPresets, BuildPreset:new(cwd, { name = "None", valid = false })) return instance end @@ -185,6 +189,10 @@ function Presets:get_configure_preset_names(opts) return get_preset_names(self.configurePresets, opts) end +function Presets:get_test_preset_names(opts) + return get_preset_names(self.testPresets, opts) +end + function Presets:get_build_preset_names(opts) local presets = get_preset_names(self.buildPresets, opts) local ret = {} @@ -227,6 +235,10 @@ function Presets:get_configure_preset(name, opts) return get_preset(name, self.configurePresets, opts) end +function Presets:get_test_preset(name, opts) + return get_preset(name, self.testPresets, opts) +end + function Presets:get_build_preset(name, opts) return get_preset(name, self.buildPresets, { include_hidden = true, include_disabled = true }) end diff --git a/lua/cmake-tools/test/ctest.lua b/lua/cmake-tools/test/ctest.lua index 10c00df9..75913fb7 100644 --- a/lua/cmake-tools/test/ctest.lua +++ b/lua/cmake-tools/test/ctest.lua @@ -5,12 +5,19 @@ local ctest = { job = nil, } -function ctest.list_all_tests(build_dir, callback) +function ctest.list_all_tests(build_dir, preset_name, callback) local result = {} + local args + if preset_name then + args = { "--preset", preset_name, "--show-only=json-v1" } + else + args = { "--test-dir", build_dir, "--show-only=json-v1" } + end + ctest.job = Job:new({ command = "ctest", - args = { "--test-dir", build_dir, "--show-only=json-v1" }, + args = args, on_exit = function(j, _, _) vim.schedule(function() local json_data = "" @@ -63,22 +70,27 @@ function ctest.get_all_labels(tests) return labels end -function ctest.run(ctest_command, test_name, build_dir, env, config, opt) - local cmd = ctest_command +function ctest.run(ctest_command, env, config, opt) opt = opt or {} - local args = { "--test-dir", utils.transform_path(build_dir) } + local args = {} + if opt.preset then + vim.list_extend(args, { "--preset", opt.preset }) + else + vim.list_extend(args, { "--test-dir", utils.transform_path(opt.build_dir) }) + end + if opt.label then - table.insert(args, "-L") - table.insert(args, opt.label) - elseif test_name then - table.insert(args, "-R") - table.insert(args, test_name) + vim.list_extend(args, { "-L", opt.label }) + elseif opt.test_name then + vim.list_extend(args, { "-R", opt.test_name }) end + if opt.args then table.insert(args, opt.args) end - utils.run(cmd, config.env_script, env, args, config.cwd, config.runner, nil) + + utils.run(ctest_command, config.env_script, env, args, config.cwd, config.runner, nil) end function ctest.stop() diff --git a/lua/cmake-tools/test_preset.lua b/lua/cmake-tools/test_preset.lua new file mode 100644 index 00000000..a403004e --- /dev/null +++ b/lua/cmake-tools/test_preset.lua @@ -0,0 +1,21 @@ +---@class TestPreset: CMakeTestPreset +local TestPreset = {} +TestPreset.__index = TestPreset + +---@return TestPreset +function TestPreset.new(cwd, obj) + local instance = setmetatable(obj or {}, TestPreset) + instance.cwd = cwd + + if instance.valid == nil then + instance.valid = true + end + + return instance +end + +function TestPreset:isValid() + return self.valid +end + +return TestPreset diff --git a/lua/types/cmake-preset/condition.lua b/lua/types/cmake-preset/condition.lua new file mode 100644 index 00000000..d8786164 --- /dev/null +++ b/lua/types/cmake-preset/condition.lua @@ -0,0 +1,12 @@ +---@meta _ + +---@class CMakeCondition +---@field type "const"|"equals"|"notEquals"|"inList"|"notInList"|"matches"|"notMatches"|"anyOf"|"allOf"|"not" +---@field value boolean? +---@field lhs string? +---@field rhs string? +---@field string string? +---@field list string[]? +---@field regex string? +---@field conditions CMakeCondition[]? +---@field condition CMakeCondition? diff --git a/lua/types/cmake-preset/test_preset.lua b/lua/types/cmake-preset/test_preset.lua new file mode 100644 index 00000000..1d75acef --- /dev/null +++ b/lua/types/cmake-preset/test_preset.lua @@ -0,0 +1,79 @@ +---@meta _ + +---@class CMakeTestPresetOutputOpts +---@field shortProgress boolean? +---@field verbosity ("default"|"verbose"|"extra")? +---@field debug boolean? +---@field outputOnFailure boolean? +---@field quiet boolean? +---@field outputLogFile string? +---@field outputJUnitFile string? +---@field labelSummary boolean? +---@field subprojectSummary boolean? +---@field maxPassedTestOutputSize integer? +---@field maxFailedTestOutputSize integer? +---@field testOutputTruncation string? +---@field maxTestNameWidth integer? + +---@class CMakeTestPresetFilterIncludeIndex +---@field start integer? +---@field end integer? +---@field stride integer? +---@field specificTests integer[]? + +---@class CMakeTestPresetFilterInclude +---@field name string? +---@field label string? +---@field useUnion boolean? +---@field index CMakeTestPresetFilterIncludeIndex|string? + +---@class CMakeTestPresetFilterExcludeFixtures +---@field any string? +---@field setup string? +---@field cleanup string? + +---@class CMakeTestPresetFilterExclude +---@field name string? +---@field label string? +---@field fixtures CMakeTestPresetFilterExcludeFixtures? + +---@class CMakeTestPresetFilter +---@field include CMakeTestPresetFilterInclude? +---@field exclude CMakeTestPresetFilterExclude? + +---@class CMakeTestPresetExecutionRepeat +---@field mode "until-fail"|"until-pass"|"after-timeout" +---@field count integer + +---@class CMakeTestPresetExecution +---@field stopOnFailure boolean? +---@field enableFailover boolean? +---@field jobs integer? +---@field resourceSpecFile string? +---@field testLoad integer? +---@field showOnly ("human"|"json-v1")? +---@field repeat CMakeTestPresetExecutionRepeat? +---@field interactiveDebugging boolean? +---@field scheduleRandom boolean? +---@field timeout integer? +---@field noTestsAction ("default"|"error"|"ignore")? + +---@class CMakeTestPreset +---@field name string +---@field valid boolean +---@field hidden boolean? +---@field inherits string|string[]? +---@field condition CMakeCondition? +---@field vendor table? +---@field displayName string? +---@field description string? +---@field environment table? +---@field configurePreset string? +---@field inheritConfigureEnvironment boolean? +---@field configuration string? +---@field overwriteConfigurationFile string[]? +---@field output CMakeTestPresetOutputOpts? +---@field filter CMakeTestPresetFilter? +---@field execution CMakeTestPresetExecution? +---@field disabled boolean? +---@field cwd string diff --git a/plugin/cmake-tools.lua b/plugin/cmake-tools.lua index d9fa0f20..ccc74651 100644 --- a/plugin/cmake-tools.lua +++ b/plugin/cmake-tools.lua @@ -193,7 +193,7 @@ vim.api.nvim_create_user_command( cmake_tools.select_configure_preset, -- command { -- opts nargs = 0, - desc = "CMake select cmake configure preset", + desc = "CMake select configure preset", } ) @@ -203,9 +203,20 @@ vim.api.nvim_create_user_command( cmake_tools.select_build_preset, -- command { -- opts nargs = 0, - desc = "CMake select cmake kit", + desc = "CMake select build preset", + } +) + +--- CMake select test preset +vim.api.nvim_create_user_command( + "CMakeSelectTestPreset", -- name + cmake_tools.select_test_preset, -- command + { -- opts + nargs = 0, + desc = "CMake select test preset", } ) + --- CMake select build target vim.api.nvim_create_user_command( "CMakeSelectBuildTarget", -- name From 3f139c5172123d6f86d697ef80e3e49a5fdb05f0 Mon Sep 17 00:00:00 2001 From: Denzel <> Date: Sat, 14 Mar 2026 11:35:09 +0100 Subject: [PATCH 3/8] feat(ctest): move last ran test to the top of the list --- lua/cmake-tools/config.lua | 1 + lua/cmake-tools/init.lua | 14 +++++++++++++- lua/cmake-tools/session.lua | 4 ++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lua/cmake-tools/config.lua b/lua/cmake-tools/config.lua index ccd3418d..7e44b438 100644 --- a/lua/cmake-tools/config.lua +++ b/lua/cmake-tools/config.lua @@ -19,6 +19,7 @@ local Config = { configure_preset = nil, build_preset = nil, test_preset = nil, + selected_test = nil, base_settings = { env = {}, build_dir = "", diff --git a/lua/cmake-tools/init.lua b/lua/cmake-tools/init.lua index f3a5494b..316a1b22 100644 --- a/lua/cmake-tools/init.lua +++ b/lua/cmake-tools/init.lua @@ -1264,10 +1264,22 @@ function cmake.run_test(opt, callback) table.insert(display, test.name) end - vim.ui.select(display, { prompt = "select test to run" }, function(_, idx) + -- Move last selected test to front of picker list + if config.selected_test then + for idx = 1, #display do + if display[idx] == config.selected_test then + table.insert(items, 1, table.remove(items, idx)) + table.insert(display, 1, table.remove(display, idx)) + break + end + end + end + + vim.ui.select(display, { prompt = "select test to run" }, function(choice, idx) if not idx then return end + config.selected_test = choice local selected = items[idx] local run_opt = vim.tbl_extend("force", opt, { preset = preset_name, diff --git a/lua/cmake-tools/session.lua b/lua/cmake-tools/session.lua index fdf2e2f7..c1e60d42 100644 --- a/lua/cmake-tools/session.lua +++ b/lua/cmake-tools/session.lua @@ -90,6 +90,9 @@ function session.update(config, old_config) if old_config.build_preset then config.build_preset = old_config.build_preset end + if old_config.selected_test then + config.selected_test = old_config.selected_test + end if old_config.env_script then config.env_script = old_config.env_script end @@ -126,6 +129,7 @@ function session.save(config) configure_preset = config.configure_preset, env_script = config.env_script, build_preset = config.build_preset, + selected_test = config.selected_test, base_settings = config.base_settings, target_settings = config.target_settings, cwd = config.cwd, From 1a8e7cf37dc918d77a06327a9908753f363deb5a Mon Sep 17 00:00:00 2001 From: Denzel <> Date: Sat, 14 Mar 2026 12:11:57 +0100 Subject: [PATCH 4/8] chore(presets): add annotations --- lua/cmake-tools/build_preset.lua | 7 +++ lua/cmake-tools/preset.lua | 7 +++ lua/cmake-tools/presets.lua | 25 +++++++++ lua/types/cmake-preset/build_preset.lua | 21 ++++++++ lua/types/cmake-preset/configure_preset.lua | 60 +++++++++++++++++++++ lua/types/cmake-preset/test_preset.lua | 2 - 6 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 lua/types/cmake-preset/build_preset.lua create mode 100644 lua/types/cmake-preset/configure_preset.lua diff --git a/lua/cmake-tools/build_preset.lua b/lua/cmake-tools/build_preset.lua index c1571684..504d8b57 100644 --- a/lua/cmake-tools/build_preset.lua +++ b/lua/cmake-tools/build_preset.lua @@ -1,8 +1,12 @@ local Path = require("plenary.path") local osys = require("cmake-tools.osys") +---@class BuildPreset: CMakeBuildPreset local BuildPreset = {} +---@param cwd string +---@param obj CMakeBuildPreset? +---@return BuildPreset function BuildPreset:new(cwd, obj) local instance = setmetatable(obj or {}, { __index = self }) instance.__index = self @@ -16,6 +20,7 @@ function BuildPreset:new(cwd, obj) return instance end +---@return string[]|nil function BuildPreset:get_build_target() if self.targets == nil then return nil @@ -28,6 +33,7 @@ function BuildPreset:get_build_target() return nil end +---@return string|nil function BuildPreset:get_build_type() if self.configuration == nil then return nil @@ -35,6 +41,7 @@ function BuildPreset:get_build_type() return self.configuration end +---@return boolean function BuildPreset:is_valid() return self.valid end diff --git a/lua/cmake-tools/preset.lua b/lua/cmake-tools/preset.lua index 24fe5b8d..9a44ae33 100644 --- a/lua/cmake-tools/preset.lua +++ b/lua/cmake-tools/preset.lua @@ -2,6 +2,7 @@ local Path = require("plenary.path") local osys = require("cmake-tools.osys") local utils = require("cmake-tools.utils") +---@class ConfigurePreset: CMakeConfigurePreset local Preset = {} local function expandMacro(self, str) @@ -255,6 +256,10 @@ local function parseTree(self, get_preset) end end +---@param cwd string +---@param obj CMakeConfigurePreset? +---@param get_preset fun(name: string): CMakeConfigurePreset? +---@return ConfigurePreset function Preset:new(cwd, obj, get_preset) local instance = setmetatable(obj or {}, self) instance.__index = self @@ -271,10 +276,12 @@ function Preset:new(cwd, obj, get_preset) return instance end +---@return string function Preset:get_build_type() return self.cacheVariables and self.cacheVariables.CMAKE_BUILD_TYPE or "Debug" end +---@return string[]|nil function Preset:get_build_configuration_types() local generator = self.generator local multi_configuration_generator = { "Visual Studio", "Xcode", "Ninja Multi-Config" } diff --git a/lua/cmake-tools/presets.lua b/lua/cmake-tools/presets.lua index d222d998..92736cdb 100644 --- a/lua/cmake-tools/presets.lua +++ b/lua/cmake-tools/presets.lua @@ -91,8 +91,14 @@ local function decode(file, visited) return data end +---@class Presets +---@field configurePresets ConfigurePreset[] +---@field buildPresets BuildPreset[] +---@field testPresets TestPreset[] local Presets = {} +---@param cwd string +---@return Presets function Presets:parse(cwd) local function merge_presets(lhs, rhs) local ret = vim.deepcopy(lhs) @@ -185,14 +191,20 @@ local function get_preset_names(presets, opts) return options end +---@param opts table? +---@return string[] function Presets:get_configure_preset_names(opts) return get_preset_names(self.configurePresets, opts) end +---@param opts table? +---@return string[] function Presets:get_test_preset_names(opts) return get_preset_names(self.testPresets, opts) end +---@param opts table +---@return string[] function Presets:get_build_preset_names(opts) local presets = get_preset_names(self.buildPresets, opts) local ret = {} @@ -231,18 +243,29 @@ local function get_preset(name, tbl, opts) end end +---@param name string +---@param opts table? +---@return ConfigurePreset? function Presets:get_configure_preset(name, opts) return get_preset(name, self.configurePresets, opts) end +---@param name string +---@param opts table? +---@return TestPreset? function Presets:get_test_preset(name, opts) return get_preset(name, self.testPresets, opts) end +---@param name string +---@param opts table? +---@return BuildPreset? function Presets:get_build_preset(name, opts) return get_preset(name, self.buildPresets, { include_hidden = true, include_disabled = true }) end +---@param cwd string +---@return string?, string? function Presets.find_preset_files(cwd) local files = vim.fn.readdir(cwd) local presetFiles = {} @@ -263,6 +286,8 @@ function Presets.find_preset_files(cwd) return unpack(presetFiles) end +---@param cwd string +---@return boolean function Presets.exists(cwd) return Presets.find_preset_files(cwd) ~= nil end diff --git a/lua/types/cmake-preset/build_preset.lua b/lua/types/cmake-preset/build_preset.lua new file mode 100644 index 00000000..c8eac3ca --- /dev/null +++ b/lua/types/cmake-preset/build_preset.lua @@ -0,0 +1,21 @@ +---@meta _ + +---@class CMakeBuildPreset +---@field name string +---@field hidden boolean? +---@field inherits string|string[]? +---@field condition CMakeCondition? +---@field vendor table? +---@field displayName string? +---@field description string? +---@field environment table? +---@field configurePreset string? +---@field inheritConfigureEnvironment boolean? +---@field jobs integer? +---@field targets string|string[]? +---@field configuration string? +---@field cleanFirst boolean? +---@field resolvePackageReferences ("on"|"off"|"only")? +---@field verbose boolean? +---@field nativeToolOptions string[]? +---@field disabled boolean? diff --git a/lua/types/cmake-preset/configure_preset.lua b/lua/types/cmake-preset/configure_preset.lua new file mode 100644 index 00000000..d134421c --- /dev/null +++ b/lua/types/cmake-preset/configure_preset.lua @@ -0,0 +1,60 @@ +---@meta _ + +---@class CMakeConfigurePresetArchitecture +---@field value string? +---@field strategy ("set"|"external")? + +---@class CMakeConfigurePresetToolset +---@field value string? +---@field strategy ("set"|"external")? + +---@class CMakeConfigurePresetCacheVariable +---@field type string? +---@field value string|boolean + +---@class CMakeConfigurePresetWarnings +---@field dev boolean? +---@field deprecated boolean? +---@field uninitialized boolean? +---@field unusedCli boolean? +---@field systemVars boolean? + +---@class CMakeConfigurePresetErrors +---@field dev boolean? +---@field deprecated boolean? + +---@class CMakeConfigurePresetDebug +---@field output boolean? +---@field tryCompile boolean? +---@field find boolean? + +---@class CMakeConfigurePresetTrace +---@field mode ("on"|"off"|"expand")? +---@field format ("human"|"json-v1")? +---@field source string|string[]? +---@field redirect string? + +---@class CMakeConfigurePreset +---@field name string +---@field hidden boolean? +---@field inherits string|string[]? +---@field condition CMakeCondition? +---@field vendor table? +---@field displayName string? +---@field description string? +---@field generator string? +---@field architecture string|CMakeConfigurePresetArchitecture? +---@field toolset string|CMakeConfigurePresetToolset? +---@field toolchainFile string? +---@field graphviz string? +---@field binaryDir string? +---@field binaryDirExpanded string? +---@field installDir string? +---@field cmakeExecutable string? +---@field cacheVariables table? +---@field environment table? +---@field warnings CMakeConfigurePresetWarnings? +---@field errors CMakeConfigurePresetErrors? +---@field debug CMakeConfigurePresetDebug? +---@field trace CMakeConfigurePresetTrace? +---@field disabled boolean? diff --git a/lua/types/cmake-preset/test_preset.lua b/lua/types/cmake-preset/test_preset.lua index 1d75acef..ec390192 100644 --- a/lua/types/cmake-preset/test_preset.lua +++ b/lua/types/cmake-preset/test_preset.lua @@ -60,7 +60,6 @@ ---@class CMakeTestPreset ---@field name string ----@field valid boolean ---@field hidden boolean? ---@field inherits string|string[]? ---@field condition CMakeCondition? @@ -76,4 +75,3 @@ ---@field filter CMakeTestPresetFilter? ---@field execution CMakeTestPresetExecution? ---@field disabled boolean? ----@field cwd string From e6c4fe4c8c08654a8c95c428b12c4140aff763af Mon Sep 17 00:00:00 2001 From: Denzel <> Date: Sat, 14 Mar 2026 12:28:07 +0100 Subject: [PATCH 5/8] refactor(Config): instance based values --- lua/cmake-tools/config.lua | 132 +++++++++++++++++++++++++------------ lua/cmake-tools/const.lua | 1 + 2 files changed, 90 insertions(+), 43 deletions(-) diff --git a/lua/cmake-tools/config.lua b/lua/cmake-tools/config.lua index 7e44b438..613f2986 100644 --- a/lua/cmake-tools/config.lua +++ b/lua/cmake-tools/config.lua @@ -7,39 +7,57 @@ local variants = require("cmake-tools.variants") local Presets = require("cmake-tools.presets") local kits = require("cmake-tools.kits") -local Config = { - build_directory = nil, - query_directory = nil, - reply_directory = nil, - build_type = nil, - variant = nil, - build_target = nil, - launch_target = nil, - kit = nil, - configure_preset = nil, - build_preset = nil, - test_preset = nil, - selected_test = nil, - base_settings = { - env = {}, - build_dir = "", - working_dir = "${dir.binary}", - use_preset = true, - generate_options = {}, - build_options = {}, - show_disabled_build_presets = true, - ctest_show_labels = false, - }, -- general config - target_settings = {}, -- target specific config - executor = nil, - runner = nil, - env_script = " ", - cwd = vim.loop.cwd(), -} - +---@class Config.BaseSettings +---@field env table +---@field build_dir string +---@field working_dir string +---@field use_preset boolean +---@field generate_options string[] +---@field build_options string[] +---@field show_disabled_build_presets boolean +---@field ctest_show_labels boolean + +---@class Config +---@field build_directory Path? +---@field query_directory Path? +---@field reply_directory Path? +---@field build_type string? +---@field variant table? +---@field build_target string|string[]|nil +---@field launch_target string? +---@field kit string? +---@field configure_preset string? +---@field build_preset string? +---@field test_preset string? +---@field selected_test string? +---@field base_settings Config.BaseSettings +---@field target_settings table +---@field executor table? +---@field runner table? +---@field env_script string +---@field cwd string +local Config = {} +Config.__index = Config + +---@param const Const +---@return Config function Config:new(const) - local obj = {} - setmetatable(obj, { __index = self }) -- when obj cannot find key in its table, it will try to find it from its __index value + local obj = { + base_settings = { + env = {}, + build_dir = "", + working_dir = "${dir.binary}", + use_preset = true, + generate_options = {}, + build_options = {}, + show_disabled_build_presets = true, + ctest_show_labels = false, + }, -- general config + target_settings = {}, -- target specific config + env_script = " ", + cwd = vim.loop.cwd(), + } + setmetatable(obj, Config) obj:update_build_dir(const.cmake_build_directory, const.cmake_build_directory) @@ -56,21 +74,22 @@ function Config:new(const) return obj end +---@return string function Config:build_directory_path() return self.build_directory.filename end +---@return boolean function Config:has_build_directory() return self.build_directory and self.build_directory:exists() end ----comment ---The reason for storing no expand build directory is to make cwd selecting easier +---@return string function Config:no_expand_build_directory_path() return self.base_settings.build_dir end ----comment ---@param build_dir string|function string or a function returning string containing path to the build dir ---@param no_expand_build_dir string|function function Config:update_build_dir(build_dir, no_expand_build_dir) @@ -107,8 +126,9 @@ function Config:update_build_dir(build_dir, no_expand_build_dir) self.base_settings.build_dir = Path:new(no_expand_build_dir):absolute() end ----Prepare build directory. Which allows macro expansion. +---Prepare build directory with macro expansion ---@param kit_list table all the kits +---@return string function Config:prepare_build_directory(kit_list) -- macro expansion: -- ${kit} @@ -140,22 +160,27 @@ function Config:prepare_build_directory(kit_list) return build_dir end +---@return string[] function Config:generate_options() return self.base_settings.generate_options and self.base_settings.generate_options or {} end +---@return string[] function Config:build_options() return self.base_settings.build_options and self.base_settings.build_options or {} end +---@return boolean function Config:show_disabled_build_presets() return self.base_settings.show_disabled_build_presets end +---@return boolean function Config:ctest_show_labels() return self.base_settings.ctest_show_labels end +---@return cmake.Result function Config:generate_build_directory() local build_directory = Path:new(self.build_directory) @@ -165,6 +190,7 @@ function Config:generate_build_directory() return self:generate_query_files() end +---@return cmake.Result function Config:generate_query_files() local query_directory = Path:new(self.query_directory) if not query_directory:mkdir({ parents = true }) then @@ -196,6 +222,7 @@ function Config:generate_query_files() return Result:new(Types.SUCCESS, true, "yeah, that could be") end +---@return cmake.Result function Config:get_cmake_files() -- if reply_directory exists local reply_directory = Path:new(self.reply_directory) @@ -212,6 +239,7 @@ function Config:get_cmake_files() return Result:new(Types.SUCCESS, codemodel_json["inputs"], "find it") end +---@return cmake.Result function Config:get_codemodel_targets() -- if reply_directory exists local reply_directory = Path:new(self.reply_directory) @@ -233,12 +261,15 @@ function Config:get_codemodel_targets() return Result:new(Types.SUCCESS, codemodel_json["configurations"][1]["targets"], "find it") -- Return the first else end +---@param codemodel_target table +---@return table function Config:get_code_model_target_info(codemodel_target) local reply_directory = Path:new(self.reply_directory) return vim.json.decode((reply_directory / codemodel_target["jsonFile"]):read()) end --- Check if launch target is built +---Check if launch target is built +---@return cmake.Result function Config:check_launch_target() -- 1. not configured local build_directory = Path:new(self.build_directory) @@ -276,6 +307,8 @@ function Config:check_launch_target() ) end +---@param target_info table +---@return cmake.Result function Config:get_launch_target_from_info(target_info) local target_path = target_info["artifacts"][1]["path"] if require("cmake-tools.osys").iswin32 then @@ -300,8 +333,8 @@ function Config:get_launch_target_from_info(target_info) return Result:new(Types.SUCCESS, target_path.filename, "yeah, that's good") end --- Retrieve launch target path: self.launch_target --- it will first check if this launch target is built +---Retrieve launch target path +---@return cmake.Result function Config:get_launch_target() local check_result = self:check_launch_target() if check_result.code ~= Types.SUCCESS then @@ -312,7 +345,8 @@ function Config:get_launch_target() return self:get_launch_target_from_info(target_info) end --- Check if build target exists +---Check if build target exists +---@return cmake.Result function Config:check_build_target() -- 1. not configured local build_directory = Path:new(self.build_directory) @@ -346,8 +380,8 @@ function Config:check_build_target() ) end --- Retrieve launch target path: self.launch_target --- it will first check if this launch target is built +---Retrieve build target path +---@return cmake.Result function Config:get_build_target() local check_result = self:check_build_target() if check_result.code ~= Types.SUCCESS then @@ -374,8 +408,8 @@ function Config:get_build_target() return Result:new(Types.SUCCESS, target_path.filename, "yeah, that's good") end --- Check if this launch target is debuggable --- use variants.debuggable +---Check if this launch target is debuggable using variants.debuggable +---@return cmake.Result function Config:validate_for_debugging() local build_type = self.build_type @@ -385,6 +419,9 @@ function Config:validate_for_debugging() return Result:new(Types.SUCCESS, true, "Yeah, it may be") end +---@param config Config +---@param opt { has_all: boolean, only_executable: boolean, query_sources: boolean? } +---@return cmake.Result local function get_targets(config, opt) local targets, display_targets, paths, abs_paths = {}, {}, {}, {} local sources = {} @@ -455,6 +492,7 @@ local function get_targets(config, opt) end end +---@return table function Config:get_code_model_info() local codemodel_targets = self:get_codemodel_targets() if codemodel_targets.code ~= Types.SUCCESS then @@ -472,24 +510,29 @@ function Config:get_code_model_info() return result end +---@return cmake.Result function Config:launch_targets() return get_targets(self, { has_all = false, only_executable = true }) end +---@return cmake.Result function Config:build_targets() return get_targets(self, { has_all = true, only_executable = false }) end +---@return cmake.Result function Config:launch_targets_with_sources() return get_targets(self, { has_all = false, only_executable = true, query_sources = true }) end local _virtual_targets = nil + function Config:update_targets() _virtual_targets = get_targets(self, { has_all = false, only_executable = false, query_sources = true }) end +---@return cmake.Result? function Config:build_targets_with_sources() if not _virtual_targets then self:update_targets() @@ -497,6 +540,7 @@ function Config:build_targets_with_sources() return _virtual_targets end +---Update build type from configure and build presets function Config:update_build_type() local presets_exists = self.base_settings.use_preset and Presets.exists(self.cwd) if not presets_exists then @@ -546,6 +590,7 @@ function Config:update_build_type() end end +---Update build target from build preset function Config:update_build_target() local presets_exists = self.base_settings.use_preset and Presets.exists(self.cwd) if not presets_exists then @@ -578,6 +623,7 @@ function Config:update_build_target() end end +---Update build directory from kits or configure preset function Config:update_build_directory() local kits_config = kits.parse(self.cmake_kits_path, self.cwd) if kits_config then diff --git a/lua/cmake-tools/const.lua b/lua/cmake-tools/const.lua index 999cde5f..c1f94df9 100644 --- a/lua/cmake-tools/const.lua +++ b/lua/cmake-tools/const.lua @@ -1,4 +1,5 @@ local osys = require("cmake-tools.osys") +---@class Const local const = { cmake_command = "cmake", -- this is used to specify cmake command path ctest_command = "ctest", -- this is used to specify ctest command path From 9f0747ccff47456cc42ca5b3429b44e5229e63cb Mon Sep 17 00:00:00 2001 From: Denzel <> Date: Sat, 14 Mar 2026 13:34:54 +0100 Subject: [PATCH 6/8] feat(session): save and load sesion on DirChanged --- lua/cmake-tools/init.lua | 39 ++++++++++++++++++++++++++++++++++--- lua/cmake-tools/session.lua | 19 +++++++++--------- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/lua/cmake-tools/init.lua b/lua/cmake-tools/init.lua index 316a1b22..a5397f5a 100644 --- a/lua/cmake-tools/init.lua +++ b/lua/cmake-tools/init.lua @@ -19,6 +19,7 @@ local Path = require("plenary.path") local ctest = require("cmake-tools.test.ctest") local config = Config:new(const) +local cwd = vim.loop.cwd() local cmake = {} @@ -39,10 +40,11 @@ function cmake.setup(values) require("cmake-tools.notification").setup(const.cmake_notifications) config = Config:new(const) + cwd = vim.loop.cwd() -- auto reload previous session - local old_config = _session.load() - _session.update(config, old_config) + local old_config = _session.load(cwd) + config = _session.update(config, old_config) local is_executor_installed = utils.get_executor(config.executor.name).is_installed() local is_runner_installed = utils.get_runner(config.runner.name).is_installed() @@ -1588,6 +1590,7 @@ end local regenerate_id = nil local termclose_id = nil local vim_leave_pre_id = nil +local cwd_changed_id = nil local group = vim.api.nvim_create_augroup("cmaketools", { clear = true }) @@ -1665,6 +1668,36 @@ function cmake.create_regenerate_on_save_autocmd() end function cmake.register_autocmd() + if cwd_changed_id then + vim.api.nvim_del_autocmd(cwd_changed_id) + end + cwd_changed_id = vim.api.nvim_create_autocmd("DirChanged", { + group = group, + callback = function() + local current_cwd = vim.loop.cwd() + if current_cwd == cwd then + return + end + + -- Save session for the project we're leaving + _session.save(cwd, config) + cwd = current_cwd + config = Config:new(const) + + if not cmake.is_cmake_project() then + return + end + + -- Load and apply session for the new cwd + config = _session.update(config, _session.load(cwd)) + + -- Re-register cwd-dependent autocmds for the new project + cmake.create_regenerate_on_save_autocmd() + cmake.register_autocmd_provided_by_users() + cmake.register_scratch_buffer(config.executor.name, config.runner.name) + end, + }) + -- preload the autocmd if the following option is true. only saves cmakelists.txt files if cmake.is_cmake_project() then if termclose_id then @@ -1679,7 +1712,7 @@ function cmake.register_autocmd() vim_leave_pre_id = vim.api.nvim_create_autocmd("VimLeavePre", { group = group, callback = function() - _session.save(config) + _session.save(cwd, config) vim.api.nvim_del_augroup_by_id(group) end, }) diff --git a/lua/cmake-tools/session.lua b/lua/cmake-tools/session.lua index c1e60d42..50945dec 100644 --- a/lua/cmake-tools/session.lua +++ b/lua/cmake-tools/session.lua @@ -23,9 +23,8 @@ local function get_cache_path() end end -local function get_current_path() - local current_path = vim.loop.cwd() - local clean_path = current_path:gsub("/", "") +local function get_current_path(cwd) + local clean_path = cwd:gsub("/", "") clean_path = clean_path:gsub("\\", "") clean_path = clean_path:gsub(":", "") return get_cache_path() .. clean_path .. ".lua" @@ -38,10 +37,10 @@ local function init_cache() end end -local function init_session() +local function init_session(cwd) init_cache() - local path = get_current_path() + local path = get_current_path(cwd) if not utils.file_exists(path) then local file = io.open(path, "w") if file then @@ -50,8 +49,8 @@ local function init_session() end end -function session.load() - local path = get_current_path() +function session.load(cwd) + local path = get_current_path(cwd) if utils.file_exists(path) then local config = dofile(path) @@ -113,10 +112,10 @@ function session.update(config, old_config) end end -function session.save(config) - init_session() +function session.save(cwd, config) + init_session(cwd) - local path = get_current_path() + local path = get_current_path(cwd) local file = io.open(path, "w") local serialized_object = { From 225286e5d837e75a5e8b764a3be99c09dd741431 Mon Sep 17 00:00:00 2001 From: Denzel <> Date: Sat, 14 Mar 2026 14:20:36 +0100 Subject: [PATCH 7/8] refactor(session): update session via deep extend --- lua/cmake-tools/session.lua | 72 ++++++++++++++----------------------- 1 file changed, 26 insertions(+), 46 deletions(-) diff --git a/lua/cmake-tools/session.lua b/lua/cmake-tools/session.lua index 50945dec..b57ee03a 100644 --- a/lua/cmake-tools/session.lua +++ b/lua/cmake-tools/session.lua @@ -9,6 +9,7 @@ local session = { }, } +---@return string local function get_cache_path() if osys.islinux then return session.dir.unix @@ -23,6 +24,8 @@ local function get_cache_path() end end +---@param cwd string neovim working directory +---@return string local function get_current_path(cwd) local clean_path = cwd:gsub("/", "") clean_path = clean_path:gsub("\\", "") @@ -37,6 +40,7 @@ local function init_cache() end end +---@param cwd string neovim working directory local function init_session(cwd) init_cache() @@ -49,6 +53,8 @@ local function init_session(cwd) end end +---@param cwd string neovim working directory (used as cache key) +---@return SerializedConfig raw session data, or empty table if none exists function session.load(cwd) local path = get_current_path(cwd) @@ -60,64 +66,38 @@ function session.load(cwd) return {} end +---@param config Config +---@param old_config SerializedConfig +---@return Config merged config with session state applied function session.update(config, old_config) - if next(old_config) ~= nil then - if old_config.build_directory and old_config.base_settings.build_dir then - config:update_build_dir(old_config.build_directory, old_config.base_settings.build_dir) - end - if old_config.build_type then - config.build_type = old_config.build_type - end - if old_config.variant then - config.variant = old_config.variant - end - if old_config.build_target then - config.build_target = old_config.build_target - if type(config.build_target) ~= "table" then -- Backwards compatibility (could be removed after a grace period) see PR!332 - config.build_target = { config.build_target } - end - end - if old_config.launch_target then - config.launch_target = old_config.launch_target - end - if old_config.kit then - config.kit = old_config.kit - end - if old_config.configure_preset then - config.configure_preset = old_config.configure_preset - end - if old_config.build_preset then - config.build_preset = old_config.build_preset - end - if old_config.selected_test then - config.selected_test = old_config.selected_test - end - if old_config.env_script then - config.env_script = old_config.env_script - end - if old_config.cwd then - config.cwd = old_config.cwd - end + if next(old_config) == nil then + return config + end - config.base_settings = - vim.tbl_deep_extend("keep", old_config.base_settings, config.base_settings) - config.target_settings = old_config.target_settings or {} + local mt = getmetatable(config) + local build_directory = old_config.build_directory + local old_build_dir = old_config.base_settings and old_config.base_settings.build_dir + old_config.build_directory = nil - -- migrate old launch args to new config - if old_config.launch_args then - for k, v in pairs(old_config.launch_args) do - config.target_settings[k].args = v - end - end + config = vim.tbl_deep_extend("force", config, old_config) + setmetatable(config, mt) + + if build_directory and old_build_dir then + config:update_build_dir(build_directory, old_build_dir) end + + return config end +---@param cwd string neovim working directory (used as cache key) +---@param config Config current config to persist function session.save(cwd, config) init_session(cwd) local path = get_current_path(cwd) local file = io.open(path, "w") + ---@class SerializedConfig local serialized_object = { build_directory = config:build_directory_path(), build_type = config.build_type, From 1ec76283c7b039b72a5aefb18fd9fa1de5eb831a Mon Sep 17 00:00:00 2001 From: Denzel <> Date: Sat, 14 Mar 2026 14:25:26 +0100 Subject: [PATCH 8/8] fix(session): serialize test_preset --- lua/cmake-tools/session.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/cmake-tools/session.lua b/lua/cmake-tools/session.lua index b57ee03a..62576138 100644 --- a/lua/cmake-tools/session.lua +++ b/lua/cmake-tools/session.lua @@ -108,6 +108,7 @@ function session.save(cwd, config) configure_preset = config.configure_preset, env_script = config.env_script, build_preset = config.build_preset, + test_preset = config.test_preset, selected_test = config.selected_test, base_settings = config.base_settings, target_settings = config.target_settings,