feat(ui): add envvar popup with warnings (#54)
* feat: add support for input popup of envvar Signed-off-by: Aaron Pham <contact@aarnphm.xyz> * chore: update README and ignore certain filetypes for popup Signed-off-by: Aaron Pham <contact@aarnphm.xyz> * fix: readme doesn't support nested callout Signed-off-by: Aaron Pham <contact@aarnphm.xyz> --------- Signed-off-by: Aaron Pham <contact@aarnphm.xyz>
This commit is contained in:
parent
f4ab995515
commit
af1f51455e
77
README.md
77
README.md
@ -69,8 +69,11 @@ Install `avante.nvim` using [lazy.nvim](https://github.com/folke/lazy.nvim):
|
|||||||
|
|
||||||
Default setup configuration:
|
Default setup configuration:
|
||||||
|
|
||||||
|
_See [config.lua#L9](./lua/avante/config.lua) for the full config_
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
{
|
{
|
||||||
|
---@alias Provider "openai" | "claude" | "azure"
|
||||||
provider = "claude", -- "claude" or "openai" or "azure"
|
provider = "claude", -- "claude" or "openai" or "azure"
|
||||||
openai = {
|
openai = {
|
||||||
endpoint = "https://api.openai.com",
|
endpoint = "https://api.openai.com",
|
||||||
@ -79,7 +82,7 @@ Default setup configuration:
|
|||||||
max_tokens = 4096,
|
max_tokens = 4096,
|
||||||
},
|
},
|
||||||
azure = {
|
azure = {
|
||||||
endpoint = "", -- Example: "https://<your-resource-name>.openai.azure.com"
|
endpoint = "", -- example: "https://<your-resource-name>.openai.azure.com"
|
||||||
deployment = "", -- Azure deployment name (e.g., "gpt-4o", "my-gpt-4o-deployment")
|
deployment = "", -- Azure deployment name (e.g., "gpt-4o", "my-gpt-4o-deployment")
|
||||||
api_version = "2024-06-01",
|
api_version = "2024-06-01",
|
||||||
temperature = 0,
|
temperature = 0,
|
||||||
@ -92,13 +95,17 @@ Default setup configuration:
|
|||||||
max_tokens = 4096,
|
max_tokens = 4096,
|
||||||
},
|
},
|
||||||
highlights = {
|
highlights = {
|
||||||
|
---@type AvanteConflictHighlights
|
||||||
diff = {
|
diff = {
|
||||||
current = "DiffText", -- need have background color
|
current = "DiffText",
|
||||||
incoming = "DiffAdd", -- need have background color
|
incoming = "DiffAdd",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mappings = {
|
mappings = {
|
||||||
ask = "<leader>aa",
|
ask = "<leader>aa",
|
||||||
|
edit = "<leader>ae",
|
||||||
|
refresh = "<leader>ar",
|
||||||
|
--- @class AvanteConflictMappings
|
||||||
diff = {
|
diff = {
|
||||||
ours = "co",
|
ours = "co",
|
||||||
theirs = "ct",
|
theirs = "ct",
|
||||||
@ -107,6 +114,20 @@ Default setup configuration:
|
|||||||
next = "]x",
|
next = "]x",
|
||||||
prev = "[x",
|
prev = "[x",
|
||||||
},
|
},
|
||||||
|
jump = {
|
||||||
|
next = "]]",
|
||||||
|
prev = "[[",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
windows = {
|
||||||
|
width = 30, -- default % based on available width
|
||||||
|
},
|
||||||
|
--- @class AvanteConflictUserConfig
|
||||||
|
diff = {
|
||||||
|
debug = false,
|
||||||
|
autojump = true,
|
||||||
|
---@type string | fun(): any
|
||||||
|
list_opener = "copen",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -115,30 +136,33 @@ Default setup configuration:
|
|||||||
|
|
||||||
Given its early stage, `avante.nvim` currently supports the following basic functionalities:
|
Given its early stage, `avante.nvim` currently supports the following basic functionalities:
|
||||||
|
|
||||||
1. Set the appropriate API key as an environment variable:
|
> [!IMPORTANT]
|
||||||
|
>
|
||||||
|
> For most consistency between neovim session, it is recommended to set the environment variables in your shell file.
|
||||||
|
> By default, `Avante` will prompt you at startup to input the API key for the provider you have selected.
|
||||||
|
>
|
||||||
|
> For Claude:
|
||||||
|
>
|
||||||
|
> ```sh
|
||||||
|
> export ANTHROPIC_API_KEY=your-api-key
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> For OpenAI:
|
||||||
|
>
|
||||||
|
> ```sh
|
||||||
|
> export OPENAI_API_KEY=your-api-key
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> For Azure OpenAI:
|
||||||
|
>
|
||||||
|
> ```sh
|
||||||
|
> export AZURE_OPENAI_API_KEY=your-api-key
|
||||||
|
> ```
|
||||||
|
|
||||||
For Claude:
|
1. Open a code file in Neovim.
|
||||||
|
2. Use the `:AvanteAsk` command to query the AI about the code.
|
||||||
```sh
|
3. Review the AI's suggestions.
|
||||||
export ANTHROPIC_API_KEY=your-api-key
|
4. Apply the recommended changes directly to your code with a simple command or key binding.
|
||||||
```
|
|
||||||
|
|
||||||
For OpenAI:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
export OPENAI_API_KEY=your-api-key
|
|
||||||
```
|
|
||||||
|
|
||||||
For Azure OpenAI:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
export AZURE_OPENAI_API_KEY=your-api-key
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Open a code file in Neovim.
|
|
||||||
3. Use the `:AvanteAsk` command to query the AI about the code.
|
|
||||||
4. Review the AI's suggestions.
|
|
||||||
5. Apply the recommended changes directly to your code with a simple command or key binding.
|
|
||||||
|
|
||||||
**Note**: The plugin is still under active development, and both its functionality and interface are subject to significant changes. Expect some rough edges and instability as the project evolves.
|
**Note**: The plugin is still under active development, and both its functionality and interface are subject to significant changes. Expect some rough edges and instability as the project evolves.
|
||||||
|
|
||||||
@ -147,6 +171,7 @@ Given its early stage, `avante.nvim` currently supports the following basic func
|
|||||||
The following key bindings are available for use with `avante.nvim`:
|
The following key bindings are available for use with `avante.nvim`:
|
||||||
|
|
||||||
- <kbd>Leader</kbd><kbd>a</kbd><kbd>a</kbd> — show sidebar
|
- <kbd>Leader</kbd><kbd>a</kbd><kbd>a</kbd> — show sidebar
|
||||||
|
- <kbd>Leader</kbd><kbd>a</kbd><kbd>r</kbd> — show sidebar
|
||||||
- <kbd>c</kbd><kbd>o</kbd> — choose ours
|
- <kbd>c</kbd><kbd>o</kbd> — choose ours
|
||||||
- <kbd>c</kbd><kbd>t</kbd> — choose theirs
|
- <kbd>c</kbd><kbd>t</kbd> — choose theirs
|
||||||
- <kbd>c</kbd><kbd>b</kbd> — choose both
|
- <kbd>c</kbd><kbd>b</kbd> — choose both
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
local fn = vim.fn
|
local fn = vim.fn
|
||||||
|
local api = vim.api
|
||||||
|
|
||||||
local curl = require("plenary.curl")
|
local curl = require("plenary.curl")
|
||||||
|
local Input = require("nui.input")
|
||||||
|
local Event = require("nui.utils.autocmd").event
|
||||||
|
|
||||||
local Utils = require("avante.utils")
|
local Utils = require("avante.utils")
|
||||||
local Config = require("avante.config")
|
local Config = require("avante.config")
|
||||||
@ -9,6 +12,153 @@ local Tiktoken = require("avante.tiktoken")
|
|||||||
---@class avante.AiBot
|
---@class avante.AiBot
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
---@class Environment: table<[string], any>
|
||||||
|
---@field [string] string the environment variable name
|
||||||
|
---@field fallback? string Optional fallback API key environment variable name
|
||||||
|
|
||||||
|
---@class EnvironmentHandler: table<[Provider], string>
|
||||||
|
local E = {
|
||||||
|
---@type table<Provider, Environment | string>
|
||||||
|
env = {
|
||||||
|
openai = "OPENAI_API_KEY",
|
||||||
|
claude = "ANTHROPIC_API_KEY",
|
||||||
|
azure = { "AZURE_OPENAI_API_KEY", fallback = "OPENAI_API_KEY" },
|
||||||
|
},
|
||||||
|
_once = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
E = setmetatable(E, {
|
||||||
|
---@param k Provider
|
||||||
|
__index = function(_, k)
|
||||||
|
local envvar = E.env[k]
|
||||||
|
if type(envvar) == "string" then
|
||||||
|
local value = os.getenv(envvar)
|
||||||
|
return value and true or false
|
||||||
|
elseif type(envvar) == "table" then
|
||||||
|
local main_key = envvar[1]
|
||||||
|
local value = os.getenv(main_key)
|
||||||
|
if value then
|
||||||
|
return true
|
||||||
|
elseif envvar.fallback then
|
||||||
|
return os.getenv(envvar.fallback) and true or false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- courtesy of https://github.com/MunifTanjim/nui.nvim/wiki/nui.input
|
||||||
|
local SecretInput = Input:extend("SecretInput")
|
||||||
|
|
||||||
|
function SecretInput:init(popup_options, options)
|
||||||
|
assert(
|
||||||
|
not options.conceal_char or vim.api.nvim_strwidth(options.conceal_char) == 1,
|
||||||
|
"conceal_char must be a single char"
|
||||||
|
)
|
||||||
|
|
||||||
|
popup_options.win_options = vim.tbl_deep_extend("force", popup_options.win_options or {}, {
|
||||||
|
conceallevel = 2,
|
||||||
|
concealcursor = "nvi",
|
||||||
|
})
|
||||||
|
|
||||||
|
SecretInput.super.init(self, popup_options, options)
|
||||||
|
|
||||||
|
self._.conceal_char = type(options.conceal_char) == "nil" and "*" or options.conceal_char
|
||||||
|
end
|
||||||
|
|
||||||
|
function SecretInput:mount()
|
||||||
|
SecretInput.super.mount(self)
|
||||||
|
|
||||||
|
local conceal_char = self._.conceal_char
|
||||||
|
local prompt_length = vim.api.nvim_strwidth(vim.fn.prompt_getprompt(self.bufnr))
|
||||||
|
|
||||||
|
vim.api.nvim_buf_call(self.bufnr, function()
|
||||||
|
vim.cmd(string.format(
|
||||||
|
[[
|
||||||
|
syn region SecretValue start=/^/ms=s+%s end=/$/ contains=SecretChar
|
||||||
|
syn match SecretChar /./ contained conceal %s
|
||||||
|
]],
|
||||||
|
prompt_length,
|
||||||
|
conceal_char and "cchar=" .. (conceal_char or "*") or ""
|
||||||
|
))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- return the environment variable name for the given provider
|
||||||
|
---@param provider? Provider
|
||||||
|
---@return string the envvar key
|
||||||
|
E.key = function(provider)
|
||||||
|
provider = provider or Config.provider
|
||||||
|
local var = E.env[provider]
|
||||||
|
return type(var) == "table" and var[1] ---@cast var string
|
||||||
|
or var
|
||||||
|
end
|
||||||
|
|
||||||
|
E.setup = function(var)
|
||||||
|
if E._once then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local input = SecretInput({
|
||||||
|
position = "50%",
|
||||||
|
size = {
|
||||||
|
width = 40,
|
||||||
|
},
|
||||||
|
border = {
|
||||||
|
style = "single",
|
||||||
|
text = {
|
||||||
|
top = "Enter " .. var,
|
||||||
|
top_align = "center",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
win_options = {
|
||||||
|
winhighlight = "Normal:Normal,FloatBorder:Normal",
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
prompt = "> ",
|
||||||
|
default_value = "",
|
||||||
|
on_submit = function(value)
|
||||||
|
vim.fn.setenv(var, value)
|
||||||
|
end,
|
||||||
|
on_close = function()
|
||||||
|
if not E[Config.provider] then
|
||||||
|
vim.notify_once("Failed to set " .. var .. ". Avante won't work as expected", vim.log.levels.WARN)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
api.nvim_create_autocmd({ "BufEnter", "BufWinEnter" }, {
|
||||||
|
pattern = "*",
|
||||||
|
callback = function()
|
||||||
|
if E._once then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.defer_fn(function()
|
||||||
|
-- only mount if given buffer is not of buftype ministarter, dashboard, alpha, qf
|
||||||
|
local exclude_buftypes = { "dashboard", "alpha", "qf", "nofile" }
|
||||||
|
local exclude_filetypes =
|
||||||
|
{ "NvimTree", "Outline", "help", "dashboard", "alpha", "qf", "ministarter", "TelescopePrompt", "gitcommit" }
|
||||||
|
if
|
||||||
|
not vim.tbl_contains(exclude_buftypes, vim.bo.buftype)
|
||||||
|
and not vim.tbl_contains(exclude_filetypes, vim.bo.filetype)
|
||||||
|
then
|
||||||
|
E._once = true
|
||||||
|
input:mount()
|
||||||
|
end
|
||||||
|
end, 200)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
input:map("n", "<Esc>", function()
|
||||||
|
input:unmount()
|
||||||
|
end, { noremap = true })
|
||||||
|
|
||||||
|
input:on(Event.BufLeave, function()
|
||||||
|
input:unmount()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
local system_prompt = [[
|
local system_prompt = [[
|
||||||
You are an excellent programming expert.
|
You are an excellent programming expert.
|
||||||
]]
|
]]
|
||||||
@ -57,10 +207,7 @@ Remember: Accurate line numbers are CRITICAL. The range start_line to end_line m
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
local function call_claude_api_stream(question, code_lang, code_content, selected_code_content, on_chunk, on_complete)
|
local function call_claude_api_stream(question, code_lang, code_content, selected_code_content, on_chunk, on_complete)
|
||||||
local api_key = os.getenv("ANTHROPIC_API_KEY")
|
local api_key = os.getenv(E.key("azure"))
|
||||||
if not api_key then
|
|
||||||
error("ANTHROPIC_API_KEY environment variable is not set")
|
|
||||||
end
|
|
||||||
|
|
||||||
local tokens = Config.claude.max_tokens
|
local tokens = Config.claude.max_tokens
|
||||||
local headers = {
|
local headers = {
|
||||||
@ -174,11 +321,7 @@ local function call_claude_api_stream(question, code_lang, code_content, selecte
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function call_openai_api_stream(question, code_lang, code_content, selected_code_content, on_chunk, on_complete)
|
local function call_openai_api_stream(question, code_lang, code_content, selected_code_content, on_chunk, on_complete)
|
||||||
local api_key = os.getenv("OPENAI_API_KEY")
|
local api_key = os.getenv(E.key("openai"))
|
||||||
if not api_key and Config.provider == "openai" then
|
|
||||||
error("OPENAI_API_KEY environment variable is not set")
|
|
||||||
end
|
|
||||||
|
|
||||||
local user_prompt = base_user_prompt
|
local user_prompt = base_user_prompt
|
||||||
.. "\n\nCODE:\n"
|
.. "\n\nCODE:\n"
|
||||||
.. "```"
|
.. "```"
|
||||||
@ -209,10 +352,7 @@ local function call_openai_api_stream(question, code_lang, code_content, selecte
|
|||||||
|
|
||||||
local url, headers, body
|
local url, headers, body
|
||||||
if Config.provider == "azure" then
|
if Config.provider == "azure" then
|
||||||
api_key = os.getenv("AZURE_OPENAI_API_KEY") or os.getenv("OPENAI_API_KEY")
|
api_key = os.getenv(E.key("azure"))
|
||||||
if not api_key then
|
|
||||||
error("Azure OpenAI API key is not set. Please set AZURE_OPENAI_API_KEY or OPENAI_API_KEY environment variable.")
|
|
||||||
end
|
|
||||||
url = Config.azure.endpoint
|
url = Config.azure.endpoint
|
||||||
.. "/openai/deployments/"
|
.. "/openai/deployments/"
|
||||||
.. Config.azure.deployment
|
.. Config.azure.deployment
|
||||||
@ -306,4 +446,11 @@ function M.call_ai_api_stream(question, code_lang, code_content, selected_conten
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function M.setup()
|
||||||
|
local has = E[Config.provider]
|
||||||
|
if not has then
|
||||||
|
E.setup(E.key())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
@ -6,6 +6,7 @@ local M = {}
|
|||||||
|
|
||||||
---@class avante.Config
|
---@class avante.Config
|
||||||
M.defaults = {
|
M.defaults = {
|
||||||
|
---@alias Provider "openai" | "claude" | "azure"
|
||||||
provider = "claude", -- "claude" or "openai" or "azure"
|
provider = "claude", -- "claude" or "openai" or "azure"
|
||||||
openai = {
|
openai = {
|
||||||
endpoint = "https://api.openai.com",
|
endpoint = "https://api.openai.com",
|
||||||
|
@ -331,9 +331,10 @@ local function register_cursor_move_events(bufnr)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local hint = string.format(
|
local hint = string.format(
|
||||||
" [Press <%s> for OURS, <%s> for THEIRS, <%s> for PREV, <%s> for NEXT] ",
|
" [Press <%s> for OURS, <%s> for THEIRS, <%s> for BOTH, <%s> for PREV, <%s> for NEXT] ",
|
||||||
Config.diff.mappings.ours,
|
Config.diff.mappings.ours,
|
||||||
Config.diff.mappings.theirs,
|
Config.diff.mappings.theirs,
|
||||||
|
Config.diff.mappings.both,
|
||||||
Config.diff.mappings.prev,
|
Config.diff.mappings.prev,
|
||||||
Config.diff.mappings.next
|
Config.diff.mappings.next
|
||||||
)
|
)
|
||||||
|
@ -4,6 +4,7 @@ local Tiktoken = require("avante.tiktoken")
|
|||||||
local Sidebar = require("avante.sidebar")
|
local Sidebar = require("avante.sidebar")
|
||||||
local Config = require("avante.config")
|
local Config = require("avante.config")
|
||||||
local Diff = require("avante.diff")
|
local Diff = require("avante.diff")
|
||||||
|
local AiBot = require("avante.ai_bot")
|
||||||
local Selection = require("avante.selection")
|
local Selection = require("avante.selection")
|
||||||
|
|
||||||
---@class Avante
|
---@class Avante
|
||||||
@ -164,6 +165,7 @@ function M.setup(opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
Diff.setup()
|
Diff.setup()
|
||||||
|
AiBot.setup()
|
||||||
M.selection = Selection:new():setup()
|
M.selection = Selection:new():setup()
|
||||||
|
|
||||||
-- setup helpers
|
-- setup helpers
|
||||||
|
@ -132,6 +132,16 @@ function Sidebar:intialize()
|
|||||||
relative = { type = "win", winid = fn.bufwinid(self.view.buf) },
|
relative = { type = "win", winid = fn.bufwinid(self.view.buf) },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.renderer:add_mappings({
|
||||||
|
{
|
||||||
|
mode = { "n" },
|
||||||
|
key = "q",
|
||||||
|
handler = function()
|
||||||
|
self.renderer:close()
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
self.renderer:on_mount(function()
|
self.renderer:on_mount(function()
|
||||||
self.winid.result = self.renderer:get_component_by_id("result").winid
|
self.winid.result = self.renderer:get_component_by_id("result").winid
|
||||||
self.winid.input = self.renderer:get_component_by_id("input").winid
|
self.winid.input = self.renderer:get_component_by_id("input").winid
|
||||||
|
Loading…
x
Reference in New Issue
Block a user