aboutsummaryrefslogtreecommitdiff
path: root/lua/cmake/commandline.lua
blob: c3d0ef199811d3c69b0e52e5f94b7582f7125a76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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