Genomgång: Visa förslag på glödlampa
Glödlampor är ikoner i Visual Studio-redigeraren som expanderas för att visa en uppsättning åtgärder, till exempel korrigeringar för problem som identifieras av de inbyggda kodanalysverktygen eller kodrefaktoriseringen.
I Visual C# och Visual Basic-redigerare kan du också använda .NET Compiler Platform ("Roslyn") för att skriva och paketera dina egna kodanalysverktyg med åtgärder som visar glödlampor automatiskt. Mer information finns i:
Gör så här: Skriva en diagnostik- och kodkorrigering för Visual Basic
Andra språk som C++ ger också glödlampor för vissa snabba åtgärder, till exempel ett förslag om att skapa en stub-implementering av den funktionen.
Så här ser en glödlampa ut. I ett Visual Basic- eller Visual C#-projekt visas en röd vågig linje under ett variabelnamn när det är ogiltigt. Om du muspekaren över den ogiltiga identifieraren visas en glödlampa nära markören.
Om du klickar på nedåtpilen vid glödlampan visas en uppsättning föreslagna åtgärder, tillsammans med en förhandsgranskning av den valda åtgärden. I det här fallet visas de ändringar som görs i koden om du kör åtgärden.
Du kan använda glödlampor för att tillhandahålla dina egna föreslagna åtgärder. Du kan till exempel ange åtgärder för att flytta inledande klammerparenteser till en ny rad eller flytta dem till slutet av föregående rad. Följande genomgång visar hur du skapar en glödlampa som visas på det aktuella ordet och har två föreslagna åtgärder: Konvertera till versaler, Konvertera till gemener.
Skapa ett MEF-projekt (Managed Extensibility Framework)
Skapa ett C#VSIX-projekt. (I dialogrutan Nytt projekt väljer du Visual C# /Extensibilityoch sedan VSIX Project.) Ge lösningen namnet
LightBulbTest
.Lägg till en Redigerarklassificerare objektmall i projektet. Mer information finns i Skapa ett tillägg med en mall för redigeringsobjekt.
Ta bort de befintliga klassfilerna.
Lägg till följande referens i projektet och ange Kopiera lokal till
False
:Microsoft.VisualStudio.Language.Intellisense
Lägg till en ny klassfil och ge den namnet LightBulbTest.
Lägg till följande med hjälp av direktiv:
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;
Implementera glödlampans källprovider
Ta bort klassen LightBulbTest i klassfilen LightBulbTest.cs. Lägg till en klass med namnet TestSuggestedActionsSourceProvider som implementerar ISuggestedActionsSourceProvider. Exportera den med namnet Test Suggested Actions och en ContentTypeAttribute med "text".
[Export(typeof(ISuggestedActionsSourceProvider))] [Name("Test Suggested Actions")] [ContentType("text")] internal class TestSuggestedActionsSourceProvider : ISuggestedActionsSourceProvider
I källproviderklassen importerar du ITextStructureNavigatorSelectorService och lägger till den som en egenskap.
[Import(typeof(ITextStructureNavigatorSelectorService))] internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
Implementera metoden CreateSuggestedActionsSource för att returnera ett ISuggestedActionsSource objekt. Källan beskrivs i nästa avsnitt.
public ISuggestedActionsSource CreateSuggestedActionsSource(ITextView textView, ITextBuffer textBuffer) { if (textBuffer == null || textView == null) { return null; } return new TestSuggestedActionsSource(this, textView, textBuffer); }
Implementera ISuggestedActionSource
Den föreslagna åtgärdskällan ansvarar för att samla in uppsättningen föreslagna åtgärder och lägga till dem i rätt kontext. I det här fallet är kontexten det aktuella ordet och de föreslagna åtgärderna är UpperCaseSuggestedAction och LowerCaseSuggestedAction, som beskrivs i följande avsnitt.
Lägg till en klass TestSuggestedActionsSource- som implementerar ISuggestedActionsSource.
internal class TestSuggestedActionsSource : ISuggestedActionsSource
Lägg till privata, skrivskyddade fält för förslagshanteraren, textbufferten och textvyn.
private readonly TestSuggestedActionsSourceProvider m_factory; private readonly ITextBuffer m_textBuffer; private readonly ITextView m_textView;
Lägg till en konstruktor som anger de privata fälten.
public TestSuggestedActionsSource(TestSuggestedActionsSourceProvider testSuggestedActionsSourceProvider, ITextView textView, ITextBuffer textBuffer) { m_factory = testSuggestedActionsSourceProvider; m_textBuffer = textBuffer; m_textView = textView; }
Lägg till en privat metod som returnerar ordet som för närvarande finns under markören. Följande metod tittar på markörens aktuella plats och frågar textstrukturens navigatör om ordets omfattning. Om markören finns på ett ord returneras TextExtent i out-parametern. annars är parametern
out
null
och metoden returnerarfalse
.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; }
Implementera metoden HasSuggestedActionsAsync. Redigeraren anropar den här metoden för att ta reda på om glödlampan ska visas. Det här anropet görs ofta, till exempel när markören flyttas från en rad till en annan, eller när musen hovrar över ett fel. Det är asynkront för att tillåta andra användargränssnittsåtgärder att fortsätta medan den här metoden fungerar. I de flesta fall måste den här metoden utföra viss parsning och analys av den aktuella raden, så bearbetningen kan ta lite tid.
I den här implementeringen hämtar den asynkront TextExtent och avgör om omfattningen är betydande, som i, om den har någon annan text än blanksteg.
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; }); }
Implementera metoden GetSuggestedActions, som returnerar en matris med SuggestedActionSet objekt som innehåller de olika ISuggestedAction objekten. Den här metoden anropas när glödlampan expanderas.
Varning
Du bör se till att implementeringarna av
HasSuggestedActionsAsync()
ochGetSuggestedActions()
är konsekventa. OmHasSuggestedActionsAsync()
returnerartrue
börGetSuggestedActions()
ha vissa åtgärder att visa. I många fall anropasHasSuggestedActionsAsync()
precis innanGetSuggestedActions()
, men så är inte alltid fallet. Om användaren till exempel anropar glödlampans åtgärder genom att trycka på (CTRL + .) anropas baraGetSuggestedActions()
.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>(); }
Definiera en
SuggestedActionsChanged
händelse.public event EventHandler<EventArgs> SuggestedActionsChanged;
För att slutföra implementeringen lägger du till implementeringar för metoderna
Dispose()
ochTryGetTelemetryId()
. Du vill inte göra telemetri, så det är bara att returnerafalse
och ange GUID tillEmpty
.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; }
Implementera åtgärder för ljuskällor
I projektet lägger du till en referens till Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll och anger Kopiera lokalt till
False
.Skapa två klasser, den första med namnet
UpperCaseSuggestedAction
och den andra med namnetLowerCaseSuggestedAction
. Båda klasserna implementerar ISuggestedAction.internal class UpperCaseSuggestedAction : ISuggestedAction internal class LowerCaseSuggestedAction : ISuggestedAction
Båda klasserna är likadana förutom att en anropar ToUpper och den andra anropar ToLower. Följande steg omfattar endast åtgärdsklassen versaler, men du måste implementera båda klasserna. Använd stegen för att implementera versaliseringsåtgärden som ett mönster för att implementera geminiseringsåtgärden.
Lägg till följande med hjälp av direktiv för dessa klasser:
using Microsoft.VisualStudio.Imaging.Interop; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media;
Deklarera en uppsättning privata fält.
private ITrackingSpan m_span; private string m_upper; private string m_display; private ITextSnapshot m_snapshot;
Lägg till en konstruktor som anger fälten.
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)); }
Implementera metoden GetPreviewAsync så att den visar åtgärdsförhandsgranskningen.
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); }
Implementera metoden GetActionSetsAsync så att den returnerar en tom SuggestedActionSet uppräkning.
public Task<IEnumerable<SuggestedActionSet>> GetActionSetsAsync(CancellationToken cancellationToken) { return Task.FromResult<IEnumerable<SuggestedActionSet>>(null); }
Implementera egenskaperna enligt följande.
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; } }
Implementera metoden Invoke genom att ersätta texten i intervallet med dess motsvarighet i versaler.
public void Invoke(CancellationToken cancellationToken) { m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper); }
Varning
Glödlampans åtgärd Anropa-metoden förväntas inte visa användargränssnittet. Om åtgärden innehåller ett nytt användargränssnitt (till exempel en dialogruta för förhandsversion eller val) ska du inte visa användargränssnittet direkt från metoden Invoke utan i stället schemalägga att visa användargränssnittet när du har återvänt från Invoke.
För att slutföra implementeringen lägger du till metoderna
Dispose()
ochTryGetTelemetryId()
.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; }
Glöm inte att göra samma sak för
LowerCaseSuggestedAction
, genom att ändra visningstexten till "Konvertera '{0}' till gemener" och ändra anropet ToUpper till ToLower.
Skapa och testa koden
Om du vill testa den här koden skapar du Lösningen LightBulbTest och kör den i den experimentella instansen.
Skapa lösningen.
När du kör det här projektet i felsökningsprogrammet startas en andra instans av Visual Studio.
Skapa en textfil och skriv text. Du bör se en glödlampa till vänster om texten.
Peka på glödlampan. Du bör se en nedåtpil.
När du klickar på glödlampan bör två föreslagna åtgärder visas, tillsammans med förhandsgranskningen av den valda åtgärden.
Om du klickar på den första åtgärden ska all text i det aktuella ordet konverteras till versaler. Om du klickar på den andra åtgärdsknappen ska all text konverteras till gemener.