ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

vscode 插件怎么实现编辑器行号处添加图标标记:从 Decoration API 到 TaoToken 统一 Key 的调试链路

vscode 插件怎么实现编辑器行号处添加图标标记:从 Decoration API 到 TaoToken 统一 Key 的调试链路 1. 行号槽图标标记到底难在哪从 bookmarks 插件效果说起VS Code 插件想在编辑器行号旁边加一个小图标看起来只是「画个图」这么简单实际动手会发现三个坑图标画在哪个区域、怎么和某一行绑定、以及插件里如果还要发 AI 请求Key 从哪来。这篇就按一条完整链路走先用 Decoration API 把行号槽glyph margin图标渲染出来再把插件内的 AI 请求接到 TaoToken 统一 Key 通道上最后用一次真实请求验证整条链路是通的。先说清楚 glyph margin 是什么。VS Code 编辑器左侧其实分了好几块区域最左边是行号line numbers行号右边紧挨着一条窄窄的竖条官方叫 glyph margin中文一般叫「字形边距」或「行号槽」。断点、Git 行状态、书签图标都画在这里。它和行号是分开的两块所以你不能直接往行号数字上贴图而是往 glyph margin 这个独立区域放 decoration。Decoration API 是 VS Code 提供给插件的装饰能力核心是TextEditorDecorationType和TextEditor.setDecorations。前者描述「长什么样」比如 gutter 图标、背景色、边框后者描述「贴在哪几行」。两者配合就能实现「第 12 行行号旁有个书签图标」这种效果。适合谁看已经会写基础 VS Code 插件、能跑通yo code脚手架但卡在「图标不显示」「图标错位」「插件里调 AI 一直 401」这几个问题上的同学。如果你还没写过插件也能跟着走因为每一步的配置和代码我都会给全。我做的插件本身是个收藏夹工具能创建不同工作空间、保存文件或文件夹路径。后来想加书签功能要求就是像 bookmarks 插件那样在行号处右键就能标记标记后行号槽出现图标。下面从 package.json 贡献点开始一步步把这条链路搭起来。2. TaoToken 前置插件内 AI 请求为什么要统一 Key插件做到后面往往不只是本地标记。比如书签功能想加「AI 帮我给这个书签写个备注」或者「根据当前行代码生成书签描述」插件里就得发 HTTP 请求调模型。这时候问题来了Key 写死在插件里会泄露让每个用户自己填又很麻烦不同模型还要换不同的 Base URL 和 Key维护成本高。TaoToken 在这里的角色是一个统一的模型调用入口。你只需要一个 Key就能通过同一套 OpenAI 兼容接口去调不同模型插件里不用为每个模型写一套鉴权逻辑。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 注意 API 地址不带 UTM 参数配置时别把查询串带进去。对插件开发者来说统一 Key 通道的价值在于插件代码里只维护一个 Base URL 和一个 Key 变量模型 ID 作为参数传。调试阶段你可以把 Key 放在环境变量或 VS Code 的settings.json里正式发布时引导用户填自己的 Key。这样插件内的 AI 请求链路和行号标记链路可以分开调试互不干扰。需要提前准备的东西一个 TaoToken 账号和 API Key在控制台创建地址 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Node.js 环境建议 18 以上VS Code 1.80 以上版本以及一个能跑起来的插件工程。如果你还没建工程用yo code选 TypeScript 模板几分钟就能生成骨架。关于 Key 的获取进入控制台后找到 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 新建一个 Key 并复制保存。这个 Key 后面会用在插件的请求头里。注意不要把它提交到 Git 仓库调试阶段建议放在本地.env或 VS Code 的用户设置里。模型 ID 这块插件里调用的模型名要和 TaoToken 支持的模型列表对齐。你可以在模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 先手动试一次确认模型能正常返回再写进插件代码。这样排障时能快速区分是「Key 问题」还是「插件代码问题」。3. 可复制配置package.json 贡献点与 decoration 片段这一节给全可复制的配置。先看 package.json 里行号右键菜单的贡献点。VS Code 从 1.76 开始支持editor/lineNumber/context这个菜单贡献点它专门控制行号区域的右键菜单。你要在contributes下加两块menus 和 commands。{ contributes: { commands: [ { command: favourite.addToBookmark, title: 添加到收藏夹书签 }, { command: favourite.addToNameBookmark, title: 添加命名书签 }, { command: favourite.deleteBookmarks, title: 删除收藏夹书签 } ], menus: { editor/lineNumber/context: [ { command: favourite.addToBookmark, group: 5_favourite }, { command: favourite.addToNameBookmark, group: 5_favourite }, { command: favourite.deleteBookmarks, group: 5_favourite } ] } } }group里的5_favourite是菜单分组数字决定排序你可以改成别的。when条件这里没写意味着任何文件的行号右键都会出现这三个命令。如果你想只在特定语言生效可以加when: editorLangId typescript。接下来是 decoration 的核心代码。注册命令时回调参数里能拿到lineNumber和uri这是行号右键菜单特有的上下文。注意lineNumber是从 1 开始的而 VS Code 的Position是从 0 开始的所以要减 1。import * as vscode from vscode; export function activate(context: vscode.ExtensionContext) { const bookmarkDeco vscode.window.createTextEditorDecorationType({ gutterIconPath: vscode.Uri.parse( data:image/svgxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIPHBhdGggZD0iTTQgMmg4djEybC00LTRsLTQgNHoiIGZpbGw9IiNmZmI4MDAiLz48L3N2Zz4 ), gutterIconSize: contain }); const disposable vscode.commands.registerCommand( favourite.addToBookmark, async (args: { lineNumber: number; uri?: vscode.Uri }) { const editor vscode.window.activeTextEditor; if (!editor) { return; } const line args.lineNumber - 1; const range new vscode.Range(line, 0, line, 0); editor.setDecorations(bookmarkDeco, [range]); } ); context.subscriptions.push(disposable, bookmarkDeco); }上面那段 base64 是一个 16x16 的 SVG 书签图标你可以换成自己的。生成方式把 SVG 文件内容用 base64 编码或者直接在线转。注意gutterIconSize建议设成contain否则图标可能被拉伸或裁切。如果你要支持多行书签setDecorations的第二个参数是数组把多个 range 传进去就行。但要注意每次调用setDecorations会覆盖该 decoration type 之前的全部标记。所以正确做法是维护一个Mapuri, Range[]每次更新时把该文件的所有书签 range 一起传进去。const bookmarkMap new Mapstring, vscode.Range[](); function refreshDecorations(editor: vscode.TextEditor) { const key editor.document.uri.toString(); const ranges bookmarkMap.get(key) ?? []; editor.setDecorations(bookmarkDeco, ranges); }这样增删书签时只改 Map再调一次refreshDecorations图标状态就和数据一致了。这个模式在 bookmarks 类插件里是标准做法避免「删了书签图标还在」的问题。4. 验证请求行号标记与 AI 请求链路一次跑通行号图标能显示后下一步验证插件内的 AI 请求。这里用 TaoToken 的统一 Key 通道在插件里发一个最小请求确认 Base URL、Key、Model ID 三件套都对。先写一个独立的调试函数不要一上来就塞进书签逻辑里。这样出问题时能快速定位是请求本身的问题还是和 decoration 耦合的问题。async function testAiRequest(apiKey: string, modelId: string) { const res await fetch(https://taotoken.net/api/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${apiKey} }, body: JSON.stringify({ model: modelId, messages: [{ role: user, content: 用一句话描述当前书签 }] }) }); if (!res.ok) { const text await res.text(); throw new Error(HTTP ${res.status}: ${text}); } const data await res.json(); return data.choices?.[0]?.message?.content; }注意 Base URL 是https://taotoken.net/api拼上/v1/chat/completions就是完整的请求地址。Key 放在Authorization头里格式是Bearer 你的Key。Model ID 用你在模型对话页面验证过的那个。在插件里调用时Key 建议从 VS Code 配置读取而不是硬编码const config vscode.workspace.getConfiguration(favourite); const apiKey config.getstring(taotokenApiKey) ?? ; const modelId config.getstring(taotokenModelId) ?? gpt-4o-mini;对应的 package.json 配置贡献点{ contributes: { configuration: { title: Favourite, properties: { favourite.taotokenApiKey: { type: string, default: , description: TaoToken API Key }, favourite.taotokenModelId: { type: string, default: gpt-4o-mini, description: 模型 ID } } } } }验证动作分两步。第一步在命令面板运行你的测试命令看控制台是否打印出模型返回的一句话。第二步回到编辑器在行号处右键添加书签确认图标出现同时触发一次 AI 请求看两者是否都正常。如果 AI 请求返回了内容说明 Key 通道通了如果图标也显示了说明 decoration 链路通了。实测下来把这两条链路分开验证比混在一起调效率高很多。因为 decoration 的问题通常是「图标不显示」或「位置不对」而请求的问题通常是 401 或 404两者的报错特征完全不同分开看一目了然。5. 常见错排查401、图标不显示、choices 读取失败这一节对照真实报错来排。第一个高频错误是 401 Unauthorized。报错长这样{ error: { message: Invalid API key, type: invalid_request_error } }原因通常是 Key 复制时带了空格、Key 已失效、或者请求头格式写错。检查Authorization头是不是Bearer加 Key中间有一个空格。另外确认你用的是 TaoToken 控制台创建的 Key而不是别的平台的。如果 Key 放在 VS Code 配置里注意配置读取时有没有被 trim。第二个错误是图标不显示。这种情况先检查gutterIconPath的 base64 是否合法。一个常见坑是 base64 字符串里混入了换行或空格导致Uri.parse解析失败。建议把 base64 放在一行里不要手动换行。另外确认gutterIconSize设置合理太小会看不见。第三个错误是读取choices时报Cannot read properties of undefined (reading choices)。这通常说明返回的不是标准 chat completions 结构可能是请求路径写错了比如漏了/v1或者模型 ID 不存在导致返回了错误对象。先打印完整响应体再取字段const data await res.json(); console.log(full response:, JSON.stringify(data));第四个错误是local proxy failed或连接超时。这类问题一般出在网络层检查你的请求地址是不是https://taotoken.net/api不要带多余的路径或查询参数。如果你在公司网络环境确认能正常访问外部 HTTPS 接口。第五个错误和 OAuth 有关。如果你在插件里用了某些需要 OAuth 的模型服务可能会遇到 token 过期。但用 TaoToken 的 Key 通道就不涉及 OAuth直接 Bearer 鉴权少一层复杂度。这也是统一 Key 通道的一个好处鉴权方式单一排障路径短。还有一个容易忽略的点setDecorations的 range 如果长度是 0start 和 end 相同在某些 VS Code 版本里 gutter 图标可能不渲染。建议把 end 设成line, 1或者更长一点确保 range 非空。我试过用new vscode.Range(line, 0, line, 0)在部分版本上图标不出现改成line, 1就正常了。6. 把 Key 通道接到 Coding Plan长期编码场景的收尾行号标记和单次请求验证通过后如果你的插件要长期在编码场景里用 AI比如自动生成书签描述、批量整理收藏夹单次请求的调试方式就不够用了。这时候可以考虑把 Key 通道接到 Coding Plan 上地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合持续性的编码和 Agent 类调用。接入方式还是那三件套Base URL 用https://taotoken.net/apiKey 用你在控制台创建的那个Model ID 按 Coding Plan 支持的模型填。插件里不需要改请求结构只是把调用频率和场景从「单次测试」变成「持续调用」。如果你用的是 Claude Code 这类工具可以参考接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里的配置说明把 Base URL 和 Key 填进去。回到插件本身最后一步是把书签数据和 decoration 状态持久化。用context.workspaceState存Map的序列化结果插件激活时读回来并刷新 decoration。这样重启 VS Code 后书签图标还在。代码不复杂但能让整个功能从「能跑」变成「能用」。整条链路走下来核心就三件事package.json 里加对菜单贡献点decoration 用对 gutterIconPath 和 rangeAI 请求用对 Base URL、Key、Model ID。这三件都对齐了行号图标和请求链路就能一次跑通。
返回列表