虚拟文档
文本文档内容提供程序 API 允许你从任意来源在 Visual Studio Code 中创建只读文档。你可以在以下位置找到包含源代码的示例扩展:https://github.com/microsoft/vscode-extension-samples/blob/main/virtual-document-sample/README.md.
TextDocumentContentProvider
API 通过声明一个 uri-scheme 来工作,你的提供程序随后将返回文本内容。该 scheme 必须在注册提供程序时提供,并且之后不能更改。同一个提供程序可以用于多个 scheme,并且可以为单个 scheme 注册多个提供程序。
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
scheme 的 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。发生变化的文档通过传递给 fire
方法的参数中的 uri 来标识。假设文档仍然处于打开状态,提供程序将再次被调用以提供更新的内容。
这就是让 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
菜单 - 它默认情况下显示所有命令。
事件和可见性
文档提供程序是 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 窗口中打开时,将在左下角的远程指示器中显示一个标签,类似于远程窗口。请参阅 虚拟工作区指南,了解扩展如何支持此设置。