虚拟文档
文本内容提供程序 API 允许你在 Visual Studio Code 中从任意来源创建只读文档。你可以在此处找到包含源代码的示例扩展:https://github.com/microsoft/vscode-extension-samples/blob/main/virtual-document-sample/README.md。
TextDocumentContentProvider
该 API 的工作方式是声明一个 URI 方案(scheme),你的提供程序将为该方案返回文本内容。方案必须在注册提供程序时提供,之后无法更改。同一个提供程序可用于多个方案,也可以为单个方案注册多个提供程序。
vscode.workspace.registerTextDocumentContentProvider(myScheme, myProvider);
调用 registerTextDocumentContentProvider 会返回一个可清理对象(disposable),用于撤销注册。提供程序只需实现 provideTextDocumentContent 函数,该函数在被调用时会传入一个 URI 和取消令牌(cancellation token)。
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。
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;
//...
})();
事件发射器(event emitter)有一个 fire 方法,可用于在文档发生更改时通知 VS Code。发生更改的文档通过传入 fire 方法的 URI 参数来标识。如果文档仍处于打开状态,提供程序随后将被再次调用以提供更新后的内容。
这就是让 VS Code 监听虚拟文档更改所需的全部操作。要查看使用此功能的更复杂示例,请参阅:https://github.com/microsoft/vscode-extension-samples/blob/main/contentprovider-sample/README.md。
添加编辑器命令
可以添加仅与关联内容提供程序提供的文档进行交互的编辑器操作。这是一个反转 cow 所说内容的示例命令。
// 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 菜单中重复使用——默认情况下它会显示所有命令。

事件与可见性
文档提供程序是 VS Code 中的一等公民,其内容显示在常规文本文档中,它们使用与文件等相同的底层架构。然而,这也意味着“你的”文档无法隐藏,它们会出现在 onDidOpenTextDocument 和 onDidCloseTextDocument 事件中,它们是 vscode.workspace.textDocuments 的一部分等等。给所有人的规则是:检查文档的 scheme,然后决定是否要对该文档进行操作。
文件系统 API
如果你需要更大的灵活性和能力,请查看 FileSystemProvider API。它允许实现一个完整的文件系统,支持文件、文件夹、二进制数据、文件删除、创建等功能。
你可以在此处找到包含源代码的示例扩展:https://github.com/microsoft/vscode-extension-samples/tree/main/fsprovider-sample/README.md。
当在这样一个文件系统的文件夹或工作区中打开 VS Code 时,我们称之为虚拟工作区。当 VS Code 窗口中打开虚拟工作区时,左下角远程指示器中的标签会显示出来,类似于远程窗口。请参阅《虚拟工作区指南》,了解扩展如何支持该设置。