Dela via


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 C#-diagnostik- och kodkorrigering

  • 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.

    glödlampa

    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.

    förhandsgranskning av glödlampa

    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)

  1. Skapa ett C#VSIX-projekt. (I dialogrutan Nytt projekt väljer du Visual C# /Extensibilityoch sedan VSIX Project.) Ge lösningen namnet LightBulbTest.

  2. Lägg till en Redigerarklassificerare objektmall i projektet. Mer information finns i Skapa ett tillägg med en mall för redigeringsobjekt.

  3. Ta bort de befintliga klassfilerna.

  4. Lägg till följande referens i projektet och ange Kopiera lokal till False:

    Microsoft.VisualStudio.Language.Intellisense

  5. Lägg till en ny klassfil och ge den namnet LightBulbTest.

  6. 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

  1. 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
    
  2. I källproviderklassen importerar du ITextStructureNavigatorSelectorService och lägger till den som en egenskap.

    [Import(typeof(ITextStructureNavigatorSelectorService))]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
  3. 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.

  1. Lägg till en klass TestSuggestedActionsSource- som implementerar ISuggestedActionsSource.

    internal class TestSuggestedActionsSource : ISuggestedActionsSource
    
  2. 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;
    
  3. 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;
    }
    
  4. 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 outnull och metoden returnerar 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. 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;
        });
    }
    
  6. 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() och GetSuggestedActions() är konsekventa. Om HasSuggestedActionsAsync() returnerar truebör GetSuggestedActions() ha vissa åtgärder att visa. I många fall anropas HasSuggestedActionsAsync() precis innan GetSuggestedActions(), men så är inte alltid fallet. Om användaren till exempel anropar glödlampans åtgärder genom att trycka på (CTRL + .) anropas bara 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. Definiera en SuggestedActionsChanged händelse.

    public event EventHandler<EventArgs> SuggestedActionsChanged;
    
  8. För att slutföra implementeringen lägger du till implementeringar för metoderna Dispose() och TryGetTelemetryId(). Du vill inte göra telemetri, så det är bara att returnera false och ange GUID till 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;
    }
    

Implementera åtgärder för ljuskällor

  1. I projektet lägger du till en referens till Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll och anger Kopiera lokalt till False.

  2. Skapa två klasser, den första med namnet UpperCaseSuggestedAction och den andra med namnet LowerCaseSuggestedAction. 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.

  3. 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;
    
    
  4. Deklarera en uppsättning privata fält.

    private ITrackingSpan m_span;
    private string m_upper;
    private string m_display;
    private ITextSnapshot m_snapshot;
    
  5. 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));
    }
    
  6. 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);
    }
    
  7. 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);
    }
    
  8. 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; }
    }
    
  9. 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.

  10. För att slutföra implementeringen lägger du till metoderna Dispose() och 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. 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.

  1. Skapa lösningen.

  2. När du kör det här projektet i felsökningsprogrammet startas en andra instans av Visual Studio.

  3. Skapa en textfil och skriv text. Du bör se en glödlampa till vänster om texten.

    testa glödlampan

  4. Peka på glödlampan. Du bör se en nedåtpil.

  5. När du klickar på glödlampan bör två föreslagna åtgärder visas, tillsammans med förhandsgranskningen av den valda åtgärden.

    testlampa, expanderad

  6. 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.