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:
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.
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ę.
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)
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
LightBulbTest
nazwę .Dodaj szablon elementu Klasyfikator edytora do projektu. Aby uzyskać więcej informacji, zobacz Tworzenie rozszerzenia za pomocą szablonu elementu edytora.
Usuń istniejące pliki klas.
Dodaj następujące odwołanie do projektu i ustaw opcję Kopiuj lokalnie na
False
:Microsoft.VisualStudio.Language.Intellisense
Dodaj nowy plik klasy i nadaj mu nazwę LightBulbTest.
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
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
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; }
Zaimplementuj metodę CreateSuggestedActionsSource ISuggestedActionsSource , 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.
Dodaj klasę TestSuggestedActionsSource , która implementuje ISuggestedActionsSourceelement .
internal class TestSuggestedActionsSource : ISuggestedActionsSource
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;
Dodaj konstruktor, który ustawia pola prywatne.
public TestSuggestedActionsSource(TestSuggestedActionsSourceProvider testSuggestedActionsSourceProvider, ITextView textView, ITextBuffer textBuffer) { m_factory = testSuggestedActionsSourceProvider; m_textBuffer = textBuffer; m_textView = textView; }
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 tonull
, a metoda zwracafalse
wartość .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; }
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; }); }
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 iGetSuggestedActions()
są spójne. Oznacza to, że w przypadkuHasSuggestedActionsAsync()
zwracaniatrue
wartości ,GetSuggestedActions()
powinny być wyświetlane pewne akcje. W wielu przypadkachHasSuggestedActionsAsync()
parametr jest wywoływany tuż przed parametremGetSuggestedActions()
, ale nie zawsze jest tak. Jeśli na przykład użytkownik wywołuje akcje żarówki, naciskając tylkoGetSuggestedActions()
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>(); }
Zdefiniuj
SuggestedActionsChanged
zdarzenie.public event EventHandler<EventArgs> SuggestedActionsChanged;
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 naEmpty
.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
W projekcie dodaj odwołanie do pliku Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll i ustaw wartość Kopiuj lokalnie na
False
.Utwórz dwie klasy, pierwszą o nazwie
UpperCaseSuggestedAction
i drugą o nazwieLowerCaseSuggestedAction
. 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.
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;
Zadeklaruj zestaw pól prywatnych.
private ITrackingSpan m_span; private string m_upper; private string m_display; private ITextSnapshot m_snapshot;
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)); }
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); }
Zaimplementuj metodę GetActionSetsAsync , aby zwracała puste SuggestedActionSet wyliczenie.
public Task<IEnumerable<SuggestedActionSet>> GetActionSetsAsync(CancellationToken cancellationToken) { return Task.FromResult<IEnumerable<SuggestedActionSet>>(null); }
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; } }
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.
Aby ukończyć implementację, dodaj
Dispose()
metody iTryGetTelemetryId()
.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; }
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 ToUpper ToLower.
Kompilowanie i testowanie kodu
Aby przetestować ten kod, skompiluj rozwiązanie LightBulbTest i uruchom je w wystąpieniu eksperymentalnym.
Stwórz rozwiązanie.
Po uruchomieniu tego projektu w debugerze zostanie uruchomione drugie wystąpienie programu Visual Studio.
Utwórz plik tekstowy i wpisz tekst. Powinna zostać wyświetlona żarówka po lewej stronie tekstu.
Wskaż żarówkę. Powinna zostać wyświetlona strzałka w dół.
Po kliknięciu żarówki powinny zostać wyświetlone dwie sugerowane akcje wraz z podglądem wybranej akcji.
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.