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.
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.
È 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)
Creare un progetto VSIX C#. (In Finestra di dialogo Nuovo progetto , selezionare Visual C# / Estendibilità e quindi progetto VSIX. Assegnare alla soluzione
LightBulbTest
il nome .Aggiungere un modello di elemento classificatore dell'editor al progetto. Per altre informazioni, vedere Creare un'estensione con un modello di elemento dell'editor.
Eliminare i file di classe esistenti.
Aggiungere il riferimento seguente al progetto e impostare Copia locale su
False
:Microsoft.VisualStudio.Language.Intellisense
Aggiungere un nuovo file di classe e denominarlo LightBulbTest.
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
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
All'interno della classe del provider di origine importare ITextStructureNavigatorSelectorService e aggiungerlo come proprietà.
[Import(typeof(ITextStructureNavigatorSelectorService))] internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
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.
Aggiungere una classe TestSuggestedActionsSource che implementa ISuggestedActionsSource.
internal class TestSuggestedActionsSource : ISuggestedActionsSource
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;
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; }
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 restituiscefalse
.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; }
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; }); }
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()
eGetSuggestedActions()
siano coerenti, ovvero seHasSuggestedActionsAsync()
restituiscetrue
,GetSuggestedActions()
devono essere visualizzate alcune azioni. In molti casi,HasSuggestedActionsAsync()
viene chiamato poco primaGetSuggestedActions()
di , ma questo non è sempre il caso. Ad esempio, se l'utente richiama le azioni della lampadina premendo (CTRL+ .) viene chiamato soloGetSuggestedActions()
.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>(); }
Definire un
SuggestedActionsChanged
evento.public event EventHandler<EventArgs> SuggestedActionsChanged;
Per completare l'implementazione, aggiungere implementazioni per i
Dispose()
metodi eTryGetTelemetryId()
. Non si vogliono eseguire i dati di telemetria, quindi è sufficiente restituirefalse
e impostare il GUID suEmpty
.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
Nel progetto aggiungere un riferimento a Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll e impostare Copia locale su
False
.Creare due classi, denominate
UpperCaseSuggestedAction
eLowerCaseSuggestedAction
. 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.
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;
Dichiarare un set di campi privati.
private ITrackingSpan m_span; private string m_upper; private string m_display; private ITextSnapshot m_snapshot;
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)); }
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); }
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); }
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; } }
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.
Per completare l'implementazione, aggiungere i
Dispose()
metodi eTryGetTelemetryId()
.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; }
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.
Compilare la soluzione.
Quando si esegue questo progetto nel debugger, viene avviata una seconda istanza di Visual Studio.
Creare un file di testo e digitare alcune parole. Verrà visualizzata una lampadina a sinistra del testo.
Puntare alla lampadina. Dovrebbe essere visualizzata una freccia giù.
Quando si fa clic sulla lampadina, verranno visualizzate due azioni suggerite, insieme all'anteprima dell'azione selezionata.
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.