目前 Neovim 中的 AI 插件,比较火的就属 avante.nvimcodecompanion.nvim 了,经过简单的测试我选择了后者。

因为 avante.nvim 相对来说有些复杂,依赖组件过多,侵略性较强, 光 readme 中列出的 Key Bindings 就有好多。
还用到了 Leader 键, 我不是很喜欢被强制快捷键,如果与我的习惯键冲突就麻烦了,让我改我一时又不知道改成什么🫤,心理负担较重。
加上他的界面与我的审美也有冲突,界面好多默认元素无法删除,在我的小屏幕下看着比较难受。

而 Code Companion 就比较简洁键,几乎没有全局快捷键,也没有多余界面元素,安装成功后只会多出 4 条命令,如果需要可以自己来设置快捷键,入门毫无负担。
但相对在功能上可能会不如 avante。

我之前做了一个视频介绍如何使用和配置,并且集成 DeepSeek 和 Copilot 双 AI, 可以先了解一下。

https://www.bilibili.com/video/BV1ExNke9Een/?aid=113986687205...

这里再介绍一些高级配置。

如何设置中文问话中文回答

默认无论用中文还是英文提问,都会得到英文回答,想要得到中文回答,需要设置 opts 项 language 如下:

require("codecompanion").setup({
    adapters = { ..... },
    strategies = { ..... },

    opts = {
        language = "Chinese",
    },

})

修复视频中第三方 DeeepSeek 源,显示思考的过程

前面的视频中我们继承了 openai_compatible adapter,由于 openai 兼容是不会显示思考过程的,有些时候 DeepSeek 反应慢其实是在思考,并不是真的慢,只是没有显示出来,
如果想要显示思考的过程必须继承 deepseek adapter 才行。 例如自定义 siliconflow 适配器,应该如下

siliconflow_r1 = function()
  return require("codecompanion.adapters").extend("deepseek", {
    name = "siliconflow_r1",
    url = "https://api.siliconflow.cn/v1/chat/completions",
    env = {
      api_key = function()
        return os.getenv("DEEPSEEK_API_KEY_S")
      end,
    },
    schema = {
      model = {
        default = "deepseek-ai/DeepSeek-R1",
        choices = {
          ["deepseek-ai/DeepSeek-R1"] = { opts = { can_reason = true } },
          "deepseek-ai/DeepSeek-V3",
        },
      },
    },
  })
end,

aliyun_deepseek = function()
  return require("codecompanion.adapters").extend("deepseek", {
    name = "aliyun_deepseek",
    url = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
    env = {
      api_key = function()
        return os.getenv("DEEPSEEK_API_ALIYUN")
      end,
    },
    schema = {
      model = {
        default = "deepseek-r1",
        choices = {
          ["deepseek-r1"] = { opts = { can_reason = true } },
        },
      },
    },
  })
end,

聊天窗口的 markdown 渲染

默认是没有渲染 markdown 的,需要手动安装 render-markdown.nvim 并指定 ft codecompanion

  {
    "MeanderingProgrammer/render-markdown.nvim",
    ft = { "markdown", "codecompanion" },
  },

自定义快捷键

自定义快捷键只需要映射到对应命令或者对应的方法即可,例如 :CodeCompanionChat 命令,或对应的方法 require("codecompanion").chat().
比如我只定义了这两个快捷键,注意这里的 [keymap 是我的自定义函数](https://github.com/nshen/InsisVim/blob/f91579548d888cd27aa019f10b60be201cd8ecf1/lua/insis/utils/global.lua#L25
)

keymap({ "n", "v", "x" },  "<leader>cc", function()
  require("codecompanion").toggle()
end)

keymap({ "n", "v", "x" },  "<leader>cp", ":CodeCompanionActions<CR>")

自定义 promps

可以参考系统自带的promps来修改,比如这个 Explain

我把他全部改为了中文,并且指定用阿里云的deepseek来解释:


  -- 放到setup函数中
  prompt_library = {
    ["DeepSeek Explain In Chinese"] = {
      strategy = "chat",
      description = "中文解释代码",
      opts = {
        index = 5,
        is_default = true,
        is_slash_cmd = false,
        modes = { "v" },
        short_name = "explain in chinese",
        auto_submit = true,
        user_prompt = false,
        stop_context_insertion = true,
        adapter = {
          name = "aliyun_deepseek",
          model = "deepseek-r1",
        },
      },
      prompts = {
        {
          role = "system",
          content = [[当被要求解释代码时,请遵循以下步骤:

  1. 识别编程语言。
  2. 描述代码的目的,并引用该编程语言的核心概念。
  3. 解释每个函数或重要的代码块,包括参数和返回值。
  4. 突出说明使用的任何特定函数或方法及其作用。
  5. 如果适用,提供该代码如何融入更大应用程序的上下文。]],
          opts = {
            visible = false,
          },
        },
        {
          role = "user",
          content = function(context)
            local input = require("codecompanion.helpers.actions").get_code(context.start_line, context.end_line)

            return string.format(
              [[请解释 buffer %d 中的这段代码:

  \`\`\`%s
  %s
  \`\`\`
  ]],
              context.bufnr,
              context.filetype,
              input
            )
          end,
          opts = {
            contains_code = true,
          },
        },
      },
    },
  }
注意要去掉上边代码块 string.format 里的\\\反斜杠,网页里不加反斜杠会被当成 markdown 结束,是渲染的渲染问题

用 fidget 实现状态提醒

fidget 就是 LSP 加载时常用的右下角的状态提醒,由于 codecompanion 开始和结束都有对应事件,所以可以使用fidget来提醒

我参考了这个讨论 实现了一个简化版

local fidget = pRequire("fidget")
local handler
if fidget then
  -- Attach:
  vim.api.nvim_create_autocmd({ "User" }, {
    pattern = "CodeCompanionRequest*",
    group = vim.api.nvim_create_augroup("CodeCompanionHooks", {}),
    callback = function(request)
      if request.match == "CodeCompanionRequestStarted" then
        if handler then
          handler.message = "Abort."
          handler:cancel()
          handler = nil
        end
        handler = fidget.progress.handle.create({
          title = "",
          message = "Thinking...",
          lsp_client = { name = "CodeCompanion" },
        })
      elseif request.match == "CodeCompanionRequestFinished" then
        if handler then
          handler.message = "Done."
          handler:finish()
          handler = nil
        end
      end
    end,
  })
end

以上就是我目前的所有配置,附加一个使用技巧,在聊天框中如果遇到大语言模型回复卡住,
可以在聊天框中按 q 取消,按 gx 清空聊天记录,然后按 ga 来快速切换其他模型。


nshen
115 声望4 粉丝

新来的,给个赞吧!