Przewodnik: uzupełnianie instrukcji wyświetlania
Można zaimplementować uzupełnianie instrukcji opartej na języku, definiując identyfikatory, dla których chcesz zapewnić ukończenie, a następnie wyzwalając sesję ukończenia. Możesz zdefiniować uzupełnianie instrukcji w kontekście usługi językowej, zdefiniować rozszerzenie nazwy pliku i typ zawartości, a następnie wyświetlić uzupełnianie tylko dla tego typu. Możesz też wyzwolić uzupełnianie dla istniejącego typu zawartości — na przykład "zwykły tekst". W tym przewodniku pokazano, jak wyzwolić uzupełnianie instrukcji dla typu zawartości "zwykły tekst", który jest typem zawartości plików tekstowych. Typ zawartości "tekst" jest elementem nadrzędnym wszystkich innych typów zawartości, w tym kodu i plików XML.
Uzupełnianie instrukcji jest zwykle wyzwalane przez wpisanie niektórych znaków — na przykład przez wpisanie początku identyfikatora, takiego jak "using". Zazwyczaj jest on odrzucany przez naciśnięcie klawisza Spacja, Klawisz Tab lub Enter w celu zatwierdzenia zaznaczenia. Funkcje intelliSense wyzwalane podczas wpisywania znaku można zaimplementować przy użyciu procedury obsługi poleceń dla naciśnięć klawiszy ( IOleCommandTarget interfejsu) i dostawcy programu obsługi, który implementuje IVsTextViewCreationListener interfejs. Aby utworzyć źródło uzupełniania, czyli listę identyfikatorów, które uczestniczą w uzupełnianiu, zaimplementuj ICompletionSource interfejs i dostawcę źródła uzupełniania (interfejs).ICompletionSourceProvider Dostawcy są częściami składników Managed Extensibility Framework (MEF). Są one odpowiedzialne za eksportowanie klas źródłowych i kontrolerów oraz importowanie usług i brokerów — na przykład ITextStructureNavigatorSelectorService, który umożliwia nawigację w buforze tekstowym i ICompletionBroker, która wyzwala sesję ukończenia.
W tym przewodniku pokazano, jak zaimplementować uzupełnianie instrukcji dla ustalonego zestawu identyfikatorów. W pełnych implementacjach usługa językowa i dokumentacja języka są odpowiedzialne za dostarczanie tej zawartości.
Tworzenie projektu MEF
Aby utworzyć projekt 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
CompletionTest
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łania do projektu i upewnij się, że właściwość CopyLocal jest ustawiona na
false
wartość :Microsoft.VisualStudio.Editor
Microsoft.VisualStudio.Language.Intellisense
Microsoft.VisualStudio.OLE.Interop
Microsoft.VisualStudio.Shell.15.0
Microsoft.VisualStudio.Shell.Immutable.10.0
Microsoft.VisualStudio.TextManager.Interop
Implementowanie źródła uzupełniania
Źródło uzupełniania jest odpowiedzialne za zbieranie zestawu identyfikatorów i dodawanie zawartości do okna uzupełniania, gdy użytkownik wpisze wyzwalacz ukończenia, na przykład pierwsze litery identyfikatora. W tym przykładzie identyfikatory i ich opisy są zakodowane w metodzie AugmentCompletionSession . W większości przypadków używanych w większości rzeczywistych analizator języka służy do pobierania tokenów w celu wypełnienia listy uzupełniania.
Aby zaimplementować źródło uzupełniania
Dodaj plik klasy i nadaj mu
TestCompletionSource
nazwę .Dodaj następujące importy:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.Composition; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Utilities;
Zmodyfikuj deklarację klasy dla elementu
TestCompletionSource
, aby implementować ICompletionSourcepolecenie :Dodaj pola prywatne dla dostawcy źródłowego, bufor tekstu i listę Completion obiektów (które odpowiadają identyfikatorom, które będą uczestniczyć w sesji ukończenia):
Dodaj konstruktor, który ustawia dostawcę źródła i bufor. Klasa jest zdefiniowana
TestCompletionSourceProvider
w kolejnych krokach:Zaimplementuj metodę AugmentCompletionSession , dodając zestaw uzupełniania zawierający uzupełnienia, które chcesz podać w kontekście. Każdy zestaw uzupełniania zawiera zestaw Completion uzupełniania i odpowiada karcie okna ukończenia. (W projektach Visual Basic karty okien uzupełniania są nazwane Wspólne i wszystkie). Metoda jest zdefiniowana
FindTokenSpanAtPosition
w następnym kroku.void ICompletionSource.AugmentCompletionSession(ICompletionSession session, IList<CompletionSet> completionSets) { List<string> strList = new List<string>(); strList.Add("addition"); strList.Add("adaptation"); strList.Add("subtraction"); strList.Add("summation"); m_compList = new List<Completion>(); foreach (string str in strList) m_compList.Add(new Completion(str, str, str, null, null)); completionSets.Add(new CompletionSet( "Tokens", //the non-localized title of the tab "Tokens", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_compList, null)); }
Następująca metoda służy do znajdowania bieżącego wyrazu z położenia kursora:
private ITrackingSpan FindTokenSpanAtPosition(ITrackingPoint point, ICompletionSession session) { SnapshotPoint currentPoint = (session.TextView.Caret.Position.BufferPosition) - 1; ITextStructureNavigator navigator = m_sourceProvider.NavigatorService.GetTextStructureNavigator(m_textBuffer); TextExtent extent = navigator.GetExtentOfWord(currentPoint); return currentPoint.Snapshot.CreateTrackingSpan(extent.Span, SpanTrackingMode.EdgeInclusive); }
Zaimplementuj metodę
Dispose()
:
Implementowanie dostawcy źródła uzupełniania
Dostawca źródła uzupełniania jest częścią składnika MEF, która tworzy wystąpienie źródła uzupełniania.
Aby zaimplementować dostawcę źródła uzupełniania
Dodaj klasę o nazwie
TestCompletionSourceProvider
, która implementuje ICompletionSourceProviderelement . Wyeksportuj tę klasę ContentTypeAttribute za pomocą "zwykłego tekstu" i NameAttribute "ukończenia testu".Zaimportuj element ITextStructureNavigatorSelectorService, który znajduje bieżące słowo w źródle uzupełniania.
Zaimplementuj metodę , TryCreateCompletionSource aby utworzyć wystąpienie źródła uzupełniania.
Implementowanie dostawcy obsługi poleceń uzupełniania
Dostawca programu obsługi poleceń uzupełniania pochodzi z IVsTextViewCreationListenerelementu , który nasłuchuje zdarzenia tworzenia widoku tekstu i konwertuje widok z IVsTextViewelementu , co umożliwia dodanie polecenia do łańcucha poleceń powłoki programu Visual Studio do elementu ITextView. Ponieważ ta klasa jest eksportem MEF, można go również użyć do zaimportowania usług wymaganych przez samą procedurę obsługi poleceń.
Aby zaimplementować dostawcę obsługi poleceń uzupełniania
Dodaj plik o nazwie
TestCompletionCommandHandler
.Dodaj następujące dyrektywy using:
using System; using System.ComponentModel.Composition; using System.Runtime.InteropServices; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Utilities;
Dodaj klasę o nazwie
TestCompletionHandlerProvider
, która implementuje IVsTextViewCreationListenerelement . Wyeksportuj tę klasę NameAttribute za pomocą "procedury obsługi uzupełniania tokenu", " ContentTypeAttribute zwykłego tekstu" i klasy TextViewRoleAttribute Editable.Zaimportuj element IVsEditorAdaptersFactoryService, który umożliwia konwersję z IVsTextView elementu na ITextView, a ICompletionBrokeri , SVsServiceProvider który umożliwia dostęp do standardowych usług programu Visual Studio.
Zaimplementuj metodę , VsTextViewCreated aby utworzyć wystąpienie programu obsługi poleceń.
public void VsTextViewCreated(IVsTextView textViewAdapter) { ITextView textView = AdapterService.GetWpfTextView(textViewAdapter); if (textView == null) return; Func<TestCompletionCommandHandler> createCommandHandler = delegate() { return new TestCompletionCommandHandler(textViewAdapter, textView, this); }; textView.Properties.GetOrCreateSingletonProperty(createCommandHandler); }
Implementowanie procedury obsługi poleceń uzupełniania
Ponieważ uzupełnianie instrukcji jest wyzwalane przez naciśnięcia klawiszy, należy zaimplementować IOleCommandTarget interfejs w celu odbierania i przetwarzania naciśnięć klawiszy wyzwalających, zatwierdzających i odrzucających sesję uzupełniania.
Aby zaimplementować procedurę obsługi poleceń uzupełniania
Dodaj klasę o nazwie
TestCompletionCommandHandler
, która implementuje IOleCommandTargetpolecenie :Dodaj pola prywatne dla następnej procedury obsługi poleceń (do której przekazujesz polecenie), widoku tekstu, dostawcy programu obsługi poleceń (który umożliwia dostęp do różnych usług) i sesji ukończenia:
Dodaj konstruktor, który ustawia widok tekstowy i pola dostawcy, a następnie dodaje polecenie do łańcucha poleceń:
Zaimplementuj metodę QueryStatus , przekazując polecenie wzdłuż:
Zaimplementuj metodę Exec . Gdy ta metoda odbiera naciśnięcie klawisza, musi wykonać jedną z następujących czynności:
Zezwalaj na zapisanie znaku w buforze, a następnie wyzwalanie lub uzupełnianie filtru. (To robią znaki drukowania).
Zatwierdź ukończenie, ale nie zezwalaj na zapisanie znaku w buforze. (Biały znak, Karta i wprowadź to po wyświetleniu sesji ukończenia).
Zezwalaj na przekazywanie polecenia do następnego programu obsługi. (Wszystkie inne polecenia).
Ponieważ ta metoda może wyświetlać interfejs użytkownika, wywołaj metodę IsInAutomationFunction , aby upewnić się, że nie jest wywoływana w kontekście automatyzacji:
public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { if (VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider)) { return m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); } //make a copy of this so we can look at it after forwarding some commands uint commandID = nCmdID; char typedChar = char.MinValue; //make sure the input is a char before getting it if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR) { typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); } //check for a commit character if (nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN || nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB || (char.IsWhiteSpace(typedChar) || char.IsPunctuation(typedChar))) { //check for a selection if (m_session != null && !m_session.IsDismissed) { //if the selection is fully selected, commit the current session if (m_session.SelectedCompletionSet.SelectionStatus.IsSelected) { m_session.Commit(); //also, don't add the character to the buffer return VSConstants.S_OK; } else { //if there is no selection, dismiss the session m_session.Dismiss(); } } } //pass along the command so the char is added to the buffer int retVal = m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); bool handled = false; if (!typedChar.Equals(char.MinValue) && char.IsLetterOrDigit(typedChar)) { if (m_session == null || m_session.IsDismissed) // If there is no active session, bring up completion { this.TriggerCompletion(); m_session.Filter(); } else //the completion session is already active, so just filter { m_session.Filter(); } handled = true; } else if (commandID == (uint)VSConstants.VSStd2KCmdID.BACKSPACE //redo the filter if there is a deletion || commandID == (uint)VSConstants.VSStd2KCmdID.DELETE) { if (m_session != null && !m_session.IsDismissed) m_session.Filter(); handled = true; } if (handled) return VSConstants.S_OK; return retVal; }
Ten kod jest prywatną metodą, która wyzwala sesję ukończenia:
private bool TriggerCompletion() { //the caret must be in a non-projection location SnapshotPoint? caretPoint = m_textView.Caret.Position.Point.GetPoint( textBuffer => (!textBuffer.ContentType.IsOfType("projection")), PositionAffinity.Predecessor); if (!caretPoint.HasValue) { return false; } m_session = m_provider.CompletionBroker.CreateCompletionSession (m_textView, caretPoint.Value.Snapshot.CreateTrackingPoint(caretPoint.Value.Position, PointTrackingMode.Positive), true); //subscribe to the Dismissed event on the session m_session.Dismissed += this.OnSessionDismissed; m_session.Start(); return true; }
Następnym przykładem jest metoda prywatna, która anuluje Dismissed subskrypcję zdarzenia:
Kompilowanie i testowanie kodu
Aby przetestować ten kod, skompiluj rozwiązanie CompletionTest i uruchom je w wystąpieniu eksperymentalnym.
Aby skompilować i przetestować rozwiązanie CompletionTest
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 zawierający wyraz "add".
Podczas wpisywania najpierw "a", a następnie "d", powinna pojawić się lista zawierająca "dodawanie" i "adaptacja". Zwróć uwagę, że zaznaczono dodanie. Po wpiseniu innego ciągu "d" lista powinna zawierać tylko wartość "add", która jest teraz zaznaczona. Możesz zatwierdzić "dodawanie", naciskając klawisz Spacja, Kartę lub Enter albo odrzucić listę, wpisując Klawisz Esc lub dowolny inny klawisz.