Procedura dettagliata: Visualizzare i suggerimenti per la lampadina

Le lampadine sono icone nell'editor di Visual Studio che si espandono per visualizzare un set di azioni, ad esempio correzioni per i problemi identificati dagli analizzatori di codice predefiniti o dal refactoring del codice.

Negli editor di Visual C# e Visual Basic è anche possibile usare .NET Compiler Platform ("Roslyn") per scrivere e creare pacchetti di analizzatori di codice personalizzati con azioni che visualizzano automaticamente le lampadine. Per altre informazioni, vedi:

  • Procedura: Scrivere una diagnostica e una correzione del codice C#

  • Procedura: Scrivere una diagnostica e una correzione del codice di Visual Basic

    Altri linguaggi come C++ forniscono anche lampadine per alcune azioni rapide, ad esempio un suggerimento per creare un'implementazione stub di tale funzione.

    Ecco l'aspetto di una lampadina. In un progetto Visual Basic o Visual C# viene visualizzata una sottolineatura ondulata rossa sotto un nome di variabile quando non è valida. Se si passa il mouse sull'identificatore non valido, viene visualizzata una lampadina vicino al cursore.

    light bulb

    Se si fa clic sulla freccia giù in base alla lampadina, viene visualizzato un set di azioni suggerite, insieme a un'anteprima dell'azione selezionata. In questo caso, mostra le modifiche apportate al codice se si esegue l'azione.

    light bulb preview

    È possibile usare le lampadine per fornire azioni suggerite. Ad esempio, è possibile fornire azioni per spostare le parentesi graffe di apertura in una nuova riga o spostarle alla fine della riga precedente. La procedura dettagliata seguente illustra come creare una lampadina visualizzata nella parola corrente e ha due azioni suggerite: Converti in lettere maiuscole e Converti in lettere minuscole.

Creare un progetto MEF (Managed Extensibility Framework)

  1. Creare un progetto VSIX C#. (In Finestra di dialogo Nuovo progetto , selezionare Visual C# / Estendibilità e quindi progetto VSIX. Assegnare alla soluzione LightBulbTestil nome .

  2. Aggiungere un modello di elemento classificatore dell'editor al progetto. Per altre informazioni, vedere Creare un'estensione con un modello di elemento dell'editor.

  3. Eliminare i file di classe esistenti.

  4. Aggiungere il riferimento seguente al progetto e impostare Copia locale su False:

    Microsoft.VisualStudio.Language.Intellisense

  5. Aggiungere un nuovo file di classe e denominarlo LightBulbTest.

  6. Aggiungere le direttive using seguenti:

    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;
    
    

Implementare il provider di origine lampadina

  1. Nel file di classe LightBulbTest.cs eliminare la classe LightBulbTest. Aggiungere una classe denominata TestSuggestedActionsSourceProvider che implementa ISuggestedActionsSourceProvider. Esportarlo con un nome di azioni suggerite di test e un ContentTypeAttribute di "testo".

    [Export(typeof(ISuggestedActionsSourceProvider))]
    [Name("Test Suggested Actions")]
    [ContentType("text")]
    internal class TestSuggestedActionsSourceProvider : ISuggestedActionsSourceProvider
    
  2. All'interno della classe del provider di origine importare ITextStructureNavigatorSelectorService e aggiungerlo come proprietà.

    [Import(typeof(ITextStructureNavigatorSelectorService))]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
  3. Implementare il CreateSuggestedActionsSource metodo per restituire un ISuggestedActionsSource oggetto . L'origine è descritta nella sezione successiva.

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

Implementare ISuggestedActionSource

L'origine azione suggerita è responsabile della raccolta del set di azioni suggerite e dell'aggiunta nel contesto corretto. In questo caso, il contesto è la parola corrente e le azioni suggerite sono UpperCaseSuggestedAction e LowerCaseSuggestedAction, descritte nella sezione seguente.

  1. Aggiungere una classe TestSuggestedActionsSource che implementa ISuggestedActionsSource.

    internal class TestSuggestedActionsSource : ISuggestedActionsSource
    
  2. Aggiungere campi privati e di sola lettura per il provider dell'origine azione suggerita, il buffer di testo e la visualizzazione testo.

    private readonly TestSuggestedActionsSourceProvider m_factory;
    private readonly ITextBuffer m_textBuffer;
    private readonly ITextView m_textView;
    
  3. Aggiungere un costruttore che imposta i campi privati.

    public TestSuggestedActionsSource(TestSuggestedActionsSourceProvider testSuggestedActionsSourceProvider, ITextView textView, ITextBuffer textBuffer)
    {
        m_factory = testSuggestedActionsSourceProvider;
        m_textBuffer = textBuffer;
        m_textView = textView;
    }
    
  4. Aggiungere un metodo privato che restituisce la parola attualmente sotto il cursore. Il metodo seguente esamina la posizione corrente del cursore e chiede allo strumento di navigazione della struttura di testo l'extent della parola. Se il cursore si trova su una parola, viene TextExtent restituito nel parametro out; in caso contrario, il out parametro è null e il metodo restituisce 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. Implementa il metodo HasSuggestedActionsAsync. L'editor chiama questo metodo per scoprire se visualizzare la lampadina. Questa chiamata viene eseguita spesso, ad esempio, ogni volta che il cursore si sposta da una riga a un'altra o quando il mouse passa sopra una sottolineatura ondulata di errore. È asincrono per consentire l'esecuzione di altre operazioni dell'interfaccia utente mentre questo metodo funziona. Nella maggior parte dei casi, questo metodo deve eseguire alcune analisi e analisi della riga corrente, quindi l'elaborazione potrebbe richiedere del tempo.

    In questa implementazione ottiene TextExtent in modo asincrono e determina se l'extent è significativo, come in, se contiene testo diverso da spazi vuoti.

    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. Implementare il GetSuggestedActions metodo , che restituisce una matrice di SuggestedActionSet oggetti che contengono i diversi ISuggestedAction oggetti . Questo metodo viene chiamato quando la lampadina viene espansa.

    Avviso

    È necessario assicurarsi che le implementazioni di HasSuggestedActionsAsync() e GetSuggestedActions() siano coerenti, ovvero se HasSuggestedActionsAsync() restituisce true, GetSuggestedActions() devono essere visualizzate alcune azioni. In molti casi, HasSuggestedActionsAsync() viene chiamato poco prima GetSuggestedActions()di , ma questo non è sempre il caso. Ad esempio, se l'utente richiama le azioni della lampadina premendo (CTRL+ .) viene chiamato solo 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. Definire un SuggestedActionsChanged evento.

    public event EventHandler<EventArgs> SuggestedActionsChanged;
    
  8. Per completare l'implementazione, aggiungere implementazioni per i Dispose() metodi e TryGetTelemetryId() . Non si vogliono eseguire i dati di telemetria, quindi è sufficiente restituire false e impostare il GUID su 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;
    }
    

Implementare azioni lampadine

  1. Nel progetto aggiungere un riferimento a Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll e impostare Copia locale su False.

  2. Creare due classi, denominate UpperCaseSuggestedAction e LowerCaseSuggestedAction. Entrambe le classi implementano ISuggestedAction.

    internal class UpperCaseSuggestedAction : ISuggestedAction
    internal class LowerCaseSuggestedAction : ISuggestedAction
    

    Le due classi sono simili, con l'unica eccezione che una chiama ToUpper e l'altra chiama ToLower. Anche se i passaggi seguenti descrivono solo la classe dell'azione per le maiuscole, è necessario implementarle entrambe. Usare i passaggi per l'implementazione dell'azione per le maiuscole come criterio per l'implementazione dell'azione per le minuscole.

  3. Aggiungere le direttive using seguenti per queste classi:

    using Microsoft.VisualStudio.Imaging.Interop;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Media;
    
    
  4. Dichiarare un set di campi privati.

    private ITrackingSpan m_span;
    private string m_upper;
    private string m_display;
    private ITextSnapshot m_snapshot;
    
  5. Aggiungere un costruttore che imposta i campi.

    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. Implementare il GetPreviewAsync metodo in modo che visualizzi l'anteprima dell'azione.

    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. Implementare il GetActionSetsAsync metodo in modo che restituisca un'enumerazione vuota SuggestedActionSet .

    public Task<IEnumerable<SuggestedActionSet>> GetActionSetsAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult<IEnumerable<SuggestedActionSet>>(null);
    }
    
  8. Implementare le proprietà nel modo indicato di seguito.

    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. Implementare il metodo Invoke sostituendo il testo nell'intervallo con l'equivalente in maiuscole.

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

    Avviso

    Il metodo Invoke dell'azione lampadina non dovrebbe mostrare l'interfaccia utente. Se l'azione visualizza una nuova interfaccia utente (ad esempio una finestra di dialogo di anteprima o selezione), non visualizzare l'interfaccia utente direttamente dall'interno del metodo Invoke, ma pianificare la visualizzazione dell'interfaccia utente dopo la restituzione da Invoke.

  10. Per completare l'implementazione, aggiungere i Dispose() metodi e 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. Non dimenticare di eseguire la stessa operazione per LowerCaseSuggestedAction modificare il testo visualizzato in "Converti '{0}' in lettere minuscole" e la chiamata ToUpper a ToLower.

Compilare e testare il codice

Per testare questo codice, compilare la soluzione LightBulbTest ed eseguirla nell'istanza sperimentale.

  1. Compilare la soluzione.

  2. Quando si esegue questo progetto nel debugger, viene avviata una seconda istanza di Visual Studio.

  3. Creare un file di testo e digitare alcune parole. Verrà visualizzata una lampadina a sinistra del testo.

    test the light bulb

  4. Puntare alla lampadina. Dovrebbe essere visualizzata una freccia giù.

  5. Quando si fa clic sulla lampadina, verranno visualizzate due azioni suggerite, insieme all'anteprima dell'azione selezionata.

    test light bulb, expanded

  6. Se si fa clic sulla prima azione, tutto il testo nella parola corrente deve essere convertito in lettere maiuscole. Se si fa clic sulla seconda azione, tutto il testo deve essere convertito in lettere minuscole.