feat: floating input (#721)
* feat: add floating input to ask method Open a floating input similar to the "edit" input for the "ask" input. Enabled in config via `Config.windows.ask.floating` or by passing `{ floating = true }` to the `api.ask` method. Includes logic to ensure the sidebar uses the correct buffer and selection if an existing sidebar is open for another code buffer. Also refactored the `selection` module to extract the floating input logic into a new `PromptInput` class. * docs: update config options * feat: more accurate annotations to prevent user misunderstandings --------- Co-authored-by: yetone <yetoneful@gmail.com>
This commit is contained in:
parent
b19573cb2a
commit
964715be64
12
README.md
12
README.md
@ -262,6 +262,18 @@ _See [config.lua#L9](./lua/avante/config.lua) for the full config_
|
|||||||
align = "center", -- left, center, right for title
|
align = "center", -- left, center, right for title
|
||||||
rounded = true,
|
rounded = true,
|
||||||
},
|
},
|
||||||
|
input = {
|
||||||
|
prefix = "> ",
|
||||||
|
},
|
||||||
|
edit = {
|
||||||
|
border = "rounded",
|
||||||
|
start_insert = true, -- Start insert mode when opening the edit window
|
||||||
|
},
|
||||||
|
ask = {
|
||||||
|
floating = false, -- Open the 'AvanteAsk' prompt in a floating window
|
||||||
|
start_insert = true, -- Start insert mode when opening the ask window, only effective if floating = true.
|
||||||
|
border = "rounded",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
highlights = {
|
highlights = {
|
||||||
---@type AvanteConflictHighlights
|
---@type AvanteConflictHighlights
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
local Config = require("avante.config")
|
local Config = require("avante.config")
|
||||||
local Utils = require("avante.utils")
|
local Utils = require("avante.utils")
|
||||||
|
local PromptInput = require("avante.prompt_input")
|
||||||
|
|
||||||
---@class avante.ApiToggle
|
---@class avante.ApiToggle
|
||||||
---@operator call(): boolean
|
---@operator call(): boolean
|
||||||
@ -88,6 +89,7 @@ end
|
|||||||
---@field question? string optional questions
|
---@field question? string optional questions
|
||||||
---@field win? table<string, any> windows options similar to |nvim_open_win()|
|
---@field win? table<string, any> windows options similar to |nvim_open_win()|
|
||||||
---@field ask? boolean
|
---@field ask? boolean
|
||||||
|
---@field floating? boolean whether to open a floating input to enter the question
|
||||||
|
|
||||||
---@param opts? AskOptions
|
---@param opts? AskOptions
|
||||||
M.ask = function(opts)
|
M.ask = function(opts)
|
||||||
@ -97,10 +99,42 @@ M.ask = function(opts)
|
|||||||
opts = { question = opts }
|
opts = { question = opts }
|
||||||
end
|
end
|
||||||
|
|
||||||
if not require("avante").toggle_sidebar(opts) then return false end
|
local has_question = opts.question ~= nil and opts.question ~= ""
|
||||||
if opts.question == nil or opts.question == "" then return true end
|
|
||||||
vim.api.nvim_exec_autocmds("User", { pattern = "AvanteInputSubmitted", data = { request = opts.question } })
|
if Utils.is_sidebar_buffer(0) and not has_question then
|
||||||
return true
|
require("avante").close_sidebar()
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
opts = vim.tbl_extend("force", { selection = Utils.get_visual_selection_and_range() }, opts)
|
||||||
|
|
||||||
|
local function ask(input)
|
||||||
|
if input == nil or input == "" then input = opts.question end
|
||||||
|
local sidebar = require("avante").get()
|
||||||
|
if sidebar and sidebar:is_open() and sidebar.code.bufnr ~= vim.api.nvim_get_current_buf() then
|
||||||
|
sidebar:close({ goto_code_win = false })
|
||||||
|
end
|
||||||
|
require("avante").open_sidebar(opts)
|
||||||
|
if input == nil or input == "" then return true end
|
||||||
|
vim.api.nvim_exec_autocmds("User", { pattern = "AvanteInputSubmitted", data = { request = input } })
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if opts.floating == true or (Config.windows.ask.floating == true and not has_question and opts.floating == nil) then
|
||||||
|
local prompt_input = PromptInput:new({
|
||||||
|
submit_callback = function(input) ask(input) end,
|
||||||
|
close_on_submit = true,
|
||||||
|
win_opts = {
|
||||||
|
border = Config.options.windows.ask.border,
|
||||||
|
title = { { "ask", "FloatTitle" } },
|
||||||
|
},
|
||||||
|
start_insert = Config.options.windows.ask.start_insert,
|
||||||
|
})
|
||||||
|
prompt_input:open()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return ask()
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param question? string
|
---@param question? string
|
||||||
|
@ -172,6 +172,12 @@ Respect and use existing conventions, libraries, etc that are already present in
|
|||||||
},
|
},
|
||||||
edit = {
|
edit = {
|
||||||
border = "rounded",
|
border = "rounded",
|
||||||
|
start_insert = true, -- Start insert mode when opening the edit window
|
||||||
|
},
|
||||||
|
ask = {
|
||||||
|
floating = false, -- Open the 'AvanteAsk' prompt in a floating window
|
||||||
|
border = "rounded",
|
||||||
|
start_insert = true, -- Start insert mode when opening the ask window
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
--- @class AvanteConflictConfig
|
--- @class AvanteConflictConfig
|
||||||
|
@ -357,6 +357,27 @@ M.toggle_sidebar = function(opts)
|
|||||||
return sidebar:toggle(opts)
|
return sidebar:toggle(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
M.is_sidebar_open = function()
|
||||||
|
local sidebar = M.get()
|
||||||
|
if not sidebar then return false end
|
||||||
|
return sidebar:is_open()
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param opts? AskOptions
|
||||||
|
M.open_sidebar = function(opts)
|
||||||
|
opts = opts or {}
|
||||||
|
if opts.ask == nil then opts.ask = true end
|
||||||
|
local sidebar = M.get()
|
||||||
|
if not sidebar then M._init(api.nvim_get_current_tabpage()) end
|
||||||
|
M.current.sidebar:open(opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
M.close_sidebar = function()
|
||||||
|
local sidebar = M.get()
|
||||||
|
if not sidebar then return end
|
||||||
|
sidebar:close()
|
||||||
|
end
|
||||||
|
|
||||||
M.toggle.debug = H.api(Utils.toggle_wrap({
|
M.toggle.debug = H.api(Utils.toggle_wrap({
|
||||||
name = "debug",
|
name = "debug",
|
||||||
get = function() return Config.debug end,
|
get = function() return Config.debug end,
|
||||||
|
316
lua/avante/prompt_input.lua
Normal file
316
lua/avante/prompt_input.lua
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
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
|
@ -3,6 +3,7 @@ local Config = require("avante.config")
|
|||||||
local Llm = require("avante.llm")
|
local Llm = require("avante.llm")
|
||||||
local Provider = require("avante.providers")
|
local Provider = require("avante.providers")
|
||||||
local RepoMap = require("avante.repo_map")
|
local RepoMap = require("avante.repo_map")
|
||||||
|
local PromptInput = require("avante.prompt_input")
|
||||||
|
|
||||||
local api = vim.api
|
local api = vim.api
|
||||||
local fn = vim.fn
|
local fn = vim.fn
|
||||||
@ -11,19 +12,14 @@ local NAMESPACE = api.nvim_create_namespace("avante_selection")
|
|||||||
local SELECTED_CODE_NAMESPACE = api.nvim_create_namespace("avante_selected_code")
|
local SELECTED_CODE_NAMESPACE = api.nvim_create_namespace("avante_selected_code")
|
||||||
local PRIORITY = vim.highlight.priorities.user
|
local PRIORITY = vim.highlight.priorities.user
|
||||||
|
|
||||||
local EDITING_INPUT_START_SPINNER_PATTERN = "AvanteEditingInputStartSpinner"
|
|
||||||
local EDITING_INPUT_STOP_SPINNER_PATTERN = "AvanteEditingInputStopSpinner"
|
|
||||||
|
|
||||||
---@class avante.Selection
|
---@class avante.Selection
|
||||||
---@field selection avante.SelectionResult | nil
|
---@field selection avante.SelectionResult | nil
|
||||||
---@field cursor_pos table | nil
|
---@field cursor_pos table | nil
|
||||||
---@field shortcuts_extmark_id integer | nil
|
---@field shortcuts_extmark_id integer | nil
|
||||||
---@field selected_code_extmark_id integer | nil
|
---@field selected_code_extmark_id integer | nil
|
||||||
---@field augroup integer | nil
|
---@field augroup integer | nil
|
||||||
---@field editing_input_bufnr integer | nil
|
|
||||||
---@field editing_input_winid integer | nil
|
|
||||||
---@field editing_input_shortcuts_hints_winid integer | nil
|
|
||||||
---@field code_winid integer | nil
|
---@field code_winid integer | nil
|
||||||
|
---@field prompt_input PromptInput | nil
|
||||||
local Selection = {}
|
local Selection = {}
|
||||||
|
|
||||||
Selection.did_setup = false
|
Selection.did_setup = false
|
||||||
@ -36,10 +32,8 @@ function Selection:new(id)
|
|||||||
augroup = api.nvim_create_augroup("avante_selection_" .. id, { clear = true }),
|
augroup = api.nvim_create_augroup("avante_selection_" .. id, { clear = true }),
|
||||||
selection = nil,
|
selection = nil,
|
||||||
cursor_pos = nil,
|
cursor_pos = nil,
|
||||||
editing_input_bufnr = nil,
|
|
||||||
editing_input_winid = nil,
|
|
||||||
editing_input_shortcuts_hints_winid = nil,
|
|
||||||
code_winid = nil,
|
code_winid = nil,
|
||||||
|
prompt_input = nil,
|
||||||
}, { __index = self })
|
}, { __index = self })
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -80,13 +74,11 @@ function Selection:close_shortcuts_hints_popup()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Selection:close_editing_input()
|
function Selection:close_editing_input()
|
||||||
self:close_editing_input_shortcuts_hints()
|
if self.prompt_input then
|
||||||
Llm.cancel_inflight_request()
|
self.prompt_input:close()
|
||||||
if api.nvim_get_mode().mode == "i" then vim.cmd([[stopinsert]]) end
|
self.prompt_input = nil
|
||||||
if self.editing_input_winid and api.nvim_win_is_valid(self.editing_input_winid) then
|
|
||||||
api.nvim_win_close(self.editing_input_winid, true)
|
|
||||||
self.editing_input_winid = nil
|
|
||||||
end
|
end
|
||||||
|
Llm.cancel_inflight_request()
|
||||||
if self.code_winid and api.nvim_win_is_valid(self.code_winid) then
|
if self.code_winid and api.nvim_win_is_valid(self.code_winid) then
|
||||||
local code_bufnr = api.nvim_win_get_buf(self.code_winid)
|
local code_bufnr = api.nvim_win_get_buf(self.code_winid)
|
||||||
api.nvim_buf_clear_namespace(code_bufnr, SELECTED_CODE_NAMESPACE, 0, -1)
|
api.nvim_buf_clear_namespace(code_bufnr, SELECTED_CODE_NAMESPACE, 0, -1)
|
||||||
@ -105,156 +97,6 @@ function Selection:close_editing_input()
|
|||||||
api.nvim_win_set_cursor(self.code_winid, { row, col })
|
api.nvim_win_set_cursor(self.code_winid, { row, col })
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
if self.editing_input_bufnr and api.nvim_buf_is_valid(self.editing_input_bufnr) then
|
|
||||||
api.nvim_buf_delete(self.editing_input_bufnr, { force = true })
|
|
||||||
self.editing_input_bufnr = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Selection:close_editing_input_shortcuts_hints()
|
|
||||||
if self.editing_input_shortcuts_hints_winid and api.nvim_win_is_valid(self.editing_input_shortcuts_hints_winid) then
|
|
||||||
api.nvim_win_close(self.editing_input_shortcuts_hints_winid, true)
|
|
||||||
self.editing_input_shortcuts_hints_winid = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Selection:show_editing_input_shortcuts_hints()
|
|
||||||
self:close_editing_input_shortcuts_hints()
|
|
||||||
|
|
||||||
if not self.editing_input_winid or not api.nvim_win_is_valid(self.editing_input_winid) then return end
|
|
||||||
|
|
||||||
local win_width = api.nvim_win_get_width(self.editing_input_winid)
|
|
||||||
local buf_height = api.nvim_buf_line_count(self.editing_input_bufnr)
|
|
||||||
-- spinner string: "⡀⠄⠂⠁⠈⠐⠠⢀⣀⢄⢂⢁⢈⢐⢠⣠⢤⢢⢡⢨⢰⣰⢴⢲⢱⢸⣸⢼⢺⢹⣹⢽⢻⣻⢿⣿⣶⣤⣀"
|
|
||||||
local spinner_chars = {
|
|
||||||
"⡀",
|
|
||||||
"⠄",
|
|
||||||
"⠂",
|
|
||||||
"⠁",
|
|
||||||
"⠈",
|
|
||||||
"⠐",
|
|
||||||
"⠠",
|
|
||||||
"⢀",
|
|
||||||
"⣀",
|
|
||||||
"⢄",
|
|
||||||
"⢂",
|
|
||||||
"⢁",
|
|
||||||
"⢈",
|
|
||||||
"⢐",
|
|
||||||
"⢠",
|
|
||||||
"⣠",
|
|
||||||
"⢤",
|
|
||||||
"⢢",
|
|
||||||
"⢡",
|
|
||||||
"⢨",
|
|
||||||
"⢰",
|
|
||||||
"⣰",
|
|
||||||
"⢴",
|
|
||||||
"⢲",
|
|
||||||
"⢱",
|
|
||||||
"⢸",
|
|
||||||
"⣸",
|
|
||||||
"⢼",
|
|
||||||
"⢺",
|
|
||||||
"⢹",
|
|
||||||
"⣹",
|
|
||||||
"⢽",
|
|
||||||
"⢻",
|
|
||||||
"⣻",
|
|
||||||
"⢿",
|
|
||||||
"⣿",
|
|
||||||
"⣶",
|
|
||||||
"⣤",
|
|
||||||
"⣀",
|
|
||||||
}
|
|
||||||
local spinner_index = 1
|
|
||||||
local timer = nil
|
|
||||||
|
|
||||||
local hint_text = (vim.fn.mode() ~= "i" and Config.mappings.submit.normal or Config.mappings.submit.insert)
|
|
||||||
.. ": submit"
|
|
||||||
|
|
||||||
local buf = api.nvim_create_buf(false, true)
|
|
||||||
api.nvim_buf_set_lines(buf, 0, -1, false, { hint_text })
|
|
||||||
vim.api.nvim_buf_add_highlight(buf, 0, "AvantePopupHint", 0, 0, -1)
|
|
||||||
|
|
||||||
local function update_spinner()
|
|
||||||
spinner_index = (spinner_index % #spinner_chars) + 1
|
|
||||||
local spinner = spinner_chars[spinner_index]
|
|
||||||
local new_text = spinner .. " " .. hint_text
|
|
||||||
|
|
||||||
api.nvim_buf_set_lines(buf, 0, -1, false, { new_text })
|
|
||||||
|
|
||||||
if
|
|
||||||
not self.editing_input_shortcuts_hints_winid
|
|
||||||
or not api.nvim_win_is_valid(self.editing_input_shortcuts_hints_winid)
|
|
||||||
then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local win_config = vim.api.nvim_win_get_config(self.editing_input_shortcuts_hints_winid)
|
|
||||||
|
|
||||||
local new_width = fn.strdisplaywidth(new_text)
|
|
||||||
|
|
||||||
if win_config.width ~= new_width then
|
|
||||||
win_config.width = new_width
|
|
||||||
win_config.col = math.max(win_width - new_width, 0)
|
|
||||||
vim.api.nvim_win_set_config(self.editing_input_shortcuts_hints_winid, win_config)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function stop_spinner()
|
|
||||||
if timer then
|
|
||||||
timer:stop()
|
|
||||||
timer:close()
|
|
||||||
timer = nil
|
|
||||||
end
|
|
||||||
api.nvim_buf_set_lines(buf, 0, -1, false, { hint_text })
|
|
||||||
|
|
||||||
if
|
|
||||||
not self.editing_input_shortcuts_hints_winid
|
|
||||||
or not api.nvim_win_is_valid(self.editing_input_shortcuts_hints_winid)
|
|
||||||
then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local win_config = vim.api.nvim_win_get_config(self.editing_input_shortcuts_hints_winid)
|
|
||||||
|
|
||||||
if win_config.width ~= #hint_text then
|
|
||||||
win_config.width = #hint_text
|
|
||||||
win_config.col = math.max(win_width - #hint_text, 0)
|
|
||||||
vim.api.nvim_win_set_config(self.editing_input_shortcuts_hints_winid, win_config)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
api.nvim_create_autocmd("User", {
|
|
||||||
pattern = EDITING_INPUT_START_SPINNER_PATTERN,
|
|
||||||
callback = function()
|
|
||||||
timer = vim.uv.new_timer()
|
|
||||||
if timer then timer:start(0, 100, vim.schedule_wrap(function() update_spinner() end)) end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
api.nvim_create_autocmd("User", {
|
|
||||||
pattern = EDITING_INPUT_STOP_SPINNER_PATTERN,
|
|
||||||
callback = function() stop_spinner() end,
|
|
||||||
})
|
|
||||||
|
|
||||||
local width = fn.strdisplaywidth(hint_text)
|
|
||||||
|
|
||||||
local opts = {
|
|
||||||
relative = "win",
|
|
||||||
win = self.editing_input_winid,
|
|
||||||
width = width,
|
|
||||||
height = 1,
|
|
||||||
row = buf_height,
|
|
||||||
col = math.max(win_width - width, 0),
|
|
||||||
style = "minimal",
|
|
||||||
border = "none",
|
|
||||||
focusable = false,
|
|
||||||
zindex = 100,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.editing_input_shortcuts_hints_winid = api.nvim_open_win(buf, false, opts)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Selection:create_editing_input()
|
function Selection:create_editing_input()
|
||||||
@ -266,9 +108,9 @@ function Selection:create_editing_input()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local code_bufnr = api.nvim_get_current_buf()
|
local code_bufnr = api.nvim_get_current_buf()
|
||||||
local code_wind = api.nvim_get_current_win()
|
local code_winid = api.nvim_get_current_win()
|
||||||
self.cursor_pos = api.nvim_win_get_cursor(code_wind)
|
self.cursor_pos = api.nvim_win_get_cursor(code_winid)
|
||||||
self.code_winid = code_wind
|
self.code_winid = code_winid
|
||||||
local code_lines = api.nvim_buf_get_lines(code_bufnr, 0, -1, false)
|
local code_lines = api.nvim_buf_get_lines(code_bufnr, 0, -1, false)
|
||||||
local code_content = table.concat(code_lines, "\n")
|
local code_content = table.concat(code_lines, "\n")
|
||||||
|
|
||||||
@ -303,58 +145,7 @@ function Selection:create_editing_input()
|
|||||||
priority = PRIORITY,
|
priority = PRIORITY,
|
||||||
})
|
})
|
||||||
|
|
||||||
local bufnr = api.nvim_create_buf(false, true)
|
local submit_input = function(input)
|
||||||
|
|
||||||
self.editing_input_bufnr = bufnr
|
|
||||||
|
|
||||||
local win_opts = {
|
|
||||||
relative = "cursor",
|
|
||||||
width = 40,
|
|
||||||
height = 2,
|
|
||||||
row = 1,
|
|
||||||
col = 0,
|
|
||||||
style = "minimal",
|
|
||||||
border = Config.windows.edit.border,
|
|
||||||
title = { { "edit selected block", "FloatTitle" } },
|
|
||||||
title_pos = "center",
|
|
||||||
}
|
|
||||||
|
|
||||||
local winid = api.nvim_open_win(bufnr, true, win_opts)
|
|
||||||
|
|
||||||
self.editing_input_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_editing_input_shortcuts_hints()
|
|
||||||
|
|
||||||
api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, {
|
|
||||||
group = self.augroup,
|
|
||||||
buffer = bufnr,
|
|
||||||
callback = function() self:show_editing_input_shortcuts_hints() end,
|
|
||||||
})
|
|
||||||
|
|
||||||
api.nvim_create_autocmd("ModeChanged", {
|
|
||||||
group = self.augroup,
|
|
||||||
pattern = "i:*",
|
|
||||||
callback = function()
|
|
||||||
local cur_buf = api.nvim_get_current_buf()
|
|
||||||
if cur_buf == bufnr then self:show_editing_input_shortcuts_hints() end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
api.nvim_create_autocmd("ModeChanged", {
|
|
||||||
group = self.augroup,
|
|
||||||
pattern = "*:i",
|
|
||||||
callback = function()
|
|
||||||
local cur_buf = api.nvim_get_current_buf()
|
|
||||||
if cur_buf == bufnr then self:show_editing_input_shortcuts_hints() end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
---@param input string
|
|
||||||
local function submit_input(input)
|
|
||||||
local full_response = ""
|
local full_response = ""
|
||||||
local start_line = self.selection.range.start.line
|
local start_line = self.selection.range.start.line
|
||||||
local finish_line = self.selection.range.finish.line
|
local finish_line = self.selection.range.finish.line
|
||||||
@ -363,7 +154,8 @@ function Selection:create_editing_input()
|
|||||||
|
|
||||||
local need_prepend_indentation = false
|
local need_prepend_indentation = false
|
||||||
|
|
||||||
api.nvim_exec_autocmds("User", { pattern = EDITING_INPUT_START_SPINNER_PATTERN })
|
self.prompt_input:start_spinner()
|
||||||
|
|
||||||
---@type AvanteChunkParser
|
---@type AvanteChunkParser
|
||||||
local on_chunk = function(chunk)
|
local on_chunk = function(chunk)
|
||||||
full_response = full_response .. chunk
|
full_response = full_response .. chunk
|
||||||
@ -397,7 +189,7 @@ function Selection:create_editing_input()
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
api.nvim_exec_autocmds("User", { pattern = EDITING_INPUT_STOP_SPINNER_PATTERN })
|
self.prompt_input:stop_spinner()
|
||||||
vim.defer_fn(function() self:close_editing_input() end, 0)
|
vim.defer_fn(function() self:close_editing_input() end, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -422,63 +214,32 @@ function Selection:create_editing_input()
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return string
|
local prompt_input = PromptInput:new({
|
||||||
local get_bufnr_input = function()
|
submit_callback = submit_input,
|
||||||
local lines = api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
cancel_callback = function() self:close_editing_input() end,
|
||||||
return lines[1] or ""
|
win_opts = {
|
||||||
end
|
border = Config.windows.edit.border,
|
||||||
|
title = { { "edit selected block", "FloatTitle" } },
|
||||||
vim.keymap.set(
|
},
|
||||||
"i",
|
start_insert = Config.windows.edit.start_insert,
|
||||||
Config.mappings.submit.insert,
|
|
||||||
function() submit_input(get_bufnr_input()) end,
|
|
||||||
{ buffer = bufnr, noremap = true, silent = true }
|
|
||||||
)
|
|
||||||
vim.keymap.set(
|
|
||||||
"n",
|
|
||||||
Config.mappings.submit.normal,
|
|
||||||
function() submit_input(get_bufnr_input()) end,
|
|
||||||
{ buffer = bufnr, noremap = true, silent = true }
|
|
||||||
)
|
|
||||||
vim.keymap.set("n", "<Esc>", function() self:close_editing_input() end, { buffer = bufnr })
|
|
||||||
vim.keymap.set("n", "q", function() self:close_editing_input() end, { buffer = bufnr })
|
|
||||||
|
|
||||||
local quit_id, close_unfocus
|
|
||||||
quit_id = api.nvim_create_autocmd("QuitPre", {
|
|
||||||
group = self.augroup,
|
|
||||||
buffer = bufnr,
|
|
||||||
once = true,
|
|
||||||
nested = true,
|
|
||||||
callback = function()
|
|
||||||
self:close_editing_input()
|
|
||||||
if not quit_id then
|
|
||||||
api.nvim_del_autocmd(quit_id)
|
|
||||||
quit_id = nil
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
close_unfocus = api.nvim_create_autocmd("WinLeave", {
|
self.prompt_input = prompt_input
|
||||||
group = self.augroup,
|
|
||||||
buffer = bufnr,
|
prompt_input:open()
|
||||||
callback = function()
|
|
||||||
self:close_editing_input()
|
|
||||||
if close_unfocus then
|
|
||||||
api.nvim_del_autocmd(close_unfocus)
|
|
||||||
close_unfocus = nil
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
api.nvim_create_autocmd("InsertEnter", {
|
api.nvim_create_autocmd("InsertEnter", {
|
||||||
group = self.augroup,
|
group = self.augroup,
|
||||||
buffer = bufnr,
|
buffer = prompt_input.bufnr,
|
||||||
once = true,
|
once = true,
|
||||||
desc = "Setup the completion of helpers in the input buffer",
|
desc = "Setup the completion of helpers in the input buffer",
|
||||||
callback = function()
|
callback = function()
|
||||||
local has_cmp, cmp = pcall(require, "cmp")
|
local has_cmp, cmp = pcall(require, "cmp")
|
||||||
if has_cmp then
|
if has_cmp then
|
||||||
cmp.register_source("avante_mentions", require("cmp_avante.mentions").new(Utils.get_mentions(), bufnr))
|
cmp.register_source(
|
||||||
|
"avante_mentions",
|
||||||
|
require("cmp_avante.mentions").new(Utils.get_mentions(), prompt_input.bufnr)
|
||||||
|
)
|
||||||
cmp.setup.buffer({
|
cmp.setup.buffer({
|
||||||
enabled = true,
|
enabled = true,
|
||||||
sources = {
|
sources = {
|
||||||
@ -488,13 +249,6 @@ function Selection:create_editing_input()
|
|||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
api.nvim_create_autocmd("User", {
|
|
||||||
pattern = "AvanteEditSubmitted",
|
|
||||||
callback = function(ev)
|
|
||||||
if ev.data and ev.data.request then submit_input(ev.data.request) end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Selection:setup_autocmds()
|
function Selection:setup_autocmds()
|
||||||
|
@ -65,18 +65,24 @@ function Sidebar:reset()
|
|||||||
self.input = nil
|
self.input = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param opts AskOptions
|
---@class SidebarOpenOptions: AskOptions
|
||||||
|
---@field selection? avante.SelectionResult
|
||||||
|
|
||||||
|
---@param opts SidebarOpenOptions
|
||||||
function Sidebar:open(opts)
|
function Sidebar:open(opts)
|
||||||
|
opts = opts or {}
|
||||||
local in_visual_mode = Utils.in_visual_mode() and self:in_code_win()
|
local in_visual_mode = Utils.in_visual_mode() and self:in_code_win()
|
||||||
if not self:is_open() then
|
if not self:is_open() then
|
||||||
self:reset()
|
self:reset()
|
||||||
self:initialize()
|
self:initialize()
|
||||||
|
if opts.selection then self.code.selection = opts.selection end
|
||||||
self:render(opts)
|
self:render(opts)
|
||||||
else
|
else
|
||||||
if in_visual_mode then
|
if in_visual_mode or opts.selection then
|
||||||
self:close()
|
self:close()
|
||||||
self:reset()
|
self:reset()
|
||||||
self:initialize()
|
self:initialize()
|
||||||
|
if opts.selection then self.code.selection = opts.selection end
|
||||||
self:render(opts)
|
self:render(opts)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -92,12 +98,19 @@ function Sidebar:open(opts)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function Sidebar:close()
|
---@class SidebarCloseOptions
|
||||||
|
---@field goto_code_win? boolean
|
||||||
|
|
||||||
|
---@param opts? SidebarCloseOptions
|
||||||
|
function Sidebar:close(opts)
|
||||||
|
opts = vim.tbl_extend("force", { goto_code_win = true }, opts or {})
|
||||||
self:delete_autocmds()
|
self:delete_autocmds()
|
||||||
for _, comp in pairs(self) do
|
for _, comp in pairs(self) do
|
||||||
if comp and type(comp) == "table" and comp.unmount then comp:unmount() end
|
if comp and type(comp) == "table" and comp.unmount then comp:unmount() end
|
||||||
end
|
end
|
||||||
if self.code and self.code.winid and api.nvim_win_is_valid(self.code.winid) then fn.win_gotoid(self.code.winid) end
|
if opts.goto_code_win and self.code and self.code.winid and api.nvim_win_is_valid(self.code.winid) then
|
||||||
|
fn.win_gotoid(self.code.winid)
|
||||||
|
end
|
||||||
|
|
||||||
vim.cmd("wincmd =")
|
vim.cmd("wincmd =")
|
||||||
end
|
end
|
||||||
@ -839,8 +852,6 @@ function Sidebar:on_mount(opts)
|
|||||||
self:render_input(opts.ask)
|
self:render_input(opts.ask)
|
||||||
self:render_selected_code()
|
self:render_selected_code()
|
||||||
|
|
||||||
self.augroup = api.nvim_create_augroup("avante_sidebar_" .. self.id .. self.result.winid, { clear = true })
|
|
||||||
|
|
||||||
local filetype = api.nvim_get_option_value("filetype", { buf = self.code.bufnr })
|
local filetype = api.nvim_get_option_value("filetype", { buf = self.code.bufnr })
|
||||||
|
|
||||||
if self.selected_code ~= nil then
|
if self.selected_code ~= nil then
|
||||||
@ -1570,6 +1581,7 @@ function Sidebar:create_input(opts)
|
|||||||
})
|
})
|
||||||
|
|
||||||
api.nvim_create_autocmd("User", {
|
api.nvim_create_autocmd("User", {
|
||||||
|
group = self.augroup,
|
||||||
pattern = "AvanteInputSubmitted",
|
pattern = "AvanteInputSubmitted",
|
||||||
callback = function(ev)
|
callback = function(ev)
|
||||||
if ev.data and ev.data.request then handle_submit(ev.data.request) end
|
if ev.data and ev.data.request then handle_submit(ev.data.request) end
|
||||||
@ -1635,6 +1647,8 @@ function Sidebar:render(opts)
|
|||||||
|
|
||||||
self.result:mount()
|
self.result:mount()
|
||||||
|
|
||||||
|
self.augroup = api.nvim_create_augroup("avante_sidebar_" .. self.id .. self.result.winid, { clear = true })
|
||||||
|
|
||||||
self.result:on(event.BufWinEnter, function()
|
self.result:on(event.BufWinEnter, function()
|
||||||
xpcall(function() api.nvim_buf_set_name(self.result.bufnr, RESULT_BUF_NAME) end, function(_) end)
|
xpcall(function() api.nvim_buf_set_name(self.result.bufnr, RESULT_BUF_NAME) end, function(_) end)
|
||||||
end)
|
end)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user