尝试以扩展 VS Code 中的代理模式!

虚拟文档

文本文档内容提供程序 API 允许您从任意来源在 Visual Studio Code 中创建只读文档。您可以在以下链接找到包含源代码的示例扩展:https://github.com/microsoft/vscode-extension-samples/blob/main/virtual-document-sample/README.md

TextDocumentContentProvider

该 API 的工作原理是声明一个 URI 方案,然后您的提供程序会为该方案返回文本内容。在注册提供程序时必须提供该方案,并且之后不能更改。同一个提供程序可以用于多个方案,并且可以为单个方案注册多个提供程序。

vscode.workspace.registerTextDocumentContentProvider(myScheme, myProvider);

调用 registerTextDocumentContentProvider 会返回一个可释放对象,通过该对象可以撤销注册。提供程序只需实现 provideTextDocumentContent 函数,该函数会以 URI 和取消令牌作为参数被调用。

const myProvider = new (class implements vscode.TextDocumentContentProvider {
  provideTextDocumentContent(uri: vscode.Uri): string {
    // invoke cowsay, use uri-path as text
    return cowsay.say({ text: uri.path });
  }
})();

请注意,提供程序不会为虚拟文档创建 URI——它的作用是在给定此类 URI 的情况下提供内容。作为回报,内容提供程序被连接到打开文档的逻辑中,以便始终考虑这些提供程序。

本示例使用了一个“cowsay”命令,该命令会生成一个 URI,然后编辑器应该显示该 URI。

vscode.commands.registerCommand('cowsay.say', async () => {
  let what = await vscode.window.showInputBox({ placeHolder: 'cow say?' });
  if (what) {
    let uri = vscode.Uri.parse('cowsay:' + what);
    let doc = await vscode.workspace.openTextDocument(uri); // calls back into the provider
    await vscode.window.showTextDocument(doc, { preview: false });
  }
});

该命令会提示输入,创建一个 cowsay 方案的 URI,为该 URI 打开一个文档,最后为该文档打开一个编辑器。在第 3 步(打开文档)中,提供程序会被要求为该 URI 提供内容。

这样,我们就拥有了一个功能完备的文本文档内容提供程序。接下来的章节将描述如何更新虚拟文档以及如何为虚拟文档注册 UI 命令。

更新虚拟文档

根据场景,虚拟文档可能会发生变化。为了支持这一点,提供程序可以实现 onDidChange 事件。

vscode.Event 类型定义了 VS Code 中事件的契约。实现事件最简单的方法是 vscode.EventEmitter,例如:

const myProvider = new (class implements vscode.TextDocumentContentProvider {
  // emitter and its event
  onDidChangeEmitter = new vscode.EventEmitter<vscode.Uri>();
  onDidChange = this.onDidChangeEmitter.event;

  //...
})();

事件发射器有一个 fire 方法,当文档发生更改时,可以使用该方法通知 VS Code。更改的文档通过其 URI(作为参数传递给 fire 方法)来标识。然后提供程序将再次被调用以提供更新的内容,前提是文档仍处于打开状态。

这就是让 VS Code 监听虚拟文档更改所需的全部。要查看一个使用此功能的更复杂示例,请查看:https://github.com/microsoft/vscode-extension-samples/blob/main/contentprovider-sample/README.md

添加编辑器命令

可以添加编辑器操作,这些操作仅与关联内容提供程序提供的文档进行交互。这是一个示例命令,它将牛说的话反转过来:

// register a command that updates the current cowsay
subscriptions.push(
  vscode.commands.registerCommand('cowsay.backwards', async () => {
    if (!vscode.window.activeTextEditor) {
      return; // no editor
    }
    let { document } = vscode.window.activeTextEditor;
    if (document.uri.scheme !== myScheme) {
      return; // not my scheme
    }
    // get path-components, reverse it, and create a new uri
    let say = document.uri.path;
    let newSay = say
      .split('')
      .reverse()
      .join('');
    let newUri = document.uri.with({ path: newSay });
    await vscode.window.showTextDocument(newUri, { preview: false });
  })
);

上面的代码片段会检查我们是否有一个活动编辑器以及它的文档是否属于我们的方案。这些检查是必需的,因为命令对所有人可用(且可执行)。然后,URI 的路径部分被反转,并从它创建一个新的 URI,最后打开一个编辑器。

要为编辑器命令添加顶部功能,需要在 package.json 中进行声明性配置。在 contributes 部分添加此配置:

"menus": {
  "editor/title": [
    {
      "command": "cowsay.backwards",
      "group": "navigation",
      "when": "resourceScheme == cowsay"
    }
  ]
}

这引用了在 contributes/commands 部分定义的 cowsay.backwards 命令,并表示它应该出现在编辑器标题菜单(右上角的工具栏)中。然而,仅仅如此将意味着该命令会始终显示,对于每个编辑器。这就是 when 子句的用途——它描述了必须满足什么条件才能显示该操作。在此示例中,它声明编辑器中文档的方案必须是 cowsay 方案。然后,此配置会针对 commandPalette 菜单重复——它默认显示所有命令。

cowsay-bwd

事件和可见性

文档提供程序是 VS Code 中的一等公民,它们的内容出现在常规文本文档中,它们使用与文件相同的底层基础设施等。然而,这也意味着“您的”文档无法隐藏,它们将出现在 onDidOpenTextDocumentonDidCloseTextDocument 事件中,它们是 vscode.workspace.textDocuments 及其他的一部分。对于所有人来说,规则是检查文档的 scheme,然后决定是否要对该文档进行操作。

文件系统 API

如果您需要更大的灵活性和功能,请查看 FileSystemProvider API。它允许实现一个完整的文件系统,包括文件、文件夹、二进制数据、文件删除、创建等。

您可以在以下链接找到包含源代码的示例扩展:https://github.com/microsoft/vscode-extension-samples/tree/main/fsprovider-sample/README.md

当 VS Code 在此类文件系统的一个文件夹或工作区中打开时,我们称之为虚拟工作区。当虚拟工作区在 VS Code 窗口中打开时,这会在左下角的远程指示器中通过标签显示,类似于远程窗口。请参阅虚拟工作区指南,了解扩展如何支持此设置。