Compartir a través de


Guía paso a paso: Mostrar sugerencias de bombilla

Las bombillas son iconos en el editor de Visual Studio que se expanden para mostrar un conjunto de acciones, por ejemplo, correcciones de problemas identificados por los analizadores de código integrados o la refactorización de código.

En los editores de Visual C# y Visual Basic, también puede usar la Plataforma del compilador de .NET ("Roslyn") para escribir y empaquetar sus propios analizadores de código con acciones que muestran bombillas automáticamente. Para obtener más información, consulte:

  • Cómo: Escribir un diagnóstico de C# y una corrección de código

  • Cómo: Escribir un diagnóstico y una corrección de código de Visual Basic

    Otros lenguajes como C++ también proporcionan indicadores para algunas acciones rápidas, como una sugerencia para crear una implementación de plantilla de esa función.

    Este es el aspecto de una bombilla. En un proyecto de Visual Basic o Visual C#, aparece un subrayado rojo debajo de los nombres de variable cuando no son válidos. Si pasa el mouse sobre el identificador no válido, aparece una bombilla cerca del cursor.

    bombilla

    Si hace clic en la flecha hacia abajo por la bombilla, aparece un conjunto de acciones sugeridas, junto con una vista previa de la acción seleccionada. En este caso, muestra los cambios realizados en el código si ejecuta la acción.

    vista previa de bombilla

    Puede usar bombillas para sugerir sus propias acciones. Por ejemplo, podría proporcionar acciones para mover las llaves de apertura a una nueva línea o al final de la línea anterior. En el siguiente tutorial se muestra cómo crear una bombilla que aparece en la palabra actual y tiene dos acciones sugeridas: Convertir a mayúsculas y Convertir a minúsculas.

Creación de un proyecto de Managed Extensibility Framework (MEF)

  1. Cree un proyecto VSIX de C#. (En el cuadro de diálogo Nuevo proyecto, seleccione Visual C# / Extensibilidady, a continuación, proyecto VSIX). Asigne al LightBulbTestel nombre de la solución .

  2. Agregue una plantilla de elemento Clasificador de editor al proyecto. Para obtener más información, vea Crear una extensión con una plantilla de elemento de editor.

  3. Elimine los archivos de clase existentes.

  4. Agregue la siguiente referencia al proyecto y establezca Copy Local en False.

    Microsoft.VisualStudio.Language.IntelliSense

  5. Agregue un nuevo archivo de clase y asígnele el nombre LightBulbTest.

  6. Agregue las siguientes directivas using:

    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Microsoft.VisualStudio.Language.Intellisense;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Text.Operations;
    using Microsoft.VisualStudio.Utilities;
    using System.ComponentModel.Composition;
    using System.Threading;
    
    

Implementación del proveedor de origen de bombillas

  1. En el archivo de clase LightBulbTest.cs, elimine la clase LightBulbTest. Agregue una clase denominada TestSuggestedActionsSourceProvider que implementa ISuggestedActionsSourceProvider. Expórtelo con el nombre Acciones sugeridas de prueba y un ContentTypeAttribute de "texto".

    [Export(typeof(ISuggestedActionsSourceProvider))]
    [Name("Test Suggested Actions")]
    [ContentType("text")]
    internal class TestSuggestedActionsSourceProvider : ISuggestedActionsSourceProvider
    
  2. Dentro de la clase de proveedor de origen, importe el ITextStructureNavigatorSelectorService y agréguelo como una propiedad.

    [Import(typeof(ITextStructureNavigatorSelectorService))]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
  3. Implemente el método CreateSuggestedActionsSource para devolver un objeto ISuggestedActionsSource. El origen se describe en la sección siguiente.

    public ISuggestedActionsSource CreateSuggestedActionsSource(ITextView textView, ITextBuffer textBuffer)
    {
        if (textBuffer == null || textView == null)
        {
            return null;
        }
        return new TestSuggestedActionsSource(this, textView, textBuffer);
    }
    

Implementación de ISuggestedActionSource

El origen de la acción sugerida es responsable de recopilar el conjunto de acciones sugeridas y agregarlas en el contexto correcto. En este caso, el contexto es la palabra actual y las acciones sugeridas son UpperCaseSuggestedAction y LowerCaseSuggestedAction, que se describe en la sección siguiente.

  1. Agregue una clase testSuggestedActionsSource que implementa ISuggestedActionsSource.

    internal class TestSuggestedActionsSource : ISuggestedActionsSource
    
  2. Agregue campos privados y de solo lectura para el proveedor de origen de acción sugerido, el búfer de texto y la vista de texto.

    private readonly TestSuggestedActionsSourceProvider m_factory;
    private readonly ITextBuffer m_textBuffer;
    private readonly ITextView m_textView;
    
  3. Agregue un constructor que establezca los campos privados.

    public TestSuggestedActionsSource(TestSuggestedActionsSourceProvider testSuggestedActionsSourceProvider, ITextView textView, ITextBuffer textBuffer)
    {
        m_factory = testSuggestedActionsSourceProvider;
        m_textBuffer = textBuffer;
        m_textView = textView;
    }
    
  4. Agregue un método privado que devuelva la palabra que está actualmente bajo el cursor. El método siguiente examina la ubicación actual del cursor y solicita al navegador de la estructura de texto la extensión de la palabra. Si el cursor está en una palabra, el TextExtent se devuelve en el parámetro out; De lo contrario, el parámetro out es null y el método devuelve false.

    private bool TryGetWordUnderCaret(out TextExtent wordExtent)
    {
        ITextCaret caret = m_textView.Caret;
        SnapshotPoint point;
    
        if (caret.Position.BufferPosition > 0)
        {
            point = caret.Position.BufferPosition - 1;
        }
        else
        {
            wordExtent = default(TextExtent);
            return false;
        }
    
        ITextStructureNavigator navigator = m_factory.NavigatorService.GetTextStructureNavigator(m_textBuffer);
    
        wordExtent = navigator.GetExtentOfWord(point);
        return true;
    }
    
  5. Implemente el método HasSuggestedActionsAsync. El editor llama a este método para averiguar si mostrar la bombilla. Esta llamada se realiza a menudo, por ejemplo, cada vez que el cursor se mueve de una línea a otra, o cuando el ratón pasa sobre un subrayado rojo de error. Es asincrónico para permitir que otras operaciones de interfaz de usuario se lleven a cabo mientras este método funciona. En la mayoría de los casos, este método debe realizar alguna interpretación y análisis de la línea actual, por lo que el procesamiento puede tardar algún tiempo.

    En esta implementación, obtiene de forma asincrónica el TextExtent y determina si la extensión es significativa, como en, si tiene algún texto distinto del espacio en blanco.

    public Task<bool> HasSuggestedActionsAsync(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(() =>
        {
            TextExtent extent;
            if (TryGetWordUnderCaret(out extent))
            {
                // don't display the action if the extent has whitespace
                return extent.IsSignificant;
              }
            return false;
        });
    }
    
  6. Implemente el método GetSuggestedActions, que devuelve una matriz de objetos SuggestedActionSet que contienen los distintos objetos ISuggestedAction. Se llama a este método cuando se expande la bombilla.

    Advertencia

    Debe asegurarse de que las implementaciones de HasSuggestedActionsAsync() y GetSuggestedActions() sean coherentes; es decir, si HasSuggestedActionsAsync() devuelve true, GetSuggestedActions() debe tener algunas acciones que mostrar. En muchos casos, se llama a HasSuggestedActionsAsync() justo antes de GetSuggestedActions(), pero esto no siempre es el caso. Por ejemplo, si el usuario invoca las acciones de la bombilla presionando (CTRL+), solo se ejecuta GetSuggestedActions().

    public IEnumerable<SuggestedActionSet> GetSuggestedActions(ISuggestedActionCategorySet requestedActionCategories, SnapshotSpan range, CancellationToken cancellationToken)
    {
        TextExtent extent;
        if (TryGetWordUnderCaret(out extent) && extent.IsSignificant)
        {
            ITrackingSpan trackingSpan = range.Snapshot.CreateTrackingSpan(extent.Span, SpanTrackingMode.EdgeInclusive);
            var upperAction = new UpperCaseSuggestedAction(trackingSpan);
            var lowerAction = new LowerCaseSuggestedAction(trackingSpan);
            return new SuggestedActionSet[] { new SuggestedActionSet(new ISuggestedAction[] { upperAction, lowerAction }) };
        }
        return Enumerable.Empty<SuggestedActionSet>();
    }
    
  7. Defina un evento SuggestedActionsChanged.

    public event EventHandler<EventArgs> SuggestedActionsChanged;
    
  8. Para completar la implementación, agregue implementaciones para los métodos Dispose() y TryGetTelemetryId(). No quieres realizar telemetría, por lo que solamente tienes que devolver false y establecer el GUID en Empty.

    public void Dispose()
    {
    }
    
    public bool TryGetTelemetryId(out Guid telemetryId)
    {
        // This is a sample provider and doesn't participate in LightBulb telemetry
        telemetryId = Guid.Empty;
        return false;
    }
    

Implementación de acciones de bombilla

  1. En el proyecto, agregue una referencia a Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll y establezca Copia local en False.

  2. Cree dos clases, la primera llamada UpperCaseSuggestedAction y la segunda llamada LowerCaseSuggestedAction. Ambas clases implementan ISuggestedAction.

    internal class UpperCaseSuggestedAction : ISuggestedAction
    internal class LowerCaseSuggestedAction : ISuggestedAction
    

    Ambas clases son iguales, excepto que una llama a ToUpper y la otra llama a ToLower. Los pasos siguientes cubren solo la clase de acción principal, pero debe implementar ambas clases. Siga los pasos para implementar la acción en mayúsculas como patrón para implementar la acción en minúsculas.

  3. Agregue las siguientes directivas using para estas clases:

    using Microsoft.VisualStudio.Imaging.Interop;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Media;
    
    
  4. Declare un conjunto de campos privados.

    private ITrackingSpan m_span;
    private string m_upper;
    private string m_display;
    private ITextSnapshot m_snapshot;
    
  5. Agregue un constructor que establezca los campos.

    public UpperCaseSuggestedAction(ITrackingSpan span)
    {
        m_span = span;
        m_snapshot = span.TextBuffer.CurrentSnapshot;
        m_upper = span.GetText(m_snapshot).ToUpper();
        m_display = string.Format("Convert '{0}' to upper case", span.GetText(m_snapshot));
    }
    
  6. Implemente el método GetPreviewAsync para que muestre la vista previa de la acción.

    public Task<object> GetPreviewAsync(CancellationToken cancellationToken)
    {
        var textBlock = new TextBlock();
        textBlock.Padding = new Thickness(5);
        textBlock.Inlines.Add(new Run() { Text = m_upper });
        return Task.FromResult<object>(textBlock);
    }
    
  7. Implemente el método GetActionSetsAsync para que devuelva una enumeración SuggestedActionSet vacía.

    public Task<IEnumerable<SuggestedActionSet>> GetActionSetsAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult<IEnumerable<SuggestedActionSet>>(null);
    }
    
  8. Implemente las propiedades como se indica a continuación.

    public bool HasActionSets
    {
        get { return false; }
    }
    public string DisplayText
    {
        get { return m_display; }
    }
    public ImageMoniker IconMoniker
    {
       get { return default(ImageMoniker); }
    }
    public string IconAutomationText
    {
        get
        {
            return null;
        }
    }
    public string InputGestureText
    {
        get
        {
            return null;
        }
    }
    public bool HasPreview
    {
        get { return true; }
    }
    
  9. Implemente el método Invoke reemplazando el texto del intervalo por su equivalente en mayúsculas.

    public void Invoke(CancellationToken cancellationToken)
    {
        m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper);
    }
    

    Advertencia

    No se espera que el método Invoke de la acción de bombilla muestre la IU. Si la acción muestra una nueva interfaz de usuario (por ejemplo, un cuadro de diálogo de vista previa o selección), no muestre la interfaz de usuario directamente desde el método Invoke, sino que programe mostrar la interfaz de usuario después de volver de Invoke.

  10. Para completar la implementación, agregue los métodos Dispose() y TryGetTelemetryId().

    public void Dispose()
    {
    }
    
    public bool TryGetTelemetryId(out Guid telemetryId)
    {
        // This is a sample action and doesn't participate in LightBulb telemetry
        telemetryId = Guid.Empty;
        return false;
    }
    
  11. No olvide hacer lo mismo para LowerCaseSuggestedAction cambiando el texto mostrado a "Convertir '{0}' a minúsculas" y la llamada ToUpper a ToLower.

Compilación y prueba del código

Para probar este código, compile la solución LightBulbTest y ejecútelo en la instancia experimental.

  1. Compile la solución.

  2. Al ejecutar este proyecto en el depurador, se inicia una segunda instancia de Visual Studio.

  3. Cree un archivo de texto y escriba texto. Debería ver una bombilla a la izquierda del texto.

    probar la bombilla

  4. Apunte a la bombilla. Debería ver una flecha hacia abajo.

  5. Al hacer clic en la bombilla, se deben mostrar dos acciones sugeridas, junto con la vista previa de la acción seleccionada.

    bombilla de prueba, expandida

  6. Si hace clic en la primera acción, todo el texto de la palabra actual debe convertirse en mayúsculas. Si hace clic en la segunda acción, todo el texto se debe convertir en minúsculas.