diff --git a/README.md b/README.md
index 2895546..0207305 100644
--- a/README.md
+++ b/README.md
@@ -448,60 +448,26 @@ or you can use [Kaiser-Yang/blink-cmp-avante](https://github.com/Kaiser-Yang/bli
Lua
```lua
- file_selector = {
- --- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string | fun(params: avante.file_selector.IParams|nil): nil
+ selector = {
+ --- @alias avante.SelectorProvider "native" | "fzf_lua" | "mini_pick" | "snacks" | "telescope" | fun(selector: avante.ui.Selector): nil
provider = "fzf",
-- Options override for custom providers
provider_opts = {},
}
```
-To create a customized file_selector, you can specify a customized function to launch a picker to select items and pass the selected items to the `handler` callback.
+To create a customized selector provider, you can specify a customized function to launch a picker to select items and pass the selected items to the `on_select` callback.
```lua
- file_selector = {
- ---@param params avante.file_selector.IParams
- provider = function(params)
- local filepaths = params.filepaths ---@type string[]
- local title = params.title ---@type string
- local handler = params.handler ---@type fun(selected_filepaths: string[]|nil): nil
+ selector = {
+ ---@param selector avante.ui.Selector
+ provider = function(selector)
+ local items = selector.items ---@type avante.ui.SelectorItem[]
+ local title = selector.title ---@type string
+ local on_select = selector.on_select ---@type fun(selected_item_ids: string[]|nil): nil
- -- Launch your customized picker with the items built from `filepaths`, then in the `on_confirm` callback,
- -- pass the selected items (convert back to file paths) to the `handler` function.
-
- local items = __your_items_formatter__(filepaths)
- __your_picker__({
- items = items,
- on_cancel = function()
- handler(nil)
- end,
- on_confirm = function(selected_items)
- local selected_filepaths = {}
- for _, item in ipairs(selected_items) do
- table.insert(selected_filepaths, item.filepath)
- end
- handler(selected_filepaths)
- end
- })
+ --- your customized picker logic here
end,
- ---below is optional
- provider_opts = {
- ---@param params avante.file_selector.opts.IGetFilepathsParams
- get_filepaths = function(params)
- local cwd = params.cwd ---@type string
- local selected_filepaths = params.selected_filepaths ---@type string[]
- local cmd = string.format("fd --base-directory '%s' --hidden", vim.fn.fnameescape(cwd))
- local output = vim.fn.system(cmd)
- local filepaths = vim.split(output, "\n", { trimempty = true })
- return vim
- .iter(filepaths)
- :filter(function(filepath)
- return not vim.tbl_contains(selected_filepaths, filepath)
- end)
- :totable()
- end
- }
- end
}
```
@@ -708,6 +674,7 @@ return {
| `:AvanteShowRepoMap` | Show repo map for project's structure | |
| `:AvanteToggle` | Toggle the Avante sidebar | |
| `:AvanteModels` | Show model list | |
+| `:AvanteSwitchSelectorProvider` | Switch avante selector provider (e.g. native, telescope, fzf_lua, mini_pick, snacks) | |
## Highlight Groups
diff --git a/README_zh.md b/README_zh.md
index 7a2e142..5b133f9 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -448,60 +448,26 @@ _请参见 [config.lua#L9](./lua/avante/config.lua) 以获取完整配置_
Lua
```lua
- file_selector = {
- --- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string | fun(params: avante.file_selector.IParams|nil): nil
+ selector = {
+ --- @alias avante.SelectorProvider "native" | "fzf_lua" | "mini_pick" | "snacks" | "telescope" | fun(selector: avante.ui.Selector): nil
provider = "fzf",
-- 自定义提供者的选项覆盖
provider_opts = {},
}
```
-要创建自定义文件选择器,您可以指定一个自定义函数来启动选择器以选择项目,并将选定的项目传递给 `handler` 回调。
+要创建自定义选择器,您可以指定一个自定义函数来启动选择器以选择项目,并将选定的项目传递给 `on_select` 回调。
```lua
- file_selector = {
- ---@param params avante.file_selector.IParams
- provider = function(params)
- local filepaths = params.filepaths ---@type string[]
- local title = params.title ---@type string
- local handler = params.handler ---@type fun(selected_filepaths: string[]|nil): nil
+ selector = {
+ ---@param selector avante.ui.Selector
+ provider = function(selector)
+ local items = selector.items ---@type avante.ui.SelectorItem[]
+ local title = selector.title ---@type string
+ local on_select = selector.on_select ---@type fun(selected_item_ids: string[]|nil): nil
- -- 使用从 `filepaths` 构建的项目启动自定义选择器,然后在 `on_confirm` 回调中,
- -- 将选定的项目(转换回文件路径)传递给 `handler` 函数。
-
- local items = __your_items_formatter__(filepaths)
- __your_picker__({
- items = items,
- on_cancel = function()
- handler(nil)
- end,
- on_confirm = function(selected_items)
- local selected_filepaths = {}
- for _, item in ipairs(selected_items) do
- table.insert(selected_filepaths, item.filepath)
- end
- handler(selected_filepaths)
- end
- })
+ --- 在这里添加您的自定义选择器逻辑
end,
- ---以下是可选的
- provider_opts = {
- ---@param params avante.file_selector.opts.IGetFilepathsParams
- get_filepaths = function(params)
- local cwd = params.cwd ---@type string
- local selected_filepaths = params.selected_filepaths ---@type string[]
- local cmd = string.format("fd --base-directory '%s' --hidden", vim.fn.fnameescape(cwd))
- local output = vim.fn.system(cmd)
- local filepaths = vim.split(output, "\n", { trimempty = true })
- return vim
- .iter(filepaths)
- :filter(function(filepath)
- return not vim.tbl_contains(selected_filepaths, filepath)
- end)
- :totable()
- end
- }
- end
}
```
diff --git a/lua/avante/api.lua b/lua/avante/api.lua
index e4006fe..70d5089 100644
--- a/lua/avante/api.lua
+++ b/lua/avante/api.lua
@@ -11,10 +11,10 @@ local PromptInput = require("avante.ui.prompt_input")
---@field toggle avante.ApiToggle
local M = {}
----@param target_provider FileSelectorProvider
-function M.switch_file_selector_provider(target_provider)
+---@param target_provider avante.SelectorProvider
+function M.switch_selector_provider(target_provider)
require("avante.config").override({
- file_selector = {
+ selector = {
provider = target_provider,
},
})
diff --git a/lua/avante/config.lua b/lua/avante/config.lua
index 51bffc4..3768785 100644
--- a/lua/avante/config.lua
+++ b/lua/avante/config.lua
@@ -508,11 +508,15 @@ M._defaults = {
},
--- @class AvanteFileSelectorConfig
file_selector = {
- --- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string | fun(params: avante.file_selector.IParams|nil): nil
- provider = "native",
+ provider = nil,
-- Options override for custom providers
provider_opts = {},
},
+ selector = {
+ ---@alias avante.SelectorProvider "native" | "fzf_lua" | "mini_pick" | "snacks" | "telescope" | fun(selector: avante.ui.Selector): nil
+ provider = "native",
+ provider_opts = {},
+ },
suggestion = {
debounce = 600,
throttle = 600,
diff --git a/lua/avante/file_selector.lua b/lua/avante/file_selector.lua
index c5468ab..ad18668 100644
--- a/lua/avante/file_selector.lua
+++ b/lua/avante/file_selector.lua
@@ -2,6 +2,7 @@ local Utils = require("avante.utils")
local Path = require("plenary.path")
local scan = require("plenary.scandir")
local Config = require("avante.config")
+local Selector = require("avante.ui.selector")
local PROMPT_TITLE = "(Avante) Add a file"
@@ -168,7 +169,7 @@ function FileSelector:off(event, callback)
end
end
-function FileSelector:open() self:show_select_ui() end
+function FileSelector:open() self:show_selector_ui() end
function FileSelector:get_filepaths()
if type(Config.file_selector.provider_opts.get_filepaths) == "function" then
@@ -203,162 +204,56 @@ function FileSelector:get_filepaths()
:totable()
end
----@type FileSelectorHandler
-function FileSelector:fzf_ui(handler)
- local success, fzf_lua = pcall(require, "fzf-lua")
- if not success then
- Utils.error("fzf-lua is not installed. Please install fzf-lua to use it as a file selector.")
- return
- end
-
- local filepaths = self:get_filepaths()
-
- local function close_action() handler(nil) end
- fzf_lua.fzf_exec(
- filepaths,
- vim.tbl_deep_extend("force", {
- prompt = string.format("%s> ", PROMPT_TITLE),
- fzf_opts = {},
- git_icons = false,
- actions = {
- ["default"] = function(selected)
- if not selected or #selected == 0 then return close_action() end
- ---@type string[]
- local selections = {}
- for _, entry in ipairs(selected) do
- local file = fzf_lua.path.entry_to_file(entry)
- if file and file.path then table.insert(selections, file.path) end
- end
-
- handler(selections)
- end,
- ["esc"] = close_action,
- ["ctrl-c"] = close_action,
- },
- }, Config.file_selector.provider_opts)
- )
-end
-
-function FileSelector:mini_pick_ui(handler)
- -- luacheck: globals MiniPick
- ---@diagnostic disable-next-line: undefined-field
- if not _G.MiniPick then
- Utils.error("mini.pick is not set up. Please install and set up mini.pick to use it as a file selector.")
- return
- end
- local function choose(item) handler(type(item) == "string" and { item } or item) end
- local function choose_marked(items_marked) handler(items_marked) end
- local source = { choose = choose, choose_marked = choose_marked }
- ---@diagnostic disable-next-line: undefined-global
- local result = MiniPick.builtin.files(nil, { source = source })
- if result == nil then handler(nil) end
-end
-
-function FileSelector:snacks_picker_ui(handler)
- ---@diagnostic disable-next-line: undefined-field
- if not _G.Snacks then
- Utils.error("Snacks is not set up. Please install and set up Snacks to use it as a file selector.")
- return
- end
- ---@diagnostic disable-next-line: undefined-global
- Snacks.picker.files({
- exclude = self.selected_filepaths,
- confirm = function(picker)
- picker:close()
- local items = picker:selected({ fallback = true })
- local files = vim.tbl_map(function(item) return item.file end, items)
- handler(files)
- end,
- })
-end
-
-function FileSelector:telescope_ui(handler)
- local success, _ = pcall(require, "telescope")
- if not success then
- Utils.error("telescope is not installed. Please install telescope to use it as a file selector.")
- return
- end
-
- local pickers = require("telescope.pickers")
- local finders = require("telescope.finders")
- local conf = require("telescope.config").values
- local actions = require("telescope.actions")
- local action_state = require("telescope.actions.state")
- local action_utils = require("telescope.actions.utils")
-
- local files = self:get_filepaths()
-
- pickers
- .new(
- {},
- vim.tbl_extend("force", {
- file_ignore_patterns = self.selected_filepaths,
- prompt_title = string.format("%s> ", PROMPT_TITLE),
- finder = finders.new_table(files),
- sorter = conf.file_sorter(),
- attach_mappings = function(prompt_bufnr, map)
- map("i", "", require("telescope.actions").close)
- actions.select_default:replace(function()
- local picker = action_state.get_current_picker(prompt_bufnr)
-
- if #picker:get_multi_selection() ~= 0 then
- local selections = {}
-
- action_utils.map_selections(prompt_bufnr, function(selection) table.insert(selections, selection[1]) end)
-
- handler(selections)
- else
- local selections = action_state.get_selected_entry()
-
- handler(selections)
- end
-
- actions.close(prompt_bufnr)
- end)
- return true
- end,
- }, Config.file_selector.provider_opts)
- )
- :find()
-end
-
-function FileSelector:native_ui(handler)
- local filepaths = self:get_filepaths()
-
- vim.ui.select(filepaths, {
- prompt = string.format("%s:", PROMPT_TITLE),
- format_item = function(item) return item end,
- }, function(item)
- if item then
- handler({ item })
- else
- handler(nil)
- end
- end)
-end
-
---@return nil
-function FileSelector:show_select_ui()
+function FileSelector:show_selector_ui()
local function handler(selected_paths) self:handle_path_selection(selected_paths) end
vim.schedule(function()
- if Config.file_selector.provider == "native" then
- self:native_ui(handler)
- elseif Config.file_selector.provider == "fzf" then
- self:fzf_ui(handler)
- elseif Config.file_selector.provider == "mini.pick" then
- self:mini_pick_ui(handler)
- elseif Config.file_selector.provider == "snacks" then
- self:snacks_picker_ui(handler)
- elseif Config.file_selector.provider == "telescope" then
- self:telescope_ui(handler)
- elseif type(Config.file_selector.provider) == "function" then
- local title = string.format("%s:", PROMPT_TITLE) ---@type string
- local filepaths = self:get_filepaths() ---@type string[]
- local params = { title = title, filepaths = filepaths, handler = handler } ---@type avante.file_selector.IParams
- Config.file_selector.provider(params)
+ if Config.file_selector.provider ~= nil then
+ Utils.warn("config.file_selector is deprecated, please use config.selector instead!")
+ if type(Config.file_selector.provider) == "function" then
+ local title = string.format("%s:", PROMPT_TITLE) ---@type string
+ local filepaths = self:get_filepaths() ---@type string[]
+ local params = { title = title, filepaths = filepaths, handler = handler } ---@type avante.file_selector.IParams
+ Config.file_selector.provider(params)
+ else
+ local provider = "native"
+ if Config.file_selector.provider == "native" then
+ provider = "native"
+ elseif Config.file_selector.provider == "fzf" then
+ provider = "fzf_lua"
+ elseif Config.file_selector.provider == "mini.pick" then
+ provider = "mini_pick"
+ elseif Config.file_selector.provider == "snacks" then
+ provider = "snacks"
+ elseif Config.file_selector.provider == "telescope" then
+ provider = "telescope"
+ elseif type(Config.file_selector.provider) == "function" then
+ provider = Config.file_selector.provider
+ end
+ ---@cast provider avante.SelectorProvider
+ local selector = Selector:new({
+ provider = provider,
+ title = PROMPT_TITLE,
+ items = vim.tbl_map(function(filepath) return { id = filepath, title = filepath } end, self:get_filepaths()),
+ default_item_id = self.selected_filepaths[1],
+ selected_item_ids = self.selected_filepaths,
+ provider_opts = Config.file_selector.provider_opts,
+ on_select = function(item_ids) self:handle_path_selection(item_ids) end,
+ })
+ selector:open()
+ end
else
- Utils.error("Unknown file selector provider: " .. Config.file_selector.provider)
+ local selector = Selector:new({
+ provider = Config.selector.provider,
+ title = PROMPT_TITLE,
+ items = vim.tbl_map(function(filepath) return { id = filepath, title = filepath } end, self:get_filepaths()),
+ default_item_id = self.selected_filepaths[1],
+ selected_item_ids = self.selected_filepaths,
+ provider_opts = Config.selector.provider_opts,
+ on_select = function(item_ids) self:handle_path_selection(item_ids) end,
+ })
+ selector:open()
end
end)
diff --git a/lua/avante/history_selector.lua b/lua/avante/history_selector.lua
index 0211244..9272e6a 100644
--- a/lua/avante/history_selector.lua
+++ b/lua/avante/history_selector.lua
@@ -1,5 +1,7 @@
local Utils = require("avante.utils")
local Path = require("avante.path")
+local Config = require("avante.config")
+local Selector = require("avante.ui.selector")
---@class avante.HistorySelector
local M = {}
@@ -8,8 +10,10 @@ local M = {}
---@return table?
local function to_selector_item(history)
local timestamp = #history.entries > 0 and history.entries[#history.entries].timestamp or history.timestamp
+ local name = history.title .. " - " .. timestamp .. " (" .. #history.entries .. ")"
+ name = name:gsub("\n", "\\n")
return {
- name = history.title .. " - " .. timestamp .. " (" .. #history.entries .. ")",
+ name = name,
filename = history.filename,
}
end
@@ -30,68 +34,33 @@ function M.open(bufnr, cb)
return
end
- local has_telescope, _ = pcall(require, "telescope")
- if has_telescope then
- local pickers = require("telescope.pickers")
- local finders = require("telescope.finders")
- local previewers = require("telescope.previewers")
- local actions = require("telescope.actions")
- local action_state = require("telescope.actions.state")
- local conf = require("telescope.config").values
- pickers
- .new({}, {
- prompt_title = "Select Avante History",
- finder = finders.new_table(vim.iter(selector_items):map(function(item) return item.name end):totable()),
- sorter = conf.generic_sorter({}),
- previewer = previewers.new_buffer_previewer({
- title = "Preview",
- define_preview = function(self, entry)
- if not entry then return end
- local item = vim.iter(selector_items):find(function(item) return item.name == entry.value end)
- if not item then return end
- local history = Path.history.load(vim.api.nvim_get_current_buf(), item.filename)
- local Sidebar = require("avante.sidebar")
- local content = Sidebar.render_history_content(history)
- local lines = vim.split(content or "", "\n")
- -- Ensure the buffer exists and is valid before setting lines
- if vim.api.nvim_buf_is_valid(self.state.bufnr) then
- vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, lines)
- -- Set filetype after content is loaded
- vim.api.nvim_set_option_value("filetype", "markdown", { buf = self.state.bufnr })
- -- Ensure cursor is within bounds
- vim.schedule(function()
- if vim.api.nvim_buf_is_valid(self.state.bufnr) then
- local row = math.min(vim.api.nvim_buf_line_count(self.state.bufnr), 1)
- pcall(vim.api.nvim_win_set_cursor, self.state.winnr, { row, 0 })
- end
- end)
- end
- end,
- }),
- attach_mappings = function(prompt_bufnr, map)
- map("i", "", function()
- local selection = action_state.get_selected_entry()
- if selection then
- actions.close(prompt_bufnr)
- local item = vim.iter(selector_items):find(function(item) return item.name == selection.value end)
- if not item then return end
- cb(item.filename)
- end
- end)
- return true
- end,
- })
- :find()
- return
- end
-
- vim.ui.select(selector_items, {
- prompt = "Select Avante History:",
- format_item = function(item) return item.name:gsub("\n", "\\n") end,
- }, function(choice)
- if not choice then return end
- cb(choice.filename)
- end)
+ local selector = Selector:new({
+ provider = Config.selector.provider,
+ title = "Select Avante History",
+ items = vim
+ .iter(selector_items)
+ :map(
+ function(item)
+ return {
+ id = item.filename,
+ title = item.name,
+ }
+ end
+ )
+ :totable(),
+ on_select = function(item_ids)
+ if not item_ids then return end
+ if #item_ids == 0 then return end
+ cb(item_ids[1])
+ end,
+ get_preview_content = function(item_id)
+ local history = Path.history.load(vim.api.nvim_get_current_buf(), item_id)
+ local Sidebar = require("avante.sidebar")
+ local content = Sidebar.render_history_content(history)
+ return content, "markdown"
+ end,
+ })
+ selector:open()
end
return M
diff --git a/lua/avante/model_selector.lua b/lua/avante/model_selector.lua
index 496d32b..3a1de4f 100644
--- a/lua/avante/model_selector.lua
+++ b/lua/avante/model_selector.lua
@@ -1,5 +1,6 @@
local Utils = require("avante.utils")
local Config = require("avante.config")
+local Selector = require("avante.ui.selector")
---@class avante.ModelSelector
local M = {}
@@ -33,10 +34,21 @@ function M.open()
return
end
- vim.ui.select(models, {
- prompt = "Select Avante Model:",
- format_item = function(item) return item.name end,
- }, function(choice)
+ local items = vim
+ .iter(models)
+ :map(function(item)
+ return {
+ id = item.name,
+ title = item.name,
+ }
+ end)
+ :totable()
+
+ local default_item = vim.iter(models):find(function(item) return item.provider == Config.provider end)
+
+ local function on_select(item_ids)
+ if not item_ids then return end
+ local choice = vim.iter(models):find(function(item) return item.name == item_ids[1] end)
if not choice then return end
-- Switch provider if needed
@@ -52,7 +64,18 @@ function M.open()
})
Utils.info("Switched to model: " .. choice.name)
- end)
+ end
+
+ local selector = Selector:new({
+ title = "Select Avante Model",
+ items = items,
+ default_item_id = default_item and default_item.name or nil,
+ provider = Config.selector.provider,
+ provider_opts = Config.selector.provider_opts,
+ on_select = on_select,
+ })
+
+ selector:open()
end
return M
diff --git a/lua/avante/ui/selector/init.lua b/lua/avante/ui/selector/init.lua
new file mode 100644
index 0000000..da5cc39
--- /dev/null
+++ b/lua/avante/ui/selector/init.lua
@@ -0,0 +1,55 @@
+local Utils = require("avante.utils")
+
+---@class avante.ui.SelectorItem
+---@field id string
+---@field title string
+
+---@class avante.ui.SelectorOption
+---@field provider avante.SelectorProvider
+---@field title string
+---@field items avante.ui.SelectorItem[]
+---@field default_item_id string | nil
+---@field selected_item_ids string[] | nil
+---@field provider_opts table | nil
+---@field on_select fun(item_ids: string[] | nil)
+---@field get_preview_content fun(item_id: string): (string, string) | nil
+
+---@class avante.ui.Selector
+---@field provider avante.SelectorProvider
+---@field title string
+---@field items avante.ui.SelectorItem[]
+---@field default_item_id string | nil
+---@field provider_opts table | nil
+---@field on_select fun(item_ids: string[] | nil)
+---@field selected_item_ids string[] | nil
+---@field get_preview_content fun(item_id: string): (string, string) | nil
+local Selector = {}
+Selector.__index = Selector
+
+---@param opts avante.ui.SelectorOption
+function Selector:new(opts)
+ local o = {}
+ setmetatable(o, Selector)
+ o.provider = opts.provider
+ o.title = opts.title
+ o.items = opts.items
+ o.default_item_id = opts.default_item_id
+ o.provider_opts = opts.provider_opts or {}
+ o.on_select = opts.on_select
+ o.selected_item_ids = opts.selected_item_ids or {}
+ o.get_preview_content = opts.get_preview_content
+ return o
+end
+
+function Selector:open()
+ if type(self.provider) == "function" then
+ self.provider(self)
+ return
+ end
+
+ local ok, provider = pcall(require, "avante.ui.selector.providers." .. self.provider)
+ if not ok then Utils.error("Unknown file selector provider: " .. self.provider) end
+ provider.show(self)
+end
+
+return Selector
diff --git a/lua/avante/ui/selector/providers/fzf_lua.lua b/lua/avante/ui/selector/providers/fzf_lua.lua
new file mode 100644
index 0000000..27ad0e2
--- /dev/null
+++ b/lua/avante/ui/selector/providers/fzf_lua.lua
@@ -0,0 +1,49 @@
+local Utils = require("avante.utils")
+local M = {}
+
+---@param selector avante.ui.Selector
+function M.show(selector)
+ local success, fzf_lua = pcall(require, "fzf-lua")
+ if not success then
+ Utils.error("fzf-lua is not installed. Please install fzf-lua to use it as a file selector.")
+ return
+ end
+
+ local formated_items = vim.iter(selector.items):map(function(item) return item.title end):totable()
+ local title_to_id = {}
+ for _, item in ipairs(selector.items) do
+ title_to_id[item.title] = item.id
+ end
+
+ local function close_action() selector.on_select(nil) end
+ fzf_lua.fzf_exec(
+ formated_items,
+ vim.tbl_deep_extend("force", {
+ prompt = selector.title,
+ preview = selector.get_preview_content and function(item)
+ local id = title_to_id[item[1]]
+ local content = selector.get_preview_content(id)
+ return content
+ end or nil,
+ fzf_opts = {},
+ git_icons = false,
+ actions = {
+ ["default"] = function(selected)
+ if not selected or #selected == 0 then return close_action() end
+ ---@type string[]
+ local selections = {}
+ for _, entry in ipairs(selected) do
+ local id = title_to_id[entry]
+ if id then table.insert(selections, id) end
+ end
+
+ selector.on_select(selections)
+ end,
+ ["esc"] = close_action,
+ ["ctrl-c"] = close_action,
+ },
+ }, selector.provider_opts)
+ )
+end
+
+return M
diff --git a/lua/avante/ui/selector/providers/mini_pick.lua b/lua/avante/ui/selector/providers/mini_pick.lua
new file mode 100644
index 0000000..4fb21fb
--- /dev/null
+++ b/lua/avante/ui/selector/providers/mini_pick.lua
@@ -0,0 +1,37 @@
+local Utils = require("avante.utils")
+local M = {}
+
+---@param selector avante.ui.Selector
+function M.show(selector)
+ -- luacheck: globals MiniPick
+ ---@diagnostic disable-next-line: undefined-field
+ if not _G.MiniPick then
+ Utils.error("mini.pick is not set up. Please install and set up mini.pick to use it as a file selector.")
+ return
+ end
+ local items = {}
+ local title_to_id = {}
+ for _, item in ipairs(selector.items) do
+ title_to_id[item.title] = item.id
+ if not vim.list_contains(selector.selected_item_ids, item.id) then table.insert(items, item) end
+ end
+ local function choose(item)
+ if not item then
+ selector.on_select(nil)
+ return
+ end
+ local item_ids = {}
+ ---item is not a list
+ for _, item_ in pairs(item) do
+ table.insert(item_ids, title_to_id[item_])
+ end
+ selector.on_select(item_ids)
+ end
+ ---@diagnostic disable-next-line: undefined-global
+ MiniPick.ui_select(items, {
+ prompt = selector.title,
+ format_item = function(item) return item.title end,
+ }, choose)
+end
+
+return M
diff --git a/lua/avante/ui/selector/providers/native.lua b/lua/avante/ui/selector/providers/native.lua
new file mode 100644
index 0000000..7cbb86f
--- /dev/null
+++ b/lua/avante/ui/selector/providers/native.lua
@@ -0,0 +1,21 @@
+local M = {}
+
+---@param selector avante.ui.Selector
+function M.show(selector)
+ local items = {}
+ for _, item in ipairs(selector.items) do
+ if not vim.list_contains(selector.selected_item_ids, item.id) then table.insert(items, item) end
+ end
+ vim.ui.select(items, {
+ prompt = selector.title,
+ format_item = function(item) return item.title end,
+ }, function(item)
+ if item then
+ selector.on_select({ item.id })
+ else
+ selector.on_select(nil)
+ end
+ end)
+end
+
+return M
diff --git a/lua/avante/ui/selector/providers/snacks.lua b/lua/avante/ui/selector/providers/snacks.lua
new file mode 100644
index 0000000..1940fe2
--- /dev/null
+++ b/lua/avante/ui/selector/providers/snacks.lua
@@ -0,0 +1,58 @@
+local Utils = require("avante.utils")
+local M = {}
+
+---@param selector avante.ui.Selector
+function M.show(selector)
+ ---@diagnostic disable-next-line: undefined-field
+ if not _G.Snacks then
+ Utils.error("Snacks is not set up. Please install and set up Snacks to use it as a file selector.")
+ return
+ end
+ local finder_items = {}
+ for i, item in ipairs(selector.items) do
+ if not vim.list_contains(selector.selected_item_ids, item.id) then
+ table.insert(finder_items, {
+ formatted = item.title,
+ text = item.title,
+ item = item,
+ idx = i,
+ preview = selector.get_preview_content and (function()
+ local content, filetype = selector.get_preview_content(item.id)
+ return {
+ text = content,
+ ft = filetype,
+ }
+ end)() or nil,
+ })
+ end
+ end
+ local completed = false
+ ---@diagnostic disable-next-line: undefined-global
+ Snacks.picker.pick({
+ source = "select",
+ items = finder_items,
+ ---@diagnostic disable-next-line: undefined-global
+ format = Snacks.picker.format.ui_select(nil, #finder_items),
+ title = selector.title,
+ preview = selector.get_preview_content and "preview" or nil,
+ layout = {
+ preset = "default",
+ preview = selector.get_preview_content ~= nil,
+ },
+ confirm = function(picker)
+ if completed then return end
+ completed = true
+ picker:close()
+ local items = picker:selected({ fallback = true })
+ local selected_item_ids = vim.tbl_map(function(item) return item.item.id end, items)
+ selector.on_select(selected_item_ids)
+ end,
+ on_close = function()
+ if completed then return end
+ completed = true
+ vim.schedule(function() selector.on_select(nil) end)
+ end,
+ })
+end
+
+return M
diff --git a/lua/avante/ui/selector/providers/telescope.lua b/lua/avante/ui/selector/providers/telescope.lua
new file mode 100644
index 0000000..7b7373e
--- /dev/null
+++ b/lua/avante/ui/selector/providers/telescope.lua
@@ -0,0 +1,92 @@
+local Utils = require("avante.utils")
+
+local M = {}
+
+---@param selector avante.ui.Selector
+function M.show(selector)
+ local success, _ = pcall(require, "telescope")
+ if not success then
+ Utils.error("telescope is not installed. Please install telescope to use it as a file selector.")
+ return
+ end
+
+ local pickers = require("telescope.pickers")
+ local finders = require("telescope.finders")
+ local conf = require("telescope.config").values
+ local actions = require("telescope.actions")
+ local action_state = require("telescope.actions.state")
+ local previewers = require("telescope.previewers")
+
+ local items = {}
+ for _, item in ipairs(selector.items) do
+ if not vim.list_contains(selector.selected_item_ids, item.id) then table.insert(items, item) end
+ end
+
+ pickers
+ .new(
+ {},
+ vim.tbl_extend("force", {
+ prompt_title = selector.title,
+ finder = finders.new_table({
+ results = items,
+ entry_maker = function(entry)
+ return {
+ value = entry.id,
+ display = entry.title,
+ ordinal = entry.title,
+ }
+ end,
+ }),
+ sorter = conf.file_sorter(),
+ previewer = selector.get_preview_content and previewers.new_buffer_previewer({
+ title = "Preview",
+ define_preview = function(self, entry)
+ if not entry then return end
+ local content, filetype = selector.get_preview_content(entry.value)
+ local lines = vim.split(content or "", "\n")
+ -- Ensure the buffer exists and is valid before setting lines
+ if vim.api.nvim_buf_is_valid(self.state.bufnr) then
+ vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, lines)
+ -- Set filetype after content is loaded
+ vim.api.nvim_set_option_value("filetype", filetype, { buf = self.state.bufnr })
+ -- Ensure cursor is within bounds
+ vim.schedule(function()
+ if vim.api.nvim_buf_is_valid(self.state.bufnr) then
+ local row = math.min(vim.api.nvim_buf_line_count(self.state.bufnr), 1)
+ pcall(vim.api.nvim_win_set_cursor, self.state.winnr, { row, 0 })
+ end
+ end)
+ end
+ end,
+ }),
+ attach_mappings = function(prompt_bufnr, map)
+ map("i", "", require("telescope.actions").close)
+ actions.select_default:replace(function()
+ local picker = action_state.get_current_picker(prompt_bufnr)
+
+ local selections
+ local multi_selection = picker:get_multi_selection()
+ if #multi_selection ~= 0 then
+ selections = multi_selection
+ else
+ selections = action_state.get_selected_entry()
+ selections = vim.islist(selections) and selections or { selections }
+ end
+
+ local selected_item_ids = vim
+ .iter(selections)
+ :map(function(selection) return selection.value end)
+ :totable()
+
+ selector.on_select(selected_item_ids)
+
+ actions.close(prompt_bufnr)
+ end)
+ return true
+ end,
+ }, selector.provider_opts)
+ )
+ :find()
+end
+
+return M
diff --git a/plugin/avante.lua b/plugin/avante.lua
index 87a7eb7..44b8ca5 100644
--- a/plugin/avante.lua
+++ b/plugin/avante.lua
@@ -122,11 +122,11 @@ cmd("SwitchProvider", function(opts) require("avante.api").switch_provider(vim.t
end,
})
cmd(
- "SwitchFileSelectorProvider",
- function(opts) require("avante.api").switch_file_selector_provider(vim.trim(opts.args or "")) end,
+ "SwitchSelectorProvider",
+ function(opts) require("avante.api").switch_selector_provider(vim.trim(opts.args or "")) end,
{
nargs = 1,
- desc = "avante: switch file selector provider",
+ desc = "avante: switch selector provider",
}
)
cmd("Clear", function(opts)