Procedura dettagliata: visualizzare i suggerimenti della 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 l'.NET Compiler Platform ("Roslyn") per scrivere e creare pacchetti di analizzatori di codice personalizzati con azioni che visualizzano automaticamente le lampadine. Per altre informazioni, vedere:

  • Procedura: scrivere una correzione del codice e di diagnostica C#

  • Procedura: scrivere un Visual Basic di diagnostica e correzione del codice

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

    Ecco come appare una lampadina. In un progetto Visual Basic o Visual C#, una zigzag rossa viene visualizzata sotto il nome di una variabile quando non è valida. Se si posiziona il puntatore del mouse sull'identificatore non valido, accanto al cursore viene visualizzata una lampadina.

    lampadina

    Se si fa clic sulla freccia rivolta verso il basso accanto alla lampadina, viene visualizzato un set di azioni suggerite insieme a un'anteprima dell'azione selezionata. In questo caso, vengono visualizzate le modifiche apportate al codice se si esegue l'azione.

    anteprima di lampadina

    È possibile usare le lampadine per fornire le proprie 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. Nella procedura dettagliata riportata di seguito viene illustrato come creare una lampadina visualizzata nella parola corrente con due azioni consigliate: convertire in lettere maiuscole e convertirle in lettere minuscole.

Prerequisiti

A partire da Visual Studio 2015, non si installa Visual Studio SDK dall'area download. È incluso come funzionalità facoltativa nel programma di installazione di Visual Studio. È anche possibile installare Visual Studio SDK in un secondo momento. Per altre informazioni, vedere installare Visual Studio SDK.

Creare un progetto di Managed Extensibility Framework (MEF)

  1. Creare un progetto VSIX in C#. Nella finestra di dialogo nuovo progetto selezionare Visual C#/extensibility, quindi progetto VSIX. Assegnare un nome alla soluzione LightBulbTest .

  2. Aggiungere un modello di elemento classificatore 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 Copy Local su False :

    Microsoft.VisualStudio.Language.Intellisense

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

  6. Aggiungere le seguenti direttive 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;
    
    

Implementare il provider di origine lampadina

  1. Nel file di classe LightBulbTest. cs eliminare la classe LightBulbTest. Aggiungere una classe denominata TestSuggestedActionsSourceProvider che implementi ISuggestedActionsSourceProvider . Esportarlo con un nome di test di azioni suggerite 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 viene illustrata 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 dell'azione suggerita è responsabile della raccolta del set di azioni suggerite e della relativa aggiunta nel contesto corretto. In questo caso, il contesto è la parola corrente e le azioni suggerite sono UpperCaseSuggestedAction e LowerCaseSuggestedAction, come illustrato nella sezione seguente.

  1. Aggiungere una classe TestSuggestedActionsSource che implementi ISuggestedActionsSource .

    internal class TestSuggestedActionsSource : ISuggestedActionsSource
    
  2. Aggiungere campi privati di sola lettura per il provider di origine dell'azione suggerito, il buffer di testo e la visualizzazione di 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 del testo l'ambito della parola. Se il cursore si trova su una parola, TextExtent viene 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. Implementare il metodo HasSuggestedActionsAsync. L'editor chiama questo metodo per verificare 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 su un errore zigzag. È asincrona per consentire l'esecuzione di altre operazioni dell'interfaccia utente mentre questo metodo funziona. Nella maggior parte dei casi, questo metodo deve eseguire alcune operazioni di analisi e analisi della riga corrente, quindi l'elaborazione potrebbe richiedere del tempo.

    In questa implementazione, ottiene in modo asincrono l'oggetto TextExtent e determina se l'extent è significativo, ad esempio, se contiene testo diverso da uno spazio vuoto.

    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() deve disporre di alcune azioni da visualizzare. In molti casi, HasSuggestedActionsAsync() viene chiamato immediatamente prima GetSuggestedActions() , ma questo non è sempre il caso. Se, ad esempio, l'utente richiama le azioni della lampadina premendo (CTRL + .), viene chiamato solo il metodo 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 le implementazioni per Dispose() i TryGetTelemetryId() metodi e. Non si vuole eseguire la 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 lampadina

  1. Nel progetto aggiungere un riferimento a Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll e impostare Copia localmente 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 seguenti direttive using 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' SuggestedActionSet enumerazione vuota.

    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 di richiamo dell'azione lampadina non è previsto per la visualizzazione dell'interfaccia utente. Se l'azione attiva la nuova interfaccia utente (ad esempio, una finestra di dialogo di anteprima o di selezione), non visualizzare l'interfaccia utente direttamente dall'interno del metodo Invoke ma pianificare per visualizzare l'interfaccia utente dopo la restituzione da Invoke.

  10. Per completare l'implementazione, aggiungere i Dispose() TryGetTelemetryId() metodi e.

    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 "convertire" {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. Dovrebbe essere visualizzata una lampadina a sinistra del testo.

    test della lampadina

  4. Puntare alla lampadina. Verrà visualizzata una freccia verso il basso.

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

    test della lampadina, espanso

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