ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

初识vscode插件开发(二)-右键菜单:从package.json到extension.ts的TaoToken配置实战

2026/10/8 6:37:12 拓冰建站 浏览量
初识vscode插件开发(二)-右键菜单:从package.json到extension.ts的TaoToken配置实战 1. 从一次右键说起为什么插件菜单总是不出现很多人第一次写 VS Code 插件命令面板里CtrlShiftP能跑通但一放到右键菜单就懵了明明package.json里写了menusF5 调试窗口里右键却什么都没有。我试过最离谱的一次是command字段大小写和registerCommand里的 ID 差了一个字母排查了半小时。这一篇要解决的就是这个场景给 VS Code 插件加右键菜单并且让菜单项真正调用一次 API。核心检索词是 vscode 插件开发右键菜单涉及两个文件——package.json负责声明菜单贡献点extension.ts负责注册命令逻辑。适合已经搭好插件工程、能跑通Hello World命令、想进一步做「右键选中代码 → 调用模型 → 返回结果」这类功能的开发者。为什么要把右键菜单和 API 调用放一起讲因为纯弹窗的右键菜单没有实战价值真正有用的是你在编辑器里选中一段代码右键点「解释这段代码」插件把选中的文本发给模型把返回结果展示出来。这个链路里右键菜单是入口API 通道是出口中间靠extension.ts串起来。TaoToken 在这里的角色是统一 API 通道。它提供 OpenAI 兼容的接口格式你不需要为每个模型单独改请求体Base URL 指向https://taotoken.net/apiKey 用统一的一把Model ID 按需切换。对插件开发来说这意味着你写一次fetch逻辑换模型只改一个字符串。下面按「声明菜单 → 注册命令 → 读取配置 → 发请求 → 验证 → 排错」的顺序走一遍每一步都给可复制的片段。你跟着做最后能拿到一个右键选中文本、调用模型、弹出结果的完整插件。2. package.json 声明 menus 贡献点explorer/context 与 editor/context 的区别package.json是插件的「说明书」VS Code 启动时读它来决定在哪里显示你的命令。右键菜单的声明全部放在contributes.menus下面。这里有两个最常用的位置新手最容易搞混。explorer/context是资源管理器左侧文件树的右键菜单。你右键一个文件或文件夹时弹出的菜单归它管。editor/context是编辑器内部打开文件后的代码区域的右键菜单。你在代码里右键时弹出的菜单归它管。两者互不影响写错位置就会出现「文件树里有、代码区没有」的情况。先看commands部分每个命令要有唯一 ID 和显示标题{ contributes: { commands: [ { command: taotokenPlugin.explainCode, title: TaoToken: 解释选中代码 }, { command: taotokenPlugin.askQuestion, title: TaoToken: 提问 } ], menus: { editor/context: [ { command: taotokenPlugin.explainCode, group: taotoken1, when: editorHasSelection } ], explorer/context: [ { command: taotokenPlugin.askQuestion, group: taotoken1 } ] } } }几个字段逐个说清楚。command必须和extension.ts里registerCommand的第一个参数完全一致大小写敏感。group决定菜单项的分组和排序VS Code 用后面的数字控制同组内顺序不同 group 之间会自动加分割线。when是显示条件editorHasSelection表示只有选中了文本才显示这个菜单项——这个条件非常实用避免用户没选代码时点了报错。when的常见取值还有几个值得记住resourceLangId javascript只在 JS 文件显示resourceLangId python只在 Python 文件显示explorerResourceIsFolder只在右键文件夹时显示。这些条件可以组合用连接比如editorHasSelection resourceLangId typescript。改完package.json后VS Code 有时不会立即刷新菜单。稳妥做法是按CtrlShiftP执行Developer: Reload Window或者直接停掉调试再 F5 重开。我踩过的坑是改完没重载对着旧菜单找了半天问题。3. extension.ts 注册命令并读取 TaoToken 配置extension.ts是插件的执行入口。activate函数在插件被激活时调用所有命令注册都放这里。下面这段代码注册了「解释选中代码」命令读取编辑器选中文本调用 TaoToken 的 API把结果展示出来。先看完整的命令注册和 API 调用逻辑import * as vscode from vscode; export function activate(context: vscode.ExtensionContext) { const explainCmd vscode.commands.registerCommand( taotokenPlugin.explainCode, async () { const editor vscode.window.activeTextEditor; if (!editor) { vscode.window.showWarningMessage(没有打开的编辑器); return; } const selection editor.selection; const selectedText editor.document.getText(selection); if (!selectedText) { vscode.window.showWarningMessage(请先选中一段代码); return; } const config vscode.workspace.getConfiguration(taotokenPlugin); const apiKey config.getstring(apiKey); const baseUrl config.getstring(baseUrl) || https://taotoken.net/api; const modelId config.getstring(modelId) || gpt-4o-mini; if (!apiKey) { vscode.window.showErrorMessage(请先在设置中配置 taotokenPlugin.apiKey); return; } await vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, title: TaoToken 正在分析... }, async () { try { const response await fetch(${baseUrl}/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${apiKey} }, body: JSON.stringify({ model: modelId, messages: [ { role: system, content: 你是一个代码解释助手用简洁中文解释代码功能。 }, { role: user, content: 解释这段代码\n${selectedText} } ] }) }); if (!response.ok) { const errText await response.text(); vscode.window.showErrorMessage(请求失败 ${response.status}: ${errText}); return; } const data await response.json(); const answer data.choices?.[0]?.message?.content || 无返回内容; const doc await vscode.workspace.openTextDocument({ content: answer, language: markdown }); await vscode.window.showTextDocument(doc, { viewColumn: vscode.ViewColumn.Beside }); } catch (err: any) { vscode.window.showErrorMessage(调用异常: ${err.message}); } } ); } ); context.subscriptions.push(explainCmd); }这段代码里有几个关键点。vscode.workspace.getConfiguration(taotokenPlugin)读取的是用户在 VS Code 设置里填的配置对应package.json里的configuration贡献点。你需要补上这段配置声明否则设置里搜不到{ contributes: { configuration: { title: TaoToken 插件配置, properties: { taotokenPlugin.apiKey: { type: string, default: , description: TaoToken API Key在控制台创建 }, taotokenPlugin.baseUrl: { type: string, default: https://taotoken.net/api, description: API 基础地址 }, taotokenPlugin.modelId: { type: string, default: gpt-4o-mini, description: 模型 ID } } } } }三件套在这里体现得很清楚Base URL 是https://taotoken.net/apiKey 是用户自己填的taotokenPlugin.apiKeyModel ID 是taotokenPlugin.modelId。换模型时只改 Model ID请求体结构不变因为 TaoToken 走的是 OpenAI 兼容格式。withProgress包住请求是为了给用户反馈网络请求有延迟没有进度提示会让人以为插件卡死。返回结果用openTextDocument在旁边开一个 Markdown 文档展示比弹窗更适合看长文本。4. F5 调试验证右键触发到 API 返回的完整链路配置写完按 F5 启动扩展开发宿主窗口。这个窗口是一个独立的 VS Code 实例里面加载了你正在开发的插件。注意调试窗口和你写代码的窗口是两个进程改代码后要在原窗口重新 F5 才会生效。验证步骤按顺序走。第一步在调试窗口里随便打开一个代码文件选中几行代码。第二步在选中的代码上右键菜单里应该出现「TaoToken: 解释选中代码」。如果没出现先检查是不是没选中文本——when条件editorHasSelection会把它藏起来。第三步点击菜单项右下角出现进度通知几秒后旁边打开一个 Markdown 文档里面是模型返回的解释。如果请求成功你会在返回的 JSON 里看到choices数组第一个元素的message.content就是答案。这个结构是 OpenAI 兼容格式的标准返回TaoToken 保持一致所以你的解析代码不用为不同模型写分支。验证时建议先用一个便宜的模型跑通链路确认 Base URL、Key、Model ID 三件套都对再换成能力更强的模型。我实测下来链路问题九成出在 Key 没填或 Base URL 写错模型本身很少是原因。调试窗口里还可以打开「输出」面板选择你的插件通道console.log的内容会打在那里。排查请求体时把JSON.stringify的结果打出来看一眼确认model字段和你在设置里填的一致。5. 常见报错排查401、local proxy failed 与 reading choices这一节对照几个真实报错给出定位思路。这些错误我在不同阶段都遇到过按出现频率排序。401 Unauthorized。返回体通常是{error:{message:Invalid API key}}。原因就三类Key 没填、Key 填错、Key 前后有空格。检查vscode.workspace.getConfiguration读出来的值在调试控制台打apiKey.length看是不是 0。另外注意设置里填 Key 时别带Bearer前缀代码里已经拼了。local proxy failed / ECONNREFUSED。这个报错说明请求根本没发出去卡在本地网络层。常见原因是 Base URL 写成了http://localhost:xxxx这类本地地址但服务没起或者公司网络环境对某些域名有限制。确认taotokenPlugin.baseUrl是https://taotoken.net/api不要多加/v1代码里已经拼了/v1/chat/completions。如果拼成/api/v1/v1/chat/completions会 404。Cannot read properties of undefined (reading choices)。这个报错说明response.json()返回的结构里没有choices。两种可能一是请求失败但你没检查response.ok就直接解析错误响应体里自然没有choices二是返回结构被中间层改了。正确做法是先判断response.ok失败时把response.text()打出来看原始内容。上面代码里已经做了这个判断。OAuth / token 相关报错。如果你在插件里同时用了其他需要 OAuth 的服务注意别把两套认证头混在一起。TaoToken 用的是Authorization: Bearer key简单直接不需要 OAuth 流程。看到 OAuth 字样先确认是不是别的插件或配置串进来了。菜单项不显示。这不是运行时报错但最让人抓狂。排查顺序commandID 是否和registerCommand一致 →when条件是否满足 → 是否重载了窗口 →menus是否写在正确的contributes层级下。我踩过的坑是把menus写在了contributes外面JSON 不报错但菜单永远不出现。请求超时。默认fetch没有超时控制网络慢时会一直转。可以在withProgress里加AbortController设置 30 秒超时超时后showErrorMessage提示用户重试。这个不是必须但体验会好很多。6. 把右键菜单接进你的日常工作流跑通这个链路后右键菜单能做的事情就多了。选中一段报错日志右键让模型分析原因选中一个函数右键生成单元测试选中 SQL右键解释查询逻辑。入口都是同一个editor/context区别只在registerCommand里的 prompt 和when条件。如果你要做更复杂的 Agent 类插件比如多轮对话、代码库检索建议把 API 调用逻辑抽成一个单独的模块命令注册只负责收集上下文和展示结果。这样换模型、加缓存、加重试都在一个地方改。长期做编码类插件的话Coding Plan 比按次调用更划算适合高频使用的场景。配置方式不变还是 Base URL Key Model ID 三件套只是 Key 的来源不同。调试技巧上VS Code 插件的activate函数只在插件第一次被触发时执行一次。如果你改了registerCommand的逻辑但没重启调试窗口旧命令还在内存里。养成改完就Reload Window的习惯能省很多「为什么改了没生效」的时间。最后留一个实用习惯在package.json里给每个命令的title加上统一前缀比如TaoToken:这样在命令面板和右键菜单里一眼就能找到自己的命令不会和内置命令混在一起。