Instrukcja: wyświetlanie podpowiedzi dotyczących ż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:
Jak napisać diagnostykę języka Visual Basic i poprawkę kodu
Inne języki, takie jak C++, również oferują podpowiedzi do szybkiego działania, takie jak sugestia utworzenia szkieletowej implementacji tej funkcji.
Oto, jak wygląda żarówka. W projekcie Visual Basic lub Visual C# czerwona falista linia pojawia się pod nazwą zmiennej, gdy jest ona nieprawidłowa. 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 przedstawić 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ższej instrukcji pokazano, jak utworzyć żarówkę pojawiającą się nad bieżącym słowem, która 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 oknie dialogowym Nowy projekt wybierz pozycję Visual C# / Rozszerzalność, a następnie projekt VSIX.) Nadaj rozwiązaniu nazwę
LightBulbTest
.Dodaj do projektu szablon elementu klasyfikatora edytora . 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 Kopiuj lokalne 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 ISuggestedActionsSourceProvider. Wyeksportuj go z nazwą "Test sugerowanych akcji" oraz ContentTypeAttribute "tekst".
[Export(typeof(ISuggestedActionsSourceProvider))] [Name("Test Suggested Actions")] [ContentType("text")] internal class TestSuggestedActionsSourceProvider : ISuggestedActionsSourceProvider
Wewnątrz klasy dostawcy źródłowego zaimportuj ITextStructureNavigatorSelectorService i dodaj ją jako właściwość.
[Import(typeof(ITextStructureNavigatorSelectorService))] internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
Zaimplementuj metodę CreateSuggestedActionsSource, aby zwrócić obiekt ISuggestedActionsSource. Ź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); }
Implementacja ISuggestedActionSource
Sugerowane źródło akcji jest odpowiedzialne za zbieranie zestawu sugerowanych akcji i dodawanie ich w odpowiednim kontekście. W tym przypadku kontekst stanowi aktualne słowo, a sugerowane akcje to UpperCaseSuggestedAction i LowerCaseSuggestedAction, które zostały omówione w poniższej sekcji.
Dodaj klasę TestSuggestedActionsSource, która implementuje ISuggestedActionsSource.
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 o zakres słowa. Jeśli kursor znajduje się na słowie, TextExtent jest zwracany w parametrze wyjściowym; w przeciwnym razie parametr
out
jestnull
, a metoda zwracafalse
.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 najeżdża na podkreślenie 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ć parsowanie i analizę aktualnej linii, więc przetwarzanie może zająć trochę czasu.
W tej implementacji asynchronicznie pobiera TextExtent i określa, czy zakres jest znaczący, czyli czy zawiera jakiś tekst inny niż puste znaki.
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ę obiektów SuggestedActionSet zawierających różne obiekty ISuggestedAction. Ta metoda jest wywoływana po rozszerzeniu żarówki.
Ostrzeżenie
Upewnij się, że implementacje
HasSuggestedActionsAsync()
iGetSuggestedActions()
są spójne; oznacza to, że jeśliHasSuggestedActionsAsync()
zwracatrue
,GetSuggestedActions()
powinny mieć kilka akcji do wyświetlenia. W wielu przypadkachHasSuggestedActionsAsync()
jest wywoływana tuż przedGetSuggestedActions()
, ale nie zawsze tak jest. Jeśli na przykład użytkownik wywołuje akcje żarówki, naciskając (ctrl+ ). Wywoływana jest tylkoGetSuggestedActions()
.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 zdarzenie
SuggestedActionsChanged
.public event EventHandler<EventArgs> SuggestedActionsChanged;
Aby ukończyć implementację, dodaj implementacje dla metod
Dispose()
iTryGetTelemetryId()
. Nie chcesz wykonywać telemetrii, więc wystarczy zwrócićfalse
i ustawić identyfikator GUID na wartość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; }
Implementacja działań dotyczących żarówki
W projekcie dodaj odwołanie do Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll i ustaw Kopiuj lokalnie na
False
.Utwórz dwie klasy, pierwszą o nazwie
UpperCaseSuggestedAction
i drugą o nazwieLowerCaseSuggestedAction
. Obie klasy implementują ISuggestedAction.internal class UpperCaseSuggestedAction : ISuggestedAction internal class LowerCaseSuggestedAction : ISuggestedAction
Obie klasy są podobne, z tą różnicą, że jedna wywołuje ToUpper, a drugie wywołuje ToLower. Poniższe kroki dotyczą tylko klasy akcji z wielkimi literami, ale należy zaimplementować obie klasy. Użyj kroków implementacji akcji zamiany na wielkie litery jako wzorca do implementacji akcji zamiany na 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, aby wyświetlić 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 pustą SuggestedActionSet enumerację.
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 określonym fragmencie jego wielkimi literami.
public void Invoke(CancellationToken cancellationToken) { m_span.TextBuffer.Replace(m_span.GetSpan(m_snapshot), m_upper); }
Ostrzeżenie
Akcja żarówki Wywołaj metodę nie powinna pokazywać interfejsu użytkownika. Jeśli akcja powoduje wyświetlenie nowego interfejsu użytkownika (na przykład okna dialogowego podglądu lub wyboru), nie wyświetlaj interfejsu użytkownika bezpośrednio z poziomu metody Invoke, ale zamiast tego zaplanuj wyświetlanie interfejsu użytkownika po powrocie z Invoke.
Aby ukończyć implementację, dodaj metody
Dispose()
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 dla
LowerCaseSuggestedAction
, zmieniając tekst wyświetlany na "Konwertuj '{0}' na małe litery", oraz zmień wywołanie ToUpper na ToLower.
Kompilowanie i testowanie kodu
Aby przetestować ten kod, skompiluj rozwiązanie LightBulbTest i uruchom je w wystąpieniu eksperymentalnym.
Skompiluj 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.