local project = require("cmake.project") local M = {} -- from https://github.com/akinsho/toggleterm.nvim/blob/main/lua/toggleterm/commandline.lua -- Get a valid base path for a user provided path -- and an optional search term ---@param typed_path string ---@return string|nil, string|nil local get_path_parts = function(typed_path) if vim.fn.isdirectory(typed_path ~= "" and typed_path or ".") == 1 then -- The string is a valid path, we just need to drop trailing slashes to -- ease joining the base path with the suggestions return typed_path:gsub("/$", ""), nil elseif typed_path:find("/", 2) ~= nil then -- Maybe the typed path is looking for a nested directory -- we need to make sure it has at least one slash in it, and that is not -- from a root path local base_path = vim.fn.fnamemodify(typed_path, ":h") local search_term = vim.fn.fnamemodify(typed_path, ":t") if vim.fn.isdirectory(base_path) then return base_path, search_term end end return nil, nil end local complete_path = function(typed_path) -- Read the typed path as the base for the directory search local base_path, search_term = get_path_parts(typed_path or "") local safe_path = base_path ~= "" and base_path or "." local paths = vim.fn.readdir(safe_path, function(entry) return vim.fn.isdirectory(safe_path .. "/" .. entry) end) if not u.str_is_empty(search_term) then paths = vim.tbl_filter(function(path) return path:match("^" .. search_term .. "*") ~= nil end, paths) end return vim.tbl_map(function(path) return u.concat_without_empty({ base_path, path }, "/") end, paths) end local generate_options = { fresh = true, } local complete_value = function(values, values_opts, match) return function(typed_value) typed_value = typed_value or "" return vim.iter(values(values_opts)) :filter(function(value) return not typed_value or #typed_value == 0 or value[match]:match("^" .. typed_value .. "*") end) :map(function(value) return value[match] end) :totable() end end local build_options = { clean = true, j = function() return {} end, target = complete_value(project.current_targets, {}, "name"), } local install_options = { explain = true, -- component = complete_value(project.current_components, {}, "name"), prefix = complete_path, } ---@param options table a dictionary of key to function ---@return fun(lead: string, command: string, _: number):table local function complete(options) ---@param lead string the leading portion of the argument currently being completed on ---@param command string the entire command line ---@param _ number the cursor position in it (byte index) ---@return table return function(lead, command, _) local parts = vim.split(lead, "=") local key = parts[1] local value = parts[2] if options[key] then if type(options[key]) == "function" then return vim.iter(options[key](value)) :map(function(opt) return key .. "=" .. opt end) :totable() else return {} end else return vim.iter(options) :filter(function(option, _) return option:match(" " .. option .. "=") == nil end) :map(function(option, option_value) if type(option_value) == "boolean" and option_value then return option else return option .. "=" end end) :totable() end end end ---Take a users command arguments in format 'key1=value key2' ---and parse this into a table of arguments ---@see https://stackoverflow.com/a/27007701 ---@param args string ---@return any function M.parse(args) local result = {} if args then for _, part in ipairs(vim.split(args, " ")) do local arg = vim.split(part, "=") local key, value = arg[1], arg[2] if not value then result[key] = true else if key == "target" then result[key] = vim.split(value, ",") else result[key] = value end end end end return result end M.cmake_generate_complete = complete(generate_options) M.cmake_build_complete = complete(build_options) M.cmake_install_complete = complete(install_options) return M