~/ ?.log $
返回文章列表
10 min read
更新于 2026年3月6日

我不知道的 VSCode 扩展(04)— API 全景与实战技巧

VSCode 扩展 API 的官方文档列了几百个接口,很多人第一次打开就晕了。其实这些 API 并不是平铺的,而是按职责域组织成几个大模块。理解这张地图,比记住每个方法名重要得多。

一、API 不是一个列表,而是一张地图

VSCode 扩展 API 的官方文档列了几百个接口,很多人第一次打开就晕了。其实这些 API 并不是平铺的,而是按职责域组织成几个大模块。理解这张地图,比记住每个方法名重要得多。

核心模块一共六个:

模块职责典型场景
vscode.window用户界面交互弹框、选择器、状态栏、Webview、终端
vscode.workspace工作区与文件系统读写文件、监听文件变化、读取配置
vscode.commands命令系统注册命令、执行命令
vscode.languages语言特性代码补全、悬浮提示、跳转定义、诊断
vscode.debug调试器启动调试、管理断点
vscode.tasks任务系统注册构建任务、运行脚本

下面按照”实际开发中最常用”的优先级,逐一拆解每个模块的核心用法。

二、vscode.window — 和用户对话

vscode.window 是使用频率最高的模块,因为几乎所有用户可见的操作都经过它。

消息框:三种级别

vscode.window.showInformationMessage('操作完成');
vscode.window.showWarningMessage('配置缺失,使用默认值');
vscode.window.showErrorMessage('连接失败');

消息框还可以带按钮,点击后返回选中的按钮文本:

const choice = await vscode.window.showWarningMessage(
  '文件已修改,是否保存?',
  '保存',
  '不保存',
  '取消',
);

if (choice === '保存') {
  // ...
}

Quick Pick:下拉选择

const lang = await vscode.window.showQuickPick(['JavaScript', 'TypeScript', 'Python', 'Go'], {
  placeHolder: '选择目标语言',
});

Quick Pick 还支持多选(canPickMany: true)和动态搜索(传入 Promise 或实现 QuickPickItem 接口)。

Input Box:文本输入

const name = await vscode.window.showInputBox({
  prompt: '输入项目名称',
  validateInput: (value) => {
    return /^[a-z0-9-]+$/.test(value) ? null : '只允许小写字母、数字和连字符';
  },
});

validateInput 回调实时校验输入,返回 null 表示合法,返回字符串表示错误信息——会直接显示在输入框下方。

终端操作

const terminal = vscode.window.createTerminal('Build');
terminal.show();
terminal.sendText('npm run build');

这里有一个很多人会忽略的细节——sendText 只是把文本”输入”到终端,相当于用户在终端里打字然后按回车。它不会等命令执行完毕,也拿不到命令的退出码。如果需要知道命令是否执行成功,得用 vscode.tasks 模块(后面会讲)。

三、vscode.workspace — 和文件系统打交道

获取工作区信息

const folders = vscode.workspace.workspaceFolders;
if (folders) {
  const rootPath = folders[0].uri.fsPath;
}

VSCode 支持多根工作区(Multi-root Workspace),所以 workspaceFolders 是一个数组,不是单个路径。很多扩展写死了 workspaceFolders[0],在多根工作区下会出问题。

读写文件

const uri = vscode.Uri.file('/path/to/file.json');

// 读取
const content = await vscode.workspace.fs.readFile(uri);
const text = Buffer.from(content).toString('utf-8');

// 写入
const data = Buffer.from(JSON.stringify({ key: 'value' }), 'utf-8');
await vscode.workspace.fs.writeFile(uri, data);

vscode.workspace.fs 是 VSCode 提供的虚拟文件系统 API,不仅支持本地文件,还支持远程文件(比如 SSH Remote 场景)。用 Node.js 的 fs 模块虽然也能读写本地文件,但在远程开发场景下会失效。

监听文件变化

const watcher = vscode.workspace.createFileSystemWatcher('**/*.json');

watcher.onDidChange((uri) => {
  console.log(`Changed: ${uri.fsPath}`);
});

watcher.onDidCreate((uri) => {
  console.log(`Created: ${uri.fsPath}`);
});

watcher.onDidDelete((uri) => {
  console.log(`Deleted: ${uri.fsPath}`);
});

Glob 模式 **/*.json 表示监听工作区内所有 JSON 文件的变化。

读取和监听配置

// 读取配置
const config = vscode.workspace.getConfiguration('myExtension');
const autoSave = config.get<boolean>('autoSave', true);

// 监听配置变化
vscode.workspace.onDidChangeConfiguration((e) => {
  if (e.affectsConfiguration('myExtension.autoSave')) {
    const newValue = config.get<boolean>('autoSave');
    // 重新应用配置
  }
});

配置项需要先在 package.jsoncontributes.configuration 中声明,然后才能通过 getConfiguration 读取。

四、TextEditor — 操作编辑器内容

vscode.window.activeTextEditor 返回当前激活的编辑器实例。通过它可以读取内容、修改文本、设置选择区域。

获取选中文本

const editor = vscode.window.activeTextEditor;
if (editor) {
  const selection = editor.selection;
  const selectedText = editor.document.getText(selection);
}

插入或替换文本

editor.edit((editBuilder) => {
  // 在光标位置插入
  editBuilder.insert(editor.selection.active, 'inserted text');

  // 替换选中区域
  editBuilder.replace(editor.selection, 'replaced text');

  // 删除指定范围
  editBuilder.delete(new vscode.Range(0, 0, 0, 10));
});

editor.edit() 接收一个回调,回调参数 editBuilder 提供 insertreplacedelete 三个操作。所有修改在回调执行完后作为一次原子操作应用——要么全部成功,要么全部失败,不会出现修改到一半的中间状态。

文本装饰器(Decorations)

文本装饰器可以给编辑器中的文本添加视觉效果——高亮、下划线、背景色、边框等。

const decorationType = vscode.window.createTextEditorDecorationType({
  backgroundColor: 'rgba(255, 255, 0, 0.2)',
  border: '1px solid rgba(255, 255, 0, 0.5)',
});

function highlightKeyword(editor: vscode.TextEditor, keyword: string) {
  const text = editor.document.getText();
  const decorations: vscode.DecorationOptions[] = [];
  const regex = new RegExp(keyword, 'gi');
  let match;

  while ((match = regex.exec(text))) {
    const start = editor.document.positionAt(match.index);
    const end = editor.document.positionAt(match.index + match[0].length);
    decorations.push({
      range: new vscode.Range(start, end),
      hoverMessage: `Found: **${keyword}**`,
    });
  }

  editor.setDecorations(decorationType, decorations);
}

说白了,Decorations 就是给编辑器文本”贴标签”的能力。GitLens 的行内 blame 信息、错误波浪线、搜索高亮,底层都是 Decorations。

五、vscode.languages — 语言智能

vscode.languages 模块是实现代码智能的入口——补全、悬浮提示、跳转定义、诊断、格式化。

代码补全

vscode.languages.registerCompletionItemProvider('typescript', {
  provideCompletionItems(document, position) {
    const item = new vscode.CompletionItem('mySnippet');
    item.kind = vscode.CompletionItemKind.Snippet;
    item.insertText = new vscode.SnippetString('console.log("${1:message}", ${2:value});');
    item.documentation = '插入一条带参数的 console.log';
    return [item];
  },
});

SnippetString 支持占位符语法(${1:default}),和 VSCode 内置 Snippet 的语法一致。

诊断信息(波浪线)

const diagnostics = vscode.languages.createDiagnosticCollection('myLinter');

function lint(document: vscode.TextDocument) {
  const problems: vscode.Diagnostic[] = [];
  const text = document.getText();

  // 查找 console.log 并标记为警告
  const regex = /console\.log/g;
  let match;
  while ((match = regex.exec(text))) {
    const start = document.positionAt(match.index);
    const end = document.positionAt(match.index + match[0].length);
    problems.push(
      new vscode.Diagnostic(
        new vscode.Range(start, end),
        '生产代码中不建议保留 console.log',
        vscode.DiagnosticSeverity.Warning,
      ),
    );
  }

  diagnostics.set(document.uri, problems);
}

DiagnosticCollection 是编辑器中波浪线(红色 = Error、黄色 = Warning、蓝色 = Information)的来源。ESLint 扩展在编辑器里标记的问题,底层就是通过这个 API 注入的。

Language Server Protocol(LSP)

当语言智能的逻辑比较复杂(比如需要解析整个项目的类型信息),在扩展进程里直接跑会阻塞 UI。这时候应该把语言分析逻辑拆到一个独立的 Language Server 进程中,通过 LSP 协议与 VSCode 通信。

import { LanguageClient, TransportKind } from 'vscode-languageclient/node';

const serverModule = context.asAbsolutePath('server/out/server.js');

const client = new LanguageClient(
  'myLanguageServer',
  'My Language Server',
  {
    run: { module: serverModule, transport: TransportKind.ipc },
    debug: { module: serverModule, transport: TransportKind.ipc },
  },
  {
    documentSelector: [{ scheme: 'file', language: 'myLang' }],
  },
);

client.start();

问题的关键在于——LSP 不是”高级功能”,而是”正确做法”。如果语言分析逻辑超过几百毫秒,就应该走 LSP,否则会让整个扩展进程卡住,影响所有已安装扩展的响应速度。

六、vscode.tasks — 运行构建任务

terminal.sendText 不同,vscode.tasks 提供了结构化的任务执行能力——可以追踪任务状态、解析输出中的错误信息、在”问题”面板中显示。

const task = new vscode.Task(
  { type: 'myBuild' },
  vscode.TaskScope.Workspace,
  'Build',
  'my-ext',
  new vscode.ShellExecution('npm run build'),
);

task.problemMatchers = ['$tsc'];

await vscode.tasks.executeTask(task);

problemMatchers 是任务系统的精华——它告诉 VSCode 如何从任务输出中提取错误和警告信息。$tsc 是 TypeScript 编译器的内置匹配器,能把 error TS2304: Cannot find name 'foo' 这样的输出解析成编辑器中的红色波浪线。

换句话说,终端 API 是”发射后不管”,任务 API 是”发射后跟踪”。

七、API 地图总结

如果你只记住一句话:window 管 UI,workspace 管文件,languages 管智能,commands 管命令,tasks 管构建,debug 管调试——六个模块,覆盖 95% 的扩展场景。

要做什么用哪个模块
弹框、选择、输入vscode.window.show*
操作编辑器文本vscode.window.activeTextEditor.edit()
读写文件vscode.workspace.fs
监听文件变化vscode.workspace.createFileSystemWatcher
读取用户配置vscode.workspace.getConfiguration
代码补全/悬浮/跳转vscode.languages.register*Provider
标记代码问题vscode.languages.createDiagnosticCollection
运行命令vscode.commands.executeCommand
运行构建任务vscode.tasks.executeTask
创建终端vscode.window.createTerminal

本系列其他文章:

延伸阅读:

share.ts

// 觉得这篇文章有帮助?

// 欢迎分享给更多人

export const  subscribe  =  "/rss.xml" ;