chore: prefer not to use function assignment (#1381)
This commit is contained in:
@@ -12,7 +12,7 @@ local PromptInput = require("avante.prompt_input")
|
||||
local M = {}
|
||||
|
||||
---@param target_provider FileSelectorProvider
|
||||
M.switch_file_selector_provider = function(target_provider)
|
||||
function M.switch_file_selector_provider(target_provider)
|
||||
require("avante.config").override({
|
||||
file_selector = {
|
||||
provider = target_provider,
|
||||
@@ -21,7 +21,7 @@ M.switch_file_selector_provider = function(target_provider)
|
||||
end
|
||||
|
||||
---@param target Provider
|
||||
M.switch_provider = function(target) require("avante.providers").refresh(target) end
|
||||
function M.switch_provider(target) require("avante.providers").refresh(target) end
|
||||
|
||||
---@param path string
|
||||
local function to_windows_path(path)
|
||||
@@ -35,7 +35,7 @@ local function to_windows_path(path)
|
||||
end
|
||||
|
||||
---@param opts? {source: boolean}
|
||||
M.build = function(opts)
|
||||
function M.build(opts)
|
||||
opts = opts or { source = true }
|
||||
local dirname = Utils.trim(string.sub(debug.getinfo(1).source, 2, #"/init.lua" * -1), { suffix = "/" })
|
||||
local git_root = vim.fs.find(".git", { path = dirname, upward = true })[1]
|
||||
@@ -101,7 +101,7 @@ end
|
||||
---@field floating? boolean whether to open a floating input to enter the question
|
||||
|
||||
---@param opts? AskOptions
|
||||
M.ask = function(opts)
|
||||
function M.ask(opts)
|
||||
opts = opts or {}
|
||||
if type(opts) == "string" then
|
||||
Utils.warn("passing 'ask' as string is deprecated, do {question = '...'} instead", { once = true })
|
||||
@@ -147,7 +147,7 @@ M.ask = function(opts)
|
||||
end
|
||||
|
||||
---@param question? string
|
||||
M.edit = function(question)
|
||||
function M.edit(question)
|
||||
local _, selection = require("avante").get()
|
||||
if not selection then return end
|
||||
selection:create_editing_input()
|
||||
@@ -157,13 +157,13 @@ M.edit = function(question)
|
||||
end
|
||||
|
||||
---@return avante.Suggestion | nil
|
||||
M.get_suggestion = function()
|
||||
function M.get_suggestion()
|
||||
local _, _, suggestion = require("avante").get()
|
||||
return suggestion
|
||||
end
|
||||
|
||||
---@param opts? AskOptions
|
||||
M.refresh = function(opts)
|
||||
function M.refresh(opts)
|
||||
opts = opts or {}
|
||||
local sidebar = require("avante").get()
|
||||
if not sidebar then return end
|
||||
@@ -185,7 +185,7 @@ M.refresh = function(opts)
|
||||
end
|
||||
|
||||
---@param opts? AskOptions
|
||||
M.focus = function(opts)
|
||||
function M.focus(opts)
|
||||
opts = opts or {}
|
||||
local sidebar = require("avante").get()
|
||||
if not sidebar then return end
|
||||
@@ -211,7 +211,7 @@ M.focus = function(opts)
|
||||
end
|
||||
end
|
||||
|
||||
M.select_model = function() require("avante.model_selector").open() end
|
||||
function M.select_model() require("avante.model_selector").open() end
|
||||
|
||||
return setmetatable(M, {
|
||||
__index = function(t, k)
|
||||
|
||||
@@ -25,7 +25,7 @@ end
|
||||
|
||||
M.support_paste_image = Config.support_paste_image
|
||||
|
||||
M.setup = function()
|
||||
function M.setup()
|
||||
get_paste_directory()
|
||||
|
||||
if not paste_directory:exists() then paste_directory:mkdir({ parent = true }) end
|
||||
@@ -34,7 +34,7 @@ M.setup = function()
|
||||
end
|
||||
|
||||
---@param line? string
|
||||
M.paste_image = function(line)
|
||||
function M.paste_image(line)
|
||||
line = line or nil
|
||||
if not Config.support_paste_image() then return false end
|
||||
|
||||
@@ -53,7 +53,7 @@ M.paste_image = function(line)
|
||||
end
|
||||
|
||||
---@param filepath string
|
||||
M.get_base64_content = function(filepath)
|
||||
function M.get_base64_content(filepath)
|
||||
local os_mapping = Utils.get_os_name()
|
||||
---@type vim.SystemCompleted
|
||||
local output
|
||||
|
||||
@@ -479,18 +479,18 @@ M = setmetatable(M, {
|
||||
end,
|
||||
})
|
||||
|
||||
M.support_paste_image = function() return Utils.has("img-clip.nvim") or Utils.has("img-clip") end
|
||||
function M.support_paste_image() return Utils.has("img-clip.nvim") or Utils.has("img-clip") end
|
||||
|
||||
M.get_window_width = function() return math.ceil(vim.o.columns * (M.windows.width / 100)) end
|
||||
function M.get_window_width() return math.ceil(vim.o.columns * (M.windows.width / 100)) end
|
||||
|
||||
---@param provider Provider
|
||||
---@return boolean
|
||||
M.has_provider = function(provider) return M._options[provider] ~= nil or M.vendors[provider] ~= nil end
|
||||
function M.has_provider(provider) return M._options[provider] ~= nil or M.vendors[provider] ~= nil end
|
||||
|
||||
---get supported providers
|
||||
---@param provider Provider
|
||||
---@return AvanteProviderFunctor
|
||||
M.get_provider = function(provider)
|
||||
function M.get_provider(provider)
|
||||
if M._options[provider] ~= nil then
|
||||
return vim.deepcopy(M._options[provider], true)
|
||||
elseif M.vendors and M.vendors[provider] ~= nil then
|
||||
|
||||
@@ -351,7 +351,7 @@ end
|
||||
-----------------------------------------------------------------------------//
|
||||
|
||||
---@param bufnr integer given buffer id
|
||||
H.setup_buffer_mappings = function(bufnr)
|
||||
function H.setup_buffer_mappings(bufnr)
|
||||
---@param desc string
|
||||
local function opts(desc) return { silent = true, buffer = bufnr, desc = "avante(conflict): " .. desc } end
|
||||
|
||||
@@ -372,7 +372,7 @@ H.setup_buffer_mappings = function(bufnr)
|
||||
end
|
||||
|
||||
---@param bufnr integer
|
||||
H.clear_buffer_mappings = function(bufnr)
|
||||
function H.clear_buffer_mappings(bufnr)
|
||||
if not bufnr or not vim.b[bufnr].avante_conflict_mappings_set then return end
|
||||
for _, mapping in pairs(Config.mappings.diff) do
|
||||
if vim.fn.hasmapto(mapping, "n") > 0 then api.nvim_buf_del_keymap(bufnr, "n", mapping) end
|
||||
@@ -382,7 +382,7 @@ H.clear_buffer_mappings = function(bufnr)
|
||||
end
|
||||
|
||||
---@param bufnr integer
|
||||
M.override_timeoutlen = function(bufnr)
|
||||
function M.override_timeoutlen(bufnr)
|
||||
if vim.b[bufnr].avante_original_timeoutlen then return end
|
||||
if Config.diff.override_timeoutlen > 0 then
|
||||
vim.b[bufnr].avante_original_timeoutlen = vim.o.timeoutlen
|
||||
@@ -391,7 +391,7 @@ M.override_timeoutlen = function(bufnr)
|
||||
end
|
||||
|
||||
---@param bufnr integer
|
||||
M.restore_timeoutlen = function(bufnr)
|
||||
function M.restore_timeoutlen(bufnr)
|
||||
if vim.b[bufnr].avante_original_timeoutlen then
|
||||
vim.o.timeoutlen = vim.b[bufnr].avante_original_timeoutlen
|
||||
vim.b[bufnr].avante_original_timeoutlen = nil
|
||||
|
||||
@@ -211,7 +211,7 @@ function FileSelector:fzf_ui(handler)
|
||||
|
||||
local filepaths = self:get_filepaths()
|
||||
|
||||
local close_action = function() handler(nil) end
|
||||
local function close_action() handler(nil) end
|
||||
fzf_lua.fzf_exec(
|
||||
filepaths,
|
||||
vim.tbl_deep_extend("force", {
|
||||
@@ -244,8 +244,8 @@ function FileSelector:mini_pick_ui(handler)
|
||||
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 choose = function(item) handler(type(item) == "string" and { item } or item) end
|
||||
local choose_marked = function(items_marked) handler(items_marked) 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 })
|
||||
|
||||
@@ -3,7 +3,7 @@ local H = require("vim.health")
|
||||
local Utils = require("avante.utils")
|
||||
local Config = require("avante.config")
|
||||
|
||||
M.check = function()
|
||||
function M.check()
|
||||
H.start("avante.nvim")
|
||||
|
||||
-- Required dependencies
|
||||
|
||||
@@ -37,7 +37,7 @@ local function has_set_colors(hl_group)
|
||||
return next(hl) ~= nil
|
||||
end
|
||||
|
||||
M.setup = function()
|
||||
function M.setup()
|
||||
if Config.behaviour.auto_set_highlight_group then
|
||||
vim
|
||||
.iter(Highlights)
|
||||
@@ -59,16 +59,16 @@ M.setup = function()
|
||||
M.setup_conflict_highlights()
|
||||
end
|
||||
|
||||
M.setup_conflict_highlights = function()
|
||||
function M.setup_conflict_highlights()
|
||||
local custom_hls = Config.highlights.diff
|
||||
|
||||
---@return number | nil
|
||||
local get_bg = function(hl_name)
|
||||
local function get_bg(hl_name)
|
||||
local hl = api.nvim_get_hl(0, { name = hl_name })
|
||||
return hl.bg
|
||||
end
|
||||
|
||||
local get_bold = function(hl_name)
|
||||
local function get_bold(hl_name)
|
||||
local hl = api.nvim_get_hl(0, { name = hl_name })
|
||||
return hl.bold
|
||||
end
|
||||
@@ -133,7 +133,7 @@ setmetatable(M, {
|
||||
---
|
||||
---@param rgb_24bit number 24-bit RGB value
|
||||
---@return {r: integer, g: integer, b: integer} with keys 'r', 'g', 'b' in [0,255]
|
||||
H.decode_24bit_rgb = function(rgb_24bit)
|
||||
function H.decode_24bit_rgb(rgb_24bit)
|
||||
vim.validate({ rgb_24bit = { rgb_24bit, "n", true } })
|
||||
local r = band(rshift(rgb_24bit, 16), 255)
|
||||
local g = band(rshift(rgb_24bit, 8), 255)
|
||||
@@ -143,7 +143,7 @@ end
|
||||
|
||||
---@param attr integer
|
||||
---@param percent integer
|
||||
H.alter = function(attr, percent) return math.floor(attr * (100 + percent) / 100) end
|
||||
function H.alter(attr, percent) return math.floor(attr * (100 + percent) / 100) end
|
||||
|
||||
---@source https://stackoverflow.com/q/5560248
|
||||
---@see https://stackoverflow.com/a/37797380
|
||||
@@ -151,7 +151,7 @@ H.alter = function(attr, percent) return math.floor(attr * (100 + percent) / 100
|
||||
---@param color number
|
||||
---@param percent number
|
||||
---@return string
|
||||
H.shade_color = function(color, percent)
|
||||
function H.shade_color(color, percent)
|
||||
percent = vim.opt.background:get() == "light" and percent / 5 or percent
|
||||
local rgb = H.decode_24bit_rgb(color)
|
||||
if not rgb.r or not rgb.g or not rgb.b then return "NONE" end
|
||||
|
||||
@@ -24,12 +24,12 @@ M.did_setup = false
|
||||
|
||||
local H = {}
|
||||
|
||||
H.load_path = function()
|
||||
function H.load_path()
|
||||
local ok, LazyConfig = pcall(require, "lazy.core.config")
|
||||
|
||||
if ok then
|
||||
local name = "avante.nvim"
|
||||
local load_path = function() require("avante_lib").load() end
|
||||
local function load_path() require("avante_lib").load() end
|
||||
|
||||
if LazyConfig.plugins[name] and LazyConfig.plugins[name]._.loaded then
|
||||
vim.schedule(load_path)
|
||||
@@ -54,7 +54,7 @@ H.load_path = function()
|
||||
end
|
||||
end
|
||||
|
||||
H.keymaps = function()
|
||||
function H.keymaps()
|
||||
vim.keymap.set({ "n", "v" }, "<Plug>(AvanteAsk)", function() require("avante.api").ask() end, { noremap = true })
|
||||
vim.keymap.set(
|
||||
{ "n", "v" },
|
||||
@@ -180,17 +180,17 @@ end
|
||||
---@class ApiCaller
|
||||
---@operator call(...): any
|
||||
|
||||
H.api = function(fun)
|
||||
function H.api(fun)
|
||||
return setmetatable({ api = true }, {
|
||||
__call = function(...) return fun(...) end,
|
||||
}) --[[@as ApiCaller]]
|
||||
end
|
||||
|
||||
H.signs = function() vim.fn.sign_define("AvanteInputPromptSign", { text = Config.windows.input.prefix }) end
|
||||
function H.signs() vim.fn.sign_define("AvanteInputPromptSign", { text = Config.windows.input.prefix }) end
|
||||
|
||||
H.augroup = api.nvim_create_augroup("avante_autocmds", { clear = true })
|
||||
|
||||
H.autocmds = function()
|
||||
function H.autocmds()
|
||||
api.nvim_create_autocmd("TabEnter", {
|
||||
group = H.augroup,
|
||||
pattern = "*",
|
||||
@@ -293,7 +293,7 @@ end
|
||||
M.toggle = { api = true }
|
||||
|
||||
---@param opts? AskOptions
|
||||
M.toggle_sidebar = function(opts)
|
||||
function M.toggle_sidebar(opts)
|
||||
opts = opts or {}
|
||||
if opts.ask == nil then opts.ask = true end
|
||||
|
||||
@@ -308,14 +308,14 @@ M.toggle_sidebar = function(opts)
|
||||
return sidebar:toggle(opts)
|
||||
end
|
||||
|
||||
M.is_sidebar_open = function()
|
||||
function M.is_sidebar_open()
|
||||
local sidebar = M.get()
|
||||
if not sidebar then return false end
|
||||
return sidebar:is_open()
|
||||
end
|
||||
|
||||
---@param opts? AskOptions
|
||||
M.open_sidebar = function(opts)
|
||||
function M.open_sidebar(opts)
|
||||
opts = opts or {}
|
||||
if opts.ask == nil then opts.ask = true end
|
||||
local sidebar = M.get()
|
||||
@@ -324,7 +324,7 @@ M.open_sidebar = function(opts)
|
||||
M.current.sidebar:open(opts)
|
||||
end
|
||||
|
||||
M.close_sidebar = function()
|
||||
function M.close_sidebar()
|
||||
local sidebar = M.get()
|
||||
if not sidebar then return end
|
||||
sidebar:close()
|
||||
|
||||
424
lua/avante/init.lua-E
Normal file
424
lua/avante/init.lua-E
Normal file
@@ -0,0 +1,424 @@
|
||||
local api = vim.api
|
||||
|
||||
local Utils = require("avante.utils")
|
||||
local Sidebar = require("avante.sidebar")
|
||||
local Selection = require("avante.selection")
|
||||
local Suggestion = require("avante.suggestion")
|
||||
local Config = require("avante.config")
|
||||
local Diff = require("avante.diff")
|
||||
local RagService = require("avante.rag_service")
|
||||
|
||||
---@class Avante
|
||||
local M = {
|
||||
---@type avante.Sidebar[] we use this to track chat command across tabs
|
||||
sidebars = {},
|
||||
---@type avante.Selection[]
|
||||
selections = {},
|
||||
---@type avante.Suggestion[]
|
||||
suggestions = {},
|
||||
---@type {sidebar?: avante.Sidebar, selection?: avante.Selection, suggestion?: avante.Suggestion}
|
||||
current = { sidebar = nil, selection = nil, suggestion = nil },
|
||||
}
|
||||
|
||||
M.did_setup = false
|
||||
|
||||
local H = {}
|
||||
|
||||
H.load_path = function()
|
||||
local ok, LazyConfig = pcall(require, "lazy.core.config")
|
||||
|
||||
if ok then
|
||||
local name = "avante.nvim"
|
||||
local load_path = function() require("avante_lib").load() end
|
||||
|
||||
if LazyConfig.plugins[name] and LazyConfig.plugins[name]._.loaded then
|
||||
vim.schedule(load_path)
|
||||
else
|
||||
api.nvim_create_autocmd("User", {
|
||||
pattern = "LazyLoad",
|
||||
callback = function(event)
|
||||
if event.data == name then
|
||||
load_path()
|
||||
return true
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
api.nvim_create_autocmd("User", {
|
||||
pattern = "VeryLazy",
|
||||
callback = load_path,
|
||||
})
|
||||
else
|
||||
require("avante_lib").load()
|
||||
end
|
||||
end
|
||||
|
||||
H.keymaps = function()
|
||||
vim.keymap.set({ "n", "v" }, "<Plug>(AvanteAsk)", function() require("avante.api").ask() end, { noremap = true })
|
||||
vim.keymap.set(
|
||||
{ "n", "v" },
|
||||
"<Plug>(AvanteChat)",
|
||||
function() require("avante.api").ask({ ask = false }) end,
|
||||
{ noremap = true }
|
||||
)
|
||||
vim.keymap.set("v", "<Plug>(AvanteEdit)", function() require("avante.api").edit() end, { noremap = true })
|
||||
vim.keymap.set("n", "<Plug>(AvanteRefresh)", function() require("avante.api").refresh() end, { noremap = true })
|
||||
vim.keymap.set("n", "<Plug>(AvanteFocus)", function() require("avante.api").focus() end, { noremap = true })
|
||||
vim.keymap.set("n", "<Plug>(AvanteBuild)", function() require("avante.api").build() end, { noremap = true })
|
||||
vim.keymap.set("n", "<Plug>(AvanteToggle)", function() M.toggle() end, { noremap = true })
|
||||
vim.keymap.set("n", "<Plug>(AvanteToggleDebug)", function() M.toggle.debug() end)
|
||||
vim.keymap.set("n", "<Plug>(AvanteToggleHint)", function() M.toggle.hint() end)
|
||||
vim.keymap.set("n", "<Plug>(AvanteToggleSuggestion)", function() M.toggle.suggestion() end)
|
||||
|
||||
vim.keymap.set({ "n", "v" }, "<Plug>(AvanteConflictOurs)", function() Diff.choose("ours") end)
|
||||
vim.keymap.set({ "n", "v" }, "<Plug>(AvanteConflictBoth)", function() Diff.choose("both") end)
|
||||
vim.keymap.set({ "n", "v" }, "<Plug>(AvanteConflictTheirs)", function() Diff.choose("theirs") end)
|
||||
vim.keymap.set({ "n", "v" }, "<Plug>(AvanteConflictAllTheirs)", function() Diff.choose("all_theirs") end)
|
||||
vim.keymap.set({ "n", "v" }, "<Plug>(AvanteConflictCursor)", function() Diff.choose("cursor") end)
|
||||
vim.keymap.set("n", "<Plug>(AvanteConflictNextConflict)", function() Diff.find_next("ours") end)
|
||||
vim.keymap.set("n", "<Plug>(AvanteConflictPrevConflict)", function() Diff.find_prev("ours") end)
|
||||
vim.keymap.set("n", "<Plug>(AvanteSelectModel)", function() require("avante.api").select_model() end)
|
||||
|
||||
if Config.behaviour.auto_set_keymaps then
|
||||
Utils.safe_keymap_set(
|
||||
{ "n", "v" },
|
||||
Config.mappings.ask,
|
||||
function() require("avante.api").ask() end,
|
||||
{ desc = "avante: ask" }
|
||||
)
|
||||
Utils.safe_keymap_set(
|
||||
"v",
|
||||
Config.mappings.edit,
|
||||
function() require("avante.api").edit() end,
|
||||
{ desc = "avante: edit" }
|
||||
)
|
||||
Utils.safe_keymap_set(
|
||||
"n",
|
||||
Config.mappings.refresh,
|
||||
function() require("avante.api").refresh() end,
|
||||
{ desc = "avante: refresh" }
|
||||
)
|
||||
Utils.safe_keymap_set(
|
||||
"n",
|
||||
Config.mappings.focus,
|
||||
function() require("avante.api").focus() end,
|
||||
{ desc = "avante: focus" }
|
||||
)
|
||||
|
||||
Utils.safe_keymap_set("n", Config.mappings.toggle.default, function() M.toggle() end, { desc = "avante: toggle" })
|
||||
Utils.safe_keymap_set(
|
||||
"n",
|
||||
Config.mappings.toggle.debug,
|
||||
function() M.toggle.debug() end,
|
||||
{ desc = "avante: toggle debug" }
|
||||
)
|
||||
Utils.safe_keymap_set(
|
||||
"n",
|
||||
Config.mappings.toggle.hint,
|
||||
function() M.toggle.hint() end,
|
||||
{ desc = "avante: toggle hint" }
|
||||
)
|
||||
Utils.safe_keymap_set(
|
||||
"n",
|
||||
Config.mappings.toggle.suggestion,
|
||||
function() M.toggle.suggestion() end,
|
||||
{ desc = "avante: toggle suggestion" }
|
||||
)
|
||||
Utils.safe_keymap_set("n", Config.mappings.toggle.repomap, function() require("avante.repo_map").show() end, {
|
||||
desc = "avante: display repo map",
|
||||
noremap = true,
|
||||
silent = true,
|
||||
})
|
||||
Utils.safe_keymap_set(
|
||||
"n",
|
||||
Config.mappings.select_model,
|
||||
function() require("avante.api").select_model() end,
|
||||
{ desc = "avante: select model" }
|
||||
)
|
||||
end
|
||||
|
||||
if Config.behaviour.auto_suggestions then
|
||||
Utils.safe_keymap_set("i", Config.mappings.suggestion.accept, function()
|
||||
local _, _, sg = M.get()
|
||||
sg:accept()
|
||||
end, {
|
||||
desc = "avante: accept suggestion",
|
||||
noremap = true,
|
||||
silent = true,
|
||||
})
|
||||
|
||||
Utils.safe_keymap_set("i", Config.mappings.suggestion.dismiss, function()
|
||||
local _, _, sg = M.get()
|
||||
if sg:is_visible() then sg:dismiss() end
|
||||
end, {
|
||||
desc = "avante: dismiss suggestion",
|
||||
noremap = true,
|
||||
silent = true,
|
||||
})
|
||||
|
||||
Utils.safe_keymap_set("i", Config.mappings.suggestion.next, function()
|
||||
local _, _, sg = M.get()
|
||||
sg:next()
|
||||
end, {
|
||||
desc = "avante: next suggestion",
|
||||
noremap = true,
|
||||
silent = true,
|
||||
})
|
||||
|
||||
Utils.safe_keymap_set("i", Config.mappings.suggestion.prev, function()
|
||||
local _, _, sg = M.get()
|
||||
sg:prev()
|
||||
end, {
|
||||
desc = "avante: previous suggestion",
|
||||
noremap = true,
|
||||
silent = true,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
---@class ApiCaller
|
||||
---@operator call(...): any
|
||||
|
||||
H.api = function(fun)
|
||||
return setmetatable({ api = true }, {
|
||||
__call = function(...) return fun(...) end,
|
||||
}) --[[@as ApiCaller]]
|
||||
end
|
||||
|
||||
H.signs = function() vim.fn.sign_define("AvanteInputPromptSign", { text = Config.windows.input.prefix }) end
|
||||
|
||||
H.augroup = api.nvim_create_augroup("avante_autocmds", { clear = true })
|
||||
|
||||
H.autocmds = function()
|
||||
api.nvim_create_autocmd("TabEnter", {
|
||||
group = H.augroup,
|
||||
pattern = "*",
|
||||
once = true,
|
||||
callback = function(ev)
|
||||
local tab = tonumber(ev.file)
|
||||
M._init(tab or api.nvim_get_current_tabpage())
|
||||
if Config.hints.enabled and not M.current.selection.did_setup then M.current.selection:setup_autocmds() end
|
||||
end,
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd("VimResized", {
|
||||
group = H.augroup,
|
||||
callback = function()
|
||||
local sidebar = M.get()
|
||||
if not sidebar then return end
|
||||
if not sidebar:is_open() then return end
|
||||
sidebar:resize()
|
||||
end,
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd("TabClosed", {
|
||||
group = H.augroup,
|
||||
pattern = "*",
|
||||
callback = function(ev)
|
||||
local tab = tonumber(ev.file)
|
||||
local s = M.sidebars[tab]
|
||||
local sl = M.selections[tab]
|
||||
if s then s:reset() end
|
||||
if sl then sl:delete_autocmds() end
|
||||
if tab ~= nil then M.sidebars[tab] = nil end
|
||||
end,
|
||||
})
|
||||
|
||||
vim.schedule(function()
|
||||
M._init(api.nvim_get_current_tabpage())
|
||||
if Config.hints.enabled then M.current.selection:setup_autocmds() end
|
||||
end)
|
||||
|
||||
api.nvim_create_autocmd("ColorSchemePre", {
|
||||
group = H.augroup,
|
||||
callback = function() require("avante.highlights").setup() end,
|
||||
})
|
||||
|
||||
api.nvim_create_autocmd("ColorScheme", {
|
||||
group = H.augroup,
|
||||
callback = function() require("avante.highlights").setup() end,
|
||||
})
|
||||
|
||||
-- automatically setup Avante filetype to markdown
|
||||
vim.treesitter.language.register("markdown", "Avante")
|
||||
|
||||
vim.filetype.add({
|
||||
extension = {
|
||||
["avanterules"] = "jinja",
|
||||
},
|
||||
pattern = {
|
||||
["%.avanterules%.[%w_.-]+"] = "jinja",
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
---@param current boolean? false to disable setting current, otherwise use this to track across tabs.
|
||||
---@return avante.Sidebar, avante.Selection, avante.Suggestion
|
||||
function M.get(current)
|
||||
local tab = api.nvim_get_current_tabpage()
|
||||
local sidebar = M.sidebars[tab]
|
||||
local selection = M.selections[tab]
|
||||
local suggestion = M.suggestions[tab]
|
||||
if current ~= false then
|
||||
M.current.sidebar = sidebar
|
||||
M.current.selection = selection
|
||||
M.current.suggestion = suggestion
|
||||
end
|
||||
return sidebar, selection, suggestion
|
||||
end
|
||||
|
||||
---@param id integer
|
||||
function M._init(id)
|
||||
local sidebar = M.sidebars[id]
|
||||
local selection = M.selections[id]
|
||||
local suggestion = M.suggestions[id]
|
||||
|
||||
if not sidebar then
|
||||
sidebar = Sidebar:new(id)
|
||||
M.sidebars[id] = sidebar
|
||||
end
|
||||
if not selection then
|
||||
selection = Selection:new(id)
|
||||
M.selections[id] = selection
|
||||
end
|
||||
if not suggestion then
|
||||
suggestion = Suggestion:new(id)
|
||||
M.suggestions[id] = suggestion
|
||||
end
|
||||
M.current = { sidebar = sidebar, selection = selection, suggestion = suggestion }
|
||||
return M
|
||||
end
|
||||
|
||||
M.toggle = { api = true }
|
||||
|
||||
---@param opts? AskOptions
|
||||
M.toggle_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())
|
||||
---@cast opts SidebarOpenOptions
|
||||
M.current.sidebar:open(opts)
|
||||
return true
|
||||
end
|
||||
|
||||
return sidebar:toggle(opts)
|
||||
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
|
||||
---@cast opts SidebarOpenOptions
|
||||
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({
|
||||
name = "debug",
|
||||
get = function() return Config.debug end,
|
||||
set = function(state) Config.override({ debug = state }) end,
|
||||
}))
|
||||
|
||||
M.toggle.hint = H.api(Utils.toggle_wrap({
|
||||
name = "hint",
|
||||
get = function() return Config.hints.enabled end,
|
||||
set = function(state) Config.override({ hints = { enabled = state } }) end,
|
||||
}))
|
||||
|
||||
M.toggle.suggestion = H.api(Utils.toggle_wrap({
|
||||
name = "suggestion",
|
||||
get = function() return Config.behaviour.auto_suggestions end,
|
||||
set = function(state)
|
||||
Config.override({ behaviour = { auto_suggestions = state } })
|
||||
local _, _, sg = M.get()
|
||||
if state ~= false then
|
||||
if sg then sg:setup_autocmds() end
|
||||
H.keymaps()
|
||||
else
|
||||
if sg then sg:delete_autocmds() end
|
||||
end
|
||||
end,
|
||||
}))
|
||||
|
||||
setmetatable(M.toggle, {
|
||||
__index = M.toggle,
|
||||
__call = function() M.toggle_sidebar() end,
|
||||
})
|
||||
|
||||
---@param opts? avante.Config
|
||||
function M.setup(opts)
|
||||
---PERF: we can still allow running require("avante").setup() multiple times to override config if users wish to
|
||||
---but most of the other functionality will only be called once from lazy.nvim
|
||||
Config.setup(opts)
|
||||
|
||||
if M.did_setup then return end
|
||||
|
||||
H.load_path()
|
||||
|
||||
require("avante.html2md").setup()
|
||||
require("avante.repo_map").setup()
|
||||
require("avante.path").setup()
|
||||
require("avante.highlights").setup()
|
||||
require("avante.diff").setup()
|
||||
require("avante.providers").setup()
|
||||
require("avante.clipboard").setup()
|
||||
|
||||
-- setup helpers
|
||||
H.autocmds()
|
||||
H.keymaps()
|
||||
H.signs()
|
||||
|
||||
M.did_setup = true
|
||||
|
||||
local function run_rag_service()
|
||||
local started_at = os.time()
|
||||
local add_resource_with_delay
|
||||
local function add_resource()
|
||||
local is_ready = RagService.is_ready()
|
||||
if not is_ready then
|
||||
local elapsed = os.time() - started_at
|
||||
if elapsed > 1000 * 60 * 15 then
|
||||
Utils.warn("Rag Service is not ready, giving up")
|
||||
return
|
||||
end
|
||||
add_resource_with_delay()
|
||||
return
|
||||
end
|
||||
vim.defer_fn(function()
|
||||
Utils.info("Adding project root to Rag Service ...")
|
||||
local uri = "file://" .. Utils.get_project_root()
|
||||
if uri:sub(-1) ~= "/" then uri = uri .. "/" end
|
||||
RagService.add_resource(uri)
|
||||
Utils.info("Added project root to Rag Service")
|
||||
end, 5000)
|
||||
end
|
||||
add_resource_with_delay = function()
|
||||
vim.defer_fn(function() add_resource() end, 5000)
|
||||
end
|
||||
vim.schedule(function()
|
||||
Utils.info("Starting Rag Service ...")
|
||||
RagService.launch_rag_service(add_resource_with_delay)
|
||||
end)
|
||||
end
|
||||
|
||||
if Config.rag_service.enabled then run_rag_service() end
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -21,7 +21,7 @@ local group = api.nvim_create_augroup("avante_llm", { clear = true })
|
||||
|
||||
---@param opts AvanteGeneratePromptsOptions
|
||||
---@return AvantePromptOptions
|
||||
M.generate_prompts = function(opts)
|
||||
function M.generate_prompts(opts)
|
||||
local provider = opts.provider or Providers[Config.provider]
|
||||
local mode = opts.mode or "planning"
|
||||
---@type AvanteProviderFunctor | AvanteBedrockProviderFunctor
|
||||
@@ -141,7 +141,7 @@ end
|
||||
|
||||
---@param opts AvanteGeneratePromptsOptions
|
||||
---@return integer
|
||||
M.calculate_tokens = function(opts)
|
||||
function M.calculate_tokens(opts)
|
||||
local prompt_opts = M.generate_prompts(opts)
|
||||
local tokens = Utils.tokens.calculate_tokens(prompt_opts.system_prompt)
|
||||
for _, message in ipairs(prompt_opts.messages) do
|
||||
@@ -151,7 +151,7 @@ M.calculate_tokens = function(opts)
|
||||
end
|
||||
|
||||
---@param opts AvanteLLMStreamOptions
|
||||
M._stream = function(opts)
|
||||
function M._stream(opts)
|
||||
local provider = opts.provider or Providers[Config.provider]
|
||||
|
||||
local prompt_opts = M.generate_prompts(opts)
|
||||
@@ -353,7 +353,7 @@ local function _collector_add_response(collector, index, response, opts)
|
||||
end
|
||||
end
|
||||
|
||||
M._dual_boost_stream = function(opts, Provider1, Provider2)
|
||||
function M._dual_boost_stream(opts, Provider1, Provider2)
|
||||
Utils.debug("Starting Dual Boost Stream")
|
||||
|
||||
local collector = {
|
||||
@@ -408,7 +408,7 @@ M._dual_boost_stream = function(opts, Provider1, Provider2)
|
||||
end
|
||||
|
||||
---@param opts AvanteLLMStreamOptions
|
||||
M.stream = function(opts)
|
||||
function M.stream(opts)
|
||||
local is_completed = false
|
||||
if opts.on_tool_log ~= nil then
|
||||
local original_on_tool_log = opts.on_tool_log
|
||||
|
||||
@@ -30,7 +30,7 @@ local History = {}
|
||||
-- Get a chat history file name given a buffer
|
||||
---@param bufnr integer
|
||||
---@return string
|
||||
History.filename = function(bufnr)
|
||||
function History.filename(bufnr)
|
||||
local project_root = Utils.root.get({
|
||||
buf = bufnr,
|
||||
})
|
||||
@@ -43,12 +43,12 @@ end
|
||||
-- Returns the Path to the chat history file for the given buffer.
|
||||
---@param bufnr integer
|
||||
---@return Path
|
||||
History.get = function(bufnr) return Path:new(Config.history.storage_path):joinpath(History.filename(bufnr)) end
|
||||
function History.get(bufnr) return Path:new(Config.history.storage_path):joinpath(History.filename(bufnr)) end
|
||||
|
||||
-- Loads the chat history for the given buffer.
|
||||
---@param bufnr integer
|
||||
---@return avante.ChatHistoryEntry[]
|
||||
History.load = function(bufnr)
|
||||
function History.load(bufnr)
|
||||
local history_file = History.get(bufnr)
|
||||
local cached_key = tostring(history_file:absolute())
|
||||
local cached_value = history_file_cache:get(cached_key)
|
||||
@@ -79,7 +79,7 @@ local Prompt = {}
|
||||
|
||||
-- Given a mode, return the file name for the custom prompt.
|
||||
---@param mode AvanteLlmMode
|
||||
Prompt.get_mode_file = function(mode) return string.format("custom.%s.avanterules", mode) end
|
||||
function Prompt.get_mode_file(mode) return string.format("custom.%s.avanterules", mode) end
|
||||
|
||||
---@class AvanteTemplates
|
||||
---@field initialize fun(directory: string): nil
|
||||
@@ -92,7 +92,7 @@ Prompt.templates = { planning = nil, editing = nil, suggesting = nil }
|
||||
-- PERF: Hmm instead of copy to cache, we can also load in globals context, but it requires some work on bindings. (eh maybe?)
|
||||
---@param project_root string
|
||||
---@return string the resulted cache_directory to be loaded with avante_templates
|
||||
Prompt.get = function(project_root)
|
||||
function Prompt.get(project_root)
|
||||
if not P.available() then error("Make sure to build avante (missing avante_templates)", 2) end
|
||||
|
||||
-- get root directory of given bufnr
|
||||
@@ -134,20 +134,20 @@ Prompt.get = function(project_root)
|
||||
end
|
||||
|
||||
---@param mode AvanteLlmMode
|
||||
Prompt.get_file = function(mode)
|
||||
function Prompt.get_file(mode)
|
||||
if Prompt.templates[mode] ~= nil then return Prompt.get_mode_file(mode) end
|
||||
return string.format("%s.avanterules", mode)
|
||||
end
|
||||
|
||||
---@param path string
|
||||
---@param opts AvanteTemplateOptions
|
||||
Prompt.render_file = function(path, opts) return templates.render(path, opts) end
|
||||
function Prompt.render_file(path, opts) return templates.render(path, opts) end
|
||||
|
||||
---@param mode AvanteLlmMode
|
||||
---@param opts AvanteTemplateOptions
|
||||
Prompt.render_mode = function(mode, opts) return templates.render(Prompt.get_file(mode), opts) end
|
||||
function Prompt.render_mode(mode, opts) return templates.render(Prompt.get_file(mode), opts) end
|
||||
|
||||
Prompt.initialize = function(directory) templates.initialize(directory) end
|
||||
function Prompt.initialize(directory) templates.initialize(directory) end
|
||||
|
||||
P.prompts = Prompt
|
||||
|
||||
@@ -157,21 +157,21 @@ local RepoMap = {}
|
||||
---@param project_root string
|
||||
---@param ext string
|
||||
---@return string
|
||||
RepoMap.filename = function(project_root, ext)
|
||||
function RepoMap.filename(project_root, ext)
|
||||
-- Replace path separators with double underscores
|
||||
local path_with_separators = fn.substitute(project_root, "/", "__", "g")
|
||||
-- Replace other non-alphanumeric characters with single underscores
|
||||
return fn.substitute(path_with_separators, "[^A-Za-z0-9._]", "_", "g") .. "." .. ext .. ".repo_map.json"
|
||||
end
|
||||
|
||||
RepoMap.get = function(project_root, ext) return Path:new(P.data_path):joinpath(RepoMap.filename(project_root, ext)) end
|
||||
function RepoMap.get(project_root, ext) return Path:new(P.data_path):joinpath(RepoMap.filename(project_root, ext)) end
|
||||
|
||||
RepoMap.save = function(project_root, ext, data)
|
||||
function RepoMap.save(project_root, ext, data)
|
||||
local file = RepoMap.get(project_root, ext)
|
||||
file:write(vim.json.encode(data), "w")
|
||||
end
|
||||
|
||||
RepoMap.load = function(project_root, ext)
|
||||
function RepoMap.load(project_root, ext)
|
||||
local file = RepoMap.get(project_root, ext)
|
||||
if file:exists() then
|
||||
local content = file:read()
|
||||
@@ -183,7 +183,7 @@ end
|
||||
P.repo_map = RepoMap
|
||||
|
||||
---@return AvanteTemplates|nil
|
||||
P._init_templates_lib = function()
|
||||
function P._init_templates_lib()
|
||||
if templates ~= nil then return templates end
|
||||
local ok, module = pcall(require, "avante_templates")
|
||||
---@cast module AvanteTemplates
|
||||
@@ -194,7 +194,7 @@ P._init_templates_lib = function()
|
||||
return templates
|
||||
end
|
||||
|
||||
P.setup = function()
|
||||
function P.setup()
|
||||
local history_path = Path:new(Config.history.storage_path)
|
||||
if not history_path:exists() then history_path:mkdir({ parents = true }) end
|
||||
P.history_path = history_path
|
||||
@@ -210,9 +210,9 @@ P.setup = function()
|
||||
vim.defer_fn(P._init_templates_lib, 1000)
|
||||
end
|
||||
|
||||
P.available = function() return P._init_templates_lib() ~= nil end
|
||||
function P.available() return P._init_templates_lib() ~= nil end
|
||||
|
||||
P.clear = function()
|
||||
function P.clear()
|
||||
P.cache_path:rm({ recursive = true })
|
||||
P.history_path:rm({ recursive = true })
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ M.parse_messages = O.parse_messages
|
||||
M.parse_response = O.parse_response
|
||||
M.parse_response_without_stream = O.parse_response_without_stream
|
||||
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
function M.parse_curl_args(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
|
||||
local headers = {
|
||||
|
||||
@@ -7,7 +7,7 @@ local M = {}
|
||||
M.api_key_name = "BEDROCK_KEYS"
|
||||
M.use_xml_format = true
|
||||
|
||||
M.load_model_handler = function()
|
||||
function M.load_model_handler()
|
||||
local provider_conf, _ = P.parse_config(P["bedrock"])
|
||||
local bedrock_model = provider_conf.model
|
||||
if provider_conf.model:match("anthropic") then bedrock_model = "claude" end
|
||||
@@ -18,17 +18,17 @@ M.load_model_handler = function()
|
||||
error(error_msg)
|
||||
end
|
||||
|
||||
M.parse_response = function(ctx, data_stream, event_state, opts)
|
||||
function M.parse_response(ctx, data_stream, event_state, opts)
|
||||
local model_handler = M.load_model_handler()
|
||||
return model_handler.parse_response(ctx, data_stream, event_state, opts)
|
||||
end
|
||||
|
||||
M.build_bedrock_payload = function(prompt_opts, body_opts)
|
||||
function M.build_bedrock_payload(prompt_opts, body_opts)
|
||||
local model_handler = M.load_model_handler()
|
||||
return model_handler.build_bedrock_payload(prompt_opts, body_opts)
|
||||
end
|
||||
|
||||
M.parse_stream_data = function(data, opts)
|
||||
function M.parse_stream_data(data, opts)
|
||||
-- @NOTE: Decode and process Bedrock response
|
||||
-- Each response contains a Base64-encoded `bytes` field, which is decoded into JSON.
|
||||
-- The `type` field in the decoded JSON determines how the response is handled.
|
||||
@@ -44,7 +44,7 @@ end
|
||||
---@param provider AvanteBedrockProviderFunctor
|
||||
---@param prompt_opts AvantePromptOptions
|
||||
---@return table
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
function M.parse_curl_args(provider, prompt_opts)
|
||||
local base, body_opts = P.parse_config(provider)
|
||||
|
||||
local api_key = provider.parse_api_key()
|
||||
@@ -86,7 +86,7 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
}
|
||||
end
|
||||
|
||||
M.on_error = function(result)
|
||||
function M.on_error(result)
|
||||
if not result.body then
|
||||
return Utils.error("API request failed with status " .. result.status, { once = true, title = "Avante" })
|
||||
end
|
||||
|
||||
@@ -16,7 +16,7 @@ M.role_map = {
|
||||
assistant = "assistant",
|
||||
}
|
||||
|
||||
M.parse_messages = function(opts)
|
||||
function M.parse_messages(opts)
|
||||
---@type AvanteBedrockClaudeMessage[]
|
||||
local messages = {}
|
||||
|
||||
@@ -78,7 +78,7 @@ M.parse_response = Claude.parse_response
|
||||
---@param prompt_opts AvantePromptOptions
|
||||
---@param body_opts table
|
||||
---@return table
|
||||
M.build_bedrock_payload = function(prompt_opts, body_opts)
|
||||
function M.build_bedrock_payload(prompt_opts, body_opts)
|
||||
local system_prompt = prompt_opts.system_prompt or ""
|
||||
local messages = M.parse_messages(prompt_opts)
|
||||
local max_tokens = body_opts.max_tokens or 2000
|
||||
|
||||
@@ -36,7 +36,7 @@ M.role_map = {
|
||||
assistant = "assistant",
|
||||
}
|
||||
|
||||
M.parse_messages = function(opts)
|
||||
function M.parse_messages(opts)
|
||||
---@type AvanteClaudeMessage[]
|
||||
local messages = {}
|
||||
|
||||
@@ -123,7 +123,7 @@ M.parse_messages = function(opts)
|
||||
return messages
|
||||
end
|
||||
|
||||
M.parse_response = function(ctx, data_stream, event_state, opts)
|
||||
function M.parse_response(ctx, data_stream, event_state, opts)
|
||||
if event_state == nil then
|
||||
if data_stream:match('"message_start"') then
|
||||
event_state = "message_start"
|
||||
@@ -214,7 +214,7 @@ end
|
||||
---@param provider AvanteProviderFunctor
|
||||
---@param prompt_opts AvantePromptOptions
|
||||
---@return table
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
function M.parse_curl_args(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
local disable_tools = provider_conf.disable_tools or false
|
||||
|
||||
@@ -256,7 +256,7 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
}
|
||||
end
|
||||
|
||||
M.on_error = function(result)
|
||||
function M.on_error(result)
|
||||
if not result.body then
|
||||
return Utils.error("API request failed with status " .. result.status, { once = true, title = "Avante" })
|
||||
end
|
||||
|
||||
@@ -47,7 +47,7 @@ M.role_map = {
|
||||
assistant = "assistant",
|
||||
}
|
||||
|
||||
M.parse_messages = function(opts)
|
||||
function M.parse_messages(opts)
|
||||
local messages = {
|
||||
{ role = "system", content = opts.system_prompt },
|
||||
}
|
||||
@@ -57,7 +57,7 @@ M.parse_messages = function(opts)
|
||||
return { messages = messages }
|
||||
end
|
||||
|
||||
M.parse_stream_data = function(data, opts)
|
||||
function M.parse_stream_data(data, opts)
|
||||
---@type CohereChatResponse
|
||||
local json = vim.json.decode(data)
|
||||
if json.type ~= nil then
|
||||
@@ -69,7 +69,7 @@ M.parse_stream_data = function(data, opts)
|
||||
end
|
||||
end
|
||||
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
function M.parse_curl_args(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
|
||||
local headers = {
|
||||
@@ -96,7 +96,7 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
}
|
||||
end
|
||||
|
||||
M.setup = function()
|
||||
function M.setup()
|
||||
P.env.parse_envvar(M)
|
||||
require("avante.tokenizers").setup(M.tokenizer_id, false)
|
||||
vim.g.avante_login = true
|
||||
|
||||
@@ -98,7 +98,7 @@ end
|
||||
---@field oauth_token string
|
||||
---
|
||||
---@return string
|
||||
H.get_oauth_token = function()
|
||||
function H.get_oauth_token()
|
||||
local xdg_config = vim.fn.expand("$XDG_CONFIG_HOME")
|
||||
local os_name = Utils.get_os_name()
|
||||
---@type string
|
||||
@@ -138,9 +138,9 @@ H.get_oauth_token = function()
|
||||
end
|
||||
|
||||
H.chat_auth_url = "https://api.github.com/copilot_internal/v2/token"
|
||||
H.chat_completion_url = function(base_url) return Utils.url_join(base_url, "/chat/completions") end
|
||||
function H.chat_completion_url(base_url) return Utils.url_join(base_url, "/chat/completions") end
|
||||
|
||||
H.refresh_token = function(async, force)
|
||||
function H.refresh_token(async, force)
|
||||
if not M.state then error("internal initialization error") end
|
||||
|
||||
async = async == nil and true or async
|
||||
@@ -166,7 +166,7 @@ H.refresh_token = function(async, force)
|
||||
insecure = Config.copilot.allow_insecure,
|
||||
}
|
||||
|
||||
local handle_response = function(response)
|
||||
local function handle_response(response)
|
||||
if response.status == 200 then
|
||||
M.state.github_token = vim.json.decode(response.body)
|
||||
local file = Path:new(copilot_path)
|
||||
@@ -209,7 +209,7 @@ M.role_map = {
|
||||
assistant = "assistant",
|
||||
}
|
||||
|
||||
M.parse_messages = function(opts)
|
||||
function M.parse_messages(opts)
|
||||
local messages = {
|
||||
{ role = "system", content = opts.system_prompt },
|
||||
}
|
||||
@@ -245,7 +245,7 @@ end
|
||||
|
||||
M.parse_response = OpenAI.parse_response
|
||||
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
function M.parse_curl_args(provider, prompt_opts)
|
||||
-- refresh token synchronously, only if it has expired
|
||||
-- (this should rarely happen, as we refresh the token in the background)
|
||||
H.refresh_token(false, false)
|
||||
@@ -282,7 +282,7 @@ end
|
||||
|
||||
M._refresh_timer = nil
|
||||
|
||||
M.setup_timer = function()
|
||||
function M.setup_timer()
|
||||
if M._refresh_timer then
|
||||
M._refresh_timer:stop()
|
||||
M._refresh_timer:close()
|
||||
@@ -305,7 +305,7 @@ M.setup_timer = function()
|
||||
)
|
||||
end
|
||||
|
||||
M.setup_file_watcher = function()
|
||||
function M.setup_file_watcher()
|
||||
if M._file_watcher then return end
|
||||
|
||||
local copilot_token_file = Path:new(copilot_path)
|
||||
@@ -321,7 +321,7 @@ M.setup_file_watcher = function()
|
||||
)
|
||||
end
|
||||
|
||||
M.setup = function()
|
||||
function M.setup()
|
||||
local copilot_token_file = Path:new(copilot_path)
|
||||
|
||||
if not M.state then M.state = {
|
||||
@@ -351,7 +351,7 @@ M.setup = function()
|
||||
vim.g.avante_login = true
|
||||
end
|
||||
|
||||
M.cleanup = function()
|
||||
function M.cleanup()
|
||||
-- Cleanup refresh timer
|
||||
if M._refresh_timer then
|
||||
M._refresh_timer:stop()
|
||||
|
||||
@@ -12,7 +12,7 @@ M.role_map = {
|
||||
}
|
||||
-- M.tokenizer_id = "google/gemma-2b"
|
||||
|
||||
M.parse_messages = function(opts)
|
||||
function M.parse_messages(opts)
|
||||
local contents = {}
|
||||
local prev_role = nil
|
||||
|
||||
@@ -64,7 +64,7 @@ M.parse_messages = function(opts)
|
||||
}
|
||||
end
|
||||
|
||||
M.parse_response = function(ctx, data_stream, _, opts)
|
||||
function M.parse_response(ctx, data_stream, _, opts)
|
||||
local ok, json = pcall(vim.json.decode, data_stream)
|
||||
if not ok then opts.on_stop({ reason = "error", error = json }) end
|
||||
if json.candidates then
|
||||
@@ -81,7 +81,7 @@ M.parse_response = function(ctx, data_stream, _, opts)
|
||||
end
|
||||
end
|
||||
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
function M.parse_curl_args(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
|
||||
request_body = vim.tbl_deep_extend("force", request_body, {
|
||||
|
||||
@@ -29,7 +29,7 @@ E.cache = {}
|
||||
|
||||
---@param Opts AvanteSupportedProvider | AvanteProviderFunctor | AvanteBedrockProviderFunctor
|
||||
---@return string | nil
|
||||
E.parse_envvar = function(Opts)
|
||||
function E.parse_envvar(Opts)
|
||||
local api_key_name = Opts.api_key_name
|
||||
if api_key_name == nil then error("Requires api_key_name") end
|
||||
|
||||
@@ -91,7 +91,7 @@ end
|
||||
--- This will only run once and spawn a UI for users to input the envvar.
|
||||
---@param opts {refresh: boolean, provider: AvanteProviderFunctor | AvanteBedrockProviderFunctor}
|
||||
---@private
|
||||
E.setup = function(opts)
|
||||
function E.setup(opts)
|
||||
opts.provider.setup()
|
||||
|
||||
local var = opts.provider.api_key_name
|
||||
@@ -180,7 +180,7 @@ end
|
||||
E.REQUEST_LOGIN_PATTERN = "AvanteRequestLogin"
|
||||
|
||||
---@param provider AvanteDefaultBaseProvider
|
||||
E.require_api_key = function(provider)
|
||||
function E.require_api_key(provider)
|
||||
if provider["local"] ~= nil then
|
||||
if provider["local"] then
|
||||
vim.deprecate('"local" = true', "api_key_name = ''", "0.1.0", "avante.nvim")
|
||||
@@ -239,7 +239,7 @@ M = setmetatable(M, {
|
||||
end,
|
||||
})
|
||||
|
||||
M.setup = function()
|
||||
function M.setup()
|
||||
vim.g.avante_login = false
|
||||
|
||||
---@type AvanteProviderFunctor | AvanteBedrockProviderFunctor
|
||||
@@ -274,7 +274,7 @@ end
|
||||
---@param opts AvanteProvider | AvanteSupportedProvider | AvanteProviderFunctor | AvanteBedrockProviderFunctor
|
||||
---@return AvanteDefaultBaseProvider provider_opts
|
||||
---@return table<string, any> request_body
|
||||
M.parse_config = function(opts)
|
||||
function M.parse_config(opts)
|
||||
---@type AvanteDefaultBaseProvider
|
||||
local provider_opts = {}
|
||||
---@type table<string, any>
|
||||
@@ -302,7 +302,7 @@ end
|
||||
---@private
|
||||
---@param provider Provider
|
||||
---@return AvanteProviderFunctor | AvanteBedrockProviderFunctor
|
||||
M.get_config = function(provider)
|
||||
function M.get_config(provider)
|
||||
provider = provider or Config.provider
|
||||
local cur = Config.get_provider(provider)
|
||||
return type(cur) == "function" and cur() or cur
|
||||
|
||||
@@ -43,10 +43,10 @@ function M.transform_tool(tool)
|
||||
return res
|
||||
end
|
||||
|
||||
M.is_openrouter = function(url) return url:match("^https://openrouter%.ai/") end
|
||||
function M.is_openrouter(url) return url:match("^https://openrouter%.ai/") end
|
||||
|
||||
---@param opts AvantePromptOptions
|
||||
M.get_user_message = function(opts)
|
||||
function M.get_user_message(opts)
|
||||
vim.deprecate("get_user_message", "parse_messages", "0.1.0", "avante.nvim")
|
||||
return table.concat(
|
||||
vim
|
||||
@@ -61,9 +61,9 @@ M.get_user_message = function(opts)
|
||||
)
|
||||
end
|
||||
|
||||
M.is_o_series_model = function(model) return model and string.match(model, "^o%d+") ~= nil end
|
||||
function M.is_o_series_model(model) return model and string.match(model, "^o%d+") ~= nil end
|
||||
|
||||
M.parse_messages = function(opts)
|
||||
function M.parse_messages(opts)
|
||||
local messages = {}
|
||||
local provider = P[Config.provider]
|
||||
local base, _ = P.parse_config(provider)
|
||||
@@ -137,7 +137,7 @@ M.parse_messages = function(opts)
|
||||
return final_messages
|
||||
end
|
||||
|
||||
M.parse_response = function(ctx, data_stream, _, opts)
|
||||
function M.parse_response(ctx, data_stream, _, opts)
|
||||
if data_stream:match('"%[DONE%]":') then
|
||||
opts.on_stop({ reason = "complete" })
|
||||
return
|
||||
@@ -205,7 +205,7 @@ M.parse_response = function(ctx, data_stream, _, opts)
|
||||
end
|
||||
end
|
||||
|
||||
M.parse_response_without_stream = function(data, _, opts)
|
||||
function M.parse_response_without_stream(data, _, opts)
|
||||
---@type AvanteOpenAIChatResponse
|
||||
local json = vim.json.decode(data)
|
||||
if json.choices and json.choices[1] then
|
||||
@@ -217,7 +217,7 @@ M.parse_response_without_stream = function(data, _, opts)
|
||||
end
|
||||
end
|
||||
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
function M.parse_curl_args(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
local disable_tools = provider_conf.disable_tools or false
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ local function execute_command(command)
|
||||
return result:match("^%s*(.-)%s*$")
|
||||
end
|
||||
|
||||
M.parse_api_key = function()
|
||||
function M.parse_api_key()
|
||||
if not M.api_key_name:match("^cmd:") then
|
||||
error("Invalid api_key_name: Expected 'cmd:<command>' format, got '" .. M.api_key_name .. "'")
|
||||
end
|
||||
@@ -31,7 +31,7 @@ M.parse_api_key = function()
|
||||
return direct_output
|
||||
end
|
||||
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
function M.parse_curl_args(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
local location = vim.fn.getenv("LOCATION") or "default-location"
|
||||
local project_id = vim.fn.getenv("PROJECT_ID") or "default-project-id"
|
||||
|
||||
@@ -289,7 +289,7 @@ end
|
||||
---@field total_count number
|
||||
|
||||
---@return AvanteRagServiceResourceListResponse | nil
|
||||
M.get_resources = function()
|
||||
function M.get_resources()
|
||||
local resp = curl.get(M.get_rag_service_url() .. "/api/v1/resources", {
|
||||
headers = {
|
||||
["Content-Type"] = "application/json",
|
||||
|
||||
@@ -146,7 +146,7 @@ function Selection:create_editing_input()
|
||||
priority = PRIORITY,
|
||||
})
|
||||
|
||||
local submit_input = function(input)
|
||||
local function submit_input(input)
|
||||
local full_response = ""
|
||||
local start_line = self.selection.range.start.lnum
|
||||
local finish_line = self.selection.range.finish.lnum
|
||||
@@ -158,10 +158,10 @@ function Selection:create_editing_input()
|
||||
self.prompt_input:start_spinner()
|
||||
|
||||
---@type AvanteLLMStartCallback
|
||||
local on_start = function(start_opts) end
|
||||
local function on_start(start_opts) end
|
||||
|
||||
---@type AvanteLLMChunkCallback
|
||||
local on_chunk = function(chunk)
|
||||
local function on_chunk(chunk)
|
||||
full_response = full_response .. chunk
|
||||
local response_lines_ = vim.split(full_response, "\n")
|
||||
local response_lines = {}
|
||||
@@ -186,7 +186,7 @@ function Selection:create_editing_input()
|
||||
end
|
||||
|
||||
---@type AvanteLLMStopCallback
|
||||
local on_stop = function(stop_opts)
|
||||
local function on_stop(stop_opts)
|
||||
if stop_opts.error then
|
||||
-- NOTE: in Ubuntu 22.04+ you will see this ignorable error from ~/.local/share/nvim/lazy/avante.nvim/lua/avante/llm.lua `on_error = function(err)`, check to avoid showing this error.
|
||||
if type(stop_opts.error) == "table" and stop_opts.error.exit == nil and stop_opts.error.stderr == "{}" then
|
||||
|
||||
@@ -1275,7 +1275,7 @@ function Sidebar:apply(current_cursor)
|
||||
|
||||
api.nvim_buf_set_lines(bufnr, 0, -1, false, new_lines)
|
||||
|
||||
local process = function(winid)
|
||||
local function process(winid)
|
||||
api.nvim_set_current_win(winid)
|
||||
api.nvim_feedkeys(api.nvim_replace_termcodes("<Esc>", true, false, true), "n", true)
|
||||
Diff.add_visited_buffer(bufnr)
|
||||
@@ -1314,7 +1314,7 @@ function Sidebar:apply(current_cursor)
|
||||
local path_ = PPath:new(filepath)
|
||||
path_:parent():mkdir({ parents = true, exists_ok = true })
|
||||
insert_conflict_contents(bufnr, snippets)
|
||||
local process = function(winid)
|
||||
local function process(winid)
|
||||
api.nvim_set_current_win(winid)
|
||||
api.nvim_feedkeys(api.nvim_replace_termcodes("<Esc>", true, false, true), "n", true)
|
||||
Diff.add_visited_buffer(bufnr)
|
||||
@@ -1947,7 +1947,7 @@ function Sidebar:update_content(content, opts)
|
||||
content = self:render_history_content(chat_history) .. "-------\n\n" .. content
|
||||
end
|
||||
if opts.stream then
|
||||
local scroll_to_bottom = function()
|
||||
local function scroll_to_bottom()
|
||||
local last_line = api.nvim_buf_line_count(self.result_container.bufnr)
|
||||
|
||||
local current_lines = Utils.get_buf_lines(last_line - 1, last_line, self.result_container.bufnr)
|
||||
@@ -2480,10 +2480,10 @@ function Sidebar:create_input_container(opts)
|
||||
vim.keymap.set("n", "G", on_G, { buffer = self.result_container.bufnr })
|
||||
|
||||
---@type AvanteLLMStartCallback
|
||||
local on_start = function(_) end
|
||||
local function on_start(_) end
|
||||
|
||||
---@type AvanteLLMChunkCallback
|
||||
local on_chunk = function(chunk)
|
||||
local function on_chunk(chunk)
|
||||
self.is_generating = true
|
||||
|
||||
original_response = original_response .. chunk
|
||||
@@ -2507,7 +2507,7 @@ function Sidebar:create_input_container(opts)
|
||||
displayed_response = cur_displayed_response
|
||||
end
|
||||
|
||||
local on_tool_log = function(tool_name, log)
|
||||
local function on_tool_log(tool_name, log)
|
||||
if transformed_response:sub(-1) ~= "\n" then transformed_response = transformed_response .. "\n" end
|
||||
transformed_response = transformed_response .. "[" .. tool_name .. "]: " .. log .. "\n"
|
||||
local breakline = ""
|
||||
@@ -2519,7 +2519,7 @@ function Sidebar:create_input_container(opts)
|
||||
end
|
||||
|
||||
---@type AvanteLLMStopCallback
|
||||
local on_stop = function(stop_opts)
|
||||
local function on_stop(stop_opts)
|
||||
self.is_generating = false
|
||||
|
||||
pcall(function()
|
||||
@@ -2586,12 +2586,12 @@ function Sidebar:create_input_container(opts)
|
||||
Llm.stream(stream_options)
|
||||
end
|
||||
|
||||
local get_position = function()
|
||||
local function get_position()
|
||||
if self:get_layout() == "vertical" then return "bottom" end
|
||||
return "right"
|
||||
end
|
||||
|
||||
local get_size = function()
|
||||
local function get_size()
|
||||
if self:get_layout() == "vertical" then return {
|
||||
height = Config.windows.input.height,
|
||||
} end
|
||||
@@ -2881,7 +2881,7 @@ end
|
||||
function Sidebar:render(opts)
|
||||
local chat_history = Path.history.load(self.code.bufnr)
|
||||
|
||||
local get_position = function()
|
||||
local function get_position()
|
||||
return (opts and opts.win and opts.win.position) and opts.win.position or calculate_config_window_position()
|
||||
end
|
||||
|
||||
@@ -2988,7 +2988,7 @@ function Sidebar:create_selected_files_container()
|
||||
|
||||
self.selected_files_container:mount()
|
||||
|
||||
local render = function()
|
||||
local function render()
|
||||
local selected_filepaths_ = self.file_selector:get_selected_filepaths()
|
||||
|
||||
if #selected_filepaths_ == 0 then
|
||||
@@ -3020,7 +3020,7 @@ function Sidebar:create_selected_files_container()
|
||||
|
||||
self.file_selector:on("update", render)
|
||||
|
||||
local remove_file = function(line_number) self.file_selector:remove_selected_filepaths_with_index(line_number) end
|
||||
local function remove_file(line_number) self.file_selector:remove_selected_filepaths_with_index(line_number) end
|
||||
|
||||
-- Function to show hint
|
||||
local function show_hint()
|
||||
|
||||
@@ -12,7 +12,7 @@ local M = {}
|
||||
|
||||
---@param model "gpt-4o" | string
|
||||
---@return AvanteTokenizer|nil
|
||||
M._init_tokenizers_lib = function(model)
|
||||
function M._init_tokenizers_lib(model)
|
||||
if tokenizers ~= nil then return tokenizers end
|
||||
|
||||
local ok, core = pcall(require, "avante_tokenizers")
|
||||
@@ -28,7 +28,7 @@ end
|
||||
|
||||
---@param model "gpt-4o" | string
|
||||
---@param warning? boolean
|
||||
M.setup = function(model, warning)
|
||||
function M.setup(model, warning)
|
||||
current_model = model
|
||||
warning = warning or true
|
||||
vim.defer_fn(function() M._init_tokenizers_lib(model) end, 1000)
|
||||
@@ -44,10 +44,10 @@ M.setup = function(model, warning)
|
||||
end
|
||||
end
|
||||
|
||||
M.available = function() return M._init_tokenizers_lib(current_model) ~= nil end
|
||||
function M.available() return M._init_tokenizers_lib(current_model) ~= nil end
|
||||
|
||||
---@param prompt string
|
||||
M.encode = function(prompt)
|
||||
function M.encode(prompt)
|
||||
if not M.available() then return nil end
|
||||
if not prompt or prompt == "" then return nil end
|
||||
if type(prompt) ~= "string" then error("Prompt is not type string", 2) end
|
||||
@@ -56,7 +56,7 @@ M.encode = function(prompt)
|
||||
end
|
||||
|
||||
---@param prompt string
|
||||
M.count = function(prompt)
|
||||
function M.count(prompt)
|
||||
if not M.available() then return math.ceil(#prompt * 0.5) end
|
||||
|
||||
local tokens = M.encode(prompt)
|
||||
|
||||
@@ -24,16 +24,16 @@ setmetatable(M, {
|
||||
---Check if a plugin is installed
|
||||
---@param plugin string
|
||||
---@return boolean
|
||||
M.has = function(plugin)
|
||||
function M.has(plugin)
|
||||
local ok, LazyConfig = pcall(require, "lazy.core.config")
|
||||
if ok then return LazyConfig.plugins[plugin] ~= nil end
|
||||
return package.loaded[plugin] ~= nil
|
||||
end
|
||||
|
||||
M.is_win = function() return jit.os:find("Windows") ~= nil end
|
||||
function M.is_win() return jit.os:find("Windows") ~= nil end
|
||||
|
||||
---@return "linux" | "darwin" | "windows"
|
||||
M.get_os_name = function()
|
||||
function M.get_os_name()
|
||||
local os_name = vim.uv.os_uname().sysname
|
||||
if os_name == "Linux" then
|
||||
return "linux"
|
||||
@@ -46,7 +46,7 @@ M.get_os_name = function()
|
||||
end
|
||||
end
|
||||
|
||||
M.get_system_info = function()
|
||||
function M.get_system_info()
|
||||
local os_name = vim.loop.os_uname().sysname
|
||||
local os_version = vim.loop.os_uname().release
|
||||
local os_machine = vim.loop.os_uname().machine
|
||||
@@ -75,7 +75,7 @@ end
|
||||
---@param input_cmd string
|
||||
---@param shell_cmd string?
|
||||
---@return vim.SystemCompleted
|
||||
M.shell_run = function(input_cmd, shell_cmd)
|
||||
function M.shell_run(input_cmd, shell_cmd)
|
||||
local shell = vim.o.shell:lower()
|
||||
---@type string
|
||||
local cmd
|
||||
@@ -114,7 +114,7 @@ end
|
||||
---@operator call:boolean
|
||||
|
||||
---@param toggle ToggleBind
|
||||
M.toggle_wrap = function(toggle)
|
||||
function M.toggle_wrap(toggle)
|
||||
return setmetatable(toggle, {
|
||||
__call = function()
|
||||
toggle.set(not toggle.get())
|
||||
@@ -139,7 +139,7 @@ end
|
||||
---@param rhs string|function Right-hand side |{rhs}| of the mapping, can be a Lua function.
|
||||
---
|
||||
---@param opts? vim.keymap.set.Opts
|
||||
M.safe_keymap_set = function(mode, lhs, rhs, opts)
|
||||
function M.safe_keymap_set(mode, lhs, rhs, opts)
|
||||
---@type boolean
|
||||
local ok
|
||||
---@module "lazy.core.handler"
|
||||
@@ -273,7 +273,7 @@ M.lsp = {}
|
||||
|
||||
---@param opts? vim.lsp.Client.filter
|
||||
---@return vim.lsp.Client[]
|
||||
M.lsp.get_clients = function(opts)
|
||||
function M.lsp.get_clients(opts)
|
||||
---@type vim.lsp.Client[]
|
||||
local ret = vim.lsp.get_clients(opts)
|
||||
return (opts and opts.filter) and vim.tbl_filter(opts.filter, ret) or ret
|
||||
@@ -412,7 +412,7 @@ end
|
||||
|
||||
---@param winnr? number
|
||||
---@return nil
|
||||
M.scroll_to_end = function(winnr)
|
||||
function M.scroll_to_end(winnr)
|
||||
winnr = winnr or 0
|
||||
local bufnr = api.nvim_win_get_buf(winnr)
|
||||
local lnum = api.nvim_buf_line_count(bufnr)
|
||||
@@ -422,7 +422,7 @@ end
|
||||
|
||||
---@param bufnr nil|integer
|
||||
---@return nil
|
||||
M.buf_scroll_to_end = function(bufnr)
|
||||
function M.buf_scroll_to_end(bufnr)
|
||||
for _, winnr in ipairs(M.buf_list_wins(bufnr or 0)) do
|
||||
M.scroll_to_end(winnr)
|
||||
end
|
||||
@@ -430,7 +430,7 @@ end
|
||||
|
||||
---@param bufnr nil|integer
|
||||
---@return integer[]
|
||||
M.buf_list_wins = function(bufnr)
|
||||
function M.buf_list_wins(bufnr)
|
||||
local wins = {}
|
||||
|
||||
if not bufnr or bufnr == 0 then bufnr = api.nvim_get_current_buf() end
|
||||
@@ -954,7 +954,7 @@ end
|
||||
|
||||
---Check if an icon plugin is installed
|
||||
---@return boolean
|
||||
M.icons_enabled = function() return M.has("nvim-web-devicons") or M.has("mini.icons") or M.has("mini.nvim") end
|
||||
function M.icons_enabled() return M.has("nvim-web-devicons") or M.has("mini.icons") or M.has("mini.nvim") end
|
||||
|
||||
---Display an string with icon, if an icon plugin is available.
|
||||
---Dev icons are an optional install for avante, this function prevents ugly chars
|
||||
@@ -962,7 +962,7 @@ M.icons_enabled = function() return M.has("nvim-web-devicons") or M.has("mini.ic
|
||||
---@param string_with_icon string
|
||||
---@param utf8_fallback string|nil
|
||||
---@return string
|
||||
M.icon = function(string_with_icon, utf8_fallback)
|
||||
function M.icon(string_with_icon, utf8_fallback)
|
||||
if M.icons_enabled() then
|
||||
return string_with_icon
|
||||
else
|
||||
|
||||
@@ -8,9 +8,9 @@ local function get_library_path()
|
||||
end
|
||||
|
||||
---@type fun(s: string): string
|
||||
local trim_semicolon = function(s) return s:sub(-1) == ";" and s:sub(1, -2) or s end
|
||||
local function trim_semicolon(s) return s:sub(-1) == ";" and s:sub(1, -2) or s end
|
||||
|
||||
M.load = function()
|
||||
function M.load()
|
||||
local library_path = get_library_path()
|
||||
if not string.find(package.cpath, library_path, 1, true) then
|
||||
package.cpath = trim_semicolon(package.cpath) .. ";" .. library_path
|
||||
|
||||
@@ -19,7 +19,7 @@ end
|
||||
|
||||
function commands_source:is_available() return api.nvim_get_current_buf() == self.bufnr end
|
||||
|
||||
commands_source.get_position_encoding_kind = function() return "utf-8" end
|
||||
function commands_source.get_position_encoding_kind() return "utf-8" end
|
||||
|
||||
function commands_source:get_trigger_characters() return { "/" } end
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ end
|
||||
|
||||
function mentions_source:is_available() return api.nvim_get_current_buf() == self.bufnr end
|
||||
|
||||
mentions_source.get_position_encoding_kind = function() return "utf-8" end
|
||||
function mentions_source.get_position_encoding_kind() return "utf-8" end
|
||||
|
||||
function mentions_source:get_trigger_characters() return { "@" } end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user