
1. 为什么我要给 VSCode 加一个只读开关你有没有过这种经历打开一个老项目只是想翻翻代码找找某个函数的实现结果手一抖按到了键盘某个字符就被改了保存的时候才发现 diff 里多了一行莫名其妙的改动。尤其是用 VSCode 看别人的仓库、看线上配置、看编译产物的时候这种误触特别烦。VSCode 本身没有内置的「只读模式」开关社区里关于这个需求的讨论一直都有但官方并没有给出一个开箱即用的方案。我翻了不少 issue发现大家的思路基本集中在两个方向一是想办法拿到 Monaco Editor 的实例直接设成只读二是从命令层面拦截输入。前者在插件 API 里基本走不通后者才是真正可落地的路子。这篇笔记就聚焦 VSCode 插件开发场景带你从零做一个 read-only 插件在状态栏放一个按钮点一下切换只读状态同时把 TaoToken 的统一 Key/API 通道接进来方便后续在插件里调用模型能力做代码解释、注释生成之类的扩展。整篇会给到可复制的package.json命令注册、statusbar 创建与切换逻辑骨架以及settings.json里的 TaoToken 配置片段和本地验证步骤。适合谁看写过一点 TypeScript、想入门 VSCode 插件开发的同学或者已经有一个内部插件、想给它加个只读开关和统一模型通道的开发者。不需要你之前做过插件跟着敲一遍就能跑起来。核心检索词先摆在这VSCode read-only 插件、extension statusbar 开关、TaoToken 统一 Key。下面按「问题场景 → 前置准备 → 可复制配置 → 验证 → 排障 → 收尾」的顺序走。2. 前置准备TaoToken 统一 Key 与插件工程骨架2.1 为什么插件里要接 TaoToken插件本身做只读切换不需要任何网络请求但如果你想让这个插件再往前走一步比如选中一段代码后让模型解释、或者自动生成注释就需要一个稳定的模型调用通道。TaoToken 提供的是统一的 Key 和 API 入口你不用在插件里硬编码某一家厂商的地址和密钥换模型、换通道都只改配置插件代码不用动。对插件开发来说这点很关键插件是要分发给别人用的如果把密钥写死在代码里既不安全也没法维护。走 TaoToken 的统一通道用户在自己机器的settings.json里填自己的 Key 就行插件只负责读配置、发请求。2.2 拿到 Key 和确认接入信息先去控制台创建一个 API Key地址是 https://taotoken.net/api-keys 创建完复制出来后面填到settings.json里。API 的基础地址是 https://taotoken.net/api 注意这个地址不带任何查询参数直接作为 base URL 用。如果你后面想先验证模型通不通可以打开模型对话页面 https://taotoken.net/models 手动发一条消息试试如果打算长期在编码和 Agent 场景里用可以看下 Coding Plan https://taotoken.net/coding-plan 接入文档在 https://taotoken.net/doc 。2.3 初始化插件工程用官方脚手架起一个 TypeScript 插件项目最省事。先装好 Node.js 和yo、generator-codenpm install -g yo generator-code yo code交互式选择里选New Extension (TypeScript)名字填vscode-readonly其余默认。生成完目录结构大致是这样vscode-readonly/ ├── package.json ├── tsconfig.json ├── src/ │ └── extension.ts └── .vscode/ └── launch.jsonpackage.json是插件的清单文件命令、配置项、激活事件都在这里声明src/extension.ts是入口activate函数在插件被激活时执行。接下来所有改动都围绕这两个文件。3. 可复制配置命令注册、statusbar 与切换逻辑3.1 package.json 里声明命令和配置项打开package.json在contributes字段里加三块内容命令、配置项、以及激活事件。命令是给状态栏按钮点击时调用的配置项是让用户在settings.json里控制默认只读状态和 TaoToken 参数。{ contributes: { commands: [ { command: readonly.toggle, title: ReadOnly: 切换只读模式 } ], configuration: { title: ReadOnly, properties: { readonly.defaultOn: { type: boolean, default: false, description: 插件启动时是否默认开启只读模式 }, readonly.taotokenApiKey: { type: string, default: , description: TaoToken API Key用于插件内的模型调用 }, readonly.taotokenBaseUrl: { type: string, default: https://taotoken.net/api, description: TaoToken API 基础地址 } } } }, activationEvents: [ onStartupFinished ] }这里onStartupFinished让插件在 VSCode 启动完成后自动激活这样状态栏按钮一打开编辑器就能看到。命令readonly.toggle是唯一对外暴露的动作状态栏点击和命令面板都会走它。3.2 创建 statusbar 并绑定切换命令打开src/extension.ts先写状态栏的创建和切换逻辑。核心思路是维护一个布尔变量isReadOnly每次切换时更新它、刷新状态栏文案同时通过vscode.commands.executeCommand把type命令接管或还原。import * as vscode from vscode; let statusBarItem: vscode.StatusBarItem; let isReadOnly false; export function activate(context: vscode.ExtensionContext) { const config vscode.workspace.getConfiguration(readonly); isReadOnly config.getboolean(defaultOn, false); statusBarItem vscode.window.createStatusBarItem( vscode.StatusBarAlignment.Right, 100 ); statusBarItem.command readonly.toggle; updateStatusBar(); statusBarItem.show(); const toggleCmd vscode.commands.registerCommand(readonly.toggle, () { isReadOnly !isReadOnly; updateStatusBar(); vscode.window.showInformationMessage( isReadOnly ? 已进入只读模式 : 已退出只读模式 ); }); context.subscriptions.push(statusBarItem, toggleCmd); } function updateStatusBar() { statusBarItem.text isReadOnly ? $(lock) ReadOnly : $(unlock) Editable; statusBarItem.tooltip isReadOnly ? 当前只读点击切换为可编辑 : 当前可编辑点击切换为只读; }$(lock)和$(unlock)是 VSCode 内置的图标语法状态栏会直接渲染成小锁图标比纯文字直观。StatusBarAlignment.Right把它放在右下角优先级 100 保证它不会被其他插件挤掉。3.3 拦截 type 命令实现真正的只读光有状态栏文案还不够得让键盘输入真的不生效。VSCode 的编辑器在输入字符时会触发type命令我们把这个命令覆盖掉只读时什么都不做可编辑时把参数透传给默认实现default:type。const typeCmd vscode.commands.registerCommand(type, async (args) { if (isReadOnly) { return; } return vscode.commands.executeCommand(default:type, args); }); context.subscriptions.push(typeCmd);把这段也放进activate里。原理很直接type是编辑器输入的统一入口覆盖它等于在输入链路上加了一道闸门。只读时直接return字符就不会进入文档可编辑时原样转发给default:type行为跟原生完全一致。注意这个方案拦截的是键盘输入复制粘贴、格式化、批量替换这些操作走的是别的命令不在本次范围内。如果你要更严格的只读可以继续覆盖paste、editor.action.formatDocument等命令思路是一样的。3.4 settings.json 里的 TaoToken 配置片段插件装好后在用户或工作区的settings.json里填上 TaoToken 相关配置。这样插件读配置就能拿到 Key 和地址不用改代码{ readonly.defaultOn: true, readonly.taotokenApiKey: sk-你的Key, readonly.taotokenBaseUrl: https://taotoken.net/api }defaultOn设成true的话VSCode 一启动就是只读状态适合专门用来看代码的场景。Key 建议放在用户级settings.json里不要提交到仓库如果是团队共享的工作区配置Key 那行留空让每个人自己填。4. 验证请求本地跑起来看结果4.1 启动调试宿主在 VSCode 里按F5会弹出一个新的「扩展开发宿主」窗口这个窗口里加载的就是你刚写的插件。第一次启动会先编译 TypeScript等编译完成新窗口出现即可。新窗口右下角应该能看到状态栏按钮默认显示$(unlock) Editable或$(lock) ReadOnly取决于你defaultOn设的是啥。点一下按钮文案会在两个状态间切换同时弹出提示。4.2 验证只读是否真的生效在新窗口里随便打开一个文件点状态栏切到ReadOnly然后敲键盘。你会发现光标不动、字符不出现文档内容完全没变化。再点一下切回Editable键盘输入恢复正常。这一步是核心验证点如果只读时还能输入说明type命令没拦截成功回去检查命令注册的时机和isReadOnly的初始值。4.3 验证 TaoToken 配置读取在extension.ts里加一段临时日志确认配置能读到const apiKey config.getstring(taotokenApiKey, ); const baseUrl config.getstring(taotokenBaseUrl, ); console.log(TaoToken baseUrl:, baseUrl, key length:, apiKey.length);按CtrlShiftI打开调试控制台能看到输出的 baseUrl 和 key 长度。key 长度不为 0 就说明配置读取正常。如果你要真的发一次请求可以用fetch打https://taotoken.net/api下的对话接口带上Authorization: Bearer key返回 200 就说明通道通了。手动验证模型是否可用直接去 https://taotoken.net/models 发一条消息更快。5. 本篇常见错排查5.1 状态栏按钮不显示最常见的原因是activationEvents没配对。如果你写的是onStartupFinished插件会在启动后激活如果写成了onCommand:readonly.toggle那只有手动执行命令才会激活状态栏自然不出现。另外检查statusBarItem.show()有没有被调用以及context.subscriptions.push里有没有把它加进去否则可能被提前回收。5.2 只读时还能输入先确认type命令的注册在activate里执行了并且isReadOnly在切换时确实变了。有个容易踩的坑isReadOnly如果声明在函数内部而不是模块顶层每次切换读到的可能是旧值。把它放在模块作用域activate和命令回调共享同一个变量。还有一种情况是别的插件也覆盖了type命令注册有先后顺序后注册的会覆盖先注册的可以调整插件加载顺序或改用vscode.commands.registerCommand的返回值做链式处理。5.3 配置项读不到vscode.workspace.getConfiguration(readonly)里的参数是配置的 section 名必须和package.json里configuration.properties的前缀一致。如果你在package.json里写的是readonly.taotokenApiKey那 section 就是readonlyget 的时候传taotokenApiKey。改完package.json记得重新按F5启动宿主配置清单的变更不会热更新。5.4 打包成 vsix 后行为不一致本地调试用的是源码打包后走的是编译产物。确认tsconfig.json的outDir和package.json的main指向一致通常是./out/extension.js。打包命令用vsce package如果提示缺少repository字段在package.json里补一个即可。装 vsix 用code --install-extension xxx.vsix。6. 把只读开关和统一通道用起来到这里一个能用的 read-only 插件就成型了状态栏按钮切换、type命令拦截、TaoToken 配置读取三块都跑通了。我自己的用法是把它设成默认只读专门用来读线上仓库和第三方库源码需要改的时候点一下解锁改完再锁上误触基本绝迹。如果你打算继续扩展几个方向可以试试把paste和editor.action.formatDocument也纳入拦截只读会更彻底把 TaoToken 的调用封装成一个explainSelection命令选中代码后直接让模型解释配合只读模式看陌生代码效率很高。长期在编码和 Agent 场景里用的话Coding Plan 那条通道会更顺接入文档里有完整的参数说明。Key 管理和接入细节都在 https://taotoken.net/api-keys 和 https://taotoken.net/doc 遇到请求报错先看返回的状态码和 message多数是 Key 没填对或者 base URL 多带了斜杠。插件代码本身不复杂真正花时间的是把命令拦截的边界想清楚哪些操作该拦、哪些该放这个取舍按你自己的使用习惯来定就行。