Exemplarische Vorgehensweise: Hilfe zum Anzeigen von Signaturen

Die Signaturhilfe (auch als Parameterinformationen bezeichnet) zeigt die Signatur einer Methode in einer QuickInfo an, wenn ein Benutzer das Startzeichen der Parameterliste eingibt (in der Regel eine öffnende Klammer). Als Parameter- und Parametertrennzeichen (in der Regel ein Komma) wird die QuickInfo aktualisiert, um den nächsten Parameter fett anzuzeigen. Sie können die Signaturhilfe auf folgende Weise definieren: Definieren Sie im Kontext eines Sprachdiensts Ihre eigene Dateinamenerweiterung und den Inhaltstyp, und zeigen Sie die Signaturhilfe nur für diesen Typ an, oder zeigen Sie die Signaturhilfe für einen vorhandenen Inhaltstyp an (z. B. "Text"). In dieser exemplarischen Vorgehensweise wird gezeigt, wie die Signaturhilfe für den Inhaltstyp "Text" angezeigt wird.

Die Signaturhilfe wird in der Regel durch Eingabe eines bestimmten Zeichens ausgelöst, z. B. "(" (öffnende Klammer) und durch Eingabe eines anderen Zeichens geschlossen, z. B. "" (schließende Klammer). IntelliSense-Features, die durch Eingabe eines Zeichens ausgelöst werden, können mithilfe eines Befehlshandlers für die Tastaturanschläge (die IOleCommandTarget Schnittstelle) und einen Handleranbieter implementiert werden, der die IVsTextViewCreationListener Schnittstelle implementiert. Um die Signaturhilfequelle zu erstellen, bei der es sich um die Liste der Signaturen handelt, die an der Signaturhilfe teilnehmen, implementieren Sie die ISignatureHelpSource Schnittstelle und einen Quellanbieter, der die ISignatureHelpSourceProvider Schnittstelle ausführt. Die Anbieter sind Komponententeile des Managed Extensibility Framework (MEF) und sind für den Export der Quell- und Controllerklassen sowie für den Import von Diensten und Brokern verantwortlich, z. B. das ITextStructureNavigatorSelectorService, mit dem Sie im Textpuffer navigieren können, und die ISignatureHelpBroker, wodurch die Signaturhilfesitzung ausgelöst wird.

In dieser exemplarischen Vorgehensweise wird gezeigt, wie Sie die Signaturhilfe für einen hartcodierten Satz von Bezeichnern einrichten. In vollständigen Implementierungen ist die Sprache für die Bereitstellung dieser Inhalte verantwortlich.

Erstellen eines MEF-Projekts

So erstellen Sie ein MEF-Projekt

  1. Erstellen Sie ein C#VSIX-Projekt. (Im Dialogfeld "Neues Projekt ", wählen Sie Visual C# / Erweiterbarkeit und dann VSIX-Projekt aus.) Benennen Sie die Lösung SignatureHelpTest.

  2. Fügen Sie dem Projekt eine Elementvorlage für Editorklassifizierer hinzu. Weitere Informationen finden Sie unter Erstellen einer Erweiterung mit einer Editorelementvorlage.

  3. Löschen Sie die vorhandenen Klassendateien.

  4. Fügen Sie dem Projekt die folgenden Verweise hinzu, und stellen Sie sicher, dass CopyLocal auf :false

    Microsoft.VisualStudio.Editor

    Microsoft.VisualStudio.Language.Intellisense

    Microsoft.VisualStudio.OLE.Interop

    Microsoft.VisualStudio.Shell.14.0

    Microsoft.VisualStudio.TextManager.Interop

Implementieren von Signaturhilfesignaturen und -parametern

Die Signaturhilfequelle basiert auf Signaturen, die implementieren ISignature, die jeweils Parameter enthalten, die implementiert werden IParameter. In einer vollständigen Implementierung würden diese Informationen aus der Sprachdokumentation abgerufen, aber in diesem Beispiel sind die Signaturen hartcodiert.

So implementieren Sie die Signaturhilfesignaturen und -parameter

  1. Fügen Sie eine Klassendatei hinzu, und nennen Sie sie SignatureHelpSource.

  2. Fügen Sie die folgenden Importe hinzu.

    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. Fügen Sie eine Klasse mit dem Namen TestParameter hinzu, die implementiert IParameterwird.

    internal class TestParameter : IParameter
    
  4. Fügen Sie einen Konstruktor hinzu, der alle Eigenschaften festlegt.

    public TestParameter(string documentation, Span locus, string name, ISignature signature)
    {
        Documentation = documentation;
        Locus = locus;
        Name = name;
        Signature = signature;
    }
    
  5. Fügen Sie die Eigenschaften von 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. Fügen Sie eine Klasse mit dem Namen TestSignature hinzu, die implementiert ISignaturewird.

    internal class TestSignature : ISignature
    
  7. Fügen Sie einige private Felder hinzu.

    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. Fügen Sie einen Konstruktor hinzu, der die Felder festlegt und das Changed Ereignis abonniert.

    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. Deklarieren eines Ereignisses CurrentParameterChanged . Dieses Ereignis wird ausgelöst, wenn der Benutzer einen der Parameter in der Signatur eingibt.

    public event EventHandler<CurrentParameterChangedEventArgs> CurrentParameterChanged;
    
  10. Implementieren Sie die CurrentParameter Eigenschaft so, dass das CurrentParameterChanged Ereignis ausgelöst wird, wenn der Eigenschaftswert geändert wird.

    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. Fügen Sie eine Methode hinzu, die das CurrentParameterChanged Ereignis auslöst.

    private void RaiseCurrentParameterChanged(IParameter prevCurrentParameter, IParameter newCurrentParameter)
    {
        EventHandler<CurrentParameterChangedEventArgs> tempHandler = this.CurrentParameterChanged;
        if (tempHandler != null)
        {
            tempHandler(this, new CurrentParameterChangedEventArgs(prevCurrentParameter, newCurrentParameter));
        }
    }
    
  12. Fügen Sie eine Methode hinzu, die den aktuellen Parameter berechnet, indem Sie die Anzahl der Kommas in der ApplicableToSpan Signatur mit der Anzahl von Kommas in der Signatur vergleichen.

    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. Fügen Sie einen Ereignishandler für das Changed Ereignis hinzu, das die ComputeCurrentParameter() Methode aufruft.

    internal void OnSubjectBufferChanged(object sender, TextContentChangedEventArgs e)
    {
        this.ComputeCurrentParameter();
    }
    
  14. Implementiert die ApplicableToSpan-Eigenschaft. Diese Eigenschaft enthält einen ITrackingSpan Wert, der der Textspanne im Puffer entspricht, auf die die Signatur angewendet wird.

    public ITrackingSpan ApplicableToSpan
    {
        get { return (m_applicableToSpan); }
        internal set { m_applicableToSpan = value; }
    }
    
  15. Implementieren Sie die anderen Parameter.

    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; }
    }
    

Implementieren der Signaturhilfequelle

Die Signaturhilfequelle ist der Satz von Signaturen, für die Sie Informationen bereitstellen.

So implementieren Sie die Signaturhilfequelle

  1. Fügen Sie eine Klasse mit dem Namen TestSignatureHelpSource hinzu, die implementiert ISignatureHelpSourcewird.

    internal class TestSignatureHelpSource : ISignatureHelpSource
    
  2. Fügen Sie einen Verweis auf den Textpuffer hinzu.

    private ITextBuffer m_textBuffer;
    
  3. Fügen Sie einen Konstruktor hinzu, der den Textpuffer und den Signaturhilfequellenanbieter festlegt.

    public TestSignatureHelpSource(ITextBuffer textBuffer)
    {
        m_textBuffer = textBuffer;
    }
    
  4. Implementieren Sie die AugmentSignatureHelpSession-Methode. In diesem Beispiel sind die Signaturen hartcodiert, aber in einer vollständigen Implementierung erhalten Sie diese Informationen aus der Sprachdokumentation.

    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. Die Hilfsmethode CreateSignature() wird nur zur Veranschaulichung bereitgestellt.

    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. Implementieren Sie die GetBestMatch-Methode. In diesem Beispiel gibt es nur zwei Signaturen, die jeweils zwei Parameter aufweisen. Daher ist diese Methode nicht erforderlich. In einer vollständigeren Implementierung, in der mehr als eine Signaturhilfequelle verfügbar ist, wird diese Methode verwendet, um zu entscheiden, ob die Signaturhilfequelle mit der höchsten Priorität eine übereinstimmende Signatur bereitstellen kann. Wenn dies nicht möglich ist, gibt die Methode NULL zurück, und die Quelle mit der nächsten Priorität wird aufgefordert, eine Übereinstimmung zu liefern.

    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. Implementieren Sie die Dispose() Methode:

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

Implementieren des Signaturhilfequellenanbieters

Der Signaturhilfe-Quellanbieter ist für den Export des MEF-Komponententeils (Managed Extensibility Framework) und für die Instanziierung der Signaturhilfequelle verantwortlich.

So implementieren Sie den Signaturhilfequellenanbieter

  1. Fügen Sie eine Klasse mit dem Namen TestSignatureHelpSourceProvider hinzu, die sie implementiertISignatureHelpSourceProvider, und exportieren Sie sie mit einem NameAttributeContentTypeAttribute "Text" und einer OrderAttribute von "Before="default".

    [Export(typeof(ISignatureHelpSourceProvider))]
    [Name("Signature Help source")]
    [Order(Before = "default")]
    [ContentType("text")]
    internal class TestSignatureHelpSourceProvider : ISignatureHelpSourceProvider
    
  2. Implementieren Durch TryCreateSignatureHelpSource Instanziieren der TestSignatureHelpSource.

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

Implementieren des Befehlshandlers

Die Signaturhilfe wird in der Regel durch eine öffnende Klammer "(" Zeichen ausgelöst und durch eine schließende Klammer ")" geschlossen. Sie können diese Tastenanschläge behandeln, indem Sie eine IOleCommandTarget Signaturhilfesitzung ausführen, wenn sie ein öffnender Klammerzeichen empfängt, das einem bekannten Methodennamen vorausgeht, und die Sitzung schließt, wenn sie ein schließende Klammerzeichen empfängt.

So implementieren Sie den Befehlshandler

  1. Fügen Sie eine Klasse mit dem Namen TestSignatureHelpCommand hinzu, die implementiert IOleCommandTargetwird.

    internal sealed class TestSignatureHelpCommandHandler : IOleCommandTarget
    
  2. Fügen Sie private Felder für den IVsTextView Adapter hinzu (mit denen Sie den Befehlshandler zur Kette von Befehlshandlern hinzufügen können), die Textansicht, den Signaturhilfebroker und die Sitzung, a ITextStructureNavigatorund die nächste IOleCommandTarget.

    IOleCommandTarget m_nextCommandHandler;
    ITextView m_textView;
    ISignatureHelpBroker m_broker;
    ISignatureHelpSession m_session;
    ITextStructureNavigator m_navigator;
    
  3. Fügen Sie einen Konstruktor hinzu, um diese Felder zu initialisieren und den Befehlsfilter zur Kette von Befehlsfiltern hinzuzufügen.

    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. Implementieren Sie die Exec Methode, um die Signaturhilfesitzung auszulösen, wenn der Befehlsfilter ein öffnende Klammer "(" Zeichen nach einem der bekannten Methodennamen empfängt, und um die Sitzung zu schließen, wenn sie ein schließende Klammer ")" erhält, während die Sitzung noch aktiv ist. In jedem Fall wird der Befehl weitergeleitet.

    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. Implementieren Sie die QueryStatus Methode, damit sie immer den Befehl weiterleitet.

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

Implementieren des Signaturhilfebefehlsanbieters

Sie können den Befehl "Signaturhilfe" bereitstellen, indem Sie den IVsTextViewCreationListener Befehlshandler implementieren, um den Befehlshandler zu instanziieren, wenn die Textansicht erstellt wird.

So implementieren Sie den Signaturhilfebefehlsanbieter

  1. Fügen Sie eine Klasse mit dem Namen TestSignatureHelpController hinzu, die sie mit IVsTextViewCreationListener dem NameAttribute, ContentTypeAttribute, und TextViewRoleAttribute.

    [Export(typeof(IVsTextViewCreationListener))]
    [Name("Signature Help controller")]
    [TextViewRole(PredefinedTextViewRoles.Editable)]
    [ContentType("text")]
    internal class TestSignatureHelpCommandProvider : IVsTextViewCreationListener
    
  2. Importieren Sie die IVsEditorAdaptersFactoryService (verwendet, um das ITextViewObjekt abzurufen), IVsTextView das ITextStructureNavigatorSelectorService (zum Suchen des aktuellen Worts) und das ISignatureHelpBroker (zum Auslösen der Signaturhilfesitzung) das aktuelle Wort.

    [Import]
    internal IVsEditorAdaptersFactoryService AdapterService;
    
    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
    [Import]
    internal ISignatureHelpBroker SignatureHelpBroker;
    
  3. Implementieren Sie die VsTextViewCreated Methode durch Instanziieren der TestSignatureCommandHandler.

    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));
    }
    

Erstellen und Testen des Codes

Um diesen Code zu testen, erstellen Sie die SignatureHelpTest-Lösung, und führen Sie sie in der experimentellen Instanz aus.

So erstellen und testen Sie die SignatureHelpTest-Lösung

  1. Erstellen Sie die Projektmappe.

  2. Wenn Sie dieses Projekt im Debugger ausführen, wird eine zweite Instanz von Visual Studio gestartet.

  3. Erstellen Sie eine Textdatei, und geben Sie Text ein, der das Wort "hinzufügen" sowie eine öffnende Klammer enthält.

  4. Nachdem Sie die öffnende Klammer eingegeben haben, sollte eine QuickInfo angezeigt werden, die eine Liste der beiden Signaturen für die add() Methode anzeigt.