Componentes de una extensión de VisualStudio.Extensibility

Una extensión que usa VisualStudio.Extensibility normalmente tiene varios componentes que interactúan juntos y también con Visual Studio.

Instancia de extensión

Las extensiones deben tener una clase que derive de Extension. Para obtener una implementación de ejemplo, consulte MarkdownLinter.

Una instancia de la Extension clase es el punto de partida para la ejecución de la extensión. Esta instancia contiene los métodos necesarios para que Visual Studio consulte los servicios proporcionados por la extensión. También proporciona métodos virtuales para que la extensión proporcione recursos localizados y servicios locales propiedad de la extensión que se compartirán entre los componentes de la extensión.

La configuración de la Extension clase también contiene los metadatos de la extensión que se muestra en la ventana Administrar extensiones de Visual Studio y, para las extensiones publicadas, en 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"),
    };
    ...

Para los desarrolladores de extensiones que están familiarizados con las API del SDK de VS existentes, se usa el Metadata elemento incluido en ExtensionConfiguration para generar el .vsixmanifest archivo. Además, la Extension clase es similar a la AsyncPackage clase que se usa en el modelo de extensibilidad del SDK de VS.

Objeto VisualStudioExtensibility

El objeto VisualStudioExtensibility actúa como punto de entrada para las características de extensibilidad expuestas por Visual Studio. Esta clase tiene varios métodos de extensión, propiedades para enumerar rápidamente las características disponibles en el SDK de extensibilidad. Consulte la documentación de la API para ver los métodos disponibles.

Elementos de extensión

En el caso de las características en las que una extensión contribuye a los componentes de Visual Studio, como comandos, agentes de escucha del editor, las extensiones usarán clases con atributos. El proceso de compilación generará los metadatos correctos para asegurarse de que Visual Studio puede detectar estos componentes.

Para las características en las que una extensión contribuye a los componentes de Visual Studio, como comandos, agentes de escucha del editor, ventanas de herramientas, etc., las extensiones usan clases marcadas con el VisualStudioContribution atributo . El proceso de compilación genera los metadatos correctos para asegurarse de que Visual Studio puede detectar estos componentes.

Actualmente, el SDK admite un conjunto limitado de componentes que se van a contribuir:

Las instancias de estas clases se crean como parte del marco de extensibilidad proporcionado por el SDK mediante una biblioteca de inserción de dependencias y los constructores se pueden usar para recuperar instancias de servicios proporcionados por el SDK o por la propia extensión para compartir el estado entre los componentes.

Duración de las partes de extensión

La duración de cada elemento se administra mediante el componente correspondiente que carga esos elementos dentro del proceso del IDE de Visual Studio.

  • Los controladores de comandos se inicializan cuando se activa el conjunto de comandos correspondiente, que puede ser durante la primera ejecución del comando. Una vez activado, los controladores de comandos solo se deben eliminar cuando se cierra el IDE.

  • De forma similar, los agentes de escucha de eventos de vista de texto se inicializan cuando la primera vista de texto que coincide con el tipo de contenido especificado se carga en el IDE. Actualmente, estos agentes de escucha están activos hasta que se cierra el IDE, pero este comportamiento puede cambiar en el futuro.

En general, para extensiones complejas, se recomienda que las extensiones proporcionen servicios locales que los elementos pueden importar en su constructor y usar esos servicios para compartir el estado entre partes y entre instancias de la misma parte. Esta práctica garantiza que el estado de la extensión no se vea afectado por los cambios de duración de las partes de extensión.

Servicios proporcionados por el SDK para la inyección

El SDK proporciona los siguientes servicios que se pueden usar en el constructor para cualquier parte de extensión:

  • VisualStudioExtensibility: cada elemento de extensión puede insertar una instancia de para interactuar con el IDE de VisualStudioExtensibility Visual Studio.

  • Extension: las partes pueden insertar Microsoft.VisualStudio.Extensibility.Extension el tipo o el tipo propio de extensiones que heredan de él a los elementos de extensión.

  • TraceSource: se crea una instancia de origen de seguimiento a petición para cada extensión que se puede usar para registrar información de diagnóstico. Estas instancias se registran con el proveedor de diagnósticos de Visual Studio, que se puede usar para combinar registros de varios servicios y usar herramientas futuras para acceder al registro en tiempo real. Consulte Registro.

  • Servicios locales: los servicios locales proporcionados por la propia extensión también estarán disponibles para la inserción de dependencias.

  • MefInjection<TService>y AsyncServiceProviderInjection<TService, TInterface>: las extensiones en proceso pueden insertar servicios del SDK de Visual Studio que tradicionalmente se consumirían a través de MEF o AsyncServiceProvider.

Servicios de extensión local

En determinados escenarios, es posible que una extensión quiera compartir el estado entre distintos componentes, como un controlador de comandos y un agente de escucha de cambios en la vista de texto, como se puede ver en MarkdownLinter el ejemplo. Estos servicios se pueden agregar a la colección de servicios en proceso reemplazando Extension.InitializeServices el método y a medida que se crean instancias de elementos de extensión, los servicios se insertan en función de los argumentos del constructor.

Hay tres opciones para agregar un servicio:

  • AddTransient: se crea una nueva instancia del servicio para cada parte que la ingiere.
  • AddScoped: se crea una nueva instancia del servicio dentro de un ámbito determinado. En el contexto de la extensibilidad de Visual Studio, el ámbito hace referencia a una sola parte de extensión.
  • AddSingleton: hay una única instancia compartida del servicio que se crea en la primera ingesta.

Debido a la duración del VisualStudioExtensibility objeto que se enlaza al ámbito de una sola parte de extensión, cualquier servicio local que ingiere debe ser un servicio de ámbito o transitorio. Si se intenta crear un servicio singleton que inserta VisualStudioExtensibility , se producirá un error.

Para obtener un ejemplo de cómo se usan los servicios locales, consulte La extensión MarkdownLinter.

Contexto de cliente

Dado que todas las extensiones del nuevo SDK se ejecutan sin proceso, presentamos el concepto de contexto de cliente para varias partes de extensión para representar el estado del IDE en el momento en que se invoca el evento o el método. Este contexto se representa mediante la IClientContext instancia del SDK y se pasa a varias operaciones, como controladores de ejecución de comandos. El SDK proporciona métodos de extensión en que IClientContext se pueden usar para recuperar objetos del contexto. Por ejemplo, las extensiones pueden obtener la vista de texto activa o el URI de los elementos seleccionados en el momento de la ejecución del comando mediante la IClientContext instancia.

Algunos componentes, como los comandos, también permiten declarar qué contextos les interesan. Esto se hace para optimizar la cantidad de datos transferidos en cada ejecución remota, ya que el contexto de cliente puede ser grande en el futuro. En la versión preliminar inicial, solo hay dos contextos disponibles y ShellEditor, y ambos se incluyen de forma predeterminada al declarar un comando mediante CommandAttribute.