虚拟文档

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

文本文档内容提供者

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标识,该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窗口中打开时,这会在左下角的远程指示器中显示一个标签,类似于远程窗口。请参阅虚拟工作空间指南了解扩展如何支持该设置。