Procédure pas à pas : Afficher les suggestions d’ampoule
Les ampoules sont des icônes dans l’éditeur Visual Studio qui s’étendent pour afficher un ensemble d’actions, par exemple, des correctifs pour les problèmes identifiés par les analyseurs de code intégrés ou la refactorisation du code.
Dans les éditeurs Visual C# et Visual Basic, vous pouvez également utiliser la plateforme du compilateur .NET (« Roslyn ») pour écrire et empaqueter vos propres analyseurs de code avec des actions qui affichent automatiquement des ampoules. Pour plus d’informations, consultez l’article suivant :
Guide pratique pour écrire un correctif de code et de diagnostic C#
Guide pratique pour écrire un correctif de code et de diagnostic Visual Basic
D’autres langages tels que C++ fournissent également des ampoules pour certaines actions rapides, telles qu’une suggestion de création d’une implémentation stub de cette fonction.
Voici à quoi ressemble une ampoule. Dans un projet Visual Basic ou Visual C#, une bascule rouge apparaît sous un nom de variable lorsqu’elle n’est pas valide. Si vous placez la souris sur l’identificateur non valide, une ampoule apparaît près du curseur.
Si vous cliquez sur la flèche vers le bas par l’ampoule, un ensemble d’actions suggérées s’affiche, ainsi qu’un aperçu de l’action sélectionnée. Dans ce cas, il affiche les modifications apportées à votre code si vous exécutez l’action.
Vous pouvez utiliser des ampoules pour fournir vos propres actions suggérées. Par exemple, vous pouvez fournir des actions pour déplacer les accolades ouvrantes vers une nouvelle ligne ou les déplacer vers la fin de la ligne précédente. La procédure pas à pas suivante montre comment créer une ampoule qui apparaît sur le mot actuel et a deux actions suggérées : Convertir en majuscules et Convertir en minuscules.
Créer un projet MEF (Managed Extensibility Framework)
Créez un projet VSIX C#. (Dans le Boîte de dialogue Nouveau projet , sélectionnez Visual C# / Extensibilité, puis VSIX Project.) Nommez la solution
LightBulbTest
.Ajoutez un modèle d’élément Classifieur d’éditeur au projet. Pour plus d’informations, consultez Créer une extension avec un modèle d’élément d’éditeur.
Supprimez les fichiers de classe existants.
Ajoutez la référence suivante au projet et définissez Copy Local sur
False
:Microsoft.VisualStudio.Language.Intellisense
Ajoutez un nouveau fichier de classe et nommez-le LightBulbTest.
Ajoutez les directives using suivantes :
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;
Implémenter le fournisseur de source d’ampoule
Dans le fichier de classe LightBulbTest.cs , supprimez la classe LightBulbTest. Ajoutez une classe nommée TestSuggestedActionsSourceProvider qui implémente ISuggestedActionsSourceProvider. Exportez-le avec un nom d’actions suggérées de test et un ContentTypeAttribute « texte ».
[Export(typeof(ISuggestedActionsSourceProvider))] [Name("Test Suggested Actions")] [ContentType("text")] internal class TestSuggestedActionsSourceProvider : ISuggestedActionsSourceProvider
Dans la classe de fournisseur source, importez et ajoutez-la ITextStructureNavigatorSelectorService en tant que propriété.
[Import(typeof(ITextStructureNavigatorSelectorService))] internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
Implémentez la CreateSuggestedActionsSource méthode pour retourner un ISuggestedActionsSource objet. La source est abordée dans la section suivante.
public ISuggestedActionsSource CreateSuggestedActionsSource(ITextView textView, ITextBuffer textBuffer) { if (textBuffer == null || textView == null) { return null; } return new TestSuggestedActionsSource(this, textView, textBuffer); }
Implémenter ISuggestedActionSource
La source d’action suggérée est chargée de collecter l’ensemble des actions suggérées et de les ajouter dans le contexte approprié. Dans ce cas, le contexte est le mot actuel et les actions suggérées sont UpperCaseSuggestedAction et LowerCaseSuggestedAction, qui sont décrites dans la section suivante.
Ajoutez une classe TestSuggestedActionsSource qui implémente ISuggestedActionsSource.
internal class TestSuggestedActionsSource : ISuggestedActionsSource
Ajoutez des champs privés et en lecture seule pour le fournisseur de source d’action suggéré, la mémoire tampon de texte et la vue de texte.
private readonly TestSuggestedActionsSourceProvider m_factory; private readonly ITextBuffer m_textBuffer; private readonly ITextView m_textView;
Ajoutez un constructeur qui définit les champs privés.
public TestSuggestedActionsSource(TestSuggestedActionsSourceProvider testSuggestedActionsSourceProvider, ITextView textView, ITextBuffer textBuffer) { m_factory = testSuggestedActionsSourceProvider; m_textBuffer = textBuffer; m_textView = textView; }
Ajoutez une méthode privée qui retourne le mot qui se trouve actuellement sous le curseur. La méthode suivante examine l’emplacement actuel du curseur et demande au navigateur de structure de texte l’étendue du mot. Si le curseur se trouve sur un mot, il TextExtent est retourné dans le paramètre out ; sinon, le
out
paramètre estnull
et la méthode retournefalse
.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; }
Implémentez la méthode HasSuggestedActionsAsync. L’éditeur appelle cette méthode pour savoir s’il faut afficher l’ampoule. Cet appel est souvent effectué, par exemple, chaque fois que le curseur passe d’une ligne à une autre, ou lorsque la souris pointe sur une erreur d’quiggle. Il est asynchrone pour permettre à d’autres opérations d’interface utilisateur de continuer pendant que cette méthode fonctionne. Dans la plupart des cas, cette méthode doit effectuer une analyse et une analyse de la ligne actuelle, de sorte que le traitement peut prendre un certain temps.
Dans cette implémentation, elle obtient de façon asynchrone les TextExtent éléments et détermine si l’étendue est significative, comme dans le cas contraire, si elle contient du texte autre que l’espace blanc.
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; }); }
Implémentez la GetSuggestedActions méthode, qui retourne un tableau d’objets SuggestedActionSet qui contiennent les différents ISuggestedAction objets. Cette méthode est appelée lorsque l’ampoule est développée.
Avertissement
Vous devez vous assurer que les implémentations et
HasSuggestedActionsAsync()
GetSuggestedActions()
sont cohérentes ; c’est-à-dire, siHasSuggestedActionsAsync()
elles sont retournéestrue
,GetSuggestedActions()
doivent avoir certaines actions à afficher. Dans de nombreux cas,HasSuggestedActionsAsync()
est appelé juste avantGetSuggestedActions()
, mais ce n’est pas toujours le cas. Par exemple, si l’utilisateur appelle les actions de l’ampoule en appuyant uniquementGetSuggestedActions()
sur (Ctrl+ .) est appelée.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>(); }
Définir un
SuggestedActionsChanged
événement.public event EventHandler<EventArgs> SuggestedActionsChanged;
Pour terminer l’implémentation, ajoutez des implémentations pour les méthodes et
TryGetTelemetryId()
lesDispose()
méthodes. Vous ne souhaitez pas effectuer de télémétrie. Il vous suffit donc de retournerfalse
et de définir le GUID surEmpty
.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; }
Implémenter des actions d’ampoule
Dans le projet, ajoutez une référence à Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll et définissez Copy Local sur
False
.Créez deux classes, la première nommée
UpperCaseSuggestedAction
et la seconde nomméeLowerCaseSuggestedAction
. Ces deux classes implémentent ISuggestedAction.internal class UpperCaseSuggestedAction : ISuggestedAction internal class LowerCaseSuggestedAction : ISuggestedAction
Elles sont identiques, sauf que l’une appelle ToUpper et l’autre appelle ToLower. Les étapes suivantes traitent uniquement de la classe d’action de mise en majuscules, mais vous devez implémenter les deux classes. Appliquez les étapes d’implémentation de l’action de mise en majuscules comme modèle pour l’implémentation de l’action de mise en minuscules.
Ajoutez les directives d’utilisation suivantes pour ces classes :
using Microsoft.VisualStudio.Imaging.Interop; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media;
Déclarez un ensemble de champs privés.
private ITrackingSpan m_span; private string m_upper; private string m_display; private ITextSnapshot m_snapshot;
Ajoutez un constructeur qui définit les champs.
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)); }
Implémentez la GetPreviewAsync méthode afin qu’elle affiche l’aperçu de l’action.
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); }
Implémentez la GetActionSetsAsync méthode afin qu’elle retourne une énumération vide SuggestedActionSet .
public Task<IEnumerable<SuggestedActionSet>> GetActionSetsAsync(CancellationToken cancellationToken) { return Task.FromResult<IEnumerable<SuggestedActionSet>>(null); }
Implémentez les propriétés comme suit.
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; } }
Implémentez la méthode Invoke en remplaçant le texte dans l’étendue par son équivalent en majuscules.
public void Invoke(CancellationToken cancellationToken) { m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper); }
Avertissement
La méthode Invoke de l’action d’ampoule n’est pas censée afficher l’interface utilisateur. Si votre action affiche une nouvelle interface utilisateur (par exemple, une boîte de dialogue d’aperçu ou de sélection), n’affichez pas l’interface utilisateur directement à partir de la méthode Invoke, mais planifiez plutôt l’affichage de votre interface utilisateur après son retour à partir d’Invoke.
Pour terminer l’implémentation, ajoutez les méthodes et
TryGetTelemetryId()
lesDispose()
méthodes.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; }
N’oubliez pas de faire la même chose pour
LowerCaseSuggestedAction
modifier le texte d’affichage en « Convertir en{0} minuscules » et l’appel ToUpper en ToLower.
Générer et tester le code
Pour tester ce code, générez la solution LightBulbTest et exécutez-la dans l’instance expérimentale.
Générez la solution.
Lorsque vous exécutez ce projet dans le débogueur, une deuxième instance de Visual Studio est démarrée.
Créez un fichier texte et tapez du texte. Vous devriez voir une ampoule à gauche du texte.
Pointez sur l’ampoule. Vous devriez voir une flèche vers le bas.
Lorsque vous cliquez sur l’ampoule, deux actions suggérées doivent s’afficher, ainsi que l’aperçu de l’action sélectionnée.
Si vous cliquez sur la première action, tout le texte du mot actuel doit être converti en majuscules. Si vous cliquez sur la deuxième action, tout le texte doit être converti en minuscules.