我不知道的 VSCode 扩展:高级 API 技巧

970 字 16 min read
前端开发 VSCode 扩展开发 API 高级技巧 Workspace API Document API Language Server Protocol Debug API Task API Terminal API Webview API

引言

🌟 在前面的系列文章中,我们已经学习了 VSCode 扩展开发的基础知识、用户界面、常用 API、生命周期、调试、发布和 CI/CD。现在,我们将深入探索 VSCode 扩展开发中的高级 API 技巧,学习如何利用更强大、更灵活的 API 来构建更复杂、更智能、更用户友好的 VSCode 扩展。掌握这些高级 API 技巧,将使你的扩展开发能力更上一层楼,让你能够创造出真正令人惊艳的 VSCode 扩展!现在,就让我们一起开启 VSCode 高级 API 的探索之旅!

高级 API 技巧

🚀 VSCode 提供了非常丰富的 API,除了我们之前介绍的常用 API 外,还有许多高级 API 可以帮助我们实现更复杂的功能。本节将介绍一些常用的高级 API 技巧,包括:

1. Workspace API:多工作区支持

📂 Workspace API 允许扩展与 VSCode 的工作区进行交互,包括获取工作区信息、监听工作区事件、管理工作区文件夹等。多工作区 (Multi-root Workspace) 是 VSCode 的一个重要特性,允许用户在同一个 VSCode 窗口中打开多个项目文件夹。如果你的扩展需要支持多工作区,或者需要根据不同的工作区文件夹执行不同的操作,Workspace API 将非常有用。

  • vscode.workspace.workspaceFolders: 获取当前工作区的所有文件夹。返回一个 WorkspaceFolder[] 数组,每个 WorkspaceFolder 对象包含文件夹的 uri (URI 对象) 和 name (文件夹名称)。
  • vscode.workspace.onDidChangeWorkspaceFolders: 监听工作区文件夹变化事件。当用户添加、移除或移动工作区文件夹时,该事件会触发。
  • vscode.workspace.getWorkspaceFolder(uri): 根据给定的 URI,获取该 URI 所属的工作区文件夹。
  • vscode.workspace.findFiles(globPattern, ignoreGlobPattern?, maxResults?, token?): 在工作区中查找文件。可以使用 Glob 模式匹配文件路径,支持排除模式、最大结果数和取消令牌。
  • vscode.workspace.createFileSystemWatcher(globPattern, ignoreGlobPattern?, ignoreChangeEvents?, ignoreCreateEvents?, ignoreDeleteEvents?): 创建文件系统监视器,监听文件变化事件。可以监听文件创建、修改、删除事件,并使用 Glob 模式过滤文件。

示例:根据不同的工作区文件夹显示不同的视图

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
    vscode.window.registerTreeDataProvider('myWorkspaceView', {
        getChildren: (element?: vscode.WorkspaceFolder) => {
            if (!vscode.workspace.workspaceFolders) {
                return Promise.resolve([]);
            }
            if (!element) {
                // 根节点,显示工作区文件夹
                return Promise.resolve(vscode.workspace.workspaceFolders);
            } else {
                // 子节点,显示文件夹下的文件 (这里只是示例,实际应用中可以根据需要显示不同的内容)
                return vscode.workspace.findFiles(`**/*`, undefined, undefined).then(files => {
                    return files.filter(file => vscode.workspace.getWorkspaceFolder(file) === element);
                });
            }
        },
        getTreeItem: (element: vscode.WorkspaceFolder | vscode.Uri) => {
            if (element instanceof vscode.WorkspaceFolder) {
                // 工作区文件夹节点
                return new vscode.TreeItem(element.name, vscode.TreeItemCollapsibleState.Collapsed);
            } else {
                // 文件节点
                return new vscode.TreeItem(vscode.workspace.asRelativePath(element));
            }
        }
    });
}

2. Document API:深入文本编辑

📝 Document API 允许扩展深入控制文本编辑器,包括获取和修改文档内容、管理文本选择、插入和删除文本、应用文本装饰器 (Decorations) 等。如果你需要开发代码编辑器增强功能、代码格式化工具、代码片段扩展等,Document API 将是你的利器。

  • vscode.window.activeTextEditor: 获取当前激活的文本编辑器。
  • vscode.window.visibleTextEditors: 获取当前所有可见的文本编辑器。
  • vscode.TextEditor.document: 获取文本编辑器关联的 TextDocument 对象,表示文档内容。
  • vscode.TextDocument.getText(range?): 获取文档的文本内容,可以指定范围。
  • vscode.TextDocument.lineCount: 获取文档的行数。
  • vscode.TextDocument.lineAt(lineNumber): 获取指定行的 TextLine 对象,包含行的文本内容、行号、Range 等信息。
  • vscode.TextEditor.selection: 获取和设置文本编辑器的当前选择区域 (Selection 对象)。
  • vscode.TextEditor.selections: 获取和设置文本编辑器的多光标选择区域 (Selection[] 数组)。
  • vscode.TextEditor.edit(editBuilder): 应用文本编辑操作。使用 TextEditorEdit 对象构建编辑操作,例如插入文本、替换文本、删除文本等。
  • vscode.TextEditor.setDecorations(decorationType, rangesOrOptions): 应用文本装饰器。可以使用 DecorationOptionsRange 数组指定装饰范围和样式,例如高亮显示、添加下划线、添加边框等。
  • vscode.TextEditor.revealRange(range, revealType?): 滚动编辑器,使指定的 Range 可见。

示例:实现一个简单的代码高亮扩展

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
    let activeEditor = vscode.window.activeTextEditor;
    let decorationType = vscode.window.createTextEditorDecorationType({
        backgroundColor: 'rgba(255, 255, 0, 0.2)',
        border: '1px solid yellow'
    });

    function updateDecorations() {
        if (!activeEditor) {
            return;
        }
        const text = activeEditor.document.getText();
        const decorations: vscode.DecorationOptions[] = [];
        let match;
        const regEx = /vscode/gi; // 匹配 "vscode" 关键词 (忽略大小写)
        while ((match = regEx.exec(text))) {
            const startPos = activeEditor.document.positionAt(match.index);
            const endPos = activeEditor.document.positionAt(match.index + match[0].length);
            const decoration = { range: new vscode.Range(startPos, endPos), hoverMessage: '关键词 **vscode**' };
            decorations.push(decoration);
        }
        activeEditor.setDecorations(decorationType, decorations);
    }

    function triggerUpdateDecorations(throttle = false) {
        if (throttle) {
            clearTimeout(timeout);
            timeout = setTimeout(updateDecorations, 500);
        } else {
            updateDecorations();
        }
    }

    let timeout: NodeJS.Timer | undefined;
    vscode.window.onDidChangeActiveTextEditor(editor => {
        activeEditor = editor;
        if (editor) {
            triggerUpdateDecorations();
        }
    }, null, context.subscriptions);

    vscode.workspace.onDidChangeTextDocument(event => {
        if (activeEditor && event.document === activeEditor.document) {
            triggerUpdateDecorations(true);
        }
    }, null, context.subscriptions);

    if (activeEditor) {
        triggerUpdateDecorations();
    }
}

3. Language Server Protocol (LSP) 客户端 API

🗣️ Language Server Protocol (LSP) 是一种标准化的协议,用于在代码编辑器和语言服务器之间进行通信,实现代码智能功能,例如自动补全、代码导航、错误检查、代码格式化等。VSCode 内置了 LSP 客户端,并提供了 LSP 客户端 API,允许扩展与语言服务器进行交互,或者实现自定义的语言客户端。如果你需要开发语言支持扩展,或者需要与现有的语言服务器集成,LSP 客户端 API 将非常有用。

  • vscode.languages.registerLanguageClient(clientOptions): 注册语言客户端。clientOptions 是一个 LanguageClientOptions 对象,用于配置语言客户端的各种选项,例如语言 ID、服务器选项、客户端能力等。
  • vscode.languages.getLanguageClient(languageId): 根据语言 ID 获取已注册的语言客户端。
  • vscode.LanguageClient: 语言客户端类,提供了与语言服务器通信的方法,例如发送请求、发送通知、监听事件等。

示例:注册一个简单的语言客户端 (假设已经有一个运行中的语言服务器)

import * as vscode from 'vscode';
import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node';

let client: LanguageClient;

export function activate(context: vscode.ExtensionContext) {
    // 语言服务器模块的路径
    const serverModule = context.asAbsolutePath('server/server.js');
    // 调试模式下,使用 TCP 传输
    const debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] };

    // 服务器选项
    const serverOptions: ServerOptions = {
        run: { module: serverModule, transport: TransportKind.ipc },
        debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
    };

    // 客户端选项
    const clientOptions: LanguageClientOptions = {
        documentSelector: [{ scheme: 'file', language: 'plaintext' }], // 关联的文档类型
        synchronize: {
            configurationSection: 'plaintextLanguageServer', // 同步配置
            fileEvents: vscode.workspace.createFileSystemWatcher('**/.clientrc') // 监听文件变化
        }
    };

    // 创建语言客户端
    client = new LanguageClient('plaintextLanguageServer', 'Plain Text Language Server', serverOptions, clientOptions);

    // 启动语言客户端
    client.start();
}

export function deactivate(): Thenable<void> | undefined {
    if (!client) {
        return undefined;
    }
    return client.stop();
}

4. Debug API:程序化控制调试器

🐞 Debug API 允许扩展程序化地控制 VSCode 的调试器,包括启动调试会话、设置断点、控制调试执行、获取调试状态等。如果你需要开发自定义的调试器扩展、或者需要在扩展中集成调试功能,Debug API 将非常有用。

  • vscode.debug.startDebugging(folder, nameOrConfiguration, parentSession): 启动调试会话。可以指定工作区文件夹、调试配置名称或配置对象、父调试会话。
  • vscode.debug.activeDebugSession: 获取当前激活的调试会话。
  • vscode.debug.activeDebugSessions: 获取当前所有激活的调试会话。
  • vscode.debug.onDidStartDebugSession: 监听调试会话启动事件.
  • vscode.debug.onDidTerminateDebugSession: 监听调试会话结束事件。
  • vscode.debug.onDidReceiveDebugSessionCustomEvent: 监听调试会话自定义事件。
  • vscode.debug.breakpoints: 获取和设置全局断点 (Breakpoint[] 数组)。
  • vscode.debug.addBreakpoints(breakpoints): 添加断点。
  • vscode.debug.removeBreakpoints(breakpoints): 移除断点。

示例:扩展中启动调试会话

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
    let disposable = vscode.commands.registerCommand('extension.startDebugger', () => {
        vscode.debug.startDebugging(undefined, 'node-configuration'); // 启动名为 "node-configuration" 的调试配置
    });
    context.subscriptions.push(disposable);
}

5. Task API:集成任务系统

🚀 Task API 允许扩展与 VSCode 的任务系统集成,创建和管理任务,例如构建任务、测试任务、部署任务等。VSCode 的任务系统允许用户在 VSCode 中运行外部命令和脚本,并与编辑器集成,例如在 "终端" 面板中显示任务输出、在 "问题" 面板中显示任务错误和警告等。如果你需要扩展提供自定义的任务,或者需要与现有的构建工具或任务运行器集成,Task API 将非常有用。

  • vscode.tasks.registerTaskProvider(taskDefinition, provider): 注册任务提供器。任务提供器负责提供任务。
  • vscode.tasks.fetchTasks(filter?): 获取所有可用的任务,可以根据任务类型、任务定义等条件进行过滤。
  • vscode.tasks.executeTask(task): 执行任务。
  • vscode.tasks.onDidStartTask: 监听任务启动事件。
  • vscode.tasks.onDidEndTask: 监听任务结束事件。
  • vscode.tasks.onDidStartTaskProcess: 监听任务进程启动事件。
  • vscode.tasks.onDidEndTaskProcess: 监听任务进程结束事件。

示例:注册一个简单的任务提供器

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
    vscode.tasks.registerTaskProvider('myTaskType', {
        provideTasks(token?: vscode.CancellationToken): vscode.ProviderResult<vscode.Task[]> {
            const taskDefinition: vscode.TaskDefinition = { type: 'myTaskType' };
            const task = new vscode.Task(taskDefinition, vscode.TaskScope.Workspace, 'My Task', 'my-extension');
            task.execution = new vscode.ShellExecution('echo Hello from My Task'); // 任务执行的命令
            task.problemMatchers = ["$jshint"]; // 问题匹配器 (可选)
            return [task];
        },
        resolveTask(task: vscode.Task, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.Task> {
            return task;
        }
    });
}

6. Terminal API:集成终端

💻 Terminal API 允许扩展与 VSCode 的集成终端进行交互,包括创建和管理终端、发送命令到终端、接收终端输出、监听终端事件等。如果你需要扩展提供与终端交互的功能,例如运行命令、监控进程、自动化终端操作等,Terminal API 将非常有用。

  • vscode.window.createTerminal(options?): 创建终端。可以指定终端名称、shell 路径、环境变量、工作目录等选项。
  • vscode.window.terminals: 获取当前所有已创建的终端 (Terminal[] 数组)。
  • vscode.window.activeTerminal: 获取当前激活的终端。
  • vscode.window.onDidOpenTerminal: 监听终端打开事件。
  • vscode.window.onDidCloseTerminal: 监听终端关闭事件。
  • vscode.Terminal.show(preserveFocus?): 显示终端。
  • vscode.Terminal.hide(): 隐藏终端。
  • vscode.Terminal.sendText(text, addNewLine?): 发送文本到终端。
  • vscode.Terminal.dispose(): 销毁终端。

示例:扩展中创建并发送命令到终端

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
    let disposable = vscode.commands.registerCommand('extension.runCommandInTerminal', () => {
        const terminal = vscode.window.createTerminal('My Terminal'); // 创建名为 "My Terminal" 的终端
        terminal.show(); // 显示终端
        terminal.sendText('echo Hello from VSCode Extension'); // 发送命令到终端
    });
    context.subscriptions.push(disposable);
}

7. Webview API (高级):更强大的 Webview 功能

🌐 Webview API 除了之前介绍的基本功能外,还提供了更高级的功能,例如:

  • WebviewPanel.reveal(viewColumn?, preserveFocus?): 显示 Webview 面板,并可以控制是否保留焦点。
  • WebviewPanel.webview.asWebviewUri(localResource): 将本地资源 (例如 HTML 文件、CSS 文件、JS 文件、图片文件) 转换为 Webview 可访问的 URI。在 Webview 中加载本地资源时,必须使用 asWebviewUri 转换 URI,以确保 Webview 能够正确加载资源,并防止安全问题。
  • WebviewPanel.webview.cspSource: 获取 Webview 的内容安全策略 (CSP) 源。在 Webview 中使用内联 JavaScript 或 CSS 时,需要配置 CSP,以确保安全。
  • WebviewPanel.onDidChangeViewState: 监听 Webview 面板的视图状态变化事件,例如 Webview 是否可见、是否激活等。
  • Webview.postMessage(message): 从扩展代码向 Webview 发送消息。
  • Webview.onDidReceiveMessage: 监听从 Webview 发送的消息。

示例:在 Webview 中加载本地 HTML 文件并使用 asWebviewUri 转换 URI

import * as vscode from 'vscode';
import * as path from 'path';

export function activate(context: vscode.ExtensionContext) {
    let disposable = vscode.commands.registerCommand('extension.openWebview', () => {
        const panel = vscode.window.createWebviewPanel(
            'myWebview', // Webview ID
            'My Webview', // Webview 标题
            vscode.ViewColumn.One, // 在编辑器的哪个列中显示 Webview
            {
                enableScripts: true, // 启用 JavaScript
                localResourceRoots: [context.extensionUri] // 允许加载本地资源的根路径
            }
        );

        // 加载本地 HTML 文件
        const htmlPath = path.join(context.extensionPath, 'webview', 'index.html');
        const htmlUri = vscode.Uri.file(htmlPath);
        const webviewUri = panel.webview.asWebviewUri(htmlUri); // 转换为 Webview URI

        panel.webview.html = `<!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>My Webview</title>
        </head>
        <body>
            <h1>Hello from Webview!</h1>
            <img src="${webviewUri}" />  // 使用转换后的 Webview URI
        </body>
        </html>`;
    });
    context.subscriptions.push(disposable);
}

总结

🎉 恭喜你,完成了 VSCode 扩展高级 API 技巧的学习!本文介绍了 Workspace API、Document API、LSP 客户端 API、Debug API、Task API、Terminal API 和 Webview API 等高级 API 技巧,并提供了丰富的示例代码。掌握这些高级 API 技巧,将使你能够开发出更强大、更智能、更用户友好的 VSCode 扩展,满足更复杂的需求。在实际开发中,不断探索和实践这些高级 API,结合你的创意和需求,相信你一定能够创造出令人惊艳的 VSCode 扩展,为 VSCode 社区贡献更多优秀的作品!