Implement :BuildDir and :Build commands in Lua

Ported over from and improved for best integration with Neovim ecosystem
https://code.infektor.net/config/vim/src/branch/master/autoload/build.vim
This commit is contained in:
Kenneth Benzie 2024-03-08 18:46:58 +00:00
parent 573c548245
commit 0656de97fa
5 changed files with 172 additions and 0 deletions

1
after/ftplugin/c.lua Normal file
View File

@ -0,0 +1 @@
require('build').create_commands()

View File

@ -1 +1,3 @@
vim.bo.commentstring = '//%s'
require('build').create_commands()

View File

@ -2,6 +2,7 @@ require('settings')
require('mappings')
require('netrw')
require('autocmds')
require('build')
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then

161
lua/build.lua Normal file
View File

@ -0,0 +1,161 @@
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
if vim.fn.filereadable(build_dir .. '/compile_commands.json') == 0 then
echo('compile_commands.json not found in: ' .. build_dir, 'Error')
return
end
build.dir = build_dir
-- Invoke async function to avoid blocking the UI
vim.schedule(function()
echo('Processing compile_commands.json with compdb ...', 'DiagnosticInfo')
-- Post-process compile_commands.json with compdb
local compile_commands = current_dir .. '/compile_commands.json'
local output = vim.fn.systemlist(
'compdb -p ' .. build.dir .. ' list > ' .. compile_commands)
if table.maxn(output) > 0 then
echo(vim.fn.join(output, '\n'), 'Error')
return
end
-- TODO: Configure cmake language server?
-- Restart clangd language server
vim.cmd [[ LspRestart clangd ]]
echo('Build directory selected: ' .. dirname, 'DiagnosticInfo')
end)
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
function build.create_commands()
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,
})
end
return build

View File

@ -4,6 +4,7 @@ return {
'nvim-lua/plenary.nvim',
'nvim-telescope/telescope-fzy-native.nvim',
'nvim-tree/nvim-web-devicons',
'axkirillov/easypick.nvim',
},
config = function()
local telescope = require('telescope')
@ -21,6 +22,9 @@ return {
['<C-l>'] = 'preview_scrolling_right',
}
},
layout_config = {
height = 0.7,
}
},
})
telescope.load_extension('fzy_native')
@ -33,5 +37,8 @@ return {
vim.keymap.set('n', '<leader>b', ':Telescope buffers<CR>', opts)
vim.keymap.set('n', '<leader>ht', ':Telescope help_tags<CR>', opts)
require('easypick').setup({
pickers = { },
})
end
}