Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
010b23851b | ||
![]() |
24b9daee42 | ||
![]() |
11c0d13b9a | ||
![]() |
a6288b8357 | ||
![]() |
183bf7d8b0 |
@ -3,6 +3,3 @@ rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
|
||||
|
||||
[target.aarch64-apple-darwin]
|
||||
rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
|
||||
|
||||
[target.x86_64-unknown-linux-musl]
|
||||
rustflags = ["-C", "target-feature=-crt-static"]
|
||||
|
46
README.md
46
README.md
@ -60,7 +60,6 @@ For building binary if you wish to build from source, then `cargo` is required.
|
||||
timeout = 30000, -- timeout in milliseconds
|
||||
temperature = 0, -- adjust if needed
|
||||
max_tokens = 4096,
|
||||
reasoning_effort = "high" -- only supported for "o" models
|
||||
},
|
||||
},
|
||||
-- if you want to build from source then do `make BUILD_FROM_SOURCE=true`
|
||||
@ -381,44 +380,12 @@ This is achieved by emulating nvim-cmp using blink.compat
|
||||
|
||||
```lua
|
||||
file_selector = {
|
||||
--- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string | fun(params: avante.file_selector.IParams|nil): nil
|
||||
--- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string
|
||||
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.
|
||||
|
||||
```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
|
||||
|
||||
-- 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
|
||||
})
|
||||
end,
|
||||
}
|
||||
```
|
||||
|
||||
Choose a selector other that native, the default as that currently has an issue
|
||||
For lazyvim users copy the full config for blink.cmp from the website or extend the options
|
||||
```lua
|
||||
@ -504,10 +471,8 @@ Given its early stage, `avante.nvim` currently supports the following basic func
|
||||
> For Amazon Bedrock:
|
||||
>
|
||||
> ```sh
|
||||
> export BEDROCK_KEYS=aws_access_key_id,aws_secret_access_key,aws_region[,aws_session_token]
|
||||
>
|
||||
> export BEDROCK_KEYS=aws_access_key_id,aws_secret_access_key,aws_region
|
||||
> ```
|
||||
> Note: The aws_session_token is optional and only needed when using temporary AWS credentials
|
||||
|
||||
1. Open a code file in Neovim.
|
||||
2. Use the `:AvanteAsk` command to query the AI about the code.
|
||||
@ -583,16 +548,15 @@ For more information, see [Custom Providers](https://github.com/yetone/avante.nv
|
||||
|
||||
## Web Search Engines
|
||||
|
||||
Avante's tools include some web search engines, currently support [tavily](https://tavily.com/), [serpapi](https://serpapi.com/), [searchapi](https://www.searchapi.io/) and google's [programmable search engine](https://developers.google.com/custom-search/v1/overview). The default is tavily, and can be changed through configuring `Config.web_search_engine.provider`:
|
||||
Avante's tools include some web search engines, currently support [tavily](https://tavily.com/) and [serpapi](https://serpapi.com/). The default is tavily, and can be changed through configuring `Config.web_search_engine.provider`:
|
||||
|
||||
```lua
|
||||
web_search_engine = {
|
||||
provider = "tavily", -- tavily, serpapi, searchapi or google
|
||||
provider = "tavily", -- tavily or serpapi
|
||||
}
|
||||
```
|
||||
|
||||
You need to set the environment variable `TAVILY_API_KEY` , `SERPAPI_API_KEY`, `SEARCHAPI_API_KEY` to use tavily or serpapi or searchapi.
|
||||
To use google, set the `GOOGLE_SEARCH_API_KEY` as the [API key](https://developers.google.com/custom-search/v1/overview), and `GOOGLE_SEARCH_ENGINE_ID` as the [search engine](https://programmablesearchengine.google.com) ID.
|
||||
You need to set the environment variable `TAVILY_API_KEY` or `SERPAPI_API_KEY` to use tavily or serpapi.
|
||||
|
||||
## Disable Tools
|
||||
|
||||
|
@ -5,11 +5,6 @@
|
||||
|
||||
local Utils = require("avante.utils")
|
||||
|
||||
---@class avante.file_selector.IParams
|
||||
---@field public title string
|
||||
---@field public filepaths string[]
|
||||
---@field public handler fun(filepaths: string[]|nil): nil
|
||||
|
||||
---@class avante.CoreConfig: avante.Config
|
||||
local M = {}
|
||||
---@class avante.Config
|
||||
@ -33,10 +28,11 @@ M._defaults = {
|
||||
tavily = {
|
||||
api_key_name = "TAVILY_API_KEY",
|
||||
extra_request_body = {
|
||||
time_range = "d",
|
||||
include_answer = "basic",
|
||||
},
|
||||
---@type WebSearchEngineProviderResponseBodyFormatter
|
||||
format_response_body = function(body) return body.answer, nil end,
|
||||
format_response_body = function(body) return body.anwser, nil end,
|
||||
},
|
||||
serpapi = {
|
||||
api_key_name = "SERPAPI_API_KEY",
|
||||
@ -56,65 +52,11 @@ M._defaults = {
|
||||
title = result.title,
|
||||
link = result.link,
|
||||
snippet = result.snippet,
|
||||
date = result.date,
|
||||
}
|
||||
end
|
||||
)
|
||||
:take(10)
|
||||
:totable()
|
||||
return vim.json.encode(jsn), nil
|
||||
end
|
||||
return "", nil
|
||||
end,
|
||||
},
|
||||
searchapi = {
|
||||
api_key_name = "SEARCHAPI_API_KEY",
|
||||
extra_request_body = {
|
||||
engine = "google",
|
||||
},
|
||||
---@type WebSearchEngineProviderResponseBodyFormatter
|
||||
format_response_body = function(body)
|
||||
if body.answer_box ~= nil then return body.answer_box.result, nil end
|
||||
if body.organic_results ~= nil then
|
||||
local jsn = vim
|
||||
.iter(body.organic_results)
|
||||
:map(
|
||||
function(result)
|
||||
return {
|
||||
title = result.title,
|
||||
link = result.link,
|
||||
snippet = result.snippet,
|
||||
date = result.date,
|
||||
}
|
||||
end
|
||||
)
|
||||
:take(10)
|
||||
:totable()
|
||||
return vim.json.encode(jsn), nil
|
||||
end
|
||||
return "", nil
|
||||
end,
|
||||
},
|
||||
google = {
|
||||
api_key_name = "GOOGLE_SEARCH_API_KEY",
|
||||
engine_id_name = "GOOGLE_SEARCH_ENGINE_ID",
|
||||
extra_request_body = {},
|
||||
---@type WebSearchEngineProviderResponseBodyFormatter
|
||||
format_response_body = function(body)
|
||||
if body.items ~= nil then
|
||||
local jsn = vim
|
||||
.iter(body.items)
|
||||
:map(
|
||||
function(result)
|
||||
return {
|
||||
title = result.title,
|
||||
link = result.link,
|
||||
snippet = result.snippet,
|
||||
}
|
||||
end
|
||||
)
|
||||
:take(10)
|
||||
:totable()
|
||||
if #jsn > 5 then jsn = vim.list_slice(jsn, 1, 5) end
|
||||
return vim.json.encode(jsn), nil
|
||||
end
|
||||
return "", nil
|
||||
@ -314,12 +256,8 @@ M._defaults = {
|
||||
files = {
|
||||
add_current = "<leader>ac", -- Add current buffer to selected files
|
||||
},
|
||||
providers = {
|
||||
show_provides = "<leader>ap", -- Show providers selector
|
||||
},
|
||||
},
|
||||
windows = {
|
||||
|
||||
---@alias AvantePosition "right" | "left" | "top" | "bottom" | "smart"
|
||||
position = "right",
|
||||
wrap = true, -- similar to vim.o.wrap
|
||||
@ -365,7 +303,7 @@ M._defaults = {
|
||||
},
|
||||
--- @class AvanteFileSelectorConfig
|
||||
file_selector = {
|
||||
--- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string | fun(params: avante.file_selector.IParams|nil): nil
|
||||
--- @alias FileSelectorProvider "native" | "fzf" | "mini.pick" | "snacks" | "telescope" | string
|
||||
provider = "native",
|
||||
-- Options override for custom providers
|
||||
provider_opts = {},
|
||||
|
@ -330,11 +330,6 @@ function FileSelector:show_select_ui()
|
||||
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)
|
||||
else
|
||||
Utils.error("Unknown file selector provider: " .. Config.file_selector.provider)
|
||||
end
|
||||
@ -368,9 +363,9 @@ end
|
||||
function FileSelector:get_selected_files_contents()
|
||||
local contents = {}
|
||||
for _, file_path in ipairs(self.selected_filepaths) do
|
||||
local lines, error = Utils.read_file_from_buf_or_disk(file_path)
|
||||
local lines, filetype, error = Utils.read_file_from_buf_or_disk(file_path)
|
||||
lines = lines or {}
|
||||
local filetype = Utils.get_filetype(file_path)
|
||||
filetype = filetype or "unknown"
|
||||
if error ~= nil then
|
||||
Utils.error("error reading file: " .. error)
|
||||
else
|
||||
|
@ -23,7 +23,9 @@ M.check = function()
|
||||
end
|
||||
|
||||
-- Optional dependencies
|
||||
if Utils.icons_enabled() then
|
||||
local has_devicons = Utils.has("nvim-web-devicons")
|
||||
local has_mini_icons = Utils.has("mini.icons") or Utils.has("mini.nvim")
|
||||
if has_devicons or has_mini_icons then
|
||||
H.ok("Found icons plugin (nvim-web-devicons or mini.icons)")
|
||||
else
|
||||
H.warn("No icons plugin found (nvim-web-devicons or mini.icons). Icons will not be displayed")
|
||||
|
@ -68,11 +68,6 @@ H.keymaps = function()
|
||||
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", "<Plug>(AvanteShowProviders)", function()
|
||||
local sidebar = M.get()
|
||||
if sidebar then sidebar:show_providers_selector() end
|
||||
end, { noremap = true, silent = true })
|
||||
|
||||
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)
|
||||
@ -126,14 +121,6 @@ H.keymaps = function()
|
||||
function() M.toggle.suggestion() end,
|
||||
{ desc = "avante: toggle suggestion" }
|
||||
)
|
||||
Utils.safe_keymap_set("n", Config.mappings.show_providers, function()
|
||||
local sidebar = M.get()
|
||||
if sidebar then sidebar:show_providers_selector() end
|
||||
end, {
|
||||
desc = "avante: show providers selector",
|
||||
noremap = true,
|
||||
silent = true,
|
||||
})
|
||||
Utils.safe_keymap_set("n", Config.mappings.toggle.repomap, function() require("avante.repo_map").show() end, {
|
||||
desc = "avante: display repo map",
|
||||
noremap = true,
|
||||
|
@ -25,8 +25,8 @@ M.generate_prompts = function(opts)
|
||||
local Provider = opts.provider or P[Config.provider]
|
||||
local mode = opts.mode or "planning"
|
||||
---@type AvanteProviderFunctor | AvanteBedrockProviderFunctor
|
||||
local _, request_body = P.parse_config(Provider)
|
||||
local max_tokens = request_body.max_tokens or 4096
|
||||
local _, body_opts = P.parse_config(Provider)
|
||||
local max_tokens = body_opts.max_tokens or 4096
|
||||
|
||||
-- Check if the instructions contains an image path
|
||||
local image_paths = {}
|
||||
@ -449,7 +449,7 @@ M.stream = function(opts)
|
||||
return original_on_stop(stop_opts)
|
||||
end)
|
||||
end
|
||||
if Config.dual_boost.enabled and opts.mode == "planning" then
|
||||
if Config.dual_boost.enabled then
|
||||
M._dual_boost_stream(opts, P[Config.dual_boost.first_provider], P[Config.dual_boost.second_provider])
|
||||
else
|
||||
M._stream(opts)
|
||||
|
@ -7,7 +7,6 @@ local M = {}
|
||||
---@param rel_path string
|
||||
---@return string
|
||||
local function get_abs_path(rel_path)
|
||||
if Path:new(rel_path):is_absolute() then return rel_path end
|
||||
local project_root = Utils.get_project_root()
|
||||
return Path:new(project_root):joinpath(rel_path):absolute()
|
||||
end
|
||||
@ -42,12 +41,13 @@ function M.list_files(opts, on_log)
|
||||
add_dirs = true,
|
||||
depth = opts.depth,
|
||||
})
|
||||
local filepaths = {}
|
||||
local result = ""
|
||||
for _, file in ipairs(files) do
|
||||
local uniform_path = Utils.uniform_path(file)
|
||||
table.insert(filepaths, uniform_path)
|
||||
result = result .. uniform_path .. "\n"
|
||||
end
|
||||
return vim.json.encode(filepaths), nil
|
||||
result = result:gsub("\n$", "")
|
||||
return result, nil
|
||||
end
|
||||
|
||||
---@param opts { rel_path: string, keyword: string }
|
||||
@ -62,11 +62,12 @@ function M.search_files(opts, on_log)
|
||||
local files = Utils.scan_directory_respect_gitignore({
|
||||
directory = abs_path,
|
||||
})
|
||||
local filepaths = {}
|
||||
local result = ""
|
||||
for _, file in ipairs(files) do
|
||||
if file:find(opts.keyword) then table.insert(filepaths, file) end
|
||||
if file:find(opts.keyword) then result = result .. file .. "\n" end
|
||||
end
|
||||
return vim.json.encode(filepaths), nil
|
||||
result = result:gsub("\n$", "")
|
||||
return result, nil
|
||||
end
|
||||
|
||||
---@param opts { rel_path: string, keyword: string }
|
||||
@ -103,9 +104,7 @@ function M.search(opts, on_log)
|
||||
if on_log then on_log("Running command: " .. cmd) end
|
||||
local result = vim.fn.system(cmd)
|
||||
|
||||
local filepaths = vim.split(result, "\n")
|
||||
|
||||
return vim.json.encode(filepaths), nil
|
||||
return result or "", nil
|
||||
end
|
||||
|
||||
---@param opts { rel_path: string }
|
||||
@ -184,10 +183,9 @@ function M.rename_file(opts, on_log)
|
||||
end
|
||||
|
||||
---@param opts { rel_path: string, new_rel_path: string }
|
||||
---@param on_log? fun(log: string): nil
|
||||
---@return boolean success
|
||||
---@return string|nil error
|
||||
function M.copy_file(opts, on_log)
|
||||
function M.copy_file(opts)
|
||||
local abs_path = get_abs_path(opts.rel_path)
|
||||
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
|
||||
if not Path:new(abs_path):exists() then return false, "File not found: " .. abs_path end
|
||||
@ -195,47 +193,38 @@ function M.copy_file(opts, on_log)
|
||||
local new_abs_path = get_abs_path(opts.new_rel_path)
|
||||
if not has_permission_to_access(new_abs_path) then return false, "No permission to access path: " .. new_abs_path end
|
||||
if Path:new(new_abs_path):exists() then return false, "File already exists: " .. new_abs_path end
|
||||
if on_log then on_log("Copying file: " .. abs_path .. " to " .. new_abs_path) end
|
||||
Path:new(new_abs_path):write(Path:new(abs_path):read())
|
||||
return true, nil
|
||||
end
|
||||
|
||||
---@param opts { rel_path: string }
|
||||
---@param on_log? fun(log: string): nil
|
||||
---@return boolean success
|
||||
---@return string|nil error
|
||||
function M.delete_file(opts, on_log)
|
||||
function M.delete_file(opts)
|
||||
local abs_path = get_abs_path(opts.rel_path)
|
||||
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
|
||||
if not Path:new(abs_path):exists() then return false, "File not found: " .. abs_path end
|
||||
if not Path:new(abs_path):is_file() then return false, "Path is not a file: " .. abs_path end
|
||||
if not M.confirm("Are you sure you want to delete the file: " .. abs_path) then return false, "User canceled" end
|
||||
if on_log then on_log("Deleting file: " .. abs_path) end
|
||||
os.remove(abs_path)
|
||||
return true, nil
|
||||
end
|
||||
|
||||
---@param opts { rel_path: string }
|
||||
---@param on_log? fun(log: string): nil
|
||||
---@return boolean success
|
||||
---@return string|nil error
|
||||
function M.create_dir(opts, on_log)
|
||||
function M.create_dir(opts)
|
||||
local abs_path = get_abs_path(opts.rel_path)
|
||||
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
|
||||
if Path:new(abs_path):exists() then return false, "Directory already exists: " .. abs_path end
|
||||
if not M.confirm("Are you sure you want to create the directory: " .. abs_path) then
|
||||
return false, "User canceled"
|
||||
end
|
||||
if on_log then on_log("Creating directory: " .. abs_path) end
|
||||
Path:new(abs_path):mkdir({ parents = true })
|
||||
return true, nil
|
||||
end
|
||||
|
||||
---@param opts { rel_path: string, new_rel_path: string }
|
||||
---@param on_log? fun(log: string): nil
|
||||
---@return boolean success
|
||||
---@return string|nil error
|
||||
function M.rename_dir(opts, on_log)
|
||||
function M.rename_dir(opts)
|
||||
local abs_path = get_abs_path(opts.rel_path)
|
||||
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
|
||||
if not Path:new(abs_path):exists() then return false, "Directory not found: " .. abs_path end
|
||||
@ -246,16 +235,14 @@ function M.rename_dir(opts, on_log)
|
||||
if not M.confirm("Are you sure you want to rename directory " .. abs_path .. " to " .. new_abs_path .. "?") then
|
||||
return false, "User canceled"
|
||||
end
|
||||
if on_log then on_log("Renaming directory: " .. abs_path .. " to " .. new_abs_path) end
|
||||
os.rename(abs_path, new_abs_path)
|
||||
return true, nil
|
||||
end
|
||||
|
||||
---@param opts { rel_path: string }
|
||||
---@param on_log? fun(log: string): nil
|
||||
---@return boolean success
|
||||
---@return string|nil error
|
||||
function M.delete_dir(opts, on_log)
|
||||
function M.delete_dir(opts)
|
||||
local abs_path = get_abs_path(opts.rel_path)
|
||||
if not has_permission_to_access(abs_path) then return false, "No permission to access path: " .. abs_path end
|
||||
if not Path:new(abs_path):exists() then return false, "Directory not found: " .. abs_path end
|
||||
@ -263,7 +250,6 @@ function M.delete_dir(opts, on_log)
|
||||
if not M.confirm("Are you sure you want to delete the directory: " .. abs_path) then
|
||||
return false, "User canceled"
|
||||
end
|
||||
if on_log then on_log("Deleting directory: " .. abs_path) end
|
||||
os.remove(abs_path)
|
||||
return true, nil
|
||||
end
|
||||
@ -340,45 +326,6 @@ function M.web_search(opts, on_log)
|
||||
if resp.status ~= 200 then return nil, "Error: " .. resp.body end
|
||||
local jsn = vim.json.decode(resp.body)
|
||||
return search_engine.format_response_body(jsn)
|
||||
elseif provider_type == "searchapi" then
|
||||
local query_params = vim.tbl_deep_extend("force", {
|
||||
api_key = api_key,
|
||||
q = opts.query,
|
||||
}, search_engine.extra_request_body)
|
||||
local query_string = ""
|
||||
for key, value in pairs(query_params) do
|
||||
query_string = query_string .. key .. "=" .. vim.uri_encode(value) .. "&"
|
||||
end
|
||||
local resp = curl.get("https://searchapi.io/api/v1/search?" .. query_string, {
|
||||
headers = {
|
||||
["Content-Type"] = "application/json",
|
||||
},
|
||||
})
|
||||
if resp.status ~= 200 then return nil, "Error: " .. resp.body end
|
||||
local jsn = vim.json.decode(resp.body)
|
||||
return search_engine.format_response_body(jsn)
|
||||
elseif provider_type == "google" then
|
||||
local engine_id = os.getenv(search_engine.engine_id_name)
|
||||
if engine_id == nil or engine_id == "" then
|
||||
return nil, "Environment variable " .. search_engine.engine_id_namee .. " is not set"
|
||||
end
|
||||
local query_params = vim.tbl_deep_extend("force", {
|
||||
key = api_key,
|
||||
cx = engine_id,
|
||||
q = opts.query,
|
||||
}, search_engine.extra_request_body)
|
||||
local query_string = ""
|
||||
for key, value in pairs(query_params) do
|
||||
query_string = query_string .. key .. "=" .. vim.uri_encode(value) .. "&"
|
||||
end
|
||||
local resp = curl.get("https://www.googleapis.com/customsearch/v1?" .. query_string, {
|
||||
headers = {
|
||||
["Content-Type"] = "application/json",
|
||||
},
|
||||
})
|
||||
if resp.status ~= 200 then return nil, "Error: " .. resp.body end
|
||||
local jsn = vim.json.decode(resp.body)
|
||||
return search_engine.format_response_body(jsn)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -18,34 +18,31 @@ M.parse_response = O.parse_response
|
||||
M.parse_response_without_stream = O.parse_response_without_stream
|
||||
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
local base, body_opts = P.parse_config(provider)
|
||||
|
||||
local headers = {
|
||||
["Content-Type"] = "application/json",
|
||||
}
|
||||
if P.env.require_api_key(provider_conf) then headers["api-key"] = provider.parse_api_key() end
|
||||
if P.env.require_api_key(base) then headers["api-key"] = provider.parse_api_key() end
|
||||
|
||||
-- NOTE: When using "o" series set the supported parameters only
|
||||
if O.is_o_series_model(provider_conf.model) then
|
||||
request_body.max_tokens = nil
|
||||
request_body.temperature = 1
|
||||
if O.is_o_series_model(base.model) then
|
||||
body_opts.max_tokens = nil
|
||||
body_opts.temperature = 1
|
||||
end
|
||||
|
||||
return {
|
||||
url = Utils.url_join(
|
||||
provider_conf.endpoint,
|
||||
"/openai/deployments/"
|
||||
.. provider_conf.deployment
|
||||
.. "/chat/completions?api-version="
|
||||
.. provider_conf.api_version
|
||||
base.endpoint,
|
||||
"/openai/deployments/" .. base.deployment .. "/chat/completions?api-version=" .. base.api_version
|
||||
),
|
||||
proxy = provider_conf.proxy,
|
||||
insecure = provider_conf.allow_insecure,
|
||||
proxy = base.proxy,
|
||||
insecure = base.allow_insecure,
|
||||
headers = headers,
|
||||
body = vim.tbl_deep_extend("force", {
|
||||
messages = M.parse_messages(prompt_opts),
|
||||
stream = true,
|
||||
}, request_body),
|
||||
}, body_opts),
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
local Utils = require("avante.utils")
|
||||
local Clipboard = require("avante.clipboard")
|
||||
local P = require("avante.providers")
|
||||
|
||||
---@alias AvanteBedrockPayloadBuilder fun(prompt_opts: AvantePromptOptions, body_opts: table<string, any>): table<string, any>
|
||||
@ -16,14 +17,17 @@ M.api_key_name = "BEDROCK_KEYS"
|
||||
M.use_xml_format = true
|
||||
|
||||
M.load_model_handler = function()
|
||||
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
|
||||
local base, _ = P.parse_config(P["bedrock"])
|
||||
local bedrock_model = base.model
|
||||
if base.model:match("anthropic") then bedrock_model = "claude" end
|
||||
|
||||
local ok, model_module = pcall(require, "avante.providers.bedrock." .. bedrock_model)
|
||||
if ok then return model_module end
|
||||
local error_msg = "Bedrock model handler not found: " .. bedrock_model
|
||||
error(error_msg)
|
||||
if ok then
|
||||
return model_module
|
||||
else
|
||||
local error_msg = "Bedrock model handler not found: " .. bedrock_model
|
||||
Utils.error(error_msg, { once = true, title = "Avante" })
|
||||
end
|
||||
end
|
||||
|
||||
M.parse_response = function(ctx, data_stream, event_state, opts)
|
||||
@ -42,8 +46,8 @@ M.parse_stream_data = function(data, opts)
|
||||
-- The `type` field in the decoded JSON determines how the response is handled.
|
||||
local bedrock_match = data:gmatch("event(%b{})")
|
||||
for bedrock_data_match in bedrock_match do
|
||||
local jsn = vim.json.decode(bedrock_data_match)
|
||||
local data_stream = vim.base64.decode(jsn.bytes)
|
||||
local data = vim.json.decode(bedrock_data_match)
|
||||
local data_stream = vim.base64.decode(data.bytes)
|
||||
local json = vim.json.decode(data_stream)
|
||||
M.parse_response({}, data_stream, json.type, opts)
|
||||
end
|
||||
@ -56,12 +60,10 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
local base, body_opts = P.parse_config(provider)
|
||||
|
||||
local api_key = provider.parse_api_key()
|
||||
if api_key == nil then error("Cannot get the bedrock api key!") end
|
||||
local parts = vim.split(api_key, ",")
|
||||
local aws_access_key_id = parts[1]
|
||||
local aws_secret_access_key = parts[2]
|
||||
local aws_region = parts[3]
|
||||
local aws_session_token = parts[4]
|
||||
|
||||
local endpoint = string.format(
|
||||
"https://bedrock-runtime.%s.amazonaws.com/model/%s/invoke-with-response-stream",
|
||||
@ -73,8 +75,6 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
["Content-Type"] = "application/json",
|
||||
}
|
||||
|
||||
if aws_session_token and aws_session_token ~= "" then headers["x-amz-security-token"] = aws_session_token end
|
||||
|
||||
local body_payload = M.build_bedrock_payload(prompt_opts, body_opts)
|
||||
|
||||
local rawArgs = {
|
||||
@ -105,6 +105,7 @@ M.on_error = function(result)
|
||||
end
|
||||
|
||||
local error_msg = body.error.message
|
||||
local error_type = body.error.type
|
||||
|
||||
Utils.error(error_msg, { once = true, title = "Avante" })
|
||||
end
|
||||
|
@ -226,7 +226,7 @@ end
|
||||
---@param prompt_opts AvantePromptOptions
|
||||
---@return table
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
local base, body_opts = P.parse_config(provider)
|
||||
|
||||
local headers = {
|
||||
["Content-Type"] = "application/json",
|
||||
@ -234,7 +234,7 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
["anthropic-beta"] = "prompt-caching-2024-07-31",
|
||||
}
|
||||
|
||||
if P.env.require_api_key(provider_conf) then headers["x-api-key"] = provider.parse_api_key() end
|
||||
if P.env.require_api_key(base) then headers["x-api-key"] = provider.parse_api_key() end
|
||||
|
||||
local messages = M.parse_messages(prompt_opts)
|
||||
|
||||
@ -246,12 +246,12 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
end
|
||||
|
||||
return {
|
||||
url = Utils.url_join(provider_conf.endpoint, "/v1/messages"),
|
||||
proxy = provider_conf.proxy,
|
||||
insecure = provider_conf.allow_insecure,
|
||||
url = Utils.url_join(base.endpoint, "/v1/messages"),
|
||||
proxy = base.proxy,
|
||||
insecure = base.allow_insecure,
|
||||
headers = headers,
|
||||
body = vim.tbl_deep_extend("force", {
|
||||
model = provider_conf.model,
|
||||
model = base.model,
|
||||
system = {
|
||||
{
|
||||
type = "text",
|
||||
@ -262,7 +262,7 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
messages = messages,
|
||||
tools = tools,
|
||||
stream = true,
|
||||
}, request_body),
|
||||
}, body_opts),
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -70,7 +70,7 @@ M.parse_stream_data = function(data, opts)
|
||||
end
|
||||
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
local base, body_opts = P.parse_config(provider)
|
||||
|
||||
local headers = {
|
||||
["Accept"] = "application/json",
|
||||
@ -82,17 +82,17 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
.. "."
|
||||
.. vim.version().patch,
|
||||
}
|
||||
if P.env.require_api_key(provider_conf) then headers["Authorization"] = "Bearer " .. provider.parse_api_key() end
|
||||
if P.env.require_api_key(base) then headers["Authorization"] = "Bearer " .. provider.parse_api_key() end
|
||||
|
||||
return {
|
||||
url = Utils.url_join(provider_conf.endpoint, "/chat"),
|
||||
proxy = provider_conf.proxy,
|
||||
insecure = provider_conf.allow_insecure,
|
||||
url = Utils.url_join(base.endpoint, "/chat"),
|
||||
proxy = base.proxy,
|
||||
insecure = base.allow_insecure,
|
||||
headers = headers,
|
||||
body = vim.tbl_deep_extend("force", {
|
||||
model = provider_conf.model,
|
||||
model = base.model,
|
||||
stream = true,
|
||||
}, M.parse_messages(prompt_opts), request_body),
|
||||
}, M.parse_messages(prompt_opts), body_opts),
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -249,7 +249,7 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
-- (this should rarely happen, as we refresh the token in the background)
|
||||
H.refresh_token(false, false)
|
||||
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
local base, body_opts = P.parse_config(provider)
|
||||
|
||||
local tools = {}
|
||||
if prompt_opts.tools then
|
||||
@ -259,10 +259,10 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
end
|
||||
|
||||
return {
|
||||
url = H.chat_completion_url(provider_conf.endpoint),
|
||||
timeout = provider_conf.timeout,
|
||||
proxy = provider_conf.proxy,
|
||||
insecure = provider_conf.allow_insecure,
|
||||
url = H.chat_completion_url(base.endpoint),
|
||||
timeout = base.timeout,
|
||||
proxy = base.proxy,
|
||||
insecure = base.allow_insecure,
|
||||
headers = {
|
||||
["Content-Type"] = "application/json",
|
||||
["Authorization"] = "Bearer " .. M.state.github_token.token,
|
||||
@ -270,11 +270,11 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
["Editor-Version"] = ("Neovim/%s.%s.%s"):format(vim.version().major, vim.version().minor, vim.version().patch),
|
||||
},
|
||||
body = vim.tbl_deep_extend("force", {
|
||||
model = provider_conf.model,
|
||||
model = base.model,
|
||||
messages = M.parse_messages(prompt_opts),
|
||||
stream = true,
|
||||
tools = tools,
|
||||
}, request_body),
|
||||
}, body_opts),
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -82,29 +82,26 @@ M.parse_response = function(ctx, data_stream, _, opts)
|
||||
end
|
||||
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
local base, body_opts = P.parse_config(provider)
|
||||
|
||||
request_body = vim.tbl_deep_extend("force", request_body, {
|
||||
body_opts = vim.tbl_deep_extend("force", body_opts, {
|
||||
generationConfig = {
|
||||
temperature = request_body.temperature,
|
||||
maxOutputTokens = request_body.max_tokens,
|
||||
temperature = body_opts.temperature,
|
||||
maxOutputTokens = body_opts.max_tokens,
|
||||
},
|
||||
})
|
||||
request_body.temperature = nil
|
||||
request_body.max_tokens = nil
|
||||
body_opts.temperature = nil
|
||||
body_opts.max_tokens = nil
|
||||
|
||||
local api_key = provider.parse_api_key()
|
||||
if api_key == nil then error("Cannot get the gemini api key!") end
|
||||
|
||||
return {
|
||||
url = Utils.url_join(
|
||||
provider_conf.endpoint,
|
||||
provider_conf.model .. ":streamGenerateContent?alt=sse&key=" .. api_key
|
||||
),
|
||||
proxy = provider_conf.proxy,
|
||||
insecure = provider_conf.allow_insecure,
|
||||
url = Utils.url_join(base.endpoint, base.model .. ":streamGenerateContent?alt=sse&key=" .. api_key),
|
||||
proxy = base.proxy,
|
||||
insecure = base.allow_insecure,
|
||||
headers = { ["Content-Type"] = "application/json" },
|
||||
body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), request_body),
|
||||
body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), body_opts),
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -64,7 +64,6 @@ local DressingState = { winid = nil, input_winid = nil, input_bufnr = nil }
|
||||
---@field __inherited_from? string
|
||||
---@field temperature? number
|
||||
---@field max_tokens? number
|
||||
---@field reasoning_effort? string
|
||||
---
|
||||
---@class AvanteLLMUsage
|
||||
---@field input_tokens number
|
||||
@ -348,9 +347,9 @@ M = setmetatable(M, {
|
||||
if t[k].has == nil then t[k].has = function() return E.parse_envvar(t[k]) ~= nil end end
|
||||
|
||||
if t[k].setup == nil then
|
||||
local provider_conf = M.parse_config(t[k])
|
||||
local base = M.parse_config(t[k])
|
||||
t[k].setup = function()
|
||||
if E.require_api_key(provider_conf) then t[k].parse_api_key() end
|
||||
if E.require_api_key(base) then t[k].parse_api_key() end
|
||||
require("avante.tokenizers").setup(t[k].tokenizer_id)
|
||||
end
|
||||
end
|
||||
|
@ -223,7 +223,7 @@ M.parse_response = function(ctx, data_stream, _, opts)
|
||||
end
|
||||
ctx.last_think_content = choice.delta.reasoning
|
||||
opts.on_chunk(choice.delta.reasoning)
|
||||
elseif choice.delta.tool_calls and choice.delta.tool_calls ~= vim.NIL then
|
||||
elseif choice.delta.tool_calls then
|
||||
local tool_call = choice.delta.tool_calls[1]
|
||||
if not ctx.tool_use_list then ctx.tool_use_list = {} end
|
||||
if not ctx.tool_use_list[tool_call.index + 1] then
|
||||
@ -272,14 +272,13 @@ end
|
||||
|
||||
local Log = require("avante.utils.log")
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
local disable_tools = provider_conf.disable_tools or false
|
||||
local base, body_opts = P.parse_config(provider)
|
||||
local disable_tools = base.disable_tools or false
|
||||
|
||||
local headers = {
|
||||
["Content-Type"] = "application/json",
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
-- Add appid header for baidu provider
|
||||
if Config.provider == "baidu" then
|
||||
local baidu_config = Config.get_provider("baidu")
|
||||
@ -290,9 +289,6 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
end
|
||||
|
||||
if P.env.require_api_key(base) then
|
||||
=======
|
||||
if P.env.require_api_key(provider_conf) then
|
||||
>>>>>>> b6ae4dfe7fe443362f5f31d71797173ec12c2598
|
||||
local api_key = provider.parse_api_key()
|
||||
if api_key == nil then
|
||||
error(Config.provider .. " API key is not set, please set it in your environment variable or config file")
|
||||
@ -300,18 +296,18 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
headers["Authorization"] = "Bearer " .. api_key
|
||||
end
|
||||
|
||||
if M.is_openrouter(provider_conf.endpoint) then
|
||||
if M.is_openrouter(base.endpoint) then
|
||||
headers["HTTP-Referer"] = "https://github.com/yetone/avante.nvim"
|
||||
headers["X-Title"] = "Avante.nvim"
|
||||
request_body.include_reasoning = true
|
||||
body_opts.include_reasoning = true
|
||||
end
|
||||
|
||||
-- NOTE: When using "o" series set the supported parameters only
|
||||
local stream = true
|
||||
if M.is_o_series_model(provider_conf.model) then
|
||||
request_body.max_completion_tokens = request_body.max_tokens
|
||||
request_body.max_tokens = nil
|
||||
request_body.temperature = 1
|
||||
if M.is_o_series_model(base.model) then
|
||||
body_opts.max_completion_tokens = body_opts.max_tokens
|
||||
body_opts.max_tokens = nil
|
||||
body_opts.temperature = 1
|
||||
end
|
||||
|
||||
local tools = nil
|
||||
@ -322,20 +318,20 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
end
|
||||
end
|
||||
|
||||
Utils.debug("endpoint", provider_conf.endpoint)
|
||||
Utils.debug("model", provider_conf.model)
|
||||
Utils.debug("endpoint", base.endpoint)
|
||||
Utils.debug("model", base.model)
|
||||
|
||||
return {
|
||||
url = Utils.url_join(provider_conf.endpoint, "/chat/completions"),
|
||||
proxy = provider_conf.proxy,
|
||||
insecure = provider_conf.allow_insecure,
|
||||
local request = {
|
||||
url = Utils.url_join(base.endpoint, "/chat/completions"),
|
||||
proxy = base.proxy,
|
||||
insecure = base.allow_insecure,
|
||||
headers = headers,
|
||||
body = vim.tbl_deep_extend("force", {
|
||||
model = provider_conf.model,
|
||||
model = base.model,
|
||||
messages = M.parse_messages(prompt_opts),
|
||||
stream = stream,
|
||||
tools = tools,
|
||||
}, request_body),
|
||||
}, body_opts),
|
||||
}
|
||||
-- 记录请求详细信息
|
||||
Log.log_request(request.url, request.headers, request.body)
|
||||
|
@ -32,22 +32,22 @@ M.parse_api_key = function()
|
||||
end
|
||||
|
||||
M.parse_curl_args = function(provider, prompt_opts)
|
||||
local provider_conf, request_body = P.parse_config(provider)
|
||||
local base, body_opts = 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"
|
||||
local model_id = provider_conf.model or "default-model-id"
|
||||
local url = provider_conf.endpoint:gsub("LOCATION", location):gsub("PROJECT_ID", project_id)
|
||||
local model_id = base.model or "default-model-id"
|
||||
local url = base.endpoint:gsub("LOCATION", location):gsub("PROJECT_ID", project_id)
|
||||
|
||||
url = string.format("%s/%s:streamGenerateContent?alt=sse", url, model_id)
|
||||
|
||||
request_body = vim.tbl_deep_extend("force", request_body, {
|
||||
body_opts = vim.tbl_deep_extend("force", body_opts, {
|
||||
generationConfig = {
|
||||
temperature = request_body.temperature,
|
||||
maxOutputTokens = request_body.max_tokens,
|
||||
temperature = body_opts.temperature,
|
||||
maxOutputTokens = body_opts.max_tokens,
|
||||
},
|
||||
})
|
||||
request_body.temperature = nil
|
||||
request_body.max_tokens = nil
|
||||
body_opts.temperature = nil
|
||||
body_opts.max_tokens = nil
|
||||
local bearer_token = M.parse_api_key()
|
||||
|
||||
return {
|
||||
@ -56,9 +56,9 @@ M.parse_curl_args = function(provider, prompt_opts)
|
||||
["Authorization"] = "Bearer " .. bearer_token,
|
||||
["Content-Type"] = "application/json; charset=utf-8",
|
||||
},
|
||||
proxy = provider_conf.proxy,
|
||||
insecure = provider_conf.allow_insecure,
|
||||
body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), request_body),
|
||||
proxy = base.proxy,
|
||||
insecure = base.allow_insecure,
|
||||
body = vim.tbl_deep_extend("force", {}, M.parse_messages(prompt_opts), body_opts),
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -29,10 +29,21 @@ end
|
||||
function RepoMap.setup() vim.defer_fn(RepoMap._init_repo_map_lib, 1000) end
|
||||
|
||||
function RepoMap.get_ts_lang(filepath)
|
||||
local filetype = Utils.get_filetype(filepath)
|
||||
local filetype = RepoMap.get_filetype(filepath)
|
||||
return filetype_map[filetype] or filetype
|
||||
end
|
||||
|
||||
function RepoMap.get_filetype(filepath)
|
||||
-- Some files are sometimes not detected correctly when buffer is not included
|
||||
-- https://github.com/neovim/neovim/issues/27265
|
||||
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
local filetype = vim.filetype.match({ filename = filepath, buf = buf })
|
||||
vim.api.nvim_buf_delete(buf, { force = true })
|
||||
|
||||
return filetype
|
||||
end
|
||||
|
||||
function RepoMap._build_repo_map(project_root, file_ext)
|
||||
local output = {}
|
||||
local gitignore_path = project_root .. "/.gitignore"
|
||||
@ -59,7 +70,7 @@ function RepoMap._build_repo_map(project_root, file_ext)
|
||||
if definitions == "" then return end
|
||||
table.insert(output, {
|
||||
path = Utils.relative_path(filepath),
|
||||
lang = Utils.get_filetype(filepath),
|
||||
lang = RepoMap.get_filetype(filepath),
|
||||
defs = definitions,
|
||||
})
|
||||
end)
|
||||
@ -131,7 +142,7 @@ function RepoMap._get_repo_map(file_ext)
|
||||
if not found then
|
||||
table.insert(repo_map, {
|
||||
path = Utils.relative_path(abs_filepath),
|
||||
lang = Utils.get_filetype(abs_filepath),
|
||||
lang = RepoMap.get_filetype(abs_filepath),
|
||||
defs = definitions,
|
||||
})
|
||||
end
|
||||
|
@ -57,7 +57,6 @@ function Sidebar:new(id)
|
||||
selected_files_container = nil,
|
||||
input_container = nil,
|
||||
file_selector = FileSelector:new(id),
|
||||
is_generating = false,
|
||||
}, { __index = self })
|
||||
end
|
||||
|
||||
@ -67,24 +66,9 @@ function Sidebar:delete_autocmds()
|
||||
end
|
||||
|
||||
function Sidebar:reset()
|
||||
-- clean up event handlers
|
||||
if self.augroup then
|
||||
api.nvim_del_augroup_by_id(self.augroup)
|
||||
self.augroup = nil
|
||||
end
|
||||
|
||||
-- clean up keymaps
|
||||
self:unbind_apply_key()
|
||||
self:unbind_sidebar_keys()
|
||||
|
||||
-- clean up file selector events
|
||||
if self.file_selector then self.file_selector:off("update") end
|
||||
|
||||
if self.result_container then self.result_container:unmount() end
|
||||
if self.selected_code_container then self.selected_code_container:unmount() end
|
||||
if self.selected_files_container then self.selected_files_container:unmount() end
|
||||
if self.input_container then self.input_container:unmount() end
|
||||
|
||||
self:delete_autocmds()
|
||||
self.code = { bufnr = 0, winid = 0, selection = nil }
|
||||
self.winids =
|
||||
{ result_container = 0, selected_files_container = 0, selected_code_container = 0, input_container = 0 }
|
||||
@ -216,34 +200,24 @@ local function transform_result_content(selected_files, result_content, prev_fil
|
||||
local current_filepath
|
||||
|
||||
local i = 1
|
||||
while true do
|
||||
if i > #result_lines then break end
|
||||
while i <= #result_lines do
|
||||
local line_content = result_lines[i]
|
||||
if line_content:match("<[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>.+</[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>") then
|
||||
local filepath = line_content:match("<[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>(.+)</[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>")
|
||||
if line_content:match("<FILEPATH>.+</FILEPATH>") then
|
||||
local filepath = line_content:match("<FILEPATH>(.+)</FILEPATH>")
|
||||
if filepath then
|
||||
current_filepath = filepath
|
||||
table.insert(transformed_lines, string.format("Filepath: %s", filepath))
|
||||
goto continue
|
||||
end
|
||||
end
|
||||
if line_content:match("^%s*<[Ss][Ee][Aa][Rr][Cc][Hh]>") then
|
||||
if line_content == "<SEARCH>" then
|
||||
is_searching = true
|
||||
|
||||
if not line_content:match("^%s*<[Ss][Ee][Aa][Rr][Cc][Hh]>%s*$") then
|
||||
local search_start_line = line_content:match("<[Ss][Ee][Aa][Rr][Cc][Hh]>(.+)$")
|
||||
line_content = "<SEARCH>"
|
||||
result_lines[i] = line_content
|
||||
if search_start_line and search_start_line ~= "" then table.insert(result_lines, i + 1, search_start_line) end
|
||||
end
|
||||
line_content = "<SEARCH>"
|
||||
|
||||
local prev_line = result_lines[i - 1]
|
||||
if
|
||||
prev_line
|
||||
and prev_filepath
|
||||
and not prev_line:match("Filepath:.+")
|
||||
and not prev_line:match("<[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>.+</[Ff][Ii][Ll][Ee][Pp][Aa][Tt][Hh]>")
|
||||
and not prev_line:match("<FILEPATH>.+</FILEPATH>")
|
||||
then
|
||||
table.insert(transformed_lines, string.format("Filepath: %s", prev_filepath))
|
||||
end
|
||||
@ -251,23 +225,7 @@ local function transform_result_content(selected_files, result_content, prev_fil
|
||||
if next_line and next_line:match("^%s*```%w+$") then i = i + 1 end
|
||||
search_start = i + 1
|
||||
last_search_tag_start_line = i
|
||||
elseif line_content:match("</[Ss][Ee][Aa][Rr][Cc][Hh]>%s*$") then
|
||||
if is_replacing then
|
||||
result_lines[i] = line_content:gsub("</[Ss][Ee][Aa][Rr][Cc][Hh]>", "</REPLACE>")
|
||||
goto continue_without_increment
|
||||
end
|
||||
|
||||
-- Handle case where </SEARCH> is a suffix
|
||||
if not line_content:match("^%s*</[Ss][Ee][Aa][Rr][Cc][Hh]>%s*$") then
|
||||
local search_end_line = line_content:match("^(.+)</[Ss][Ee][Aa][Rr][Cc][Hh]>")
|
||||
line_content = "</SEARCH>"
|
||||
result_lines[i] = line_content
|
||||
if search_end_line and search_end_line ~= "" then
|
||||
table.insert(result_lines, i, search_end_line)
|
||||
goto continue_without_increment
|
||||
end
|
||||
end
|
||||
|
||||
elseif line_content == "</SEARCH>" then
|
||||
is_searching = false
|
||||
|
||||
local search_end = i
|
||||
@ -290,28 +248,24 @@ local function transform_result_content(selected_files, result_content, prev_fil
|
||||
|
||||
if not the_matched_file then
|
||||
if not PPath:new(filepath):exists() then
|
||||
the_matched_file = {
|
||||
filepath = filepath,
|
||||
content = "",
|
||||
file_type = nil,
|
||||
}
|
||||
else
|
||||
if not PPath:new(filepath):is_file() then
|
||||
Utils.warn("Not a file: " .. filepath)
|
||||
goto continue
|
||||
end
|
||||
local lines = Utils.read_file_from_buf_or_disk(filepath)
|
||||
if lines == nil then
|
||||
Utils.warn("Failed to read file: " .. filepath)
|
||||
goto continue
|
||||
end
|
||||
local content = table.concat(lines, "\n")
|
||||
the_matched_file = {
|
||||
filepath = filepath,
|
||||
content = content,
|
||||
file_type = nil,
|
||||
}
|
||||
Utils.warn("File not found: " .. filepath)
|
||||
goto continue
|
||||
end
|
||||
if not PPath:new(filepath):is_file() then
|
||||
Utils.warn("Not a file: " .. filepath)
|
||||
goto continue
|
||||
end
|
||||
local lines = Utils.read_file_from_buf_or_disk(filepath)
|
||||
if lines == nil then
|
||||
Utils.warn("Failed to read file: " .. filepath)
|
||||
goto continue
|
||||
end
|
||||
local content = table.concat(lines, "\n")
|
||||
the_matched_file = {
|
||||
filepath = filepath,
|
||||
content = content,
|
||||
file_type = nil,
|
||||
}
|
||||
end
|
||||
|
||||
local file_content = vim.split(the_matched_file.content, "\n")
|
||||
@ -338,7 +292,8 @@ local function transform_result_content(selected_files, result_content, prev_fil
|
||||
-- can happen if the llm tries to edit or create a file outside of it's context.
|
||||
if not match_filetype then
|
||||
local snippet_file_path = current_filepath or prev_filepath
|
||||
match_filetype = Utils.get_filetype(snippet_file_path)
|
||||
local snippet_file_type = vim.filetype.match({ filename = snippet_file_path }) or "unknown"
|
||||
match_filetype = snippet_file_type
|
||||
end
|
||||
|
||||
local search_start_tag_idx_in_transformed_lines = 0
|
||||
@ -356,31 +311,13 @@ local function transform_result_content(selected_files, result_content, prev_fil
|
||||
string.format("```%s", match_filetype),
|
||||
})
|
||||
goto continue
|
||||
elseif line_content:match("^%s*<[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>") then
|
||||
elseif line_content == "<REPLACE>" then
|
||||
is_replacing = true
|
||||
if not line_content:match("^%s*<[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>%s*$") then
|
||||
local replace_first_line = line_content:match("<[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>(.+)$")
|
||||
line_content = "<REPLACE>"
|
||||
result_lines[i] = line_content
|
||||
if replace_first_line and replace_first_line ~= "" then
|
||||
table.insert(result_lines, i + 1, replace_first_line)
|
||||
end
|
||||
end
|
||||
local next_line = result_lines[i + 1]
|
||||
if next_line and next_line:match("^%s*```%w+$") then i = i + 1 end
|
||||
last_replace_tag_start_line = i
|
||||
goto continue
|
||||
elseif line_content:match("</[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>%s*$") then
|
||||
-- Handle case where </REPLACE> is a suffix
|
||||
if not line_content:match("^%s*</[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>%s*$") then
|
||||
local replace_end_line = line_content:match("^(.+)</[Rr][Ee][Pp][Ll][Aa][Cc][Ee]>")
|
||||
line_content = "</REPLACE>"
|
||||
result_lines[i] = line_content
|
||||
if replace_end_line and replace_end_line ~= "" then
|
||||
table.insert(result_lines, i, replace_end_line)
|
||||
goto continue_without_increment
|
||||
end
|
||||
end
|
||||
elseif line_content == "</REPLACE>" then
|
||||
is_replacing = false
|
||||
local prev_line = result_lines[i - 1]
|
||||
if not (prev_line and prev_line:match("^%s*```$")) then table.insert(transformed_lines, "```") end
|
||||
@ -395,7 +332,6 @@ local function transform_result_content(selected_files, result_content, prev_fil
|
||||
table.insert(transformed_lines, line_content)
|
||||
::continue::
|
||||
i = i + 1
|
||||
::continue_without_increment::
|
||||
end
|
||||
|
||||
return {
|
||||
@ -461,8 +397,8 @@ local function get_searching_hint()
|
||||
end
|
||||
|
||||
local thinking_spinner_chars = {
|
||||
Utils.icon("🤯", "?"),
|
||||
Utils.icon("🙄", "¿"),
|
||||
"🤯",
|
||||
"🙄",
|
||||
}
|
||||
local thinking_spinner_index = 1
|
||||
|
||||
@ -501,10 +437,8 @@ local function generate_display_content(replacement)
|
||||
return string.format(" > %s", line)
|
||||
end)
|
||||
:totable()
|
||||
local result_lines = vim.list_extend(
|
||||
vim.list_slice(lines, 1, replacement.last_search_tag_start_line),
|
||||
{ Utils.icon("🤔 ") .. "Thought content:" }
|
||||
)
|
||||
local result_lines =
|
||||
vim.list_extend(vim.list_slice(lines, 1, replacement.last_search_tag_start_line), { "🤔 Thought content:" })
|
||||
result_lines = vim.list_extend(result_lines, formatted_thinking_content_lines)
|
||||
result_lines = vim.list_extend(result_lines, vim.list_slice(lines, last_think_tag_end_line + 1))
|
||||
return table.concat(result_lines, "\n")
|
||||
@ -761,22 +695,28 @@ local function minimize_snippet(original_lines, snippet)
|
||||
return new_snippets
|
||||
end
|
||||
|
||||
---@param filepath string
|
||||
---@param snippets AvanteCodeSnippet[]
|
||||
---@param snippets_map table<string, AvanteCodeSnippet[]>
|
||||
---@return table<string, AvanteCodeSnippet[]>
|
||||
function Sidebar:minimize_snippets(filepath, snippets)
|
||||
function Sidebar:minimize_snippets(snippets_map)
|
||||
local original_lines = {}
|
||||
|
||||
local original_lines_ = Utils.read_file_from_buf_or_disk(filepath)
|
||||
if original_lines_ then original_lines = original_lines_ end
|
||||
if vim.tbl_count(snippets_map) > 0 then
|
||||
local filepaths = vim.tbl_keys(snippets_map)
|
||||
local original_lines_, _, err = Utils.read_file_from_buf_or_disk(filepaths[1])
|
||||
if err ~= nil then return {} end
|
||||
if original_lines_ then original_lines = original_lines_ end
|
||||
end
|
||||
|
||||
local results = {}
|
||||
|
||||
for _, snippet in ipairs(snippets) do
|
||||
local new_snippets = minimize_snippet(original_lines, snippet)
|
||||
if new_snippets then
|
||||
for _, new_snippet in ipairs(new_snippets) do
|
||||
table.insert(results, new_snippet)
|
||||
for filepath, snippets in pairs(snippets_map) do
|
||||
for _, snippet in ipairs(snippets) do
|
||||
local new_snippets = minimize_snippet(original_lines, snippet)
|
||||
if new_snippets then
|
||||
results[filepath] = results[filepath] or {}
|
||||
for _, new_snippet in ipairs(new_snippets) do
|
||||
table.insert(results[filepath], new_snippet)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -809,13 +749,12 @@ function Sidebar:apply(current_cursor)
|
||||
selected_snippets_map = all_snippets_map
|
||||
end
|
||||
|
||||
if Config.behaviour.minimize_diff then selected_snippets_map = self:minimize_snippets(selected_snippets_map) end
|
||||
|
||||
vim.defer_fn(function()
|
||||
api.nvim_set_current_win(self.code.winid)
|
||||
for filepath, snippets in pairs(selected_snippets_map) do
|
||||
if Config.behaviour.minimize_diff then snippets = self:minimize_snippets(filepath, snippets) end
|
||||
local bufnr = Utils.get_or_create_buffer_with_filepath(filepath)
|
||||
local path_ = PPath:new(filepath)
|
||||
path_:parent():mkdir({ parents = true, exists_ok = true })
|
||||
insert_conflict_contents(bufnr, snippets)
|
||||
local process = function(winid)
|
||||
api.nvim_set_current_win(winid)
|
||||
@ -906,7 +845,7 @@ function Sidebar:render_result()
|
||||
then
|
||||
return
|
||||
end
|
||||
local header_text = Utils.icon(" ") .. "Avante"
|
||||
local header_text = " Avante"
|
||||
self:render_header(
|
||||
self.result_container.winid,
|
||||
self.result_container.bufnr,
|
||||
@ -928,15 +867,13 @@ function Sidebar:render_input(ask)
|
||||
end
|
||||
|
||||
local header_text = string.format(
|
||||
"%s%s (" .. Config.mappings.sidebar.switch_windows .. ": switch focus)",
|
||||
Utils.icon(" "),
|
||||
" %s (" .. Config.mappings.sidebar.switch_windows .. ": switch focus)",
|
||||
ask and "Ask" or "Chat with"
|
||||
)
|
||||
|
||||
if self.code.selection ~= nil then
|
||||
header_text = string.format(
|
||||
"%s%s (%d:%d) (<Tab>: switch focus)",
|
||||
Utils.icon(" "),
|
||||
" %s (%d:%d) (<Tab>: switch focus)",
|
||||
ask and "Ask" or "Chat with",
|
||||
self.code.selection.range.start.lnum,
|
||||
self.code.selection.range.finish.lnum
|
||||
@ -969,8 +906,7 @@ function Sidebar:render_selected_code()
|
||||
selected_code_lines_count = #selected_code_lines
|
||||
end
|
||||
|
||||
local header_text = Utils.icon(" ")
|
||||
.. "Selected Code"
|
||||
local header_text = " Selected Code"
|
||||
.. (
|
||||
selected_code_lines_count > selected_code_max_lines_count
|
||||
and " (Show only the first " .. tostring(selected_code_max_lines_count) .. " lines)"
|
||||
@ -1081,76 +1017,6 @@ function Sidebar:on_mount(opts)
|
||||
})
|
||||
end
|
||||
|
||||
-- Add keymap to add current buffer while sidebar is open
|
||||
if Config.mappings.providers and Config.mappings.providers.show_providers then
|
||||
vim.keymap.set("n", Config.mappings.providers.show_providers, function()
|
||||
local providers = require("avante.providers")
|
||||
local current_provider = Config.provider
|
||||
|
||||
-- Create a new buffer
|
||||
local bufnr = api.nvim_create_buf(false, true)
|
||||
|
||||
-- Set buffer content
|
||||
local lines = {}
|
||||
for provider, _ in pairs(providers) do
|
||||
if provider == current_provider then
|
||||
table.insert(lines, "> " .. provider .. " <")
|
||||
else
|
||||
table.insert(lines, " " .. provider)
|
||||
end
|
||||
end
|
||||
api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
|
||||
|
||||
-- Create a floating window
|
||||
local width = 40
|
||||
local height = #lines
|
||||
local win_opts = {
|
||||
relative = "editor",
|
||||
width = width,
|
||||
height = height,
|
||||
col = (vim.o.columns - width) / 2,
|
||||
row = (vim.o.lines - height) / 2 - 1,
|
||||
style = "minimal",
|
||||
border = "rounded",
|
||||
}
|
||||
|
||||
local winid = api.nvim_open_win(bufnr, true, win_opts)
|
||||
|
||||
-- Highlight current provider
|
||||
api.nvim_buf_add_highlight(bufnr, -1, "AvanteProviderCurrent", 0, 0, -1)
|
||||
|
||||
-- Key mappings
|
||||
local function move_selection(direction)
|
||||
local cursor = api.nvim_win_get_cursor(winid)
|
||||
local line = cursor[1]
|
||||
|
||||
if direction == "up" and line > 1 then
|
||||
api.nvim_win_set_cursor(winid, { line - 1, 0 })
|
||||
elseif direction == "down" and line < #lines then
|
||||
api.nvim_win_set_cursor(winid, { line + 1, 0 })
|
||||
end
|
||||
end
|
||||
|
||||
local function select_provider()
|
||||
local cursor = api.nvim_win_get_cursor(winid)
|
||||
local line = cursor[1]
|
||||
local selected_provider = lines[line]:match("%S+$")
|
||||
|
||||
if selected_provider and selected_provider ~= current_provider then providers.refresh(selected_provider) end
|
||||
|
||||
api.nvim_win_close(winid, true)
|
||||
end
|
||||
|
||||
vim.keymap.set("n", "<Up>", function() move_selection("up") end, { buffer = bufnr })
|
||||
vim.keymap.set("n", "<Down>", function() move_selection("down") end, { buffer = bufnr })
|
||||
vim.keymap.set("n", "<CR>", select_provider, { buffer = bufnr })
|
||||
vim.keymap.set("n", "q", function() api.nvim_win_close(winid, true) end, { buffer = bufnr })
|
||||
end, {
|
||||
desc = "avante: show providers selector",
|
||||
noremap = true,
|
||||
silent = true,
|
||||
})
|
||||
end
|
||||
api.nvim_set_option_value("wrap", Config.windows.wrap, { win = self.result_container.winid })
|
||||
|
||||
local current_apply_extmark_id = nil
|
||||
@ -1353,12 +1219,6 @@ end
|
||||
--- Initialize the sidebar instance.
|
||||
--- @return avante.Sidebar The Sidebar instance.
|
||||
function Sidebar:initialize()
|
||||
-- Add keymap to show providers selector
|
||||
vim.keymap.set("n", "<leader>ap", function() self:show_providers_selector() end, { noremap = true, silent = true })
|
||||
|
||||
self.code.winid = api.nvim_get_current_win()
|
||||
self.code.bufnr = api.nvim_get_current_buf()
|
||||
self.code.selection = Utils.get_visual_selection_and_range()
|
||||
self.code.winid = api.nvim_get_current_win()
|
||||
self.code.bufnr = api.nvim_get_current_buf()
|
||||
self.code.selection = Utils.get_visual_selection_and_range()
|
||||
@ -1376,20 +1236,6 @@ function Sidebar:initialize()
|
||||
return self
|
||||
end
|
||||
|
||||
function Sidebar:is_focused()
|
||||
if not self:is_open() then return false end
|
||||
|
||||
local current_winid = api.nvim_get_current_win()
|
||||
if self.winids.result_container and self.winids.result_container == current_winid then return true end
|
||||
if self.winids.selected_files_container and self.winids.selected_files_container == current_winid then
|
||||
return true
|
||||
end
|
||||
if self.winids.selected_code_container and self.winids.selected_code_container == current_winid then return true end
|
||||
if self.winids.input_container and self.winids.input_container == current_winid then return true end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function Sidebar:is_focused_on_result()
|
||||
return self:is_open() and self.result_container and self.result_container.winid == api.nvim_get_current_win()
|
||||
end
|
||||
@ -1970,8 +1816,6 @@ function Sidebar:create_input_container(opts)
|
||||
|
||||
---@type AvanteLLMChunkCallback
|
||||
local on_chunk = function(chunk)
|
||||
self.is_generating = true
|
||||
|
||||
original_response = original_response .. chunk
|
||||
|
||||
local selected_files = self.file_selector:get_selected_files_contents()
|
||||
@ -2006,8 +1850,6 @@ function Sidebar:create_input_container(opts)
|
||||
|
||||
---@type AvanteLLMStopCallback
|
||||
local on_stop = function(stop_opts)
|
||||
self.is_generating = false
|
||||
|
||||
pcall(function()
|
||||
---remove keymaps
|
||||
vim.keymap.del("n", "j", { buffer = self.result_container.bufnr })
|
||||
@ -2121,7 +1963,6 @@ function Sidebar:create_input_container(opts)
|
||||
local request = table.concat(lines, "\n")
|
||||
if request == "" then return end
|
||||
api.nvim_buf_set_lines(self.input_container.bufnr, 0, -1, false, {})
|
||||
api.nvim_win_set_cursor(self.input_container.winid, { 1, 0 })
|
||||
handle_submit(request)
|
||||
end
|
||||
|
||||
@ -2415,70 +2256,6 @@ function Sidebar:render(opts)
|
||||
return self
|
||||
end
|
||||
|
||||
function Sidebar:show_providers_selector()
|
||||
local providers = require("avante.providers")
|
||||
local current_provider = Config.provider
|
||||
|
||||
-- Create a new buffer
|
||||
local bufnr = api.nvim_create_buf(false, true)
|
||||
|
||||
-- Set buffer content
|
||||
local lines = {}
|
||||
for provider, _ in pairs(providers) do
|
||||
if provider == current_provider then
|
||||
table.insert(lines, "> " .. provider .. " <")
|
||||
else
|
||||
table.insert(lines, " " .. provider)
|
||||
end
|
||||
end
|
||||
api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
|
||||
|
||||
-- Create a floating window
|
||||
local width = 40
|
||||
local height = #lines
|
||||
local win_opts = {
|
||||
relative = "editor",
|
||||
width = width,
|
||||
height = height,
|
||||
col = (vim.o.columns - width) / 2,
|
||||
row = (vim.o.lines - height) / 2 - 1,
|
||||
style = "minimal",
|
||||
border = "rounded",
|
||||
}
|
||||
|
||||
local winid = api.nvim_open_win(bufnr, true, win_opts)
|
||||
|
||||
-- Highlight current provider
|
||||
api.nvim_buf_add_highlight(bufnr, -1, "AvanteProviderCurrent", 0, 0, -1)
|
||||
|
||||
-- Key mappings
|
||||
local function move_selection(direction)
|
||||
local cursor = api.nvim_win_get_cursor(winid)
|
||||
local line = cursor[1]
|
||||
|
||||
if direction == "up" and line > 1 then
|
||||
api.nvim_win_set_cursor(winid, { line - 1, 0 })
|
||||
elseif direction == "down" and line < #lines then
|
||||
api.nvim_win_set_cursor(winid, { line + 1, 0 })
|
||||
end
|
||||
end
|
||||
|
||||
local function select_provider()
|
||||
local cursor = api.nvim_win_get_cursor(winid)
|
||||
local line = cursor[1]
|
||||
local selected_provider = lines[line]:match("%S+$")
|
||||
|
||||
if selected_provider and selected_provider ~= current_provider then providers.refresh(selected_provider) end
|
||||
|
||||
api.nvim_win_close(winid, true)
|
||||
end
|
||||
|
||||
vim.keymap.set("n", "<Up>", function() move_selection("up") end, { buffer = bufnr })
|
||||
vim.keymap.set("n", "<Down>", function() move_selection("down") end, { buffer = bufnr })
|
||||
vim.keymap.set("n", "<CR>", select_provider, { buffer = bufnr })
|
||||
vim.keymap.set("n", "q", function() api.nvim_win_close(winid, true) end, { buffer = bufnr })
|
||||
end
|
||||
|
||||
function Sidebar:create_selected_files_container()
|
||||
if self.selected_files_container then self.selected_files_container:unmount() end
|
||||
|
||||
@ -2537,7 +2314,7 @@ function Sidebar:create_selected_files_container()
|
||||
self:render_header(
|
||||
self.selected_files_container.winid,
|
||||
selected_files_buf,
|
||||
Utils.icon(" ") .. "Selected Files",
|
||||
" Selected Files",
|
||||
Highlights.SUBTITLE,
|
||||
Highlights.REVERSED_SUBTITLE
|
||||
)
|
||||
|
@ -84,8 +84,8 @@ function Suggestion:suggest()
|
||||
L1: def fib
|
||||
L2:
|
||||
L3: if __name__ == "__main__":
|
||||
L4: # just pass
|
||||
L5: pass
|
||||
L4: # just pass
|
||||
L5: pass
|
||||
</code>
|
||||
]],
|
||||
},
|
||||
@ -95,7 +95,7 @@ L5: pass
|
||||
},
|
||||
{
|
||||
role = "user",
|
||||
content = '<question>{"insertSpaces":true,"tabSize":4,"indentSize":4,"position":{"row":1,"col":7}}</question>',
|
||||
content = '<question>{ "indentSize": 4, "position": { "row": 1, "col": 2 } }</question>',
|
||||
},
|
||||
{
|
||||
role = "assistant",
|
||||
|
@ -10,17 +10,10 @@
|
||||
Act as an expert software developer.
|
||||
Always use best practices when coding.
|
||||
Respect and use existing conventions, libraries, etc that are already present in the code base.
|
||||
Don't directly search for code context in historical messages. Instead, prioritize using tools to obtain context first, then use context from historical messages as a secondary source, since context from historical messages is often not up to date.
|
||||
|
||||
Tools Usage Guide:
|
||||
- You have access to tools, but only use them when necessary. If a tool is not required, respond as normal.
|
||||
- If you encounter a URL, prioritize using the fetch tool to obtain its content.
|
||||
- If you have information that you don't know, please proactively use the tools provided by users! Especially the web search tool.
|
||||
- When available tools cannot meet the requirements, please try to use the `run_command` tool to solve the problem whenever possible.
|
||||
- When attempting to modify a file that is not in the context, please first use the `list_files` tool and `search_files` tool to check if the file you want to modify exists, then use the `read_file` tool to read the file content. Don't modify blindly!
|
||||
- When generating files, first use `list_files` tool to read the directory structure, don't generate blindly!
|
||||
- When creating files, first check if the directory exists. If it doesn't exist, create the directory before creating the file.
|
||||
- After `web_search`, if you don't get detailed enough information, do not continue use `web_search`, just continue using the `fetch` tool to get more information you need from the links in the search results.
|
||||
You have access to tools, but only use them when necessary. If a tool is not required, respond as normal.
|
||||
If you encounter a URL, prioritize using the fetch tool to obtain its content.
|
||||
If you have information that you don't know, please proactively use the tools provided by users! Especially the web search tool.
|
||||
When available tools cannot meet the requirements, please try to use the `run_command` tool to solve the problem whenever possible.
|
||||
|
||||
{% if system_info -%}
|
||||
Use the appropriate shell based on the user's system info:
|
||||
|
@ -903,19 +903,9 @@ function M.is_same_file(filepath_a, filepath_b) return M.uniform_path(filepath_a
|
||||
|
||||
function M.trim_think_content(content) return content:gsub("^<think>.-</think>", "", 1) end
|
||||
|
||||
function M.get_filetype(filepath)
|
||||
-- Some files are sometimes not detected correctly when buffer is not included
|
||||
-- https://github.com/neovim/neovim/issues/27265
|
||||
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
local filetype = vim.filetype.match({ filename = filepath, buf = buf }) or ""
|
||||
vim.api.nvim_buf_delete(buf, { force = true })
|
||||
|
||||
return filetype
|
||||
end
|
||||
|
||||
---@param file_path string
|
||||
---@return string[]|nil lines
|
||||
---@return string|nil file_type
|
||||
---@return string|nil error
|
||||
function M.read_file_from_buf_or_disk(file_path)
|
||||
--- Lookup if the file is loaded in a buffer
|
||||
@ -923,7 +913,8 @@ function M.read_file_from_buf_or_disk(file_path)
|
||||
if bufnr ~= -1 and vim.api.nvim_buf_is_loaded(bufnr) then
|
||||
-- If buffer exists and is loaded, get buffer content
|
||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
return lines, nil
|
||||
local file_type = vim.api.nvim_get_option_value("filetype", { buf = bufnr })
|
||||
return lines, file_type, nil
|
||||
end
|
||||
|
||||
-- Fallback: read file from disk
|
||||
@ -931,28 +922,12 @@ function M.read_file_from_buf_or_disk(file_path)
|
||||
if file then
|
||||
local content = file:read("*all")
|
||||
file:close()
|
||||
return vim.split(content, "\n"), nil
|
||||
-- Detect the file type using the specific file's content
|
||||
local file_type = vim.filetype.match({ filename = file_path, contents = { content } }) or "unknown"
|
||||
return vim.split(content, "\n"), file_type, nil
|
||||
else
|
||||
-- M.error("failed to open file: " .. file_path .. " with error: " .. open_err)
|
||||
return {}, open_err
|
||||
end
|
||||
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
|
||||
|
||||
---Display an string with icon, if an icon plugin is available.
|
||||
---Dev icons are an optional install for avante, this function prevents ugly chars
|
||||
---being displayed by displaying fallback options or nothing at all.
|
||||
---@param string_with_icon string
|
||||
---@param utf8_fallback string|nil
|
||||
---@return string
|
||||
M.icon = function(string_with_icon, utf8_fallback)
|
||||
if M.icons_enabled() then
|
||||
return string_with_icon
|
||||
else
|
||||
return utf8_fallback or ""
|
||||
M.error("failed to open file: " .. file_path .. " with error: " .. open_err)
|
||||
return {}, nil, open_err
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -81,9 +81,11 @@ function M.detect(opts)
|
||||
opts.spec = opts.spec or type(vim.g.root_spec) == "table" and vim.g.root_spec or M.spec
|
||||
opts.buf = (opts.buf == nil or opts.buf == 0) and vim.api.nvim_get_current_buf() or opts.buf
|
||||
|
||||
|
||||
local ret = {} ---@type AvanteRoot[]
|
||||
for _, spec in ipairs(opts.spec) do
|
||||
local paths = M.resolve(spec)(opts.buf)
|
||||
|
||||
paths = paths or {}
|
||||
paths = type(paths) == "table" and paths or { paths }
|
||||
local roots = {} ---@type string[]
|
||||
@ -100,6 +102,7 @@ function M.detect(opts)
|
||||
return ret
|
||||
end
|
||||
|
||||
|
||||
---@type table<number, string>
|
||||
M.cache = {}
|
||||
|
||||
@ -113,14 +116,22 @@ M.cache = {}
|
||||
function M.get(opts)
|
||||
opts = opts or {}
|
||||
local buf = opts.buf or vim.api.nvim_get_current_buf()
|
||||
|
||||
local ret = M.cache[buf]
|
||||
|
||||
if not ret then
|
||||
local roots = M.detect({ all = false, buf = buf })
|
||||
ret = roots[1] and roots[1].paths[1] or vim.uv.cwd()
|
||||
M.cache[buf] = ret
|
||||
end
|
||||
|
||||
if opts and opts.normalize then return ret end
|
||||
return Utils.is_win() and ret:gsub("/", "\\") or ret
|
||||
|
||||
-- Ensure a non-empty return value
|
||||
ret = ret ~= "" and ret or vim.uv.cwd()
|
||||
|
||||
--- return Utils.is_win() and ret:gsub("/", "\\") or ret
|
||||
return ret
|
||||
end
|
||||
|
||||
function M.git()
|
||||
|
22
note.txt
22
note.txt
@ -1,22 +0,0 @@
|
||||
Failed to run `config` for avante.nvim
|
||||
|
||||
...share/nvim/lazy/lazy.nvim/lua/lazy/core/handler/keys.lua:35: attempt to index local 'ret' (a nil value)
|
||||
|
||||
# stacktrace:
|
||||
- /lazy.nvim/lua/lazy/core/util.lua:123 _in_ **__index**
|
||||
- /lazy.nvim/lua/lazy/core/handler/keys.lua:35 _in_ **parse**
|
||||
- /lazy.nvim/lua/lazy/core/handler/keys.lua:63 _in_ **have**
|
||||
- /avante.nvim/lua/avante/utils/init.lua:163 _in_ **func**
|
||||
- vim/shared.lua:274 _in_ **tbl_filter**
|
||||
- /avante.nvim/lua/avante/utils/init.lua:163 _in_ **safe_keymap_set**
|
||||
- /avante.nvim/lua/avante/init.lua:129 _in_ **keymaps**
|
||||
- /avante.nvim/lua/avante/init.lua:386 _in_ **setup**
|
||||
- /lazy.nvim/lua/lazy/core/loader.lua:387
|
||||
- /lazy.nvim/lua/lazy/core/util.lua:135 _in_ **try**
|
||||
- /lazy.nvim/lua/lazy/core/loader.lua:395 _in_ **config**
|
||||
- /lazy.nvim/lua/lazy/core/loader.lua:362 _in_ **_load**
|
||||
- /lazy.nvim/lua/lazy/core/loader.lua:197 _in_ **load**
|
||||
- /lazy.nvim/lua/lazy/core/loader.lua:127 _in_ **startup**
|
||||
- /lazy.nvim/lua/lazy/init.lua:112 _in_ **setup**
|
||||
- ~/.config/nvim/lua/config/lazy.lua:17
|
||||
- ~/.config/nvim/init.lua:2
|
Loading…
x
Reference in New Issue
Block a user