local pr = require("cmake.project") local config = require("cmake.config") local t = require("cmake.terminal") local utils = require("cmake.utils") local constants = require("cmake.constants") local Path = require("plenary.path") local uv = vim.uv local M = {} local default_generate_exe_opts = { notify = { ok_message = "CMake generate finished", err_message = function(code) return "CMake generate failed with code " .. tostring(code) end, }, } local default_build_exe_opts = { notify = { ok_message = "CMake build finished", err_message = function(code) return "CMake build failed with code " .. tostring(code) end, }, } local _explain = function(command) vim.notify( table.concat({ table.concat( vim.iter(command.env or {}) :map(function(k, v) if v:find(" ") then return k .. '="' .. v .. '"' end return k .. "=" .. v end) :totable(), " " ), command.cmd, command.args, }, " "), vim.log.levels.INFO ) end --- Extends generate command by given options ---@param command table ---@param opts GenerateOpts ---@return table local _extend_generate_command = function(command, opts) opts = opts or {} local new = vim.deepcopy(command) return new end --- Extends build command by given options ---@param command table ---@param opts BuildOpts ---@return table local _extend_build_command = function(command, opts) local new = vim.deepcopy(command) if opts.j then new.args = new.args .. " -j " .. tostring(opts.j) end if opts.clean then new.args = new.args .. " --clean-first" end if opts.target and #opts.target ~= 0 then new.args = new.args .. " --target " .. table.concat(opts.target, " ") end return new end local _generate = function(option, opts) opts = opts or {} local main_path = function() pr.create_fileapi_query({}, function() vim.schedule(function() t.cmake_execute(_extend_generate_command(option.generate_command, opts), default_generate_exe_opts) end) end) end if opts.fresh then pr.clear_cache() end main_path() end local _for_current_generate_option = function(func) local idx = pr.current_generate_option_idx() if not idx then vim.notify("CMake: no configuration to generate", vim.log.levels.WARN) else func(pr.current_generate_option()) end end ---@class GenerateOpts ---@field fresh boolean|nil --- Generate project with current generate option --- @param opts GenerateOpts M.generate = function(opts) opts = opts or {} _for_current_generate_option(function(option) _generate(option, opts) end) end --- Generate project with current generate option --- @param opts GenerateOpts M.generate_explain = function(opts) opts = opts or {} _for_current_generate_option(function(option) _explain(_extend_generate_command(option.generate_command, opts)) end) end --- Generate project with current generate option --- @param opts table|nil M.generate_select = function(opts) opts = opts or {} local items = pr.generate_options(opts) vim.ui.select(items, { prompt = "Select configuration to generate:", format_item = function(item) return table.concat(item.name, config.variants_display.short.sep) end, }, function(_, idx) if not idx then return end pr.set_current_generate_option(idx) end) end local _for_current_build_option = function(func) local idx = pr.current_build_option() if not idx then vim.notify("CMake: no build configuration to generate", vim.log.levels.WARN) else func(pr.current_build_option()) end end local _build = function(option, opts) opts = opts or {} pr.create_fileapi_query({}, function() vim.schedule(function() t.cmake_execute(_extend_build_command(option.command, opts), default_build_exe_opts) end) end) end ---@class BuildOpts ---@field clean boolean|nil ---@field j number|nil ---@field target string[]|nil --- Build project with current build option --- @param opts BuildOpts M.build = function(opts) opts = opts or {} _for_current_build_option(function(option) _build(option, opts) end) end --- Build project with current build option --- @param opts BuildOpts M.build_explain = function(opts) opts = opts or {} _for_current_build_option(function(option) _explain(_extend_build_command(option.command, opts)) end) end ---Change current build option ---@param opts any|nil M.build_select = function(opts) local items = pr.current_generate_option().build_options vim.ui.select(items, { prompt = "Select build option to generate:", format_item = function(item) return table.concat(item.name, config.variants_display.short.sep) end, }, function(choice, idx) if not idx then return end pr.set_current_build_option(idx) end) end local _run_target = function(opts) local command = { cmd = opts.path, cwd = pr.current_directory(), } t.target_execute(command) end ---@class RunTargetOpts ---@field explain boolean|nil --- Run target --- @param opts RunTargetOpts M.run_target = function(opts) opts = opts or {} local _curr_exe_cmd = pr.current_executable_target() if not _curr_exe_cmd then M.run_target_select(opts) else _run_target({ path = _curr_exe_cmd.path }) end end --- Select target to run M.run_target_select = function(opts) opts = opts or {} opts.type = "EXECUTABLE" local items = pr.current_targets(opts) vim.ui.select(items, { prompt = "Select tagret to run:", format_item = function(item) return item.name end, }, function(_, idx) if not idx then return end pr.set_current_executable_target(idx) end) end ---Toggle CMake terminal window M.toggle = function() t.cmake_toggle() end ---Edit `.cmake-variants.yaml` file M.edit_variants = function() utils.file_exists(constants.variants_yaml_filename, function(variants_exists) if variants_exists then vim.schedule(function() vim.cmd(string.format("e %s", constants.variants_yaml_filename)) end) else local default_yaml = require("cmake.lyaml").dump(config.cmake.variants) utils.write_file(constants.variants_yaml_filename, default_yaml, function() vim.schedule(function() vim.cmd(string.format("e %s", constants.variants_yaml_filename)) end) end) end end) end M.reset_project = function(opts) require("cmake.project").setup(opts) end return M