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 服务,这些服务传统上通过 MEFAsyncServiceProvider 来使用。

本地扩展服务

在某些情况下,扩展可能希望在不同组件(如命令处理程序和文本视图更改侦听器)之间共享状态,如 MarkdownLinter 示例所示。 这些服务可通过覆盖 Extension.InitializeServices 方法添加到进程内服务集合中,在创建扩展部件实例时,会根据构造函数参数注入服务。

添加服务有三个选项:

  • AddTransient:为每个引入服务的部件创建一个新的服务实例。
  • AddScoped:在一定范围内创建服务的新实例。 在 Visual Studio 可扩展性中,范围指的是单个扩展部件。
  • AddSingleton:首次引入时会创建一个共享服务实例。

由于 VisualStudioExtensibility 对象的生存期与单个扩展部件的范围绑定,任何引入该对象的本地服务都必须是作用域或暂时性服务。 尝试创建注入 VisualStudioExtensibility 的单一实例服务将导致失败。

有关如何使用本地服务的示例,请参阅 MarkdownLinter 扩展

客户端上下文

由于新 SDK 中的所有扩展都是在进程外运行的,因此我们为各种扩展部件引入了客户端上下文的概念,以表示调用事件或方法时 IDE 的状态。 此上下文由 SDK 中的 IClientContext 实例表示,并会传递给各种操作,如命令执行处理程序。 SDK 在 IClientContext 上提供了可用于从上下文中检索对象的扩展方法。 例如,扩展可以利用 IClientContext 实例,在执行命令时获取活动文本视图或所选项目的 URI。

某些组件(如命令)还允许你声明它们对哪些上下文感兴趣。 这样做是为了优化每次远程执行时传输的数据量,因为客户端上下文将来可能会变得很大。 在初始预览版中,只有 ShellEditor 两种可用上下文,而在使用 CommandAttribute 声明命令时,这两种上下文默认都包含在内。