Przewodnik: wyświetlanie pomocy dotyczącej podpisu

Pomoc podpisu (nazywana również informacjami o parametrach) wyświetla podpis metody w etykietce narzędzia, gdy użytkownik wpisze znak początkowy listy parametrów (zazwyczaj nawias otwierający). Jako separator parametru i parametru (zazwyczaj przecinek) są wpisywane, etykietka narzędzia jest aktualizowana w celu wyświetlenia następnego parametru pogrubionego. Pomoc podpisu można zdefiniować na następujące sposoby: w kontekście usługi językowej zdefiniuj rozszerzenie nazwy pliku i typ zawartości oraz wyświetlić Pomoc podpisu dla tylko tego typu lub wyświetlić Pomoc podpisu dla istniejącego typu zawartości (na przykład "tekst"). W tym przewodniku pokazano, jak wyświetlić pomoc dotyczącą podpisu dla typu zawartości "tekst".

Pomoc podpisu jest zwykle wyzwalana przez wpisanie określonego znaku, na przykład "(" (nawias otwierający) i odrzucone przez wpisanie innego znaku, na przykład ")" (nawias zamykający). Funkcje intelliSense wyzwalane przez wpisanie znaku można zaimplementować za pomocą procedury obsługi poleceń dla naciśnięć klawiszy ( IOleCommandTarget interfejsu) i dostawcy obsługi implementujących IVsTextViewCreationListener interfejs. Aby utworzyć źródło pomocy sygnatury, czyli listę podpisów, które uczestniczą w Pomocy podpisu, zaimplementuj ISignatureHelpSource interfejs i dostawcę źródłowego, który uruchamia ISignatureHelpSourceProvider interfejs. Dostawcy są częściami składników Managed Extensibility Framework (MEF) i są odpowiedzialni za eksportowanie klas źródłowych i kontrolerów oraz importowanie usług i brokerów, na przykład ITextStructureNavigatorSelectorService, który umożliwia nawigowanie w buforze tekstowym i ISignatureHelpBroker, który wyzwala sesję Pomocy sygnatury.

W tym przewodniku pokazano, jak skonfigurować pomoc sygnatury dla ustalonego zestawu identyfikatorów. W pełnych implementacjach język jest odpowiedzialny za dostarczanie tej zawartości.

Tworzenie projektu MEF

Aby utworzyć projekt 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 SignatureHelpTestnazwę .

  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łania do projektu i upewnij się, że właściwość CopyLocal jest ustawiona na false:

    Microsoft.VisualStudio.Editor

    Microsoft.VisualStudio.Language.Intellisense

    Microsoft.VisualStudio.OLE.Interop

    Microsoft.VisualStudio.Shell.14.0

    Microsoft.VisualStudio.TextManager.Interop

Implementowanie podpisów i parametrów pomocy dotyczącej podpisu

Źródło pomocy sygnatury jest oparte na podpisach, które implementują ISignatureelement , z których każdy zawiera parametry implementujące IParameterelement . W pełnej implementacji te informacje będą uzyskiwane z dokumentacji języka, ale w tym przykładzie podpisy są zakodowane w kodzie.

Aby zaimplementować podpisy i parametry pomocy podpisów podpisów

  1. Dodaj plik klasy i nadaj mu SignatureHelpSourcenazwę .

  2. Dodaj następujące importy.

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel.Composition;
    using System.Runtime.InteropServices;
    using Microsoft.VisualStudio.Language.Intellisense;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Utilities;
    using Microsoft.VisualStudio.Editor;
    using Microsoft.VisualStudio.Text.Operations;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.TextManager.Interop;
    using Microsoft.VisualStudio.OLE.Interop;
    
  3. Dodaj klasę o nazwie TestParameter , która implementuje IParameterelement .

    internal class TestParameter : IParameter
    
  4. Dodaj konstruktor, który ustawia wszystkie właściwości.

    public TestParameter(string documentation, Span locus, string name, ISignature signature)
    {
        Documentation = documentation;
        Locus = locus;
        Name = name;
        Signature = signature;
    }
    
  5. Dodaj właściwości elementu IParameter.

    public string Documentation { get; private set; }
    public Span Locus { get; private set; }
    public string Name { get; private set; }
    public ISignature Signature { get; private set; }
    public Span PrettyPrintedLocus { get; private set; }
    
  6. Dodaj klasę o nazwie TestSignature , która implementuje ISignatureelement .

    internal class TestSignature : ISignature
    
  7. Dodaj niektóre pola prywatne.

    private ITextBuffer m_subjectBuffer;
    private IParameter m_currentParameter;
    private string m_content;
    private string m_documentation;
    private ITrackingSpan m_applicableToSpan;
    private ReadOnlyCollection<IParameter> m_parameters;
    private string m_printContent;
    
  8. Dodaj konstruktor, który ustawia pola i subskrybuje Changed zdarzenie.

    internal TestSignature(ITextBuffer subjectBuffer, string content, string doc, ReadOnlyCollection<IParameter> parameters)
    {
        m_subjectBuffer = subjectBuffer;
        m_content = content;
        m_documentation = doc;
        m_parameters = parameters;
        m_subjectBuffer.Changed += new EventHandler<TextContentChangedEventArgs>(OnSubjectBufferChanged);
    }
    
  9. Zadeklaruj CurrentParameterChanged zdarzenie. To zdarzenie jest zgłaszane, gdy użytkownik wypełni jeden z parametrów w podpisie.

    public event EventHandler<CurrentParameterChangedEventArgs> CurrentParameterChanged;
    
  10. Zaimplementuj CurrentParameter właściwość tak, aby zgłaszała CurrentParameterChanged zdarzenie po zmianie wartości właściwości.

    public IParameter CurrentParameter
    {
        get { return m_currentParameter; }
        internal set
        {
            if (m_currentParameter != value)
            {
                IParameter prevCurrentParameter = m_currentParameter;
                m_currentParameter = value;
                this.RaiseCurrentParameterChanged(prevCurrentParameter, m_currentParameter);
            }
        }
    }
    
  11. Dodaj metodę, która zgłasza CurrentParameterChanged zdarzenie.

    private void RaiseCurrentParameterChanged(IParameter prevCurrentParameter, IParameter newCurrentParameter)
    {
        EventHandler<CurrentParameterChangedEventArgs> tempHandler = this.CurrentParameterChanged;
        if (tempHandler != null)
        {
            tempHandler(this, new CurrentParameterChangedEventArgs(prevCurrentParameter, newCurrentParameter));
        }
    }
    
  12. Dodaj metodę, która oblicza bieżący parametr, porównując liczbę przecinków z ApplicableToSpan liczbą przecinków w podpisie.

    internal void ComputeCurrentParameter()
    {
        if (Parameters.Count == 0)
        {
            this.CurrentParameter = null;
            return;
        }
    
        //the number of commas in the string is the index of the current parameter
        string sigText = ApplicableToSpan.GetText(m_subjectBuffer.CurrentSnapshot);
    
        int currentIndex = 0;
        int commaCount = 0;
        while (currentIndex < sigText.Length)
        {
            int commaIndex = sigText.IndexOf(',', currentIndex);
            if (commaIndex == -1)
            {
                break;
            }
            commaCount++;
            currentIndex = commaIndex + 1;
        }
    
        if (commaCount < Parameters.Count)
        {
            this.CurrentParameter = Parameters[commaCount];
        }
        else
        {
            //too many commas, so use the last parameter as the current one.
            this.CurrentParameter = Parameters[Parameters.Count - 1];
        }
    }
    
  13. Dodaj procedurę obsługi zdarzeń dla Changed zdarzenia, które wywołuje metodę ComputeCurrentParameter() .

    internal void OnSubjectBufferChanged(object sender, TextContentChangedEventArgs e)
    {
        this.ComputeCurrentParameter();
    }
    
  14. Zaimplementuj ApplicableToSpan właściwość . Ta właściwość zawiera właściwość odpowiadającą ITrackingSpan zakresowi tekstu w buforze, do którego ma zastosowanie podpis.

    public ITrackingSpan ApplicableToSpan
    {
        get { return (m_applicableToSpan); }
        internal set { m_applicableToSpan = value; }
    }
    
  15. Zaimplementuj inne parametry.

    public string Content
    {
        get { return (m_content); }
        internal set { m_content = value; }
    }
    
    public string Documentation
    {
        get { return (m_documentation); }
        internal set { m_documentation = value; }
    }
    
    public ReadOnlyCollection<IParameter> Parameters
    {
        get { return (m_parameters); }
        internal set { m_parameters = value; }
    }
    
    public string PrettyPrintedContent
    {
        get { return (m_printContent); }
        internal set { m_printContent = value; }
    }
    

Implementowanie źródła pomocy dotyczącej podpisu

Źródło Pomocy podpisu to zestaw podpisów, dla których podajesz informacje.

Aby zaimplementować źródło pomocy sygnatury

  1. Dodaj klasę o nazwie TestSignatureHelpSource , która implementuje ISignatureHelpSourceelement .

    internal class TestSignatureHelpSource : ISignatureHelpSource
    
  2. Dodaj odwołanie do buforu tekstowego.

    private ITextBuffer m_textBuffer;
    
  3. Dodaj konstruktor, który ustawia bufor tekstowy i dostawcę źródła Pomocy sygnatury.

    public TestSignatureHelpSource(ITextBuffer textBuffer)
    {
        m_textBuffer = textBuffer;
    }
    
  4. Zaimplementuj metodę AugmentSignatureHelpSession . W tym przykładzie podpisy są zakodowane na twardo, ale w pełnej implementacji uzyskasz te informacje z dokumentacji językowej.

    public void AugmentSignatureHelpSession(ISignatureHelpSession session, IList<ISignature> signatures)
    {
        ITextSnapshot snapshot = m_textBuffer.CurrentSnapshot;
        int position = session.GetTriggerPoint(m_textBuffer).GetPosition(snapshot);
    
        ITrackingSpan applicableToSpan = m_textBuffer.CurrentSnapshot.CreateTrackingSpan(
         new Span(position, 0), SpanTrackingMode.EdgeInclusive, 0);
    
        signatures.Add(CreateSignature(m_textBuffer, "add(int firstInt, int secondInt)", "Documentation for adding integers.", applicableToSpan));
        signatures.Add(CreateSignature(m_textBuffer, "add(double firstDouble, double secondDouble)", "Documentation for adding doubles.", applicableToSpan));
    
    }
    
  5. Metoda CreateSignature() pomocnika jest udostępniana tylko na potrzeby ilustracji.

    private TestSignature CreateSignature(ITextBuffer textBuffer, string methodSig, string methodDoc, ITrackingSpan span)
    {
        TestSignature sig = new TestSignature(textBuffer, methodSig, methodDoc, null);
        textBuffer.Changed += new EventHandler<TextContentChangedEventArgs>(sig.OnSubjectBufferChanged);
    
        //find the parameters in the method signature (expect methodname(one, two)
        string[] pars = methodSig.Split(new char[] { '(', ',', ')' });
        List<IParameter> paramList = new List<IParameter>();
    
        int locusSearchStart = 0;
        for (int i = 1; i < pars.Length; i++)
        {
            string param = pars[i].Trim();
    
            if (string.IsNullOrEmpty(param))
                continue;
    
            //find where this parameter is located in the method signature
            int locusStart = methodSig.IndexOf(param, locusSearchStart);
            if (locusStart >= 0)
            {
                Span locus = new Span(locusStart, param.Length);
                locusSearchStart = locusStart + param.Length;
                paramList.Add(new TestParameter("Documentation for the parameter.", locus, param, sig));
            }
        }
    
        sig.Parameters = new ReadOnlyCollection<IParameter>(paramList);
        sig.ApplicableToSpan = span;
        sig.ComputeCurrentParameter();
        return sig;
    }
    
  6. Zaimplementuj metodę GetBestMatch . W tym przykładzie istnieją tylko dwa podpisy, z których każdy ma dwa parametry. W związku z tym ta metoda nie jest wymagana. W pełnijszej implementacji, w której jest dostępne więcej niż jedno źródło pomocy sygnatury, ta metoda służy do decydowania, czy źródło pomocy sygnatury o najwyższym priorycie może dostarczyć pasujący podpis. Jeśli nie, metoda zwraca wartość null, a źródło o następnym najwyższym priorytetzie zostanie poproszone o podanie dopasowania.

    public ISignature GetBestMatch(ISignatureHelpSession session)
    {
        if (session.Signatures.Count > 0)
        {
            ITrackingSpan applicableToSpan = session.Signatures[0].ApplicableToSpan;
            string text = applicableToSpan.GetText(applicableToSpan.TextBuffer.CurrentSnapshot);
    
            if (text.Trim().Equals("add"))  //get only "add" 
                return session.Signatures[0];
        }
        return null;
    }
    
  7. Zaimplementuj metodę Dispose() :

    private bool m_isDisposed;
    public void Dispose()
    {
        if (!m_isDisposed)
        {
            GC.SuppressFinalize(this);
            m_isDisposed = true;
        }
    }
    

Implementowanie dostawcy źródła pomocy sygnatury

Dostawca źródła pomocy sygnatury jest odpowiedzialny za eksportowanie części składnika Managed Extensibility Framework (MEF) i tworzenia wystąpienia źródła pomocy sygnatury.

Aby zaimplementować dostawcę źródła pomocy sygnatury

  1. Dodaj klasę o nazwie TestSignatureHelpSourceProvider , która implementuje ISignatureHelpSourceProviderelement , i eksportuj go za pomocą NameAttributeelementu , z wartością ContentTypeAttribute "text" i wartością OrderAttribute Before="default".

    [Export(typeof(ISignatureHelpSourceProvider))]
    [Name("Signature Help source")]
    [Order(Before = "default")]
    [ContentType("text")]
    internal class TestSignatureHelpSourceProvider : ISignatureHelpSourceProvider
    
  2. Zaimplementuj TryCreateSignatureHelpSource , tworząc wystąpienie obiektu TestSignatureHelpSource.

    public ISignatureHelpSource TryCreateSignatureHelpSource(ITextBuffer textBuffer)
    {
        return new TestSignatureHelpSource(textBuffer);
    }
    

Implementowanie programu obsługi poleceń

Pomoc podpisu jest zwykle wyzwalana przez nawias otwierający "(" znak i odrzucany przez nawias zamykający ")". Możesz obsłużyć te naciśnięcia klawiszy, uruchamiając IOleCommandTarget element , aby wyzwolić sesję Pomocy sygnatury, gdy otrzymuje znak nawiasu otwierającego poprzedzony znaną nazwą metody, i odrzuca sesję po otrzymaniu znaku nawiasu zamykającego.

Aby zaimplementować procedurę obsługi poleceń

  1. Dodaj klasę o nazwie TestSignatureHelpCommand , która implementuje IOleCommandTargetelement .

    internal sealed class TestSignatureHelpCommandHandler : IOleCommandTarget
    
  2. Dodaj pola prywatne dla IVsTextView karty (która umożliwia dodanie programu obsługi poleceń do łańcucha procedur obsługi poleceń), widoku tekstowego, brokera i sesji Pomocy sygnatury, a ITextStructureNavigatori następnego IOleCommandTarget.

    IOleCommandTarget m_nextCommandHandler;
    ITextView m_textView;
    ISignatureHelpBroker m_broker;
    ISignatureHelpSession m_session;
    ITextStructureNavigator m_navigator;
    
  3. Dodaj konstruktor, aby zainicjować te pola i dodać filtr poleceń do łańcucha filtrów poleceń.

    internal TestSignatureHelpCommandHandler(IVsTextView textViewAdapter, ITextView textView, ITextStructureNavigator nav, ISignatureHelpBroker broker)
    {
        this.m_textView = textView;
        this.m_broker = broker;
        this.m_navigator = nav;
    
        //add this to the filter chain
        textViewAdapter.AddCommandFilter(this, out m_nextCommandHandler);
    }
    
  4. Zaimplementuj metodę Exec , aby wyzwolić sesję Pomocy sygnatury, gdy filtr polecenia odbiera nawias otwierający "(" znak po jednej ze znanych nazw metod, i odrzucić sesję, gdy otrzymuje nawias zamykający ")" znak, podczas gdy sesja jest nadal aktywna. W każdym przypadku polecenie jest przekazywane.

    public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
    {
        char typedChar = char.MinValue;
    
        if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR)
        {
            typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn);
            if (typedChar.Equals('('))
            {
                //move the point back so it's in the preceding word
                SnapshotPoint point = m_textView.Caret.Position.BufferPosition - 1;
                TextExtent extent = m_navigator.GetExtentOfWord(point);
                string word = extent.Span.GetText();
                if (word.Equals("add"))
                    m_session = m_broker.TriggerSignatureHelp(m_textView);
    
            }
            else if (typedChar.Equals(')') && m_session != null)
            {
                m_session.Dismiss();
                m_session = null;
            }
        }
        return m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
    }
    
  5. Zaimplementuj metodę QueryStatus , aby zawsze przekazywać dalej polecenie.

    public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
    {
        return m_nextCommandHandler.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
    }
    

Implementowanie dostawcy poleceń Pomocy sygnatury

Możesz podać polecenie Pomoc podpisu, implementując polecenie IVsTextViewCreationListener , aby utworzyć wystąpienie programu obsługi poleceń po utworzeniu widoku tekstowego.

Aby zaimplementować dostawcę poleceń Pomocy sygnatury

  1. Dodaj klasę o nazwie TestSignatureHelpController , która implementuje i eksportuje IVsTextViewCreationListener ją za NameAttributepomocą elementów , ContentTypeAttributei TextViewRoleAttribute.

    [Export(typeof(IVsTextViewCreationListener))]
    [Name("Signature Help controller")]
    [TextViewRole(PredefinedTextViewRoles.Editable)]
    [ContentType("text")]
    internal class TestSignatureHelpCommandProvider : IVsTextViewCreationListener
    
  2. Zaimportuj IVsEditorAdaptersFactoryService element (używany do pobrania ITextViewobiektu ), IVsTextView ( ITextStructureNavigatorSelectorService używany do znalezienia bieżącego wyrazu) i ISignatureHelpBroker (w celu wyzwolenia sesji Pomocy podpisu).

    [Import]
    internal IVsEditorAdaptersFactoryService AdapterService;
    
    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
    [Import]
    internal ISignatureHelpBroker SignatureHelpBroker;
    
  3. Zaimplementuj metodę VsTextViewCreated , tworząc TestSignatureCommandHandlerwystąpienie klasy .

    public void VsTextViewCreated(IVsTextView textViewAdapter)
    {
        ITextView textView = AdapterService.GetWpfTextView(textViewAdapter);
        if (textView == null)
            return;
    
        textView.Properties.GetOrCreateSingletonProperty(
             () => new TestSignatureHelpCommandHandler(textViewAdapter,
                textView,
                NavigatorService.GetTextStructureNavigator(textView.TextBuffer),
                SignatureHelpBroker));
    }
    

Kompilowanie i testowanie kodu

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

Aby skompilować i przetestować rozwiązanie SignatureHelpTest

  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 zawierający wyraz "add" oraz nawias otwierający.

  4. Po wpisaniu nawiasu otwierającego powinna zostać wyświetlona etykietka narzędzia zawierająca listę dwóch podpisów dla add() metody .