feat: implement user-defined text shortcuts (#2512)

Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
This commit is contained in:
doodleEsc
2025-07-23 11:54:59 +08:00
committed by GitHub
parent 8b8a777ec3
commit faa3945428
8 changed files with 278 additions and 18 deletions

View File

@@ -642,11 +642,65 @@ For lazyvim users copy the full config for blink.cmp from the website or extend
For other users just add a custom provider
### Available Completion Sources
Avante.nvim provides several completion sources that can be integrated with blink.cmp:
#### Mentions (`@` trigger)
Mentions allow you to quickly reference specific features or add files to the chat context:
- `@codebase` - Enable project context and repository mapping
- `@diagnostics` - Enable diagnostics information
- `@file` - Open file selector to add files to chat context
- `@quickfix` - Add files from quickfix list to chat context
- `@buffers` - Add open buffers to chat context
#### Slash Commands (`/` trigger)
Built-in slash commands for common operations:
- `/help` - Show help message with available commands
- `/init` - Initialize AGENTS.md based on current project
- `/clear` - Clear chat history
- `/new` - Start a new chat
- `/compact` - Compact history messages to save tokens
- `/lines <start>-<end> <question>` - Ask about specific lines
- `/commit` - Generate commit message for changes
#### Shortcuts (`#` trigger)
Shortcuts provide quick access to predefined prompt templates. You can customize these in your config:
```lua
{
shortcuts = {
{
name = "refactor",
description = "Refactor code with best practices",
details = "Automatically refactor code to improve readability, maintainability, and follow best practices while preserving functionality",
prompt = "Please refactor this code following best practices, improving readability and maintainability while preserving functionality."
},
{
name = "test",
description = "Generate unit tests",
details = "Create comprehensive unit tests covering edge cases, error scenarios, and various input conditions",
prompt = "Please generate comprehensive unit tests for this code, covering edge cases and error scenarios."
},
-- Add more custom shortcuts...
}
}
```
When you type `#refactor` in the input, it will automatically be replaced with the corresponding prompt text.
### Configuration Example
Here's a complete blink.cmp configuration example with all Avante sources:
```lua
default = {
...
"avante_commands",
"avante_mentions",
"avante_shortcuts",
"avante_files",
}
```
@@ -670,6 +724,12 @@ For other users just add a custom provider
module = "blink.compat.source",
score_offset = 1000, -- show at a higher priority than lsp
opts = {},
},
avante_shortcuts = {
name = "avante_shortcuts",
module = "blink.compat.source",
score_offset = 1000, -- show at a higher priority than lsp
opts = {},
}
...
}
@@ -679,24 +739,6 @@ For other users just add a custom provider
## Usage
### @mentions
avante.nvim supports the following @mentions to help you reference different parts of your codebase:
| Mention | Description |
| -------------- | ----------------------------------- |
| `@codebase` | Include the entire codebase context |
| `@diagnostics` | Include current diagnostic issues |
| `@file` | Include the current file |
| `@quickfix` | Include the quickfix list |
| `@buffers` | Include all open buffers |
You can use these mentions in your conversations with avante.nvim to provide relevant context. For example:
- `@file what are the issues in this code?` - analyzes the current file
- `@codebase explain the project structure` - looks at the entire codebase
- `@diagnostics how do I fix these errors?` - helps resolve diagnostic issues
### Basic Functionality
Given its early stage, `avante.nvim` currently supports the following basic functionalities:

View File

@@ -509,11 +509,65 @@ _请参见 [config.lua#L9](./lua/avante/config.lua) 以获取完整配置_
对于其他用户,只需添加自定义提供者
### 可用的补全项
Avante.nvim 提供了多个可以与 blink.cmp 集成的补全项:
#### 提及功能 (`@` 触发器)
提及功能允许您快速引用特定功能或将文件添加到聊天上下文:
- `@codebase` - 启用项目上下文和仓库映射
- `@diagnostics` - 启用诊断信息
- `@file` - 打开文件选择器以将文件添加到聊天上下文
- `@quickfix` - 将快速修复列表中的文件添加到聊天上下文
- `@buffers` - 将打开的缓冲区添加到聊天上下文
#### 斜杠命令 (`/` 触发器)
内置斜杠命令用于常见操作:
- `/help` - 显示可用命令的帮助信息
- `/init` - 基于当前项目初始化 AGENTS.md
- `/clear` - 清除聊天历史
- `/new` - 开始新聊天
- `/compact` - 压缩历史消息以节省令牌
- `/lines <start>-<end> <question>` - 询问特定行的问题
- `/commit` - 为更改生成提交消息
#### 快捷方式 (`#` 触发器)
快捷方式提供对预定义提示模板的快速访问。您可以在配置中自定义这些:
```lua
{
shortcuts = {
{
name = "refactor",
description = "使用最佳实践重构代码",
details = "自动重构代码以提高可读性、可维护性,并遵循最佳实践,同时保持功能不变",
prompt = "请按照最佳实践重构此代码,提高可读性和可维护性,同时保持功能不变。"
},
{
name = "test",
description = "生成单元测试",
details = "创建全面的单元测试,涵盖边界情况、错误场景和各种输入条件",
prompt = "请为此代码生成全面的单元测试,涵盖边界情况和错误场景。"
},
-- 添加更多自定义快捷方式...
}
}
```
当您在输入中键入 `#refactor` 时,它将自动替换为相应的提示文本。
### 配置示例
以下是包含所有 Avante 源的完整 blink.cmp 配置示例:
```lua
default = {
...
"avante_commands",
"avante_mentions",
"avante_shortcuts",
"avante_files",
}
```
@@ -537,6 +591,12 @@ _请参见 [config.lua#L9](./lua/avante/config.lua) 以获取完整配置_
module = "blink.compat.source",
score_offset = 1000, -- 显示优先级高于 lsp
opts = {},
},
avante_shortcuts = {
name = "avante_shortcuts",
module = "blink.compat.source",
score_offset = 1000, -- 显示优先级高于 lsp
opts = {},
}
...
}

View File

@@ -676,6 +676,8 @@ M._defaults = {
custom_tools = {},
---@type AvanteSlashCommand[]
slash_commands = {},
---@type AvanteShortcut[]
shortcuts = {},
}
---@type avante.Config

View File

@@ -496,11 +496,14 @@ function M.setup(opts)
cmp.register_source("avante_prompt_mentions", require("cmp_avante.mentions"):new(Utils.get_mentions))
cmp.register_source("avante_shortcuts", require("cmp_avante.shortcuts"):new())
cmp.setup.filetype({ "AvanteInput" }, {
enabled = true,
sources = {
{ name = "avante_commands" },
{ name = "avante_mentions" },
{ name = "avante_shortcuts" },
{ name = "avante_files" },
},
})

View File

@@ -2446,6 +2446,10 @@ function Sidebar:create_input_container()
end
end
-- Process shortcut replacements
local new_content, has_shortcuts = Utils.extract_shortcuts(request)
if has_shortcuts then request = new_content end
local selected_filepaths = self.file_selector:get_selected_filepaths()
---@type AvanteSelectedCode | nil

View File

@@ -521,3 +521,9 @@ vim.g.avante_login = vim.g.avante_login
---@alias AvanteMentions "codebase" | "diagnostics" | "file" | "quickfix" | "buffers"
---@alias AvanteMentionCallback fun(args: string, cb?: fun(args: string): nil): nil
---@alias AvanteMention {description: string, command: AvanteMentions, details: string, shorthelp?: string, callback?: AvanteMentionCallback}
---@class AvanteShortcut
---@field name string
---@field details string
---@field description string
---@field prompt string

View File

@@ -1076,6 +1076,98 @@ function M.get_chat_mentions()
return mentions
end
---@return AvanteShortcut[]
function M.get_shortcuts()
local Config = require("avante.config")
-- Built-in shortcuts
local builtin_shortcuts = {
{
name = "refactor",
description = "Refactor code with best practices",
details = "Automatically refactor code to improve readability, maintainability, and follow best practices while preserving functionality",
prompt = "Please refactor this code following best practices, improving readability and maintainability while preserving functionality.",
},
{
name = "test",
description = "Generate unit tests",
details = "Create comprehensive unit tests covering edge cases, error scenarios, and various input conditions",
prompt = "Please generate comprehensive unit tests for this code, covering edge cases and error scenarios.",
},
{
name = "document",
description = "Add documentation",
details = "Add clear and comprehensive documentation including function descriptions, parameter explanations, and usage examples",
prompt = "Please add clear and comprehensive documentation to this code, including function descriptions, parameter explanations, and usage examples.",
},
{
name = "debug",
description = "Add debugging information",
details = "Add comprehensive debugging information including logging statements, error handling, and debugging utilities",
prompt = "Please add comprehensive debugging information to this code, including logging statements, error handling, and debugging utilities.",
},
{
name = "optimize",
description = "Optimize performance",
details = "Analyze and optimize code for better performance considering time complexity, memory usage, and algorithmic improvements",
prompt = "Please analyze and optimize this code for better performance, considering time complexity, memory usage, and algorithmic improvements.",
},
{
name = "security",
description = "Security review",
details = "Perform a security review identifying potential vulnerabilities, security best practices, and recommendations for improvement",
prompt = "Please perform a security review of this code, identifying potential vulnerabilities, security best practices, and recommendations for improvement.",
},
}
local user_shortcuts = Config.shortcuts or {}
local result = {}
-- Create a map of builtin shortcuts by name for quick lookup
local builtin_map = {}
for _, shortcut in ipairs(builtin_shortcuts) do
builtin_map[shortcut.name] = shortcut
end
-- Process user shortcuts first (they take precedence)
for _, user_shortcut in ipairs(user_shortcuts) do
if builtin_map[user_shortcut.name] then
-- User has overridden a builtin shortcut
table.insert(result, user_shortcut)
builtin_map[user_shortcut.name] = nil -- Remove from builtin map
else
-- User has added a new shortcut
table.insert(result, user_shortcut)
end
end
-- Add remaining builtin shortcuts that weren't overridden
for _, builtin_shortcut in pairs(builtin_map) do
table.insert(result, builtin_shortcut)
end
return result
end
---@param content string
---@return string new_content
---@return boolean has_shortcuts
function M.extract_shortcuts(content)
local shortcuts = M.get_shortcuts()
local new_content = content
local has_shortcuts = false
for _, shortcut in ipairs(shortcuts) do
local pattern = "#" .. shortcut.name
if content:match(pattern) then
has_shortcuts = true
new_content = new_content:gsub(pattern, shortcut.prompt)
end
end
return new_content, has_shortcuts
end
---@param path string
---@param set_current_buf? boolean
---@return integer bufnr

View File

@@ -0,0 +1,51 @@
local api = vim.api
---@class ShortcutsSource : cmp.Source
local ShortcutsSource = {}
ShortcutsSource.__index = ShortcutsSource
function ShortcutsSource:new()
local instance = setmetatable({}, ShortcutsSource)
return instance
end
function ShortcutsSource:is_available() return vim.bo.filetype == "AvanteInput" end
function ShortcutsSource.get_position_encoding_kind() return "utf-8" end
function ShortcutsSource:get_trigger_characters() return { "#" } end
function ShortcutsSource:get_keyword_pattern() return [[\%(@\|#\|/\)\k*]] end
function ShortcutsSource:complete(_, callback)
local Utils = require("avante.utils")
local kind = require("cmp").lsp.CompletionItemKind.Variable
local shortcuts = Utils.get_shortcuts()
local items = {}
for _, shortcut in ipairs(shortcuts) do
table.insert(items, {
label = "#" .. shortcut.name,
kind = kind,
detail = shortcut.details,
data = {
name = shortcut.name,
prompt = shortcut.prompt,
details = shortcut.details,
},
})
end
callback({
items = items,
isIncomplete = false,
})
end
function ShortcutsSource:execute(item, callback)
-- ShortcutsSource should only provide completion, not perform replacement
-- The actual shortcut replacement is handled in sidebar.lua handle_submit function
callback()
end
return ShortcutsSource