feat: add preview of tasks to task plugin
This commit is contained in:
parent
d45f53b6ba
commit
b5f34cb9ed
|
|
@ -1,4 +1,5 @@
|
||||||
local M = {}
|
local M = {}
|
||||||
|
local preview_ns = vim.api.nvim_create_namespace 'TaskPreview'
|
||||||
|
|
||||||
M._win = nil
|
M._win = nil
|
||||||
M._buf = nil
|
M._buf = nil
|
||||||
|
|
@ -15,7 +16,7 @@ local defaults = {
|
||||||
auto = true,
|
auto = true,
|
||||||
},
|
},
|
||||||
keymaps = {
|
keymaps = {
|
||||||
rerun = '<leader>rt'
|
rerun = '<leader>rt',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,9 +33,9 @@ local function setup_global_keymaps()
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.setup(opts)
|
function M.setup(opts)
|
||||||
vim.validate({
|
vim.validate {
|
||||||
opts = { opts, 'table', true }
|
opts = { opts, 'table', true },
|
||||||
})
|
}
|
||||||
M._options = vim.tbl_deep_extend('force', {}, defaults, opts or {})
|
M._options = vim.tbl_deep_extend('force', {}, defaults, opts or {})
|
||||||
|
|
||||||
if M._options.keymaps ~= false then
|
if M._options.keymaps ~= false then
|
||||||
|
|
@ -42,33 +43,49 @@ function M.setup(opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function float_size()
|
local function calculate_dimensions(percent_width, percent_height)
|
||||||
local cfg = M._options.float
|
local width = math.floor(vim.o.columns * percent_width)
|
||||||
local width = math.floor(vim.o.columns * (cfg.width or 0.8))
|
local height = math.floor(vim.o.lines * percent_height)
|
||||||
local height = math.floor(vim.o.lines * (cfg.height or 0.8))
|
|
||||||
local row = math.floor((vim.o.lines - height) / 2)
|
local row = math.floor((vim.o.lines - height) / 2)
|
||||||
local col = math.floor((vim.o.columns - width) / 2)
|
local col = math.floor((vim.o.columns - width) / 2)
|
||||||
|
return width, height, row, col
|
||||||
|
end
|
||||||
|
|
||||||
|
local function float_size()
|
||||||
|
local cfg = M._options.float
|
||||||
|
local width, height, row, col = calculate_dimensions(cfg.width or 0.8, cfg.height or 0.8)
|
||||||
return width, height, row, col, cfg.border or 'rounded'
|
return width, height, row, col, cfg.border or 'rounded'
|
||||||
end
|
end
|
||||||
|
|
||||||
local function scroll_to_bottom()
|
local function scroll_to_bottom()
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
pcall(vim.cmd, 'normal! G')
|
vim.cmd 'normal! G'
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function open_floating_win(buf, opts, enter)
|
||||||
|
return vim.api.nvim_open_win(
|
||||||
|
buf,
|
||||||
|
enter or false,
|
||||||
|
vim.tbl_extend('force', {
|
||||||
|
relative = 'editor',
|
||||||
|
style = 'minimal',
|
||||||
|
border = 'single',
|
||||||
|
}, opts or {})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
local function create_terminal_window()
|
local function create_terminal_window()
|
||||||
local width, height, row, col, border = float_size()
|
local width, height, row, col, border = float_size()
|
||||||
local buf = vim.api.nvim_create_buf(false, true)
|
local buf = vim.api.nvim_create_buf(false, true)
|
||||||
local win = vim.api.nvim_open_win(buf, true, {
|
local win = open_floating_win(buf, {
|
||||||
relative = 'editor',
|
|
||||||
row = row,
|
row = row,
|
||||||
col = col,
|
col = col,
|
||||||
width = width,
|
width = width,
|
||||||
height = height,
|
height = height,
|
||||||
style = 'minimal',
|
|
||||||
border = border,
|
border = border,
|
||||||
})
|
}, true)
|
||||||
|
|
||||||
vim.api.nvim_set_current_buf(buf)
|
vim.api.nvim_set_current_buf(buf)
|
||||||
return buf, win
|
return buf, win
|
||||||
end
|
end
|
||||||
|
|
@ -82,6 +99,23 @@ local function cleanup_terminal()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function clean_dry_output(lines)
|
||||||
|
local cleaned = {}
|
||||||
|
for _, line in ipairs(lines) do
|
||||||
|
local cleaned_line = line:gsub('^task:%s+%[.-%]%s*', '')
|
||||||
|
table.insert(cleaned, cleaned_line)
|
||||||
|
end
|
||||||
|
return cleaned
|
||||||
|
end
|
||||||
|
|
||||||
|
local function highlight_line(buf, ns, line)
|
||||||
|
local content = vim.api.nvim_buf_get_lines(buf, line, line + 1, false)[1] or ''
|
||||||
|
vim.api.nvim_buf_set_extmark(buf, ns, line, 0, {
|
||||||
|
end_col = #content,
|
||||||
|
hl_group = 'Visual',
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
local function run_task_in_terminal(buf, task)
|
local function run_task_in_terminal(buf, task)
|
||||||
local opts = {}
|
local opts = {}
|
||||||
if M._options.scroll.auto then
|
if M._options.scroll.auto then
|
||||||
|
|
@ -111,7 +145,7 @@ function M.execute_task(task)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.get_tasks()
|
function M.get_tasks()
|
||||||
if vim.fn.executable('task') ~= 1 then
|
if vim.fn.executable 'task' ~= 1 then
|
||||||
vim.notify("'task' executable not found in PATH", vim.log.levels.ERROR)
|
vim.notify("'task' executable not found in PATH", vim.log.levels.ERROR)
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
|
|
@ -130,7 +164,7 @@ end
|
||||||
|
|
||||||
function M.on_choice(item)
|
function M.on_choice(item)
|
||||||
if not item or not item.name then
|
if not item or not item.name then
|
||||||
vim.notify('Invalid task selection', vim.log.levels.WARN)
|
vim.notify("Invalid task selection: no 'name' field", vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
M.execute_task(item.name)
|
M.execute_task(item.name)
|
||||||
|
|
@ -150,6 +184,99 @@ function M.open_window()
|
||||||
}, M.on_choice)
|
}, M.on_choice)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function M.select_task_with_preview()
|
||||||
|
local tasks = M.get_tasks()
|
||||||
|
if #tasks == 0 then
|
||||||
|
vim.notify('No tasks available', vim.log.levels.WARN)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local total_width, total_height, row, col = calculate_dimensions(0.8, 0.6)
|
||||||
|
local list_width = math.floor(total_width * 0.4)
|
||||||
|
local preview_width = total_width - list_width - 2
|
||||||
|
local list_buf = vim.api.nvim_create_buf(false, true)
|
||||||
|
local preview_buf = vim.api.nvim_create_buf(false, true)
|
||||||
|
|
||||||
|
local lines = {}
|
||||||
|
for _, task in ipairs(tasks) do
|
||||||
|
table.insert(lines, string.format('%-20s %s', task.name, task.desc or ''))
|
||||||
|
end
|
||||||
|
vim.api.nvim_buf_set_lines(list_buf, 0, -1, false, lines)
|
||||||
|
|
||||||
|
local list_win = open_floating_win(list_buf, {
|
||||||
|
row = row,
|
||||||
|
col = col,
|
||||||
|
width = list_width,
|
||||||
|
height = total_height,
|
||||||
|
title = 'Tasks',
|
||||||
|
title_pos = 'center',
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
local preview_win = open_floating_win(preview_buf, {
|
||||||
|
row = row,
|
||||||
|
col = col + list_width + 2,
|
||||||
|
width = preview_width,
|
||||||
|
height = total_height,
|
||||||
|
title = 'Preview',
|
||||||
|
title_pos = 'center',
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
local current_line = 1
|
||||||
|
local line = current_line - 1
|
||||||
|
highlight_line(list_buf, preview_ns, line)
|
||||||
|
|
||||||
|
local function update_preview(index)
|
||||||
|
local task = tasks[index]
|
||||||
|
if not task then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local output = vim.fn.system { 'task', task.name, '--dry' }
|
||||||
|
local cleaned_output = clean_dry_output(vim.split(output, '\n'))
|
||||||
|
vim.api.nvim_buf_set_lines(preview_buf, 0, -1, false, cleaned_output)
|
||||||
|
end
|
||||||
|
|
||||||
|
update_preview(current_line)
|
||||||
|
|
||||||
|
vim.keymap.set('n', '<CR>', function()
|
||||||
|
local task = tasks[current_line]
|
||||||
|
if task then
|
||||||
|
vim.api.nvim_win_close(list_win, true)
|
||||||
|
vim.api.nvim_win_close(preview_win, true)
|
||||||
|
M.execute_task(task.name)
|
||||||
|
end
|
||||||
|
end, { buffer = list_buf })
|
||||||
|
|
||||||
|
vim.keymap.set('n', 'q', function()
|
||||||
|
vim.api.nvim_win_close(list_win, true)
|
||||||
|
vim.api.nvim_win_close(preview_win, true)
|
||||||
|
end, { buffer = list_buf })
|
||||||
|
|
||||||
|
vim.keymap.set('n', '<Esc>', function()
|
||||||
|
vim.api.nvim_win_close(list_win, true)
|
||||||
|
vim.api.nvim_win_close(preview_win, true)
|
||||||
|
end, { buffer = list_buf })
|
||||||
|
|
||||||
|
vim.keymap.set('n', 'j', function()
|
||||||
|
if current_line < #tasks then
|
||||||
|
vim.api.nvim_buf_clear_namespace(list_buf, preview_ns, 0, -1)
|
||||||
|
current_line = current_line + 1
|
||||||
|
highlight_line(list_buf, preview_ns, current_line - 1)
|
||||||
|
update_preview(current_line)
|
||||||
|
vim.api.nvim_win_set_cursor(list_win, { current_line, 0 })
|
||||||
|
end
|
||||||
|
end, { buffer = list_buf })
|
||||||
|
|
||||||
|
vim.keymap.set('n', 'k', function()
|
||||||
|
if current_line > 1 then
|
||||||
|
vim.api.nvim_buf_clear_namespace(list_buf, preview_ns, 0, -1)
|
||||||
|
current_line = current_line - 1
|
||||||
|
highlight_line(list_buf, preview_ns, current_line - 1)
|
||||||
|
update_preview(current_line)
|
||||||
|
vim.api.nvim_win_set_cursor(list_win, { current_line, 0 })
|
||||||
|
end
|
||||||
|
end, { buffer = list_buf })
|
||||||
|
end
|
||||||
|
|
||||||
local function complete(ArgLead, _, _)
|
local function complete(ArgLead, _, _)
|
||||||
local matches = {}
|
local matches = {}
|
||||||
for _, task in ipairs(M.get_tasks()) do
|
for _, task in ipairs(M.get_tasks()) do
|
||||||
|
|
@ -165,7 +292,7 @@ vim.api.nvim_create_user_command('Task', function(input)
|
||||||
if input.args ~= '' then
|
if input.args ~= '' then
|
||||||
M.execute_task(input.args)
|
M.execute_task(input.args)
|
||||||
else
|
else
|
||||||
M.open_window()
|
M.select_task_with_preview()
|
||||||
end
|
end
|
||||||
end, { bang = true, desc = 'Run tasks defined in a Taskfile', nargs = '?', complete = complete })
|
end, { bang = true, desc = 'Run tasks defined in a Taskfile', nargs = '?', complete = complete })
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue