Przewodnik: wyświetlanie sugestii żarówki

Żarówki to ikony w edytorze programu Visual Studio, które rozszerzają się, aby wyświetlić zestaw akcji, na przykład poprawki problemów zidentyfikowanych przez wbudowane analizatory kodu lub refaktoryzację kodu.

W edytorach Visual C# i Visual Basic można również użyć platformy kompilatora .NET ("Roslyn"), aby napisać i spakować własne analizatory kodu za pomocą akcji, które automatycznie wyświetlają żarówki. Aby uzyskać więcej informacji, zobacz:

  • Instrukcje: pisanie poprawki diagnostyki i kodu w języku C#

  • Porady: pisanie poprawki diagnostyki i kodu języka Visual Basic

    Inne języki, takie jak C++, zapewniają również żarówki dla niektórych szybkich akcji, takich jak sugestia utworzenia implementacji wycinków tej funkcji.

    Oto, jak wygląda żarówka. W projekcie Visual Basic lub Visual C# czerwony wywiórz pojawia się pod nazwą zmiennej, gdy jest on nieprawidłowy. W przypadku myszy nad nieprawidłowym identyfikatorem żarówka pojawi się w pobliżu kursora.

    light bulb

    Po kliknięciu strzałki w dół przez żarówkę zostanie wyświetlony zestaw sugerowanych akcji wraz z podglądem wybranej akcji. W takim przypadku pokazuje zmiany wprowadzone w kodzie, jeśli wykonasz akcję.

    light bulb preview

    Możesz użyć żarówek, aby zapewnić własne sugerowane akcje. Można na przykład podać akcje, aby przenieść nawiasy klamrowe otwierające do nowego wiersza lub przenieść je na koniec poprzedniego wiersza. W poniższym przewodniku pokazano, jak utworzyć żarówkę wyświetlaną w bieżącym słowie i ma dwie sugerowane akcje: Konwertuj na wielkie litery i Konwertuj na małe litery.

Tworzenie projektu zarządzanej struktury rozszerzalności (MEF)

  1. Utwórz projekt VSIX w języku C#. (W Okno dialogowe Nowy projekt , wybierz pozycję Visual C# / Rozszerzalność, a następnie projekt VSIX. Nadaj rozwiązaniu LightBulbTestnazwę .

  2. Dodaj szablon elementu Klasyfikator edytora do projektu. Aby uzyskać więcej informacji, zobacz Tworzenie rozszerzenia za pomocą szablonu elementu edytora.

  3. Usuń istniejące pliki klas.

  4. Dodaj następujące odwołanie do projektu i ustaw opcję Kopiuj lokalnie na False:

    Microsoft.VisualStudio.Language.Intellisense

  5. Dodaj nowy plik klasy i nadaj mu nazwę LightBulbTest.

  6. Dodaj następujące dyrektywy 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;
    
    

Implementowanie dostawcy źródła żarówki

  1. W pliku klasy LightBulbTest.cs usuń klasę LightBulbTest. Dodaj klasę o nazwie TestSuggestedActionsSourceProvider , która implementuje ISuggestedActionsSourceProviderelement . Wyeksportuj go za pomocą nazwy sugerowanych akcji testowych i wartości ContentTypeAttribute "text".

    [Export(typeof(ISuggestedActionsSourceProvider))]
    [Name("Test Suggested Actions")]
    [ContentType("text")]
    internal class TestSuggestedActionsSourceProvider : ISuggestedActionsSourceProvider
    
  2. Wewnątrz klasy dostawcy źródłowego zaimportuj ITextStructureNavigatorSelectorService element i dodaj go jako właściwość.

    [Import(typeof(ITextStructureNavigatorSelectorService))]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
  3. Zaimplementuj metodę CreateSuggestedActionsSourceISuggestedActionsSource , aby zwrócić obiekt. Źródło zostało omówione w następnej sekcji.

    public ISuggestedActionsSource CreateSuggestedActionsSource(ITextView textView, ITextBuffer textBuffer)
    {
        if (textBuffer == null || textView == null)
        {
            return null;
        }
        return new TestSuggestedActionsSource(this, textView, textBuffer);
    }
    

Implementowanie interfejsu ISuggestedActionSource

Sugerowane źródło akcji jest odpowiedzialne za zbieranie zestawu sugerowanych akcji i dodawanie ich w odpowiednim kontekście. W tym przypadku kontekst jest bieżącym słowem, a sugerowane akcje to UpperCaseSuggestedAction i LowerCaseSuggestedAction, które zostały omówione w poniższej sekcji.

  1. Dodaj klasę TestSuggestedActionsSource , która implementuje ISuggestedActionsSourceelement .

    internal class TestSuggestedActionsSource : ISuggestedActionsSource
    
  2. Dodaj prywatne pola tylko do odczytu dla sugerowanego dostawcy źródła akcji, bufor tekstu i widok tekstu.

    private readonly TestSuggestedActionsSourceProvider m_factory;
    private readonly ITextBuffer m_textBuffer;
    private readonly ITextView m_textView;
    
  3. Dodaj konstruktor, który ustawia pola prywatne.

    public TestSuggestedActionsSource(TestSuggestedActionsSourceProvider testSuggestedActionsSourceProvider, ITextView textView, ITextBuffer textBuffer)
    {
        m_factory = testSuggestedActionsSourceProvider;
        m_textBuffer = textBuffer;
        m_textView = textView;
    }
    
  4. Dodaj prywatną metodę zwracającą słowo, które znajduje się obecnie pod kursorem. Poniższa metoda analizuje bieżącą lokalizację kursora i pyta nawigator struktury tekstu pod kątem zakresu słowa. Jeśli kursor znajduje się na słowie, TextExtent element jest zwracany w parametrze out. W przeciwnym razie out parametr to null , a metoda zwraca falsewartość .

    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. Zaimplementuj metodę HasSuggestedActionsAsync . Edytor wywołuje tę metodę, aby dowiedzieć się, czy ma być wyświetlana żarówka. To wywołanie jest wykonywane często, na przykład za każdym razem, gdy kursor przesuwa się z jednego wiersza do innego lub gdy wskaźnik myszy nawiąż na wywijanie błędu. Jest asynchroniczna, aby umożliwić wykonywanie innych operacji interfejsu użytkownika podczas pracy tej metody. W większości przypadków ta metoda musi wykonać analizę i analizę bieżącego wiersza, więc przetwarzanie może zająć trochę czasu.

    W tej implementacji asynchronicznie pobiera TextExtent wartość i określa, czy zakres jest znaczący, podobnie jak w przypadku, gdy ma jakiś tekst inny niż biały znak.

    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. Zaimplementuj metodę GetSuggestedActions , która zwraca tablicę SuggestedActionSet obiektów zawierających różne ISuggestedAction obiekty. Ta metoda jest wywoływana po rozwinięciu żarówki.

    Ostrzeżenie

    Upewnij się, że implementacje HasSuggestedActionsAsync() elementów i GetSuggestedActions() są spójne. Oznacza to, że w przypadku HasSuggestedActionsAsync() zwracania truewartości , GetSuggestedActions() powinny być wyświetlane pewne akcje. W wielu przypadkach HasSuggestedActionsAsync() parametr jest wywoływany tuż przed parametrem GetSuggestedActions(), ale nie zawsze jest tak. Jeśli na przykład użytkownik wywołuje akcje żarówki, naciskając tylko GetSuggestedActions() klawisze CTRL+ .) jest wywoływana.

    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. Zdefiniuj SuggestedActionsChanged zdarzenie.

    public event EventHandler<EventArgs> SuggestedActionsChanged;
    
  8. Aby ukończyć implementację, dodaj implementacje dla Dispose() metod i .TryGetTelemetryId() Nie chcesz wykonywać telemetrii, więc wystarczy zwrócić false i ustawić identyfikator GUID na 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;
    }
    

Implementowanie akcji żarówki

  1. W projekcie dodaj odwołanie do pliku Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll i ustaw wartość Kopiuj lokalnie na False.

  2. Utwórz dwie klasy, pierwszą o nazwie UpperCaseSuggestedAction i drugą o nazwie LowerCaseSuggestedAction. Obie klasy implementują klasę ISuggestedAction.

    internal class UpperCaseSuggestedAction : ISuggestedAction
    internal class LowerCaseSuggestedAction : ISuggestedAction
    

    Obie klasy są podobne, z tą różnicą, że jedno wywołanie ToUpper i drugie wywołuje metodę ToLower. Poniższe kroki obejmują tylko wielką klasę akcji, ale należy zaimplementować obie klasy. Wykonaj kroki implementacji akcji wielkie litery jako wzorzec implementacji akcji małe litery.

  3. Dodaj następujące dyrektywy using dla tych klas:

    using Microsoft.VisualStudio.Imaging.Interop;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Media;
    
    
  4. Zadeklaruj zestaw pól prywatnych.

    private ITrackingSpan m_span;
    private string m_upper;
    private string m_display;
    private ITextSnapshot m_snapshot;
    
  5. Dodaj konstruktor, który ustawia pola.

    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. Zaimplementuj metodę GetPreviewAsync tak, aby wyświetlała podgląd akcji.

    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. Zaimplementuj metodę GetActionSetsAsync , aby zwracała puste SuggestedActionSet wyliczenie.

    public Task<IEnumerable<SuggestedActionSet>> GetActionSetsAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult<IEnumerable<SuggestedActionSet>>(null);
    }
    
  8. Zaimplementuj właściwości w następujący sposób.

    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. Zaimplementuj metodę Invoke , zastępując tekst w zakresie jego wielkimi literami.

    public void Invoke(CancellationToken cancellationToken)
    {
        m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper);
    }
    

    Ostrzeżenie

    Nie oczekuje się, że akcja żarówki Wywołaj metodę wywołania interfejsu użytkownika. Jeśli akcja powoduje wyświetlenie nowego interfejsu użytkownika (na przykład okna dialogowego podglądu lub zaznaczenia), nie wyświetlaj interfejsu użytkownika bezpośrednio z metody Invoke , ale zamiast tego zaplanuj wyświetlanie interfejsu użytkownika po powrocie z wywołania.

  10. Aby ukończyć implementację, dodaj Dispose() metody i 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. Nie zapomnij wykonać tej samej czynności w celu LowerCaseSuggestedAction zmiany tekstu wyświetlanego na "Konwertuj "{0}" na małe litery" i wywołanie metody ToUpperToLower.

Kompilowanie i testowanie kodu

Aby przetestować ten kod, skompiluj rozwiązanie LightBulbTest i uruchom je w wystąpieniu eksperymentalnym.

  1. Stwórz rozwiązanie.

  2. Po uruchomieniu tego projektu w debugerze zostanie uruchomione drugie wystąpienie programu Visual Studio.

  3. Utwórz plik tekstowy i wpisz tekst. Powinna zostać wyświetlona żarówka po lewej stronie tekstu.

    test the light bulb

  4. Wskaż żarówkę. Powinna zostać wyświetlona strzałka w dół.

  5. Po kliknięciu żarówki powinny zostać wyświetlone dwie sugerowane akcje wraz z podglądem wybranej akcji.

    test light bulb, expanded

  6. Jeśli klikniesz pierwszą akcję, cały tekst w bieżącym słowie powinien zostać przekonwertowany na wielkie litery. Jeśli klikniesz drugą akcję, cały tekst powinien zostać przekonwertowany na małe litery.