diff --git a/README.md b/README.md index 8c4cfc7..ad5f30b 100644 --- a/README.md +++ b/README.md @@ -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 - ` - 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: diff --git a/README_zh.md b/README_zh.md index 1741c06..6d88947 100644 --- a/README_zh.md +++ b/README_zh.md @@ -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 - ` - 询问特定行的问题 +- `/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 = {}, } ... } diff --git a/lua/avante/config.lua b/lua/avante/config.lua index 0f944fc..7f6f3fd 100644 --- a/lua/avante/config.lua +++ b/lua/avante/config.lua @@ -676,6 +676,8 @@ M._defaults = { custom_tools = {}, ---@type AvanteSlashCommand[] slash_commands = {}, + ---@type AvanteShortcut[] + shortcuts = {}, } ---@type avante.Config diff --git a/lua/avante/init.lua b/lua/avante/init.lua index bf9795b..33b0bf5 100644 --- a/lua/avante/init.lua +++ b/lua/avante/init.lua @@ -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" }, }, }) diff --git a/lua/avante/sidebar.lua b/lua/avante/sidebar.lua index 52abe71..f22e8c8 100644 --- a/lua/avante/sidebar.lua +++ b/lua/avante/sidebar.lua @@ -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 diff --git a/lua/avante/types.lua b/lua/avante/types.lua index 8ddd192..38ca135 100644 --- a/lua/avante/types.lua +++ b/lua/avante/types.lua @@ -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 diff --git a/lua/avante/utils/init.lua b/lua/avante/utils/init.lua index 5eae622..914aa7f 100644 --- a/lua/avante/utils/init.lua +++ b/lua/avante/utils/init.lua @@ -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 diff --git a/lua/cmp_avante/shortcuts.lua b/lua/cmp_avante/shortcuts.lua new file mode 100644 index 0000000..3c25835 --- /dev/null +++ b/lua/cmp_avante/shortcuts.lua @@ -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