自定义编辑器 API
自定义编辑器允许扩展创建完全可定制的读/写编辑器,这些编辑器用于 VS Code 标准文本编辑器的特定类型资源。它们有多种使用案例,例如:
- 在 VS Code 中直接预览资产,例如着色器或 3D 模型。
- 为Markdown或XAML等语言创建所见即所得的编辑器。
- 为CSV、JSON或XML等数据文件提供替代的视觉呈现。
- 为二进制或文本文件构建完全可定制的编辑体验。
本文档概述了自定义编辑器 API 和实现自定义编辑器的基本知识。我们将查看这两种类型的自定义编辑器及其差异,并确定哪种类型的编辑器适合您的用例。然后,我们将分别讨论每种自定义编辑器类型,并涵盖构建良好行为自定义编辑器的基本知识。
尽管自定义编辑器是一个强大的新扩展点,但实现一个基本的自定义编辑器实际上并不难!不过,如果你正在开发第一个 VS Code 扩展,你可能希望在更熟悉 VS Code API 的基础知识之前,先暂停深入自定义编辑器。自定义编辑器依赖于许多 VS Code 概念——例如 webviews 和文本文档——所以如果你同时学习所有这些新概念,可能会有点令人应接不暇。
但是如果你已经准备好,并且在考虑要创建的所有酷炫的自定义编辑器,那么让我们开始吧!请务必下载自定义编辑器扩展示例,以便你可以按照文档进行操作,并看到自定义编辑器API是如何组合的。
链接
VS Code API 使用情况
自定义编辑器 API 基础知识
自定义编辑器是 VS Code 标准文本编辑器的替代视图,用于特定资源。自定义编辑器有两个部分:用户与其互动的视图和您的扩展用来与底层资源互动的文档模型。
自定义编辑器的视图部分是使用webview实现的。这使您能够使用标准的HTML、CSS和JavaScript来构建自定义编辑器的用户界面。Webviews无法直接访问VS Code API,但可以通过来回传递消息与扩展进行通信。请查看我们的webview文档,以获取有关Webviews的更多信息和与它们一起工作的最佳实践。
自定义编辑器的另一部分是文档模型。该模型是您的扩展如何理解它正在处理的资源(文件)。自定义文本编辑器提供程序 使用 VS Code 的标准 TextDocument 作为其文档模型,并且所有对文件的更改都使用 VS Code 的标准文本编辑 API 表示。自定义只读编辑器提供程序和自定义编辑器提供者另一方面,允许你提供自己的文档模型,这样它们就可以用于非文本文件格式。
自定义编辑器对每个资源有一个文档模型,但这个文档可能会有多个编辑器实例(视图)。例如,假设你打开一个文件,该文件有一个自定义文本编辑器提供程序 然后运行 视图:拆分编辑器 命令。在这种情况下,仍然只有一个 文本文档由于工作区中该资源仍然只有一份副本,但现在该资源有两个网页视图。
自定义编辑器 vs自定义文本编辑器
有两类自定义编辑器:自定义文本编辑器和自定义编辑器。它们之间的主要区别在于如何定义文档模型。
一个自定义文本编辑器提供程序使用 VS Code 的标准文本文档作为其数据模型。你可以使用一个自定义文本编辑器适用于任何基于文本的文件类型。自定义文本编辑器s 相对更容易实现,因为 VS Code 已经知道如何处理文本文件,因此可以实现保存文件和为热退出备份文件等操作。
随着一个自定义编辑器提供者另一方面,您的扩展带来了自己的文档模型。这意味着您可以使用一个自定义编辑器对于二进制格式如图像,但这也意味着你的扩展需要负责更多的事情,包括实现保存和撤销。如果你的自定义编辑器是只读的,例如预览的自定义编辑器,你可以跳过大部分的复杂性。
在尝试决定使用哪种类型的自定义编辑器时,通常的决定很简单:如果你正在处理基于文本的文件格式,请使用自定义文本编辑器提供程序对于二进制文件格式,请使用自定义编辑器提供者输入:.
贡献点
该自定义编辑器 贡献点是你的扩展如何向VS Code告知它提供的自定义编辑器。例如,VS Code需要知道你的自定义编辑器处理哪些类型的比赛文件以及如何在任何用户界面中识别你的自定义编辑器。
这是一个基本的自定义编辑器 贡献于 自定义编辑器扩展示例:
"贡献": {
"自定义编辑器": [
{
"视图类型": "catEdit.catScratch",
"显示名称": "猫抓痕",
"选择器": [
{
"文件名模式": "*.cscratch"
}
],
"优先级": "默认"
}
]
}
自定义编辑器是一个数组,因此您的扩展可以贡献多个自定义编辑器。让我们来分解一下自定义编辑器条目本身:
-
视图类型- 您自定义编辑器的唯一标识符。这是 VS Code 将自定义编辑器贡献绑定在一起的方式
package.json在代码中实现您的自定义编辑器。这必须在所有扩展中唯一,因此不能使用通用的视图类型例如预览确保使用您扩展的独特ID,例如"viewType": "myAmazingExtension.svgPreview" -
显示名称- 在 VS Code 的用户界面中标识自定义编辑器的名称。显示名称在 VS Code UI 中显示给用户,例如 视图:重新打开 下拉菜单。
-
选择器- 指定自定义编辑器适用于哪些文件。该
选择器是一个或多个 通配符模式 的数组。这些通配符模式会与文件名进行匹配,以确定是否可以为这些文件使用自定义编辑器。一个文件名模式例如*.png将为所有PNG文件启用自定义编辑器。您还可以创建更具体的模式,以匹配文件或目录名称,例如
**/ translations/*.json输入:. -
优先级-(可选)指定何时使用自定义编辑器。优先级控制在打开资源时使用自定义编辑器。可能的值是:"默认"- 尽量为每个符合自定义编辑器的文件使用自定义编辑器选择器如果一个文件有多个自定义编辑器,用户将需要选择他们想要使用的自定义编辑器。"选项"- 不默认使用自定义编辑器,而是允许用户切换到它或将其配置为默认编辑器。
自定义编辑器激活
当用户打开其中一个自定义编辑器时,VS Code 会触发一个自定义编辑器:视图类型激活事件。在激活期间,您的扩展必须调用注册自定义编辑器提供者注册一个自定义编辑器,预期视图类型输入:.
需要注意的是自定义编辑器 只有在 VS Code 需要创建您自定义编辑器的实例时才会被调用。如果 VS Code 仅向用户显示有关可用自定义编辑器的信息——例如,使用 查看:重新打开 命令——您的扩展将不会被激活。
自定义文本编辑器
自定义文本编辑器允许您为文本文件创建自定义编辑器。这可以从纯文本到CSV到JSON或XML。自定义文本编辑器使用VS Code的标TextDocument作为其文档模型。
该自定义编辑器扩展示例包括一个简单的猫抓文件(即以结尾的JSON文件)自定义文本编辑器示例.cscratch文件扩展名)。让我们来看一下实现自定义文本编辑器的一些重要部分。
自定义文本编辑器生命周期
VS Code 处理自定义文本编辑器视图组件(webviews)和模型组件的生命周期。文本文档VS Code会在需要创建新的自定义编辑器实例时调用你的扩展,并在用户关闭标签页时清理编辑器实例和文档模型。
为了理解这些在实际中的运作方式,请让我们从扩展的角度来看用户打开和关闭自定义文本编辑器时发生了什么。
打开自定义文本编辑器
使用自定义编辑器扩展示例,当用户首次打开一个.cscratch文件:
-
VS Code 发射一个
自定义编辑器:猫自定义.猫抓激活事件。这将激活我们的扩展,如果尚未激活。在激活过程中,我们的扩展必须确保扩展注册一个
自定义文本编辑器提供程序为猫海关。猫抓挠。通过拨打注册自定义编辑器提供者输入:. -
VS Code 然后调用
解析自定义文本编辑器在注册自定义文本编辑器提供程序为猫海关。猫抓挠。输入:.该方法采取
文本文档打开的资源和一个网页视图面板扩展必须为该WebView面板填充初始HTML内容。
一旦解析自定义文本编辑器在用户返回时,我们的自定义编辑器会显示给用户。WebView内部所绘制的内容完全取决于我们的扩展。
每次打开自定义编辑器时,都会发生同样的流程,即使你在拆分自定义编辑器时也是如此。每个自定义编辑器实例都有其自己的网页视图面板虽然多个自定义文本编辑器将共享相同的文本文档如果它们是针对相同的资源。记住:考虑文本文档作为资源的模型,而WebView面板是该模型的视图。
关闭自定义文本编辑器
当用户关闭自定义文本编辑器时,VS Code 会触发WebviewPanel.onDidDispose事件网页视图面板在此刻,您的扩展应该清理与该编辑器相关的任何资源(事件订阅、文件监视器等)。
当给定资源的最后一个自定义编辑器关闭时,文本文档如果没有任何其他编辑器在使用该资源,并且没有其他扩展程序保留它,则该资源也将被丢弃。您可以检查TextDocument.isClosed属性以查看文本文档已经关闭。一旦一个文本文档已关闭,使用自定义编辑器打开相同资源将导致一个新的文本文档待打开。
同步文本文档的更改
由于自定义文本编辑器使用一个文本文档作为其文档模型,他们负责更新文本文档每当自定义编辑器中发生编辑操作以及更新自身每当文本文档更改。
从网页视图到文本文档
在自定义文本编辑器中进行的编辑可以有多种形式——点击按钮、更改一些文本、拖动一些项目。每当用户在自定义文本编辑器中编辑文件本身时,扩展必须更新文本文档以下是猫抓扩展如何实现这一点的:
-
扩展程序接收消息。然后更新其对文档的内部模型(在猫抓示例中,只是向JSON添加一条新条目)。
-
该扩展创建了一个
工作区编辑将更新后的JSON写入文档。此编辑是通过应用vscode.workspace.applyEdit输入:.
尽量将工作区编辑限制在更新文档所需的最小更改。同时,请记住,如果您正在处理像JSON这样的语言,您的扩展应尽量遵守用户现有的格式规范(空格与制表符、缩进大小等)。
从文本文档到网页视图
当一个文本文档更改,您的扩展还需要确保其 webviews 反映文档的新状态。TextDocuments 可以通过用户操作(如撤销、重做或还原文件)或其他扩展使用来更改。工作区编辑; 或者由在 VS Code 的默认文本编辑器中打开该文件的用户执行。以下是 cat-scratch 扩展如何实现这一点的:
-
在扩展中,我们订阅了
vscode.workspace.onDidChangeTextDocument事件。此事件会在每次更改时触发文本文档(包括我们定制编辑所做的更改!) -
当有对文档的更改提交时,我们发布一条消息到WebView,包含文档的新状态。然后,WebView会更新自身以渲染更新后的文档。
重要的是要记住,任何由自定义编辑器触发的文件编辑都会导致onDidChangeTextDocument触发。确保您的扩展不会进入更新循环,即用户在WebView中进行编辑,这会触发onDidChangeTextDocument,这将导致网页视图更新,这将导致网页视图在你的扩展上触发另一个更新,这会触发onDidChangeTextDocument等等。
请记住,如果您正在处理像JSON或XML这样的结构化语言,文档可能并不总是处于有效状态。您的扩展必须能够优雅地处理错误,或者向用户显示错误消息,以便他们了解问题所在并知道如何修复。
最后,如果更新你的网页视图很昂贵,请考虑防抖你的网页视图的更新。
定制编辑器
自定义编辑器提供者和自定义只读编辑器提供程序让你为二进制文件格式创建自定义编辑器。此 API 让你完全控制文件如何显示给用户,如何对文件进行编辑,并允许你的扩展进行钩子连接保存和其他文件操作一样。再次强调,如果你正在为基于文本的文件格式构建一个编辑器,强烈建议使用一个自定义文本编辑器而是,因为它们更简单易行。
该自定义编辑器扩展示例包括一个简单的自定义二进制编辑器,用于paw draw文件(这些文件只是以.pawdraw文件扩展名)。让我们看看如何构建二进制文件的自定义编辑器。
自定义文档
使用自定义编辑器时,您的扩展负责实现其自己的文档模型自定义文档接口。这使您的扩展可以自由地存储所需的数据自定义文档为了与您的自定义编辑器进行交互,但它也意味着您的扩展必须实现基本的文档操作,例如保存和备份文件数据以进行热退出。
有一个自定义文档每个打开的文件。用户可以为单个资源打开多个编辑器——例如,通过拆分当前的自定义编辑器——但所有这些编辑器都将支持相同的自定义文档输入:.
自定义编辑器生命周期
支持每个文档多个编辑器
默认情况下,VS Code 只允许每个自定义文档有一个编辑器。这个限制使得更容易正确实现自定义编辑器,因为你无需担心将多个自定义编辑器实例同步在一起。
如果您的扩展可以支持的话,我们建议设置每个文档支持多个编辑器:真在注册您的自定义编辑器时,以便可以为同一文档打开多个编辑器实例。这将使您的自定义编辑器更像 VS Code 的普通文本编辑器。
打开自定义编辑器
当用户打开一个匹配的文件时自定义编辑器贡献点,VS Code 发射一个自定义编辑器 激活事件,然后调用注册用于所提供视图类型的提供者。A自定义编辑器提供者有两个角色:提供自定义编辑器的文档,然后提供编辑器本身。以下是发生的事情的有序列表猫海关.爪绘 来自 自定义编辑器扩展示例的编辑器:
-
VS Code 发射一个
自定义编辑器:猫海关。画爪激活事件。这将激活我们的扩展,如果它还没有被激活。我们还必须确保我们的扩展注册了一个
自定义只读编辑器提供程序或自定义编辑器提供者为猫海关.爪绘在激活期间。 -
VS Code 调用
打开自定义文档在我们自定义只读编辑器提供程序或自定义编辑器提供者已注册猫海关.爪绘编辑。在这里,我们的扩展会得到一个资源uri,并且必须返回一个新的
自定义文档为此资源。这是我们的扩展应该为此资源创建其文档内部模型的这一点。这可能涉及从磁盘读取和解析初始资源状态或初始化我们的新自定义文档输入:.我们的扩展可以通过创建一个实现该模型的新类来定义这个模型
自定义文档请记住,这个初始化阶段完全由扩展控制;VS Code 不关心扩展存储在 a 上的任何附加信息自定义文档输入:. -
VS Code 调用
解析自定义编辑器与自定义文档从步骤2和一个新的网页视图面板输入:.在这里,我们的扩展必须填写自定义编辑器的初始HTML。如果我们需要,我们还可以保留一个对
网页视图面板这样我们可以在以后引用它,例如在命令中。
一旦解析自定义编辑器退货时,我们的自定义编辑器会显示给用户。
如果用户在另一个编辑器组中使用我们自定义的编辑器打开相同的资源——例如通过拆分第一个编辑器——扩展的工作变得简单。在这种情况下,VS Code 只是调用解析自定义编辑器相同自定义文档我们创建了第一个编辑器打开时。
关闭自定义编辑器
假设我们为同一资源打开了两个自定义编辑器的实例。当用户关闭这些编辑器时,VS Code 会向我们的扩展发送信号,以便它可以清理与编辑器关联的任何资源。
当第一个编辑器实例关闭时,VS Code 会触发WebviewPanel.onDidDispose事件网页视图面板来自已关闭的编辑器。此时,我们的扩展必须清理与该特定编辑器实例相关的任何资源。
当第二个编辑器关闭时,VS Code再次触发WebviewPanel.onDidDispose然而,现在我们已经关闭了所有与该相关的编辑器自定义文档当没有更多的编辑者时自定义文档VS Code 调用CustomDocument.dispose在进行中。我们扩展的实现处理必须清理与文档相关的任何资源。
如果用户然后使用我们的自定义编辑器重新打开相同的资源,我们将重新经历整个打开自定义文档,解析自定义编辑器与新的事物一起流动自定义文档输入:.
只读自定义编辑器
以下章节中的许多内容仅适用于支持编辑的自定义编辑器,虽然这听起来可能有些矛盾,但许多自定义编辑器根本不需要编辑功能。例如,考虑一下图像预览。或者内存转储的可视化渲染。两者都可以使用自定义编辑器实现,但都不需要可编辑。那就是自定义只读编辑器提供程序进来。
一个自定义只读编辑器提供程序允许你创建不支持编辑的自定义编辑器。它们仍然可以是交互式的,但不支持撤销和保存等操作。与完全可编辑的编辑器相比,实现只读自定义编辑器也要简单得多。
可编辑自定义编辑器基础知识
可编辑的自定义编辑器允许您钩入标准 VS Code 操作,例如撤销和重做、保存和热退出。这使得可编辑的自定义编辑器非常强大,但也意味着正确实现一个比实现一个可编辑的自定义文本编辑器或只读的自定义编辑器要复杂得多。
可编辑的自定义编辑器由自定义编辑器提供者. 该接口扩展自定义只读编辑器提供程序所以你将不得不实现基本操作,例如打开自定义文档和解析自定义编辑器以及一组特定的编辑操作。让我们看看特定编辑部分的自定义编辑器提供者输入:.
编辑
对可编辑自定义文档的更改通过编辑来表达。编辑可以从更改文本、旋转图像到重新排序列表。VS Code将编辑的具体内容完全交由你的扩展来决定,但VS Code需要知道何时发生了编辑。编辑是VS Code标记文档为脏(即已修改)的方式,从而启用自动保存和备份功能。
每当用户在任何自定义编辑器的网页视图中进行编辑时,您的扩展必须触发一个onDidChangeCustomDocument事件从其自定义编辑器提供者。onDidChangeCustomDocument事件可以根据您的自定义编辑器实现触发两种事件类型:自定义文档内容更改事件和自定义文档编辑事件输入:.
自定义文档内容更改事件
一个自定义文档内容更改事件是一个简约的编辑。它的唯一功能是告诉 VS Code 文档已被编辑。
当一个扩展触发一个自定义文档内容更改事件从onDidChangeCustomDocumentVS Code将标记关联文档为脏。在此刻,文档要变为非脏的唯一方法是用户保存或撤销它。使用定制编辑器自定义文档内容更改事件不支持撤销/重做。
自定义文档编辑事件
一个自定义文档编辑事件是一个更复杂的编辑,允许撤销/重做。您应该始终尝试使用 来实现您的自定义编辑器自定义文档编辑事件并仅限于使用自定义文档内容更改事件如果无法实现撤销/重做。
一个自定义文档编辑事件具有以下字段:
文档— The自定义文档编辑是为了。Tab— 可选文本,用于描述所做的编辑类型(例如:“裁剪”、“插入”等)撤销— VS Code 在需要撤销编辑时调用的函数。重做— VS Code 在需要重新应用更改时调用的函数。
当一个扩展触发一个自定义文档编辑事件从onDidChangeCustomDocumentVS Code将关联文档标记为脏。要使文档不再脏,用户可以选择保存或还原文档,或者撤销/重做回到文档的上次保存状态。
该撤销和重做编辑器的方法在 VS Code 需要撤销或重新应用该特定编辑时被调用。VS Code 会维护一个编辑的内部堆栈,所以如果你的扩展触发onDidChangeCustomDocument经过三次修改,我们称之为一个,输入:b,输入:c输入:
onDidChangeCustomDocument(a);
onDidChangeCustomDocument(b);
onDidChangeCustomDocument(c);
以下用户操作序列导致了这些调用:
撤销 — c.undo()
撤销 — b.undo()
重做 — b.redo()
重做 — c.redo()
重做 — 无操作,没有更多的编辑
为了实现撤销/重做功能,您的扩展必须更新其相关自定义文档的内部状态,并更新所有与文档相关的 webviews,以使其反映文档的新状态。请记住,一个资源可能有多个 webviews。这些必须始终显示相同的文档数据。例如,多个图像编辑器实例必须始终显示相同的像素数据,但可以允许每个编辑器实例具有自己的缩放级别和 UI 状态。
保存
当用户保存自定义编辑器时,您的扩展负责将当前状态的保存资源写入磁盘。您的自定义编辑器如何做到这一点在很大程度上取决于您的扩展的自定义文档输入类型以及您的扩展如何在内部跟踪编辑。
保存的第一步是将数据流写入磁盘。常见的方法包括:
-
跟踪资源的状态,以便可以快速序列化。
例如,一个基本的图像编辑器可能会维护一个像素数据缓冲区。
-
自上次保存以来重放编辑以生成新文件。
例如,一个更高效的图像编辑器可能会跟踪自上次保存以来的编辑,例如
作物,旋转,刻度在保存时,它会将这些更改应用到文件的上次保存状态,以生成新文件。 -
问一个
网页视图面板用于文件数据的自定义编辑器保存。不过,请记住,即使定制编辑器不可见,它们也可以被保存。因此,建议您的扩展实现
保存不依赖于 a网页视图面板如果这不可能,您可以使用WebviewPanelOptions.保留上下文当隐藏时设置使WebView即使在隐藏时也能保持活动状态。隐藏时保留上下文确实有显著的内存开销,因此在使用时要谨慎。
获取资源数据后,通常应使用工作区FS API将数据写入磁盘。FS API接受一个UInt8数组数据和可以写入基于二进制和文本的文件。对于二进制文件数据,只需将二进制数据放入UInt8数组对于文本文件数据,请使用缓冲区将字符串转换为UInt8数组输入:
常量 写入数据 = 缓冲区.从('我的文本数据', 'utf8');
vscode.工作区.fs.写入文件(文件uri, 写入数据);
下一步
如果您想了解更多关于 VS Code 可扩展性的信息,请尝试这些主题: