aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniil Rozanov <daniilrozzanov@gmail.com>2024-03-29 04:37:56 +0300
committerDaniil Rozanov <daniilrozzanov@gmail.com>2024-03-29 04:37:56 +0300
commit672f0d32e322b79661b5d7959887adaa9e41ad98 (patch)
treec2c3f4e2157d47c7e3f8dfd2f3229e37e0919b3e
parentd453f54d98536eb3a52daebfe279c4e624979c31 (diff)
feat: build from current variant
-rw-r--r--lua/cmake-explorer.lua112
-rw-r--r--lua/cmake-explorer/build_list.lua57
-rw-r--r--lua/cmake-explorer/cache.lua47
-rw-r--r--lua/cmake-explorer/capabilities.lua8
-rw-r--r--lua/cmake-explorer/config.lua37
-rw-r--r--lua/cmake-explorer/file_api.lua117
-rw-r--r--lua/cmake-explorer/init.lua67
-rw-r--r--lua/cmake-explorer/notification.lua1
-rw-r--r--lua/cmake-explorer/project.lua375
-rw-r--r--lua/cmake-explorer/project_uauaua.lua113
-rw-r--r--lua/cmake-explorer/runner.lua27
-rw-r--r--lua/cmake-explorer/telescope/make_entry.lua57
-rw-r--r--lua/cmake-explorer/telescope/pickers.lua103
-rw-r--r--lua/cmake-explorer/telescope/previewers.lua40
-rw-r--r--lua/cmake-explorer/telescope/test.lua46
-rw-r--r--lua/cmake-explorer/utils.lua102
-rw-r--r--lua/telescope/_extensions/cmake-explorer.lua0
-rw-r--r--plugin/cmake-explorer.lua33
18 files changed, 941 insertions, 401 deletions
diff --git a/lua/cmake-explorer.lua b/lua/cmake-explorer.lua
deleted file mode 100644
index eec4f5d..0000000
--- a/lua/cmake-explorer.lua
+++ /dev/null
@@ -1,112 +0,0 @@
-local config = require("cmake-explorer.config")
-local runner = require("cmake-explorer.runner")
-local Project = require("cmake-explorer.project")
-local capabilities = require("cmake-explorer.capabilities")
-local utils = require("cmake-explorer.utils")
-local Path = require("plenary.path")
-
-local M = {}
-
-local project = nil
-
-local format_build_dir = function()
- if Path:new(config.build_dir):is_absolute() then
- return function(v)
- return Path:new(v.path):make_relative(vim.env.HOME)
- end
- else
- return function(v)
- return Path:new(v.path):make_relative(project.path)
- end
- end
-end
-
-function M.list_build_dirs()
- if project then
- vim.print(project:list_build_dirs())
- end
-end
-
-function M.configure()
- assert(project)
- local generators = capabilities.generators()
- table.insert(generators, 1, "Default")
- vim.ui.select(generators, { prompt = "Select generator" }, function(generator)
- if not generator then
- return
- end
- -- TODO: handle default generator from env (or from anywhere else)
- generator = utils.is_neq(generator, "Default")
- vim.ui.select(config.build_types, { prompt = "Select build type" }, function(build_type)
- if not build_type then
- return
- end
- vim.ui.input({ prompt = "Input additional args" }, function(args)
- if not build_type then
- return
- end
- local task = project:configure({ generator = generator, build_type = build_type, args = args })
- runner.start(task)
- end)
- end)
- end)
-end
-
-function M.configure_dir()
- assert(project)
-
- vim.ui.select(
- project:list_build_dirs(),
- { prompt = "Select directory to build", format_item = format_build_dir() },
- function(dir)
- if not dir then
- return
- end
- local task = project:configure(dir.path)
- runner.start(task)
- end
- )
-end
-
-function M.configure_last()
- local task = project:configure_last()
- runner.start(task)
-end
-
-M.setup = function(cfg)
- cfg = cfg or {}
-
- config.setup(cfg)
- capabilities.setup()
-
- project = Project:new(vim.loop.cwd())
- if not project then
- print("cmake-explorer: no CMakeLists.txt file found. Aborting setup")
- return
- end
- project:scan_build_dirs()
-
- local cmd = vim.api.nvim_create_user_command
-
- cmd("CMakeConfigure", M.configure, { -- opts
- nargs = 0,
- bang = true,
- desc = "CMake configure with parameters",
- })
-
- cmd(
- "CMakeConfigureLast",
- M.configure_last,
- { nargs = 0, bang = true, desc = "CMake configure last if exists. Otherwise default" }
- )
-
- cmd(
- "CMakeConfigureDir",
- M.configure_dir,
- { nargs = 0, bang = true, desc = "CMake configure last if exists. Otherwise default" }
- )
-
- cmd("CMakeListBuilds", M.list_build_dirs, { nargs = 0 })
-end
-
-return M
diff --git a/lua/cmake-explorer/build_list.lua b/lua/cmake-explorer/build_list.lua
deleted file mode 100644
index 7abaddc..0000000
--- a/lua/cmake-explorer/build_list.lua
+++ /dev/null
@@ -1,57 +0,0 @@
-local Build = require("cmake-explorer.build")
-
-local BuildFilter = {}
-
-BuildFilter.__call = function(self, build)
- for k, v in pairs(self) do
- if type(k) == "string" then
- if v ~= build[k] then
- return false
- end
- end
- end
- return true
-end
-
-local BuildList = {
- __newindex = function(t, k, v)
- for _, value in ipairs(t) do
- if value == v then
- return
- end
- end
- rawset(t, k, v)
- end,
-}
-
-function BuildList:new()
- local obj = {
- current = nil,
- }
- setmetatable(obj, BuildList)
- return obj
-end
-
-function BuildList:insert(o)
- local build = Build:new(o)
- table.insert(self, build)
- self.current = build
-end
-
-function BuildList:filter(pred)
- pred = pred or {}
- local i, n = 0, #self
- if type(pred) == "table" then
- setmetatable(pred, BuildFilter)
- end
- return function()
- repeat
- i = i + 1
- if pred(self[i]) then
- return self[i]
- end
- until i ~= n
- end
-end
-
-return BuildList
diff --git a/lua/cmake-explorer/cache.lua b/lua/cmake-explorer/cache.lua
new file mode 100644
index 0000000..046a9f9
--- /dev/null
+++ b/lua/cmake-explorer/cache.lua
@@ -0,0 +1,47 @@
+local Cache = {}
+
+local os = {
+ iswin32 = vim.fn.has("win32") == 1,
+ ismac = vim.fn.has("mac") == 1,
+ iswsl = vim.fn.has("wsl") == 1,
+ islinux = vim.fn.has("linux") == 1,
+}
+
+local dir = {
+ unix = vim.fn.expand("~") .. "/.cache/cmake_explorer_nvim/",
+ mac = vim.fn.expand("~") .. "/.cache/cmake_explorer_nvim/",
+ win = vim.fn.expand("~") .. "/AppData/Local/cmake_explorer_nvim/",
+}
+
+local function get_cache_path()
+ if os.islinux then
+ return dir.unix
+ elseif os.ismac then
+ return dir.mac
+ elseif os.iswsl then
+ return dir.unix
+ elseif os.iswin32 then
+ return dir.win
+ end
+end
+
+local function get_clean_path(path)
+ local current_path = path
+ local clean_path = current_path:gsub("/", "")
+ clean_path = clean_path:gsub("\\", "")
+ clean_path = clean_path:gsub(":", "")
+ return clean_path
+end
+
+function Cache.load(path)
+ return
+end
+
+function Cache.save_global(tbl)
+ local to_save = vim.tbl_deep_extend("keep", tbl)
+ setmetatable(to_save, nil)
+ local path = get_project_path()
+ local file = io.open(path, "w")
+end
+
+return Cache
diff --git a/lua/cmake-explorer/capabilities.lua b/lua/cmake-explorer/capabilities.lua
index f3a966c..052f484 100644
--- a/lua/cmake-explorer/capabilities.lua
+++ b/lua/cmake-explorer/capabilities.lua
@@ -8,7 +8,7 @@ local multiconfig_generators = {
"Visual Studio 15 2017",
"Visual Studio 16 2019",
"Visual Studio 17 2022",
- -- "Green Hills MULTI"
+ "Green Hills MULTI",
}
local Capabilities = {
@@ -27,6 +27,10 @@ function Capabilities.generators()
end
function Capabilities.is_multiconfig_generator(generator)
+ -- if generator is nil, assume is is not multiconifg
+ if not generator then
+ return
+ end
return vim.tbl_contains(multiconfig_generators, generator)
end
@@ -35,7 +39,7 @@ function Capabilities.has_fileapi()
end
Capabilities.setup = function()
- local output = vim.fn.system({ config.cmake_cmd, "-E", "capabilities" })
+ local output = vim.fn.system({ config.cmake_path, "-E", "capabilities" })
Capabilities.json = vim.json.decode(output)
end
diff --git a/lua/cmake-explorer/config.lua b/lua/cmake-explorer/config.lua
index 21788ce..611f526 100644
--- a/lua/cmake-explorer/config.lua
+++ b/lua/cmake-explorer/config.lua
@@ -1,9 +1,36 @@
local default_config = {
- cmake_cmd = "cmake",
- build_dir_template = { "build", "${buildType}", sep = "-", case = nil },
- build_dir = "",
- build_types = { "Debug", "Release" },
- options = {},
+ cmake_path = "cmake",
+ environment = {},
+ configure_environment = {},
+ build_directory = "${workspaceFolder}/build-${buildType}",
+ build_environment = {},
+ build_args = {},
+ build_tool_args = {},
+ generator = nil,
+ default_variants = {
+ {
+ default = "debug",
+ description = "Build type",
+ choices = {
+ debug = { short = "Debug", long = "Long debug", buildType = "Debug" },
+ release = { short = "Release", long = "Long release", buildType = "Release" },
+ },
+ },
+ {
+ default = "static",
+ choices = {
+ static = { short = "Static", long = "Long static", linkage = "static" },
+ shared = { short = "Shared", long = "Long shared", linkage = "shared" },
+ },
+ },
+ },
+ variants_display = {
+ short_sep = " × ",
+ long_sep = " ❄ ",
+ },
+ parallel_jobs = nil,
+ save_before_build = true,
+ source_directory = "${workspaceFolder}",
}
local M = vim.deepcopy(default_config)
diff --git a/lua/cmake-explorer/file_api.lua b/lua/cmake-explorer/file_api.lua
index cd83be4..37eda3a 100644
--- a/lua/cmake-explorer/file_api.lua
+++ b/lua/cmake-explorer/file_api.lua
@@ -2,6 +2,7 @@ local capabilities = require("cmake-explorer.capabilities")
local Path = require("plenary.path")
local Scandir = require("plenary.scandir")
local notif = require("cmake-explorer.notification")
+local utils = require("cmake-explorer.utils")
local query_path_suffix = { ".cmake", "api", "v1", "query", "client-cmake-explorer", "query.json" }
local reply_dir_suffix = { ".cmake", "api", "v1", "reply" }
@@ -11,83 +12,81 @@ local FileApi = {}
FileApi.__index = FileApi
function FileApi:new(opts)
- if not capabilities.has_fileapi() then
- notif.notify("No fileapi files", vim.log.levels.ERROR)
- return
- end
- local path
- if type(opts) == "string" then
- path = opts
- end
- local obj = {
- path = Path:new(path):absolute(),
- index = nil,
- cmakefiles = nil,
- codemodel = nil,
- targets = nil,
- }
- Path:new(path):mkdir({ parents = true })
- setmetatable(obj, FileApi)
- return obj
+ if not capabilities.has_fileapi() then
+ notif.notify("No fileapi files", vim.log.levels.ERROR)
+ return
+ end
+ local path
+ if type(opts) == "string" then
+ path = opts
+ end
+ local obj = {
+ path = path,
+ index = nil,
+ cmakefiles = nil,
+ codemodel = nil,
+ targets = {},
+ }
+ setmetatable(obj, FileApi)
+ return obj
end
function FileApi:create()
- local query = Path:new(self.path, unpack(query_path_suffix))
- if not query:exists() then
- if not query:touch({ parents = true }) then
- notif.notify("Cannot create query file", vim.log.levels.ERROR)
- return
- end
- query:write(vim.json.encode(capabilities.json.fileApi), "w")
- end
- Path:new(self.path, unpack(reply_dir_suffix)):mkdir({ parents = true })
- return true
+ local query = Path:new(self.path, unpack(query_path_suffix))
+ if not query:exists() then
+ if not query:touch({ parents = true }) then
+ notif.notify("Cannot create query file", vim.log.levels.ERROR)
+ return
+ end
+ query:write(vim.json.encode(capabilities.json.fileApi), "w")
+ end
+ Path:new(self.path, unpack(reply_dir_suffix)):mkdir({ parents = true })
+ return true
end
function FileApi:read_reply()
- if not self:reply_exists() then
- notif.notify("No reply directory", vim.log.levels.ERROR)
- return
- end
- local reply_dir = Path:new(self.path, unpack(reply_dir_suffix))
- local index = Scandir.scan_dir(tostring(reply_dir), { search_pattern = "index*" })
- if #index == 0 then
- notif.notify("No files in reply", vim.log.levels.ERROR)
- return
- end
- self.index = vim.json.decode(Path:new(index[1]):read())
- for _, object in ipairs(self.index.objects) do
- if object.kind == "codemodel" then
- self.codemodel = vim.json.decode((reply_dir / object.jsonFile):read())
- self.targets = {}
- for _, target in ipairs(self.codemodel.configurations[1].targets) do
- table.insert(self.targets, vim.json.decode((reply_dir / target.jsonFile):read()))
- end
- elseif object.kind == "cmakeFiles" then
- self.cmakefiles = vim.json.decode(Path:new(reply_dir / object.jsonFile):read())
- end
- end
- return true
+ if not self:reply_exists() then
+ notif.notify("No reply directory", vim.log.levels.ERROR)
+ return
+ end
+ local reply_dir = Path:new(self.path, unpack(reply_dir_suffix))
+ local index = Scandir.scan_dir(tostring(reply_dir), { search_pattern = "index*" })
+ if #index == 0 then
+ notif.notify("No files in reply", vim.log.levels.ERROR)
+ return
+ end
+ self.index = vim.json.decode(Path:new(index[1]):read())
+ for _, object in ipairs(self.index.objects) do
+ if object.kind == "codemodel" then
+ self.codemodel = vim.json.decode((reply_dir / object.jsonFile):read())
+ for _, target in ipairs(self.codemodel.configurations[1].targets) do
+ self.targets[target.name] = vim.json.decode((reply_dir / target.jsonFile):read())
+ end
+ elseif object.kind == "cmakeFiles" then
+ self.cmakefiles = vim.json.decode(Path:new(reply_dir / object.jsonFile):read())
+ end
+ end
+ return true
end
function FileApi:query_exists()
- return Path:new(self.path, unpack(query_path_suffix)):exists()
+ return Path:new(self.path, unpack(query_path_suffix)):exists()
end
function FileApi:reply_exists()
- local reply_dir = Path:new(self.path, unpack(reply_dir_suffix))
- if not reply_dir:exists() then
- return
- end
- return true
+ local reply_dir = Path:new(self.path, unpack(reply_dir_suffix))
+ if not reply_dir:exists() then
+ return
+ end
+ return true
end
function FileApi:exists()
- return self:query_exists() and self:reply_exists()
+ return self:query_exists() and self:reply_exists()
end
function FileApi.is_fileapi(other)
- return getmetatable(other) == FileApi
+ return getmetatable(other) == FileApi
end
return FileApi
diff --git a/lua/cmake-explorer/init.lua b/lua/cmake-explorer/init.lua
new file mode 100644
index 0000000..622eebd
--- /dev/null
+++ b/lua/cmake-explorer/init.lua
@@ -0,0 +1,67 @@
+local config = require("cmake-explorer.config")
+local runner = require("cmake-explorer.runner")
+local Project = require("cmake-explorer.project")
+local capabilities = require("cmake-explorer.capabilities")
+local utils = require("cmake-explorer.utils")
+local Path = require("plenary.path")
+local pickers = require("cmake-explorer.telescope.pickers")
+local notif = require("cmake-explorer.notification")
+
+local M = {}
+
+M.project = nil
+
+local format_build_dir = function()
+ if Path:new(config.build_dir):is_absolute() then
+ return function(v)
+ return Path:new(v.path):make_relative(vim.env.HOME)
+ end
+ else
+ return function(v)
+ return Path:new(v.path):make_relative(M.project.path)
+ end
+ end
+end
+
+function M.configure(opts)
+ assert(M.project)
+ opts = opts or {}
+ pickers.configure(opts)
+end
+
+function M.configure_last(opts)
+ if not M.project.current_config then
+ notif.notify("No current configuration")
+ return
+ end
+ runner.start(M.project:configure_command())
+end
+
+function M.build(opts)
+ opts = opts or {}
+ pickers.build(opts)
+end
+
+function M.build_last(opts)
+ if not M.project.current_config then
+ notif.notify("No current configuration")
+ return
+ end
+ runner.start(M.project:build_command())
+end
+
+function M.setup(opts)
+ opts = opts or {}
+
+ config.setup(opts)
+ capabilities.setup()
+
+ M.project = Project:from_variants(config.default_variants)
+
+ if not M.project then
+ print("fuuuuuuuuuuuu")
+ return
+ end
+end
+
+return M
diff --git a/lua/cmake-explorer/notification.lua b/lua/cmake-explorer/notification.lua
index fa8ff6e..0029241 100644
--- a/lua/cmake-explorer/notification.lua
+++ b/lua/cmake-explorer/notification.lua
@@ -6,6 +6,7 @@ function Notification.notify(msg, lvl, opts)
opts = opts or {}
if has_notify then
opts.hide_from_history = true
+ opts.title = "CMake Explorer"
return notify(msg, lvl, opts)
end
end
diff --git a/lua/cmake-explorer/project.lua b/lua/cmake-explorer/project.lua
index 7006656..ca77728 100644
--- a/lua/cmake-explorer/project.lua
+++ b/lua/cmake-explorer/project.lua
@@ -1,114 +1,283 @@
local config = require("cmake-explorer.config")
-local capabilities = require("cmake-explorer.capabilities")
-local FileApi = require("cmake-explorer.file_api")
local Path = require("plenary.path")
-local Scandir = require("plenary.scandir")
local utils = require("cmake-explorer.utils")
-local notif = require("cmake-explorer.notification")
+local FileApi = require("cmake-explorer.file_api")
+
+local VariantConfig = {}
+
+VariantConfig.__index = VariantConfig
+
+local variant_subs = {
+ ["${workspaceFolder}"] = vim.loop.cwd(),
+ ["${userHome}"] = vim.loop.os_homedir(),
+}
+
+function VariantConfig:new(obj)
+ setmetatable(obj, VariantConfig)
+ obj.subs = obj:_subs()
+ obj.build_directory = obj:_build_directory()
+ obj.configure_args = obj:_configure_args()
+ obj.configure_command = obj:_configure_command()
+ obj.build_args = obj:_build_args()
+ obj.build_command = obj:_build_command()
+ if not obj.fileapis[obj.build_directory] then
+ local fa = FileApi:new(obj.build_directory)
+ if fa and fa:exists() then
+ fa:read_reply()
+ obj.fileapis[obj.build_directory] = fa
+ end
+ end
+
+ return obj
+end
+
+function VariantConfig:_subs()
+ return vim.tbl_deep_extend("keep", variant_subs, { ["${buildType}"] = self.buildType })
+end
+
+function VariantConfig:_build_directory()
+ return utils.substitude(config.build_directory, self.subs)
+end
+
+function VariantConfig:_configure_args()
+ local args = {}
+ if self.generator then
+ table.insert(args, "-G " .. '"' .. self.generator .. '"')
+ end
+ if self.buildType then
+ table.insert(args, "-DCMAKE_BUILD_TYPE=" .. self.buildType)
+ end
+ if self.linkage and string.lower(self.linkage) == "static" then
+ table.insert(args, "-DCMAKE_BUILD_SHARED_LIBS=OFF")
+ elseif self.linkage and string.lower(self.linkage) == "shared" then
+ table.insert(args, "-DCMAKE_BUILD_SHARED_LIBS=ON")
+ end
+ for k, v in pairs(self.settings or {}) do
+ table.insert(args, "-D" .. k .. "=" .. v)
+ end
+ table.insert(args, "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON")
+ table.insert(
+ args,
+ "-B" .. Path:new(self.build_directory):make_relative(utils.substitude(config.source_directory, self.subs))
+ )
+ return args
+end
+
+function VariantConfig:_configure_command()
+ local ret = {}
+ ret.cmd = config.cmake_path
+ ret.args = self.configure_args
+ ret.cwd = variant_subs["${workspaceFolder}"]
+ ret.env = vim.tbl_deep_extend("keep", self.env, config.configure_environment, config.environment)
+ ret.after_success = function()
+ utils.symlink_compile_commands(self.build_directory, variant_subs["${workspaceFolder}"])
+ self.fileapis[self.build_directory]:read_reply()
+ end
+ ret.before_run = function()
+ self.current_config_ref = self
+ local fa = FileApi:new(self.build_directory)
+ if not fa then
+ return
+ end
+ if not fa:create() then
+ return
+ end
+ self.fileapis[self.build_directory] = fa
+ return true
+ end
+ return ret
+end
+
+function VariantConfig:_build_args()
+ local args = { "--build" }
+ table.insert(
+ args,
+ Path:new(self.build_directory):make_relative(utils.substitude(config.source_directory, self.subs))
+ )
+ if #self.buildArgs ~= 0 then
+ for _, v in ipairs(self.buildArgs) do
+ table.insert(args, v)
+ end
+ elseif #config.build_args ~= 0 then
+ for _, v in ipairs(config.build_args) do
+ table.insert(args, v)
+ end
+ end
+ if #self.buildToolArgs ~= 0 or #config.build_tool_args ~= 0 then
+ table.insert(args, "--")
+ if #self.buildToolArgs ~= 0 then
+ for _, v in ipairs(self.buildToolArgs) do
+ table.insert(args, v)
+ end
+ elseif #config.build_tool_args ~= 0 then
+ for _, v in ipairs(config.build_tool_args) do
+ table.insert(args, v)
+ end
+ end
+ end
+ return args
+end
+
+function VariantConfig:_build_command()
+ local ret = {}
+ ret.cmd = config.cmake_path
+ ret.args = self.build_args
+ ret.cwd = variant_subs["${workspaceFolder}"]
+ ret.env = vim.tbl_deep_extend("keep", self.env, config.configure_environment, config.environment)
+ return ret
+end
+
+local function cartesian_product(sets)
+ local function collapse_result(res)
+ local ret = {
+ short = {},
+ long = {},
+ buildType = nil,
+ linkage = nil,
+ generator = nil,
+ buildArgs = {},
+ buildToolArgs = {},
+ settings = {},
+ env = {},
+ }
+ local is_default = true
+ for _, v in ipairs(res) do
+ if not v.default then
+ is_default = false
+ end
+ ret.short[#ret.short + 1] = v.short
+ ret.long[#ret.long + 1] = v.long
+ ret.buildType = v.buildType or ret.buildType
+ ret.linkage = v.linkage or ret.linkage
+ ret.generator = v.generator or ret.generator
+ ret.buildArgs = v.buildArgs or ret.buildArgs
+ ret.buildToolArgs = v.buildToolArgs or ret.buildToolArgs
+ for sname, sval in pairs(v.settings or {}) do
+ ret.settings[sname] = sval
+ end
+ for ename, eres in pairs(v.env or {}) do
+ ret.env[ename] = eres
+ end
+ end
+ ret.display = {}
+ ret.display.short = table.concat(ret.short, config.variants_display.short_sep)
+ ret.display.long = table.concat(ret.long, config.variants_display.short_sep)
+ ret.default = is_default or nil
+ return ret
+ end
+ local result = {}
+ local set_count = #sets
+ local function descend(depth)
+ for k, v in pairs(sets[depth].choices) do
+ if sets[depth].default ~= k then
+ result.default = false
+ end
+ result[depth] = v
+ result[depth].default = (k == sets[depth].default)
+ if depth == set_count then
+ coroutine.yield(collapse_result(result))
+ else
+ descend(depth + 1)
+ end
+ end
+ end
+ return coroutine.wrap(function()
+ descend(1)
+ end)
+end
local Project = {}
Project.__index = Project
-function Project:new(o)
- o = o or {}
-
- local path
- if type(o) == "string" then
- path = o
- elseif type(o) == "table" and o.path then
- path = o.path
- else
- return
- end
-
- if not Path:new(path, "CMakeLists.txt"):exists() then
- return
- end
-
- local obj = {
- path = Path:new(path):absolute(),
- fileapis = {},
- last_generate = {},
- }
- setmetatable(obj, Project)
- return obj
-end
-
-function Project:scan_build_dirs()
- local builds_root = utils.is_eq(
- Path:new(config.build_dir):absolute(),
- true,
- config.build_dir,
- Path:new(self.path, config.build_dir):absolute()
- )
- local candidates = Scandir.scan_dir(builds_root, { hidden = false, only_dirs = true, depth = 0, silent = true })
- for _, v in ipairs(candidates) do
- local fa = FileApi:new(v)
- if fa and fa:exists() and fa:read_reply() then
- self.fileapis[v] = fa
- end
- end
-end
-
-function Project:symlink_compile_commands(path)
- local src = Path:new(path, "compile_commands.json")
- if src:exists() then
- vim.cmd(
- 'silent exec "!'
- .. config.cmake_cmd
- .. " -E create_symlink "
- .. src:absolute()
- .. " "
- .. Path:new(self.path, "compile_commands.json"):absolute()
- .. '"'
- )
- end
-end
-
-function Project:configure(params)
- params = params or {}
- local args = utils.generate_args(params, self.path)
- local build_dir = utils.build_path(params, self.path)
- if not args then
- return
- end
- if not self.fileapis[build_dir] then
- local fa = FileApi:new(build_dir)
- if not fa then
- notif.notify("Cannot fileapi object", vim.log.levels.ERROR)
- return
- end
- if not fa:create() then
- return
- end
- self.fileapis[build_dir] = fa
- end
-
- return {
- cmd = config.cmake_cmd,
- args = args,
- cwd = Path:new(self.path):absolute(),
- after_success = function()
- self.last_generate = build_dir
- self.fileapis[build_dir]:read_reply()
- self:symlink_compile_commands(build_dir)
- end,
- }
-end
-
-function Project:configure_last()
- return self:configure(self.last_generate)
-end
-
-function Project:list_build_dirs()
- local ret = {}
- for k, _ in pairs(self.fileapis) do
- local build = {}
- build.path = k
- table.insert(ret, build)
- end
- return ret
+function Project:from_variants(variants)
+ local obj =
+ { headers = {}, display = { short_len = 10, long_len = 30 }, configs = {}, current_config = nil, fileapis = {} }
+ for _, v in pairs(variants) do
+ table.insert(obj.headers, v.description or "")
+ end
+ for v in cartesian_product(variants) do
+ v.fileapis = obj.fileapis
+ v.current_config_ref = obj.current_config
+ v = VariantConfig:new(v)
+ obj.display.short_len = math.max(obj.display.short_len, string.len(v.display.short))
+ table.insert(obj.configs, v)
+ if v.default then
+ obj.current_config = v
+ end
+ if not obj.fileapis[v.build_directory] then
+ local fa = FileApi:new(v.build_directory)
+ if fa and fa:exists() then
+ fa:read_reply()
+ obj.fileapis[v.build_directory] = fa
+ end
+ end
+ end
+ setmetatable(obj, Project)
+ return obj
+end
+
+function Project:from_presets(presets)
+ local obj = { { 1, 3, ddf = "" } }
+ return setmetatable(obj, self)
+end
+
+function Project:set_current_config(idx)
+ self.current_config = self.configs[idx]
+end
+
+function Project:set_current_build() end
+
+function Project:configure_command()
+ return self.current_config.configure_command
+end
+
+function Project:current_configure_index()
+ for k, v in ipairs(self.configs) do
+ if v == self.current_config then
+ return k
+ end
+ end
+ return 1
+end
+
+function Project:current_build_index()
+ if not self.current_config then
+ return 1
+ end
+ if getmetatable(self.current_config) == VariantConfig then
+ return 1
+ end
+end
+
+function Project:configure_display_options()
+ return self.display_options
+end
+
+function Project:build_command()
+ if not self.current_config then
+ return
+ end
+ if getmetatable(self.current_config) == VariantConfig then
+ return self.current_config.build_command
+ end
+end
+
+function Project:build_directory()
+ if not self.current_config then
+ return
+ end
+ return self.current_config.build_directory
+end
+
+function Project:list_configs()
+ return self.configs
+end
+
+function Project:list_builds(opts)
+ if getmetatable(self.current_config) == VariantConfig then
+ return { self.current_config }
+ end
end
return Project
diff --git a/lua/cmake-explorer/project_uauaua.lua b/lua/cmake-explorer/project_uauaua.lua
new file mode 100644
index 0000000..b54abd6
--- /dev/null
+++ b/lua/cmake-explorer/project_uauaua.lua
@@ -0,0 +1,113 @@
+local config = require("cmake-explorer.config")
+local capabilities = require("cmake-explorer.capabilities")
+local FileApi = require("cmake-explorer.file_api")
+local Path = require("plenary.path")
+local Scandir = require("plenary.scandir")
+local utils = require("cmake-explorer.utils")
+local notif = require("cmake-explorer.notification")
+
+local Project = {}
+
+Project.__index = Project
+
+function Project:new(o)
+ o = o or {}
+
+ local path
+ if type(o) == "string" then
+ path = o
+ elseif type(o) == "table" and o.path then
+ path = o.path
+ else
+ return
+ end
+
+ if not Path:new(path, "CMakeLists.txt"):exists() then
+ return
+ end
+
+ local obj = {
+ path = path,
+ fileapis = {},
+ last_generate = nil,
+ }
+ notif.notify("PATH " .. obj.path)
+ setmetatable(
+ obj.fileapis,
+ utils.make_maplike_list(function(v)
+ return v.path
+ end)
+ )
+ setmetatable(obj, Project)
+ return obj
+end
+
+function Project:scan_build_dirs()
+ local builds_root = utils.is_eq(
+ Path:new(config.build_dir):is_absolute(),
+ true,
+ Path:new(config.build_dir),
+ Path:new(self.path, config.build_dir)
+ )
+ local candidates =
+ Scandir.scan_dir(builds_root:absolute(), { hidden = false, only_dirs = true, depth = 0, silent = true })
+ for _, v in ipairs(candidates) do
+ local fa = FileApi:new(v)
+ if fa and fa:exists() and fa:read_reply() then
+ self.fileapis[fa.path] = fa
+ end
+ end
+end
+
+function Project:symlink_compile_commands(path)
+ local src = Path:new(path, "compile_commands.json")
+ if src:exists() then
+ vim.cmd(
+ 'silent exec "!'
+ .. config.cmake_cmd
+ .. " -E create_symlink "
+ .. src:normalize()
+ .. " "
+ .. Path:new(self.path, "compile_commands.json"):normalize()
+ .. '"'
+ )
+ end
+end
+
+function Project:configure(params)
+ params = params or {}
+ local args = utils.generate_args(params, self.path)
+ local build_dir = utils.build_path(params, self.path)
+ if not args then
+ return
+ end
+ if not self.fileapis[build_dir] then
+ local fa = FileApi:new(build_dir)
+ if not fa then
+ notif.notify("Cannot fileapi object", vim.log.levels.ERROR)
+ return
+ end
+ if not fa:create() then
+ return
+ end
+ self.fileapis[build_dir] = fa
+ end
+
+ local job_args = {
+ cmd = config.cmake_cmd,
+ args = args,
+ cwd = self.path,
+ after_success = function()
+ self.last_generate = job_args
+ self.fileapis[build_dir]:read_reply()
+ self:symlink_compile_commands(build_dir)
+ end,
+ }
+ return job_args
+end
+
+function Project:configure_last()
+ return self.last_generate
+end
+
+return Project
diff --git a/lua/cmake-explorer/runner.lua b/lua/cmake-explorer/runner.lua
index ca596ac..f2765fe 100644
--- a/lua/cmake-explorer/runner.lua
+++ b/lua/cmake-explorer/runner.lua
@@ -1,4 +1,5 @@
local Job = require("plenary.job")
+local notif = require("cmake-explorer.notification")
local M = {}
@@ -12,17 +13,33 @@ function M.start(command)
end
local env = vim.tbl_extend("force", vim.loop.os_environ(), command.env and command.env or {})
- vim.notify(command.cmd .. " " .. table.concat(command.args, " "))
+ if command.before_run then
+ if not command.before_run() then
+ notif.notify("Before run command failed", vim.log.levels.ERROR)
+ return
+ end
+ end
+ notif.notify(command.cmd .. " " .. table.concat(command.args, " "))
local job = Job:new({
command = command.cmd,
args = command.args,
env = env,
+ on_stdout = vim.schedule_wrap(function(err, data) end),
on_exit = vim.schedule_wrap(function(_, code, signal)
- if code == 0 and signal == 0 and command.after_success then
- command.after_success()
+ if code == 0 and signal == 0 then
+ if command.after_success then
+ command.after_success()
+ end
else
- vim.notify(
- "Code " .. tostring(code) .. ": " .. command.cmd .. " " .. table.concat(command.args, " "),
+ notif.notify(
+ "Code "
+ .. code
+ .. " Signal "
+ .. signal
+ .. ": "
+ .. command.cmd
+ .. " "
+ .. table.concat(command.args, " "),
vim.log.levels.ERROR
)
end
diff --git a/lua/cmake-explorer/telescope/make_entry.lua b/lua/cmake-explorer/telescope/make_entry.lua
new file mode 100644
index 0000000..cc919bd
--- /dev/null
+++ b/lua/cmake-explorer/telescope/make_entry.lua
@@ -0,0 +1,57 @@
+local make_entry = require("telescope.make_entry")
+local entry_display = require("telescope.pickers.entry_display")
+local config = require("cmake-explorer.config")
+
+local M = {}
+
+M.gen_from_configure = function(opts)
+ local project = require("cmake-explorer").project
+ local displayer = entry_display.create({
+ separator = " ",
+ items = {
+ { width = project.display.short_len + 5 },
+ { remaining = true },
+ },
+ })
+ local make_display = function(entry)
+ vim.print(entry)
+ return displayer({
+ { entry.value.display.short, "TelescopeResultsIdentifier" },
+ { entry.value.display.long, "TelescopeResultsComment" },
+ })
+ end
+ return function(entry)
+ return make_entry.set_default_entry_mt({
+ value = entry,
+ ordinal = table.concat(entry.short, config.variants_display.short_sep),
+ display = make_display,
+ }, opts)
+ end
+end
+
+M.gen_from_build = function(opts)
+ local project = require("cmake-explorer").project
+ local displayer = entry_display.create({
+ separator = " ",
+ items = {
+ { width = project.display.short_len + 5 },
+ { remaining = true },
+ },
+ })
+ local make_display = function(entry)
+ vim.print(entry)
+ return displayer({
+ { entry.value.display.short, "TelescopeResultsIdentifier" },
+ { entry.value.display.long, "TelescopeResultsComment" },
+ })
+ end
+ return function(entry)
+ return make_entry.set_default_entry_mt({
+ value = entry,
+ ordinal = table.concat(entry.short, config.variants_display.short_sep),
+ display = make_display,
+ }, opts)
+ end
+end
+
+return M
diff --git a/lua/cmake-explorer/telescope/pickers.lua b/lua/cmake-explorer/telescope/pickers.lua
new file mode 100644
index 0000000..b2c7f56
--- /dev/null
+++ b/lua/cmake-explorer/telescope/pickers.lua
@@ -0,0 +1,103 @@
+local pickers = require("telescope.pickers")
+local finders = require("telescope.finders")
+local conf = require("telescope.config").values
+local actions = require("telescope.actions")
+local action_state = require("telescope.actions.state")
+local cmake_make_entry = require("cmake-explorer.telescope.make_entry")
+local notif = require("cmake-explorer.notification")
+local previewers = require("cmake-explorer.telescope.previewers")
+
+local M = {}
+
+M.build_dirs = function(opts)
+ local cmake = require("cmake-explorer")
+ pickers
+ .new(opts, {
+ prompt_title = "CMake Builds",
+ finder = finders.new_table({
+ results = cmake.project.fileapis,
+ -- entry_maker = cmake_make_entry.gen_from_fileapi(opts),
+ entry_maker = function(entry)
+ return {
+ value = entry,
+ display = entry.path,
+ ordinal = entry.path,
+ }
+ end,
+ sorter = conf.generic_sorter(opts),
+ -- attach_mappings = function(prompt_bufnr, map)
+ -- actions.select_default:replace(function() end)
+ -- return true
+ -- end,
+ }),
+ })
+ :find()
+end
+
+M.configure = function(opts)
+ local cmake = require("cmake-explorer")
+ local runner = require("cmake-explorer.runner")
+ opts.layout_strategy = "vertical"
+ opts.layout_config = {
+ prompt_position = "top",
+ preview_cutoff = 0,
+ preview_height = 5,
+ mirror = true,
+ }
+ pickers
+ .new(opts, {
+ default_selection_index = cmake.project:current_configure_index(),
+ prompt_title = "CMake Configure Options",
+ finder = finders.new_table({
+ results = cmake.project:list_configs(),
+ entry_maker = cmake_make_entry.gen_from_configure(opts),
+ }),
+ sorter = conf.generic_sorter(opts),
+ previewer = previewers.configure_previewer(),
+ attach_mappings = function(prompt_bufnr, map)
+ actions.select_default:replace(function()
+ actions.close(prompt_bufnr)
+ local selection = action_state.get_selected_entry()
+ cmake.project.current_config = selection.value
+ runner.start(selection.value.configure_command)
+ end)
+ return true
+ end,
+ })
+ :find()
+end
+
+M.build = function(opts)
+ local cmake = require("cmake-explorer")
+ local runner = require("cmake-explorer.runner")
+ opts.layout_strategy = "vertical"
+ opts.layout_config = {
+ prompt_position = "top",
+ preview_cutoff = 0,
+ preview_height = 5,
+ mirror = true,
+ }
+ pickers
+ .new(opts, {
+ default_selection_index = cmake.project:current_build_index(),
+ prompt_title = "CMake Build Options",
+ finder = finders.new_table({
+ results = cmake.project:list_builds(),
+ entry_maker = cmake_make_entry.gen_from_configure(opts),
+ }),
+ sorter = conf.generic_sorter(opts),
+ previewer = previewers.build_previewer(),
+ attach_mappings = function(prompt_bufnr, map)
+ actions.select_default:replace(function()
+ actions.close(prompt_bufnr)
+ local selection = action_state.get_selected_entry()
+ cmake.project.current_config = selection.value
+ runner.start(selection.value.build_command)
+ end)
+ return true
+ end,
+ })
+ :find()
+end
+
+return M
diff --git a/lua/cmake-explorer/telescope/previewers.lua b/lua/cmake-explorer/telescope/previewers.lua
new file mode 100644
index 0000000..39fea4a
--- /dev/null
+++ b/lua/cmake-explorer/telescope/previewers.lua
@@ -0,0 +1,40 @@
+local previewers = require("telescope.previewers")
+local config = require("cmake-explorer.config")
+
+local M = {}
+
+M.configure_previewer = function(opts)
+ return previewers.new_buffer_previewer({
+ title = "Configure Details",
+
+ define_preview = function(self, entry)
+ if self.state.bufname then
+ return
+ end
+ local entries = {
+ "Command:",
+ config.cmake_path .. " " .. table.concat(entry.value.configure_args, " "),
+ }
+ vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, entries)
+ end,
+ })
+end
+
+M.build_previewer = function(opts)
+ return previewers.new_buffer_previewer({
+ title = "Build Details",
+
+ define_preview = function(self, entry)
+ if self.state.bufname then
+ return
+ end
+ local entries = {
+ "Command:",
+ config.cmake_path .. " " .. table.concat(entry.value.build_args, " "),
+ }
+ vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, entries)
+ end,
+ })
+end
+
+return M
diff --git a/lua/cmake-explorer/telescope/test.lua b/lua/cmake-explorer/telescope/test.lua
new file mode 100644
index 0000000..7b8bb00
--- /dev/null
+++ b/lua/cmake-explorer/telescope/test.lua
@@ -0,0 +1,46 @@
+local pickers = require("telescope.pickers")
+local finders = require("telescope.finders")
+local conf = require("telescope.config").values
+local actions = require("telescope.actions")
+local action_state = require("telescope.actions.state")
+
+-- our picker function: colors
+local colors = function(opts)
+ opts = opts or {}
+ pickers
+ .new(opts, {
+ prompt_title = "colors",
+ finder = finders.new_table({
+ results = {
+ { "red", "#ff0000" },
+ { "green", "#00ff00" },
+ { "blue", "#0000ff" },
+ },
+ entry_maker = function(entry)
+ return {
+ value = entry,
+ display = entry[1],
+ ordinal = entry[1],
+ }
+ end,
+ }),
+ sorter = conf.generic_sorter(opts),
+ attach_mappings = function(prompt_bufnr, map)
+ map({ "i", "n" }, "<C-r>", function(_prompt_bufnr)
+ print("You typed <C-r>")
+ end)
+
+ actions.select_default:replace(function()
+ actions.close(prompt_bufnr)
+ local selection = action_state.get_selected_entry()
+ -- print(vim.inspect(selection))
+ vim.api.nvim_put({ selection[1] }, "", false, true)
+ end)
+ return true
+ end,
+ })
+ :find()
+end
+
+-- to execute the function
+colors()
diff --git a/lua/cmake-explorer/utils.lua b/lua/cmake-explorer/utils.lua
index e944642..61b5c98 100644
--- a/lua/cmake-explorer/utils.lua
+++ b/lua/cmake-explorer/utils.lua
@@ -2,75 +2,40 @@ local config = require("cmake-explorer.config")
local capabilities = require("cmake-explorer.capabilities")
local Path = require("plenary.path")
-local utils = {
- plugin_prefix = "CM",
-}
+local utils = {}
-utils.build_dir_name = function(params)
- if capabilities.is_multiconfig_generator(params.generator) then
- return config.build_dir_template[1]
- else
- local paths = {}
- for k, v in ipairs(config.build_dir_template) do
- local path = v:gsub("${buildType}", params.build_type)
- if k ~= 1 and config.build_dir_template.case then
- if config.build_dir_template.case == "lower" then
- path = string.lower(path)
- elseif config.build_dir_template.case == "upper" then
- path = string.upper(path)
- end
- end
- table.insert(paths, path)
- end
- return table.concat(paths, config.build_dir_template.sep)
- end
-end
-
-utils.build_path = function(params, source_dir)
- if type(params) == "string" then
- return params
- end
+utils.build_path = function(build_dir, source_dir)
local build_path = Path:new(config.build_dir)
if build_path:is_absolute() then
- return (build_path / utils.build_dir_name(params)):absolute()
+ return (build_path / build_dir):absolute()
else
- return Path:new(source_dir, build_path, utils.build_dir_name(params)):absolute()
+ return Path:new(build_path, build_dir):normalize()
end
end
-utils.generate_args = function(params, source_dir)
- local ret = {}
-
- if type(params) == "string" then
- table.insert(ret, "-B" .. Path:new(params):make_relative(source_dir))
- else
- if params.preset then
- table.insert(ret, "--preset " .. params.preset)
- else
- if params.generator and vim.tbl_contains(capabilities.generators(), params.generator) then
- table.insert(ret, "-G" .. params.generator)
- end
-
- params.build_type = params.build_type or config.build_types[1]
- if params.build_type then
- table.insert(ret, "-DCMAKE_BUILD_TYPE=" .. params.build_type)
- end
-
- table.insert(ret, "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON")
-
- if type(params.args) == "table" then
- for k, v in pairs(params.args) do
- table.insert(ret, "-D" .. k .. "=" .. v)
- end
- elseif type(params.args) == "string" then
- table.insert(ret, params.args)
- end
- table.insert(ret, "-B" .. Path:new(utils.build_path(params, source_dir)):make_relative(source_dir))
- end
+utils.substitude = function(str, subs)
+ local ret = str
+ for k, v in pairs(subs) do
+ ret = ret:gsub(k, v)
end
return ret
end
+function utils.symlink_compile_commands(src_path, dst_path)
+ local src = Path:new(src_path, "compile_commands.json")
+ if src:exists() then
+ vim.cmd(
+ 'silent exec "!'
+ .. config.cmake_path
+ .. " -E create_symlink "
+ .. src:normalize()
+ .. " "
+ .. Path:new(dst_path, "compile_commands.json"):normalize()
+ .. '"'
+ )
+ end
+end
+
utils.is_eq = function(val, cmp, if_eq, if_not_eq)
if val == cmp then
if if_eq then
@@ -103,4 +68,25 @@ utils.is_neq = function(val, cmp, if_eq, if_not_eq)
end
end
+utils.make_maplike_list = function(proj)
+ local mt = {}
+ mt.__index = function(t, k)
+ for _, value in ipairs(t) do
+ if proj(value) == k then
+ return value
+ end
+ end
+ end
+ mt.__newindex = function(t, k, v)
+ for key, value in ipairs(t) do
+ if proj(value) == k then
+ rawset(t, key, v)
+ return
+ end
+ end
+ rawset(t, #t + 1, v)
+ end
+ return mt
+end
+
return utils
diff --git a/lua/telescope/_extensions/cmake-explorer.lua b/lua/telescope/_extensions/cmake-explorer.lua
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lua/telescope/_extensions/cmake-explorer.lua
diff --git a/plugin/cmake-explorer.lua b/plugin/cmake-explorer.lua
new file mode 100644
index 0000000..8413923
--- /dev/null
+++ b/plugin/cmake-explorer.lua
@@ -0,0 +1,33 @@
+local cmd = vim.api.nvim_create_user_command
+
+cmd("CMakeSelectBehaviour", function()
+ require("cmake-explorer").change_current_behaviour()
+end, { desc = "Configure one of existings directories" })
+
+cmd("CMakeSelectConfig", function()
+ require("cmake-explorer").change_current_config()
+end, { desc = "Configure with parameters" })
+
+cmd("CMakeConfigure", function()
+ require("cmake-explorer").configure()
+end, { desc = "Configure with parameters" })
+
+cmd("CMakeConfigureLast", function()
+ require("cmake-explorer").configure_last()
+end, { desc = "Configure last if exists. Otherwise default" })
+
+cmd("CMakeBuild", function()
+ require("cmake-explorer").build()
+end, { desc = "Configure one of existings directories" })
+
+cmd("CMakeBuildLast", function()
+ require("cmake-explorer").build_last()
+end, { desc = "Configure one of existings directories" })
+
+cmd("CMakeConfigureProject", function()
+ require("cmake-explorer").configure_project()
+end, { desc = "Configure one of existings directories" })
+
+cmd("CMakeInitProject", function()
+ require("cmake-explorer").init_project()
+end, { desc = "Configure one of existings directories" })