317 lines
7.7 KiB
Lua
317 lines
7.7 KiB
Lua
![]() |
local api = vim.api
|
||
|
local fn = vim.fn
|
||
|
local Config = require("avante.config")
|
||
|
local Utils = require("avante.utils")
|
||
|
|
||
|
---@class PromptInput
|
||
|
---@field bufnr integer | nil
|
||
|
---@field winid integer | nil
|
||
|
---@field win_opts table
|
||
|
---@field shortcuts_hints_winid integer | nil
|
||
|
---@field augroup integer | nil
|
||
|
---@field start_insert boolean
|
||
|
---@field submit_callback function | nil
|
||
|
---@field cancel_callback function | nil
|
||
|
---@field close_on_submit boolean
|
||
|
---@field spinner_chars table
|
||
|
---@field spinner_index integer
|
||
|
---@field spinner_timer uv_timer_t | nil
|
||
|
---@field spinner_active boolean
|
||
|
local PromptInput = {}
|
||
|
|
||
|
---@class PromptInputOptions
|
||
|
---@field start_insert? boolean
|
||
|
---@field submit_callback? fun(input: string):nil
|
||
|
---@field cancel_callback? fun():nil
|
||
|
---@field close_on_submit? boolean
|
||
|
---@field win_opts? table
|
||
|
|
||
|
---@param opts? PromptInputOptions
|
||
|
function PromptInput:new(opts)
|
||
|
opts = opts or {}
|
||
|
local obj = setmetatable({}, { __index = self })
|
||
|
obj.bufnr = nil
|
||
|
obj.winid = nil
|
||
|
obj.shortcuts_hints_winid = nil
|
||
|
obj.augroup = api.nvim_create_augroup("PromptInput", { clear = true })
|
||
|
obj.start_insert = opts.start_insert or false
|
||
|
obj.submit_callback = opts.submit_callback
|
||
|
obj.cancel_callback = opts.cancel_callback
|
||
|
obj.close_on_submit = opts.close_on_submit or false
|
||
|
obj.win_opts = opts.win_opts
|
||
|
obj.spinner_chars = {
|
||
|
"⡀",
|
||
|
"⠄",
|
||
|
"⠂",
|
||
|
"⠁",
|
||
|
"⠈",
|
||
|
"⠐",
|
||
|
"⠠",
|
||
|
"⢀",
|
||
|
"⣀",
|
||
|
"⢄",
|
||
|
"⢂",
|
||
|
"⢁",
|
||
|
"⢈",
|
||
|
"⢐",
|
||
|
"⢠",
|
||
|
"⣠",
|
||
|
"⢤",
|
||
|
"⢢",
|
||
|
"⢡",
|
||
|
"⢨",
|
||
|
"⢰",
|
||
|
"⣰",
|
||
|
"⢴",
|
||
|
"⢲",
|
||
|
"⢱",
|
||
|
"⢸",
|
||
|
"⣸",
|
||
|
"⢼",
|
||
|
"⢺",
|
||
|
"⢹",
|
||
|
"⣹",
|
||
|
"⢽",
|
||
|
"⢻",
|
||
|
"⣻",
|
||
|
"⢿",
|
||
|
"⣿",
|
||
|
"⣶",
|
||
|
"⣤",
|
||
|
"⣀",
|
||
|
}
|
||
|
obj.spinner_index = 1
|
||
|
obj.spinner_timer = nil
|
||
|
obj.spinner_active = false
|
||
|
return obj
|
||
|
end
|
||
|
|
||
|
function PromptInput:open()
|
||
|
self:close()
|
||
|
|
||
|
local bufnr = api.nvim_create_buf(false, true)
|
||
|
self.bufnr = bufnr
|
||
|
vim.bo[bufnr].filetype = "AvanteInput"
|
||
|
Utils.mark_as_sidebar_buffer(bufnr)
|
||
|
|
||
|
local win_opts = vim.tbl_extend("force", {
|
||
|
relative = "cursor",
|
||
|
width = 40,
|
||
|
height = 2,
|
||
|
row = 1,
|
||
|
col = 0,
|
||
|
style = "minimal",
|
||
|
border = Config.windows.edit.border,
|
||
|
title = { { "Input", "FloatTitle" } },
|
||
|
title_pos = "center",
|
||
|
}, self.win_opts)
|
||
|
|
||
|
local winid = api.nvim_open_win(bufnr, true, win_opts)
|
||
|
self.winid = winid
|
||
|
|
||
|
api.nvim_set_option_value("wrap", false, { win = winid })
|
||
|
api.nvim_set_option_value("cursorline", true, { win = winid })
|
||
|
api.nvim_set_option_value("modifiable", true, { buf = bufnr })
|
||
|
|
||
|
self:show_shortcuts_hints()
|
||
|
|
||
|
self:setup_keymaps()
|
||
|
self:setup_autocmds()
|
||
|
|
||
|
if self.start_insert then vim.cmd([[startinsert]]) end
|
||
|
end
|
||
|
|
||
|
function PromptInput:close()
|
||
|
if not self.bufnr then return end
|
||
|
self:stop_spinner()
|
||
|
self:close_shortcuts_hints()
|
||
|
if api.nvim_get_mode().mode == "i" then vim.cmd([[stopinsert]]) end
|
||
|
if self.winid and api.nvim_win_is_valid(self.winid) then
|
||
|
api.nvim_win_close(self.winid, true)
|
||
|
self.winid = nil
|
||
|
end
|
||
|
if self.bufnr and api.nvim_buf_is_valid(self.bufnr) then
|
||
|
api.nvim_buf_delete(self.bufnr, { force = true })
|
||
|
self.bufnr = nil
|
||
|
end
|
||
|
if self.augroup then
|
||
|
api.nvim_del_augroup_by_id(self.augroup)
|
||
|
self.augroup = nil
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function PromptInput:cancel()
|
||
|
self:close()
|
||
|
if self.cancel_callback then self.cancel_callback() end
|
||
|
end
|
||
|
|
||
|
function PromptInput:submit(input)
|
||
|
if self.close_on_submit then self:close() end
|
||
|
if self.submit_callback then self.submit_callback(input) end
|
||
|
end
|
||
|
|
||
|
function PromptInput:show_shortcuts_hints()
|
||
|
self:close_shortcuts_hints()
|
||
|
|
||
|
if not self.winid or not api.nvim_win_is_valid(self.winid) then return end
|
||
|
|
||
|
local win_width = api.nvim_win_get_width(self.winid)
|
||
|
local buf_height = api.nvim_buf_line_count(self.bufnr)
|
||
|
|
||
|
local hint_text = (vim.fn.mode() ~= "i" and Config.mappings.submit.normal or Config.mappings.submit.insert)
|
||
|
.. ": submit"
|
||
|
|
||
|
local display_text = hint_text
|
||
|
|
||
|
if self.spinner_active then
|
||
|
local spinner = self.spinner_chars[self.spinner_index]
|
||
|
display_text = spinner .. " " .. hint_text
|
||
|
end
|
||
|
|
||
|
local buf = api.nvim_create_buf(false, true)
|
||
|
api.nvim_buf_set_lines(buf, 0, -1, false, { display_text })
|
||
|
vim.api.nvim_buf_add_highlight(buf, 0, "AvantePopupHint", 0, 0, -1)
|
||
|
|
||
|
local width = fn.strdisplaywidth(display_text)
|
||
|
|
||
|
local opts = {
|
||
|
relative = "win",
|
||
|
win = self.winid,
|
||
|
width = width,
|
||
|
height = 1,
|
||
|
row = buf_height,
|
||
|
col = math.max(win_width - width, 0),
|
||
|
style = "minimal",
|
||
|
border = "none",
|
||
|
focusable = false,
|
||
|
zindex = 100,
|
||
|
}
|
||
|
|
||
|
self.shortcuts_hints_winid = api.nvim_open_win(buf, false, opts)
|
||
|
end
|
||
|
|
||
|
function PromptInput:close_shortcuts_hints()
|
||
|
if self.shortcuts_hints_winid and api.nvim_win_is_valid(self.shortcuts_hints_winid) then
|
||
|
api.nvim_win_close(self.shortcuts_hints_winid, true)
|
||
|
self.shortcuts_hints_winid = nil
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function PromptInput:start_spinner()
|
||
|
self.spinner_active = true
|
||
|
self.spinner_index = 1
|
||
|
|
||
|
if self.spinner_timer then
|
||
|
self.spinner_timer:stop()
|
||
|
self.spinner_timer:close()
|
||
|
self.spinner_timer = nil
|
||
|
end
|
||
|
|
||
|
self.spinner_timer = vim.loop.new_timer()
|
||
|
local spinner_timer = self.spinner_timer
|
||
|
|
||
|
if self.spinner_timer then
|
||
|
self.spinner_timer:start(0, 100, function()
|
||
|
vim.schedule(function()
|
||
|
if not self.spinner_active or spinner_timer ~= self.spinner_timer then return end
|
||
|
self.spinner_index = (self.spinner_index % #self.spinner_chars) + 1
|
||
|
self:show_shortcuts_hints()
|
||
|
end)
|
||
|
end)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function PromptInput:stop_spinner()
|
||
|
self.spinner_active = false
|
||
|
if self.spinner_timer then
|
||
|
self.spinner_timer:stop()
|
||
|
self.spinner_timer:close()
|
||
|
self.spinner_timer = nil
|
||
|
end
|
||
|
self:show_shortcuts_hints()
|
||
|
end
|
||
|
|
||
|
function PromptInput:setup_keymaps()
|
||
|
local bufnr = self.bufnr
|
||
|
|
||
|
local function get_input()
|
||
|
local lines = api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||
|
return lines[1] or ""
|
||
|
end
|
||
|
|
||
|
vim.keymap.set(
|
||
|
"i",
|
||
|
Config.mappings.submit.insert,
|
||
|
function() self:submit(get_input()) end,
|
||
|
{ buffer = bufnr, noremap = true, silent = true }
|
||
|
)
|
||
|
|
||
|
vim.keymap.set(
|
||
|
"n",
|
||
|
Config.mappings.submit.normal,
|
||
|
function() self:submit(get_input()) end,
|
||
|
{ buffer = bufnr, noremap = true, silent = true }
|
||
|
)
|
||
|
|
||
|
vim.keymap.set("n", "<Esc>", function() self:cancel() end, { buffer = bufnr })
|
||
|
vim.keymap.set("n", "q", function() self:cancel() end, { buffer = bufnr })
|
||
|
end
|
||
|
|
||
|
function PromptInput:setup_autocmds()
|
||
|
local bufnr = self.bufnr
|
||
|
local group = self.augroup
|
||
|
|
||
|
api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, {
|
||
|
group = group,
|
||
|
buffer = bufnr,
|
||
|
callback = function() self:show_shortcuts_hints() end,
|
||
|
})
|
||
|
|
||
|
api.nvim_create_autocmd("ModeChanged", {
|
||
|
group = group,
|
||
|
pattern = "i:*",
|
||
|
callback = function()
|
||
|
local cur_buf = api.nvim_get_current_buf()
|
||
|
if cur_buf == bufnr then self:show_shortcuts_hints() end
|
||
|
end,
|
||
|
})
|
||
|
|
||
|
api.nvim_create_autocmd("ModeChanged", {
|
||
|
group = group,
|
||
|
pattern = "*:i",
|
||
|
callback = function()
|
||
|
local cur_buf = api.nvim_get_current_buf()
|
||
|
if cur_buf == bufnr then self:show_shortcuts_hints() end
|
||
|
end,
|
||
|
})
|
||
|
|
||
|
local quit_id, close_unfocus
|
||
|
quit_id = api.nvim_create_autocmd("QuitPre", {
|
||
|
group = group,
|
||
|
buffer = bufnr,
|
||
|
once = true,
|
||
|
nested = true,
|
||
|
callback = function()
|
||
|
self:cancel()
|
||
|
if not quit_id then
|
||
|
api.nvim_del_autocmd(quit_id)
|
||
|
quit_id = nil
|
||
|
end
|
||
|
end,
|
||
|
})
|
||
|
|
||
|
close_unfocus = api.nvim_create_autocmd("WinLeave", {
|
||
|
group = group,
|
||
|
buffer = bufnr,
|
||
|
callback = function()
|
||
|
self:cancel()
|
||
|
if close_unfocus then
|
||
|
api.nvim_del_autocmd(close_unfocus)
|
||
|
close_unfocus = nil
|
||
|
end
|
||
|
end,
|
||
|
})
|
||
|
end
|
||
|
|
||
|
return PromptInput
|