VisualStudio.Extensibility 扩展的组件
一个使用 VisualStudio.Extensibility 的扩展通常包含几个组件,这些组件可以一起交互,也可以与 Visual Studio 交互。
扩展实例
扩展必须有一个派生自 Extension
的类。 有关实现示例,请参阅 MarkdownLinter。
Extension
类的实例是扩展执行的起点。 此实例包含 Visual Studio 查询扩展所提供服务的必要方法。 它还为扩展提供虚拟方法,以提供本地化资源和扩展拥有的本地服务,以便在扩展的组件间共享。
Extension
类的配置还包含扩展的元数据,这些元数据将显示在 Visual Studio 管理扩展窗口中,而对于已发布的扩展,则显示在 Visual Studio Marketplace 中。
[VisualStudioContribution]
public class MarkdownLinterExtension : Extension
{
/// <inheritdoc/>
public override ExtensionConfiguration ExtensionConfiguration => new()
{
Metadata = new(
id: "MarkdownLinter.0cf26ba2-edd5-4419-8646-a55d0a83f7d8",
version: this.ExtensionAssemblyVersion,
publisherName: "Microsoft",
displayName: "Markdown Linter Sample Extension",
description: "Sample markdown linter extension"),
};
...
对于熟悉现有 VSSDK API 的扩展开发人员而言,ExtensionConfiguration
中包含的 Metadata
用于生成 .vsixmanifest
文件。 此外,Extension
类与 VSSDK 可扩展性模型中使用的 AsyncPackage
类类似。
VisualStudioExtensibility 对象
对象 VisualStudioExtensibility
是 Visual Studio 提供的可扩展性功能的入口点。 此类具有各种扩展方法和属性,可快速枚举可扩展性 SDK 中的可用功能。 有关可用方法,请参阅 API 文档。
扩展部件
对于扩展为 Visual Studio 提供组件(如命令、编辑器侦听器)的功能,扩展将使用属性化类。 生成过程将生成正确的元数据,以确保 Visual Studio 可以发现这些组件。
对于扩展为 Visual Studio 提供组件(如命令、编辑器侦听器、工具窗口等)的功能,扩展会使用标有 VisualStudioContribution
属性的类。 生成过程会生成正确的元数据,以确保 Visual Studio 可以发现这些组件。
目前,SDK 只支持一组有限的参与组件:
这些类的实例是作为 SDK 提供的可扩展性框架的一部分,使用依赖项注入库中创建的,构造函数可用于检索 SDK 或扩展本身提供的服务实例,以便跨组件共享状态。
扩展部件的生存期
每个部件的生命周期由相应的组件管理,这些组件会在 Visual Studio IDE 进程中加载这些部件。
命令处理程序会在相应的命令集被激活时初始化,这可能是在第一次执行命令时。 一旦激活,命令处理程序只应在 IDE 关闭时才被释放。
同样,当 IDE 加载第一个与指定内容类型相匹配的文本视图时,文本视图事件侦听器也会被初始化。 目前,此类侦听器在 IDE 关闭前一直处于活动状态,但这种行为今后可能会改变。
一般来说,对于复杂的扩展,建议扩展提供本地服务,部件可以在其构造函数中导入这些服务,并使用这些服务在部件间和同一部件的实例间共享状态。 这种做法可确保扩展状态不受扩展部件生存期变化的影响。
由用于注入的 SDK 提供的服务
SDK 提供以下服务,可在任何扩展部件的构造函数中使用:
VisualStudioExtensibility
:每个扩展部件都可以注入VisualStudioExtensibility
的实例,以便与 Visual Studio IDE 交互。Extension
:部件可以向扩展部件注入Microsoft.VisualStudio.Extensibility.Extension
类型或从其继承的扩展自身类型。TraceSource
:为每个扩展名按需创建一个跟踪源实例,用于记录诊断信息。 这些实例在 Visual Studio 诊断提供程序中注册,可用于合并多个服务的日志,并利用未来的工具访问实时日志。 请参阅日志记录。本地服务:扩展本身提供的任何本地服务也可用于依赖项注入。
MefInjection<TService>
和AsyncServiceProviderInjection<TService, TInterface>
:进程内扩展可以注入 Visual Studio SDK 服务,这些服务传统上通过 MEF 或 AsyncServiceProvider 来使用。
本地扩展服务
在某些情况下,扩展可能希望在不同组件(如命令处理程序和文本视图更改侦听器)之间共享状态,如 MarkdownLinter
示例所示。 这些服务可通过覆盖 Extension.InitializeServices
方法添加到进程内服务集合中,在创建扩展部件实例时,会根据构造函数参数注入服务。
添加服务有三个选项:
AddTransient
:为每个引入服务的部件创建一个新的服务实例。AddScoped
:在一定范围内创建服务的新实例。 在 Visual Studio 可扩展性中,范围指的是单个扩展部件。AddSingleton
:首次引入时会创建一个共享服务实例。
由于 VisualStudioExtensibility
对象的生存期与单个扩展部件的范围绑定,任何引入该对象的本地服务都必须是作用域或暂时性服务。 尝试创建注入 VisualStudioExtensibility
的单一实例服务将导致失败。
有关如何使用本地服务的示例,请参阅 MarkdownLinter 扩展。
客户端上下文
由于新 SDK 中的所有扩展都是在进程外运行的,因此我们为各种扩展部件引入了客户端上下文的概念,以表示调用事件或方法时 IDE 的状态。 此上下文由 SDK 中的 IClientContext
实例表示,并会传递给各种操作,如命令执行处理程序。 SDK 在 IClientContext
上提供了可用于从上下文中检索对象的扩展方法。 例如,扩展可以利用 IClientContext
实例,在执行命令时获取活动文本视图或所选项目的 URI。
某些组件(如命令)还允许你声明它们对哪些上下文感兴趣。 这样做是为了优化每次远程执行时传输的数据量,因为客户端上下文将来可能会变得很大。 在初始预览版中,只有 Shell
和 Editor
两种可用上下文,而在使用 CommandAttribute
声明命令时,这两种上下文默认都包含在内。