aboutsummaryrefslogtreecommitdiff
path: root/lua/cmake/commandline.lua
blob: 06577d5523063204cc742796dd6ce162030702d8 (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
152
153
154
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