171 lines
5.0 KiB
Lua
171 lines
5.0 KiB
Lua
local build = {}
|
|
|
|
build.dir = nil
|
|
|
|
local function echo(message, highlight)
|
|
vim.api.nvim_echo({ { message, highlight }, }, true, {})
|
|
end
|
|
|
|
function build.set_dir(dirname)
|
|
if not dirname then
|
|
build.select_dir()
|
|
return
|
|
end
|
|
local current_dir = vim.fn.getcwd()
|
|
local build_dir = current_dir .. '/' .. dirname
|
|
-- print(build_dir)
|
|
|
|
if vim.fn.isdirectory(build_dir) == 0 then
|
|
echo('directory does not exist: ' .. build_dir, 'Error')
|
|
return
|
|
end
|
|
|
|
build.dir = build_dir
|
|
|
|
echo('Processing compile_commands.json with compdb ...', 'DiagnosticInfo')
|
|
local compile_commands = current_dir .. '/compile_commands.json'
|
|
-- Post-process compile_commands.json with compdb as an async job to avoid
|
|
-- blocking the user interface
|
|
vim.fn.jobstart(
|
|
'compdb -p ' .. build.dir .. ' list > ' .. compile_commands, {
|
|
|
|
-- Restart clangd language server
|
|
-- TODO: Configure cmake language server?
|
|
on_exit = function()
|
|
vim.cmd [[ LspRestart clangd ]]
|
|
echo('Build directory selected: ' .. dirname, 'DiagnosticInfo')
|
|
end,
|
|
|
|
-- Display any error messages to the user
|
|
on_stderr = function(_, output, _)
|
|
-- Remove any lines containing a compdb warning
|
|
local error = {}
|
|
local warning = 'WARNING:compdb'
|
|
for _, line in ipairs(output) do
|
|
if string.sub(line, 1, #warning) ~= warning then
|
|
table.insert(error, line)
|
|
end
|
|
end
|
|
|
|
-- Display the error message if there was one
|
|
if table.maxn(error) > 0 then
|
|
echo(vim.fn.join(error, '\n'), 'Error')
|
|
return
|
|
end
|
|
end,
|
|
|
|
stderr_buffered = true,
|
|
}
|
|
)
|
|
end
|
|
|
|
function build.select_dir(callback)
|
|
local dirs = build.list_dirs()
|
|
callback = callback or nil
|
|
|
|
if table.maxn(dirs) == 1 then
|
|
build.set_dir(dirs[1])
|
|
return
|
|
end
|
|
|
|
if vim.fn.exists(':Telescope') > 0 then
|
|
-- Create a Telescope picker with custom action.
|
|
require('telescope.pickers').new({}, {
|
|
prompt_title = 'Select build directory',
|
|
finder = require('telescope.finders').new_table({ results = dirs }),
|
|
sorter = require("telescope.config").values.generic_sorter({}),
|
|
attach_mappings = function(bufnr, map)
|
|
-- Override the default confirm action
|
|
map('i', '<CR>', function()
|
|
-- Get the select build directory
|
|
local state = require('telescope.actions.state')
|
|
build.set_dir(state.get_selected_entry().value)
|
|
-- Close the Telescope floating window
|
|
require('telescope.actions').close(bufnr)
|
|
-- Invoke the callback if provided
|
|
if callback then callback() end
|
|
end)
|
|
return true
|
|
end,
|
|
layout_config = {
|
|
height = function(_, _, max_lines)
|
|
-- Limit the height based on the number of dirs
|
|
return math.min(max_lines, table.maxn(dirs) + 5)
|
|
end
|
|
}
|
|
}):find()
|
|
else
|
|
-- Prompt user to choose dir with to inputlist
|
|
local choices = {}
|
|
for index, choice in ipairs(dirs) do
|
|
table.insert(choices, tostring(index) .. ': ' .. choice)
|
|
end
|
|
local index = vim.fn.inputlist(choices)
|
|
build.dir = dirs[index]
|
|
if callback then
|
|
callback()
|
|
end
|
|
end
|
|
end
|
|
|
|
function build.run(args)
|
|
-- Check if build dir set, if not prompt user to select one
|
|
if not build.dir then
|
|
build.select_dir(function() build.run(args) end)
|
|
return
|
|
end
|
|
local command = nil
|
|
if vim.fn.filereadable(build.dir .. '/build.ninja') then
|
|
command = 'ninja'
|
|
elseif vim.fn.filereadable(build.dir .. '/Makefile') then
|
|
command = 'make'
|
|
else
|
|
echo('could not find build.ninja or Makefile in: ' .. build.dir)
|
|
return
|
|
end
|
|
command = command .. ' -C ' .. build.dir .. ' ' .. vim.fn.join(args, ' ')
|
|
-- TODO: Figure out how to make the terminal window close on success
|
|
-- TODO: Running the terminal in a togglable floating window might be nice
|
|
vim.fn.execute('split | terminal ' .. command)
|
|
end
|
|
|
|
function build.list_dirs()
|
|
local dirs = vim.fn.globpath('.', 'build*')
|
|
dirs = vim.fn.substitute(dirs, '\\.\\/', '', 'g')
|
|
return vim.fn.split(dirs)
|
|
end
|
|
|
|
function build.list_targets()
|
|
local targets = {}
|
|
if not build.dir then
|
|
return targets
|
|
end
|
|
if vim.fn.filereadable(build.dir .. '/build.ninja') then
|
|
-- Query ninja for the list of targets
|
|
targets = vim.fn.systemlist('ninja -C ' .. build.dir .. ' -t targets')
|
|
for index, target in ipairs(targets) do
|
|
targets[index] = string.sub(target, 1, string.find(target, ':') - 1)
|
|
end
|
|
end
|
|
-- TODO: Support make, it's much less straight forwards than ninja.
|
|
return targets
|
|
end
|
|
|
|
local buffer = vim.api.nvim_get_current_buf()
|
|
|
|
-- Create :BuildDir command
|
|
vim.api.nvim_buf_create_user_command(buffer, 'BuildDir', function(opts)
|
|
build.set_dir(opts.fargs[1])
|
|
end, {
|
|
bang = true, nargs = '?', complete = build.list_dirs,
|
|
})
|
|
|
|
-- Create :Build command
|
|
vim.api.nvim_buf_create_user_command(buffer, 'Build', function(opts)
|
|
build.run(opts.fargs)
|
|
end, {
|
|
bang = true, nargs = '*', complete = build.list_targets,
|
|
})
|
|
|
|
return build
|