aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lua/cmake/actions.lua198
-rw-r--r--lua/cmake/autocmds.lua25
-rw-r--r--lua/cmake/capabilities.lua27
-rw-r--r--lua/cmake/commandline.lua148
-rw-r--r--lua/cmake/commands.lua109
-rw-r--r--lua/cmake/config.lua23
-rw-r--r--lua/cmake/fileapi.lua9
-rw-r--r--lua/cmake/init.lua4
-rw-r--r--lua/cmake/project.lua20
-rw-r--r--lua/cmake/terminal.lua131
-rw-r--r--lua/cmake/utils.lua39
-rw-r--r--lua/cmake/variants.lua2
12 files changed, 535 insertions, 200 deletions
diff --git a/lua/cmake/actions.lua b/lua/cmake/actions.lua
index 8c5a300..972e07e 100644
--- a/lua/cmake/actions.lua
+++ b/lua/cmake/actions.lua
@@ -5,13 +5,13 @@ local utils = require("cmake.utils")
local constants = require("cmake.constants")
local Path = require("plenary.path")
-local uv = vim.uv or vim.loop
+local uv = vim.uv
local M = {}
local default_generate_exe_opts = {
notify = {
- ok_message = "CMake build finished",
+ ok_message = "CMake generate finished",
err_message = function(code)
return "CMake generate failed with code " .. tostring(code)
end,
@@ -27,57 +27,161 @@ local default_build_exe_opts = {
},
}
-M.reset_project = function(opts)
- require("cmake.project").setup(opts)
+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
-M.generate = function(opts)
+--- 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.tbl_deep_extend("keep", 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.tbl_deep_extend("keep", 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
+ 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(main_path)
+ else
+ main_path()
+ end
+end
+
+local _for_current_generate_option = function(func)
local idx = pr.current_generate_option_idx()
if not idx then
- vim.notify("CMake: no project to generate")
- return
+ vim.notify("CMake: no configuration to generate", vim.log.levels.WARN)
+ else
+ func(pr.current_generate_option())
end
- pr.create_fileapi_query({ idx = idx }, function()
- vim.schedule(function()
- t.cmake_execute(pr.current_generate_option().generate_command, default_generate_exe_opts)
- 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(choice, idx)
+ }, function(_, idx)
if not idx then
return
end
pr.set_current_generate_option(idx)
- pr.create_fileapi_query({ idx = idx }, function()
- vim.schedule(function()
- t.cmake_execute(choice.generate_command, default_generate_exe_opts)
- end)
- end)
end)
end
-M.build = function(opts)
- if not pr.current_build_option_idx() then
- M.build_select(opts)
+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
- pr.create_fileapi_query({ idx = pr.current_build_option_idx() }, function()
- vim.schedule(function()
- t.cmake_execute(pr.current_build_option().command, default_build_exe_opts)
- end)
- end)
+ 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 number|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(opts).build_options
+ local items = pr.current_generate_option().build_options
vim.ui.select(items, {
prompt = "Select build option to generate:",
format_item = function(item)
@@ -88,28 +192,34 @@ M.build_select = function(opts)
return
end
pr.set_current_build_option(idx)
- pr.create_fileapi_query({ idx = idx }, function()
- vim.schedule(function()
- t.cmake_execute(choice.command, default_build_exe_opts)
- end)
- end)
end)
end
-M.run_tagret = function(opts)
+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_tagret_select(opts)
+ M.run_target_select(opts)
else
- local command = {
- cmd = Path:new(pr.current_directory(), _curr_exe_cmd.path):make_relative(uv.cwd()),
- }
- t.target_execute(command)
+ _run_target({ path = _curr_exe_cmd.path })
end
end
-M.run_tagret_select = function(opts)
+--- Select target to run
+M.run_target_select = function(opts)
opts = opts or {}
opts.type = "EXECUTABLE"
local items = pr.current_targets(opts)
@@ -118,22 +228,20 @@ M.run_tagret_select = function(opts)
format_item = function(item)
return item.name
end,
- }, function(choice, idx)
+ }, function(_, idx)
if not idx then
return
end
pr.set_current_executable_target(idx)
- local command = {
- cmd = Path:new(pr.current_directory(), choice.path):make_relative(uv.cwd()),
- }
- t.target_execute(command)
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
@@ -151,4 +259,8 @@ M.edit_variants = function()
end)
end
+M.reset_project = function(opts)
+ require("cmake.project").setup(opts)
+end
+
return M
diff --git a/lua/cmake/autocmds.lua b/lua/cmake/autocmds.lua
index deb193d..5bbfbe8 100644
--- a/lua/cmake/autocmds.lua
+++ b/lua/cmake/autocmds.lua
@@ -32,17 +32,20 @@ function autocmds.setup()
--without CMakeLists.txt neovim starts like `nvim CMakeLists.txt`. In this case initial
--setup will not make the affect and to correctry process the file save, we need to create
--this autocommand so it reinitializes the project if it has not been done before. IMHO this
- --is not the best way to do this
- if config.generate_after_save then
- vim.api.nvim_create_autocmd({ "BufEnter" }, {
- group = cmake_nvim_augroup,
- pattern = constants.cmakelists,
- callback = function(args)
- actions.reset_project({ first_time_only = true })
- end,
- desc = "Set up project on open CMakeLists.txt if not set before",
- })
- end
+ --is not the best way to do this. Also, if newly buffer associated with CMakeLists.txt will not
+ --be saved and just closed, but user will continue to use nvim, CMake commands still will be
+ --able while it sholdn't. Two options is give up or handle all this corner cases
+ --
+ -- if config.generate_after_save then
+ -- vim.api.nvim_create_autocmd({ "BufEnter" }, {
+ -- group = cmake_nvim_augroup,
+ -- pattern = constants.cmakelists,
+ -- callback = function(args)
+ -- actions.reset_project({ first_time_only = true })
+ -- end,
+ -- desc = "Set up project on open CMakeLists.txt if not set before",
+ -- })
+ -- end
end
return autocmds
diff --git a/lua/cmake/capabilities.lua b/lua/cmake/capabilities.lua
index 36bc4b1..0b76fc0 100644
--- a/lua/cmake/capabilities.lua
+++ b/lua/cmake/capabilities.lua
@@ -38,27 +38,18 @@ function Capabilities.has_fileapi()
return vim.tbl_get(Capabilities.json, "fileApi") ~= nil
end
--- TODO: make this async
Capabilities.setup = function(callback)
- local lines = {}
vim.schedule(function()
- vim.fn.jobstart({ config.cmake.cmake_path, "-E", "capabilities" }, {
- on_stdout = function(_, data)
- if data then
- vim.list_extend(lines, data)
+ vim.system({ config.cmake.cmake_path, "-E", "capabilities" }, { text = true }, function(obj)
+ if obj.code == 0 then
+ Capabilities.json = vim.json.decode(obj.stdout)
+ if type(callback) == "function" then
+ callback()
end
- end,
- on_exit = function(_, code, _)
- if code == 0 then
- Capabilities.json = vim.json.decode(table.concat(lines, ""))
- if type(callback) == "function" then
- callback()
- end
- else
- vim.notify("error " .. tostring(code) .. ". 'cmake -E capabilities'", vim.log.levels.ERROR)
- end
- end,
- })
+ else
+ vim.notify("error " .. tostring(obj.code) .. ". 'cmake -E capabilities'", vim.log.levels.ERROR)
+ end
+ end)
end)
end
diff --git a/lua/cmake/commandline.lua b/lua/cmake/commandline.lua
new file mode 100644
index 0000000..55c671c
--- /dev/null
+++ b/lua/cmake/commandline.lua
@@ -0,0 +1,148 @@
+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,
+ targets = 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 == "targets" 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
diff --git a/lua/cmake/commands.lua b/lua/cmake/commands.lua
index c2f6e75..d763958 100644
--- a/lua/cmake/commands.lua
+++ b/lua/cmake/commands.lua
@@ -1,39 +1,88 @@
+local commandline = require("cmake.commandline")
+local actions = require("cmake.actions")
+
local M = {}
local cmd = vim.api.nvim_create_user_command
-M.register_commands = function()
- cmd("CMakeGenerate", function()
- require("cmake.actions").generate()
- end, { desc = "Generate with last configuration" })
-
- cmd("CMakeGenerateSelect", function()
- require("cmake.actions").generate_select()
- end, { desc = "Select configuration and generate" })
-
- cmd("CMakeBuild", function()
- require("cmake.actions").build()
- end, { desc = "Build with last build option" })
+local prefix = "CMake"
- cmd("CMakeBuildSelect", function()
- require("cmake.actions").build_select()
- end, { desc = "Select build option and build" })
+local commands = {
+ ["Generate"] = {
+ command = actions.generate,
+ parse = true,
+ default_opts = { fresh = false },
+ cmd_opts = {
+ desc = "Generate with last configuration",
+ nargs = "*",
+ complete = commandline.cmake_generate_complete,
+ },
+ },
+ ["GenerateExplain"] = {
+ command = actions.generate_explain,
+ parse = true,
+ default_opts = { fresh = false },
+ cmd_opts = {
+ desc = "Explain current generate command",
+ nargs = "*",
+ complete = commandline.cmake_generate_complete,
+ },
+ },
+ ["GenerateSelect"] = {
+ command = actions.generate_select,
+ cmd_opts = { desc = "Select generate configuration" },
+ },
+ ["Build"] = {
+ command = actions.build,
+ parse = true,
+ cmd_opts = {
+ desc = "Build with last configuration",
+ nargs = "*",
+ complete = commandline.cmake_build_complete,
+ },
+ },
+ ["BuildExplain"] = {
+ command = actions.build_explain,
+ parse = true,
+ cmd_opts = {
+ desc = "Explain current build command",
+ nargs = "*",
+ complete = commandline.cmake_build_complete,
+ },
+ },
+ ["BuildSelect"] = {
+ command = actions.build_select,
+ cmd_opts = { desc = "Select build configuration" },
+ },
+ -- ["Install"] = {},
+ ["Run"] = {
+ command = actions.run_target,
+ cmd_opts = { desc = "Run current executable target", nargs = "*" },
+ },
+ ["RunSelect"] = {
+ command = actions.run_target_select,
+ cmd_opts = { desc = "Select executable target" },
+ },
+ ["Toggle"] = {
+ command = actions.toggle,
+ cmd_opts = { desc = "Toggle cmake terminal" },
+ },
+ ["EditVariants"] = {
+ command = actions.edit_variants,
+ cmd_opts = { desc = "Edit variants" },
+ },
+}
- cmd("CMakeRun", function()
- require("cmake.actions").run_tagret()
- end, { desc = "Select build option and build" })
-
- cmd("CMakeRunSelect", function()
- require("cmake.actions").run_tagret_select()
- end, { desc = "Select build option and build" })
-
- cmd("CMakeToggle", function()
- require("cmake.actions").toggle()
- end, { desc = "Toggle terminal with cmake command" })
-
- cmd("CMakeEditVariants", function()
- require("cmake.actions").edit_variants()
- end, { desc = "Edit variants" })
+M.register_commands = function()
+ for k, v in pairs(commands) do
+ cmd(prefix .. k, function(opts)
+ local action_opts = v.default_opts or {}
+ if v.parse then
+ action_opts = vim.tbl_deep_extend("keep", commandline.parse(opts.args) or {}, action_opts)
+ end
+ v.command(action_opts)
+ end, v.cmd_opts)
+ end
end
return M
diff --git a/lua/cmake/config.lua b/lua/cmake/config.lua
index 9f7d04a..0cc95ca 100644
--- a/lua/cmake/config.lua
+++ b/lua/cmake/config.lua
@@ -22,25 +22,23 @@ local default_config = {
},
},
},
- parallel_jobs = nil, --#(vim.uv or vim.loop).cpu_info(),
- -- source_directory = "${workspaceFolder}", --TODO: not used
+ parallel_jobs = nil,
},
save_before_build = true,
generate_after_save = true,
- terminal = {
- direction = "horizontal",
- display_name = "CMake", --TODO: not used
+ cmake_terminal = {
+ split = "below",
+ size = 15,
close_on_exit = "success",
- hidden = false,
+ open_on_start = true,
clear_env = false,
- focus = false,
+ enter = false,
},
- runner_terminal = {
- direction = "horizontal",
- close_on_exit = false,
- hidden = false,
+ target_terminal = {
+ split = "below",
+ size = 15,
clear_env = false,
- focus = false,
+ enter = true,
},
notification = {
after = "success",
@@ -49,6 +47,7 @@ local default_config = {
short = { sep = " × ", show = true },
long = { sep = " ❄ ", show = false },
},
+ keybinds = {},
}
local M = vim.deepcopy(default_config)
diff --git a/lua/cmake/fileapi.lua b/lua/cmake/fileapi.lua
index d33d477..1aa7de6 100644
--- a/lua/cmake/fileapi.lua
+++ b/lua/cmake/fileapi.lua
@@ -2,7 +2,6 @@ local capabilities = require("cmake.capabilities")
local Path = require("plenary.path")
local scan = require("plenary.scandir")
local utils = require("cmake.utils")
-local uv = vim.uv or vim.loop
local query_path_suffix = { ".cmake", "api", "v1", "query", "client-cmake", "query.json" }
local reply_dir_suffix = { ".cmake", "api", "v1", "reply" }
@@ -15,7 +14,6 @@ function FileApi.create(path, callback)
if not exists then
if capabilities.json.fileApi then
vim.schedule(function()
- --TODO: change to async
vim.fn.mkdir(Path:new(vim.fs.dirname(query)):absolute(), "p")
utils.write_file(query, vim.json.encode(capabilities.json.fileApi), callback)
end)
@@ -35,6 +33,7 @@ function FileApi.read_reply(path, callback)
return
end
local ret = { targets = {} }
+ --TODO: replace with uv scandir
scan.scan_dir_async(reply_dir, {
search_pattern = "index*",
on_exit = function(results)
@@ -47,8 +46,10 @@ function FileApi.read_reply(path, callback)
if object.kind == "codemodel" then
utils.read_file(Path:new(reply_dir, object.jsonFile):absolute(), function(codemodel_data)
local codemodel = vim.json.decode(codemodel_data)
- --FIX: this loop does not read all files if codemodel contains many targets. This is because libuv (or some external settings) forbids to open files
- -- in async mode more than some limit number. Seems like the solution is to queue these calls and limit max number for opened files per time
+ --FIX: this loop does not read all files if codemodel contains many targets (is will crash actually).
+ --This is because libuv (or some external settings) forbids to open files
+ -- in async mode more than some limit number. Seems like the solution is
+ -- to queue these calls and limit max number for opened files per time
for _, target in ipairs(codemodel.configurations[1].targets) do
utils.read_file(
Path:new(reply_dir, target.jsonFile):absolute(),
diff --git a/lua/cmake/init.lua b/lua/cmake/init.lua
index adfa84e..be5308b 100644
--- a/lua/cmake/init.lua
+++ b/lua/cmake/init.lua
@@ -4,7 +4,7 @@ local autocmds = require("cmake.autocmds")
local utils = require("cmake.utils")
local constants = require("cmake.constants")
-local uv = vim.uv or vim.loop
+local uv = vim.uv
local M = {}
@@ -19,7 +19,7 @@ function M.setup(opts)
autocmds.set_on_variants()
commands.register_commands()
end)
- require("cmake.project").setup({ first_time_only = true })
+ require("cmake.project").setup()
else
end
end)
diff --git a/lua/cmake/project.lua b/lua/cmake/project.lua
index 8f7d97f..a5d6cdd 100644
--- a/lua/cmake/project.lua
+++ b/lua/cmake/project.lua
@@ -3,7 +3,7 @@ local VariantConfig = require("cmake.variants")
local FileApi = require("cmake.fileapi")
local utils = require("cmake.utils")
local constants = require("cmake.constants")
-local uv = vim.uv or vim.loop
+local uv = vim.uv
local Project = {}
@@ -23,7 +23,6 @@ end
local append_after_success_actions = function()
local read_reply = function(v, not_presented)
if (not_presented and not fileapis[v.directory]) or not not_presented then
- --TODO: replace to vim.fs.joinpath after nvim 0.10 release
utils.symlink(v.directory .. "/compile_commands.json", uv.cwd())
fileapis[v.directory] = { targets = {} }
FileApi.read_reply(v.directory, function(target)
@@ -60,9 +59,11 @@ local init_fileapis = function()
end
-- TODO: validate yaml and fallback to config's variants if not valid
+-- TODO: make variants order more stable. at least when reading from file
function Project.from_variants(variants)
+ local variants_copy = vim.deepcopy(variants)
local list_variants = {}
- for k, v in pairs(variants) do
+ for k, v in pairs(variants_copy) do
table.insert(list_variants, v)
list_variants[#list_variants]._name = k
end
@@ -81,6 +82,17 @@ function Project.from_variants(variants)
init_fileapis()
end
+--NOTE: Neovim craches on this code
+function Project.clear_cache(callback)
+ uv.fs_unlink(vim.fs.joinpath(Project.current_directory(), "CMakeCache.txt"), function(f_err)
+ assert(f_err, f_err)
+ uv.fs_unlink(vim.fs.joinpath(Project.current_directory(), "CMakeFiles"), function(d_err)
+ assert(d_err, d_err)
+ callback()
+ end)
+ end)
+end
+
function Project.generate_options(opts)
opts = opts or {}
return configs
@@ -192,6 +204,7 @@ function Project.create_fileapi_query(opts, callback)
end
local do_setup = function(opts)
+ reset_internals()
local variants_path = vim.fs.joinpath(uv.cwd(), constants.variants_yaml_filename)
utils.file_exists(variants_path, function(variants_exists)
if variants_exists then
@@ -210,7 +223,6 @@ function Project.setup(opts)
if opts.first_time_only and initialised then
return
end
- reset_internals()
if not initialised then
require("cmake.capabilities").setup(function()
do_setup(opts)
diff --git a/lua/cmake/terminal.lua b/lua/cmake/terminal.lua
index 358d731..d1d8a75 100644
--- a/lua/cmake/terminal.lua
+++ b/lua/cmake/terminal.lua
@@ -1,32 +1,39 @@
-local Terminal = require("toggleterm.terminal").Terminal
-local ui = require("toggleterm.ui")
local config = require("cmake.config")
+local api = vim.api
+
local M = {}
-local cmake
+local cmake = {
+ bufnr = nil,
+ window = nil,
+ jobid = nil,
+}
+
local runnable
---TODO: cmake must be an id, not terminal
+local prepare_cmake_buf = function()
+ if cmake.bufnr and api.nvim_buf_is_valid(cmake.bufnr) then
+ api.nvim_buf_delete(cmake.bufnr, { force = true })
+ end
+ cmake.bufnr = api.nvim_create_buf(false, false)
+end
-M.cmake_execute = function(command, opts)
- opts = opts or {}
- if cmake then
- cmake:shutdown()
- cmake = nil
+local termopen = function(command, opts)
+ -- For some reason termopen() doesn't like an empty env table
+ if command.env and vim.tbl_isempty(command.env) then
+ command.env = nil
end
- local term_opts = {
- direction = config.terminal.direction,
- display_name = config.terminal.display_name,
- hidden = config.terminal.hidden,
- clear_env = config.terminal.clear_env,
- cmd = command.cmd .. " " .. command.args,
- -- env = command.env,
- on_exit = function(t, pid, code, name)
+ vim.fn.termopen(command.cmd .. " " .. command.args, {
+ -- detach = 1,
+ cwd = command.cwd,
+ env = command.env,
+ clear_env = config.clear_env,
+ on_exit = function(pid, code, event)
if code == 0 then
command.after_success()
- if config.terminal.close_on_exit == "success" then
- t:close()
+ if config.cmake_terminal.close_on_exit == "success" or config.cmake_terminal.close_on_exit == true then
+ api.nvim_win_close(cmake.window, true)
end
if config.notification.after == "success" or config.notification.after == true then
vim.notify(
@@ -34,56 +41,68 @@ M.cmake_execute = function(command, opts)
vim.log.levels.INFO
)
end
- elseif config.notification.after == "failure" or config.notification.after == true then
- local msg = "CMake failed. Code " .. tostring(code)
- local opt_msg = vim.tbl_get(opts, "notify", "err_message")
- if type(opt_msg) == "string" then
- msg = opt_msg
- elseif type(opt_msg) == "function" then
- msg = opt_msg(code)
+ else
+ if config.notification.after == "failure" or config.notification.after == true then
+ local msg = "CMake failed. Code " .. tostring(code)
+ local opt_msg = vim.tbl_get(opts, "notify", "err_message")
+ if type(opt_msg) == "string" then
+ msg = opt_msg
+ elseif type(opt_msg) == "function" then
+ msg = opt_msg(code)
+ end
+ vim.notify(msg, vim.log.levels.ERROR)
end
- vim.notify(msg, vim.log.levels.ERROR)
end
end,
- on_open = function(t)
- t:set_mode("n")
- end,
- }
- term_opts.close_on_exit = type(config.terminal.close_on_exit) == "boolean" and config.terminal.close_on_exit
- or false
- cmake = Terminal:new(term_opts)
- cmake:open()
- if not config.terminal.focus and cmake:is_focused() then
- ui.goto_previous()
- ui.stopinsert()
+ })
+end
+
+local open_window = function()
+ if not cmake.bufnr then
+ vim.notify("No CMake buffer created yet", vim.log.levels.INFO)
+ return
end
+ cmake.window = api.nvim_open_win(cmake.bufnr, config.cmake_terminal.enter, {
+ win = 0,
+ split = config.cmake_terminal.split,
+ height = config.cmake_terminal.size,
+ width = config.cmake_terminal.size,
+ })
+end
+
+M.cmake_execute = function(command, opts)
+ opts = opts or {}
+
+ prepare_cmake_buf()
+ if config.cmake_terminal.open_on_start and not (cmake.window and api.nvim_win_is_valid(cmake.window)) then
+ open_window()
+ end
+ vim.api.nvim_buf_call(cmake.bufnr, function()
+ termopen(command, opts)
+ end)
end
M.cmake_toggle = function()
- if cmake then
- cmake:toggle()
+ if cmake.window and api.nvim_win_is_valid(cmake.window) then
+ api.nvim_win_close(cmake.window, true)
else
- vim.notify("No CMake terminal")
+ open_window()
end
end
M.target_execute = function(command, opts)
opts = opts or {}
- local term_opts = {
- direction = config.runner_terminal.direction,
- close_on_exit = config.runner_terminal.close_on_exit,
- hidden = config.runner_terminal.hidden,
- clear_env = config.clear_env,
- }
- if not runnable then
- runnable = Terminal:new(term_opts)
- end
- if not runnable:is_open() then
- runnable:open()
- end
- if command.cmd then
- runnable:send(command.cmd, not config.runner_terminal.focus)
- end
+ local bufnr = api.nvim_create_buf(true, false)
+ api.nvim_open_win(bufnr, config.target_terminal.enter, {
+ win = 0,
+ split = config.target_terminal.split,
+ height = config.target_terminal.size,
+ width = config.target_terminal.size,
+ })
+ api.nvim_buf_call(bufnr, function()
+ vim.cmd.terminal()
+ api.nvim_chan_send(vim.bo.channel, command.cwd .. "/" .. command.cmd)
+ end)
end
return M
diff --git a/lua/cmake/utils.lua b/lua/cmake/utils.lua
index b9bcdf8..0c16658 100644
--- a/lua/cmake/utils.lua
+++ b/lua/cmake/utils.lua
@@ -1,8 +1,5 @@
local config = require("cmake.config")
-local capabilities = require("cmake.capabilities")
-local scan = require("plenary.scandir")
-local Path = require("plenary.path")
-local uv = vim.uv or vim.loop
+local uv = vim.uv
local utils = {}
@@ -75,22 +72,26 @@ function utils.write_file(path, txt, callback)
end)
end
---TODO: async mkdir -p
-
function utils.symlink(src_path, dst_path, callback)
- --TODO: replace to vim.fs.joinpath after nvim 0.10 release
- 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
+ local src = vim.fs.joinpath(src_path, "compile_commands.json")
+ utils.file_exists(src, function()
+ uv.spawn(config.cmake.cmake_path, {
+ args = {
+ "-E",
+ "create_symlink",
+ src,
+ vim.fs.joinpath(dst_path, "compile_commands.json"),
+ },
+ }, function(code, signal)
+ if code ~= 0 then
+ vim.notify("CMake: error while creating symlink. Code " .. tostring(code), vim.log.levels.ERROR)
+ return
+ end
+ if type(callback) == "function" then
+ callback()
+ end
+ end)
+ end)
end
return utils
diff --git a/lua/cmake/variants.lua b/lua/cmake/variants.lua
index b68ff05..6a4c859 100644
--- a/lua/cmake/variants.lua
+++ b/lua/cmake/variants.lua
@@ -1,7 +1,7 @@
local config = require("cmake.config")
local utils = require("cmake.utils")
-local uv = vim.uv or vim.loop
+local uv = vim.uv
local VariantConfig = {}