Procédure pas à pas : Afficher l’aide sur la signature

L’aide sur la signature (également appelée Informations sur les paramètres) affiche la signature d’une méthode dans une info-bulle lorsqu’un utilisateur tape le caractère de début de la liste de paramètres (généralement une parenthèse ouvrante). En tant que paramètre et séparateur de paramètres (généralement une virgule) sont tapés, l’info-bulle est mise à jour pour afficher le paramètre suivant en gras. Vous pouvez définir l’aide de signature de la manière suivante : dans le contexte d’un service de langage, définissez votre propre extension de nom de fichier et votre propre type de contenu et affichez l’aide de signature pour ce type, ou affichez l’aide de signature pour un type de contenu existant (par exemple, « text »). Cette procédure pas à pas montre comment afficher l’aide de signature pour le type de contenu « text ».

L’aide de signature est généralement déclenchée en tapant un caractère spécifique, par exemple « (« (« (parenthèse ouvrante) et ignorée en tapant un autre caractère, par exemple , ») (parenthèse fermante). Les fonctionnalités IntelliSense déclenchées en tapant un caractère peuvent être implémentées à l’aide d’un gestionnaire de commandes pour les séquences de touches (l’interface IOleCommandTarget ) et d’un fournisseur de gestionnaires qui implémente l’interface IVsTextViewCreationListener . Pour créer la source d’aide de signature, qui est la liste des signatures qui participent à l’aide de signature, implémentez l’interface ISignatureHelpSource et un fournisseur source qui exécute l’interface ISignatureHelpSourceProvider . Les fournisseurs sont des composants MEF (Managed Extensibility Framework) et sont responsables de l’exportation des classes source et du contrôleur et de l’importation de services et de répartiteurs, par exemple, le ITextStructureNavigatorSelectorService, qui vous permet de naviguer dans la mémoire tampon de texte et le ISignatureHelpBroker, qui déclenche la session d’aide de signature.

Cette procédure pas à pas montre comment configurer l’aide de signature pour un ensemble codé en dur d’identificateurs. Dans les implémentations complètes, la langue est chargée de fournir ce contenu.

Création d’un projet MEF

Pour créer un projet MEF

  1. Créez un projet VSIX C#. (Dans le Boîte de dialogue Nouveau projet , sélectionnez Visual C# / Extensibilité, puis VSIX Project.) Nommez la solution SignatureHelpTest.

  2. Ajoutez un modèle d’élément Classifieur d’éditeur au projet. Pour plus d’informations, consultez Créer une extension avec un modèle d’élément d’éditeur.

  3. Supprimez les fichiers de classe existants.

  4. Ajoutez les références suivantes au projet et vérifiez que CopyLocal est défini sur false:

    Microsoft.VisualStudio.Editor

    Microsoft.VisualStudio.Language.Intellisense

    Microsoft.VisualStudio.OLE.Interop

    Microsoft.VisualStudio.Shell.14.0

    Microsoft.VisualStudio.TextManager.Interop

Implémenter des signatures et des paramètres d’aide sur la signature

La source d’aide de signature est basée sur les signatures qui implémentent ISignaturechacune des paramètres qui implémentent IParameter. Dans une implémentation complète, ces informations sont obtenues à partir de la documentation linguistique, mais dans cet exemple, les signatures sont codées en dur.

Pour implémenter les signatures et paramètres d’aide de signature

  1. Ajoutez un fichier de classe et nommez-le SignatureHelpSource.

  2. Ajoutez les importations suivantes.

    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. Ajoutez une classe nommée TestParameter qui implémente IParameter.

    internal class TestParameter : IParameter
    
  4. Ajoutez un constructeur qui définit toutes les propriétés.

    public TestParameter(string documentation, Span locus, string name, ISignature signature)
    {
        Documentation = documentation;
        Locus = locus;
        Name = name;
        Signature = signature;
    }
    
  5. Ajoutez les propriétés de 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. Ajoutez une classe nommée TestSignature qui implémente ISignature.

    internal class TestSignature : ISignature
    
  7. Ajoutez des champs privés.

    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. Ajoutez un constructeur qui définit les champs et s’abonne à l’événement Changed .

    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. Déclarez un CurrentParameterChanged événement. Cet événement est déclenché lorsque l’utilisateur remplit l’un des paramètres de la signature.

    public event EventHandler<CurrentParameterChangedEventArgs> CurrentParameterChanged;
    
  10. Implémentez la CurrentParameter propriété afin qu’elle déclenche l’événement CurrentParameterChanged lorsque la valeur de la propriété est modifiée.

    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. Ajoutez une méthode qui déclenche l’événement CurrentParameterChanged .

    private void RaiseCurrentParameterChanged(IParameter prevCurrentParameter, IParameter newCurrentParameter)
    {
        EventHandler<CurrentParameterChangedEventArgs> tempHandler = this.CurrentParameterChanged;
        if (tempHandler != null)
        {
            tempHandler(this, new CurrentParameterChangedEventArgs(prevCurrentParameter, newCurrentParameter));
        }
    }
    
  12. Ajoutez une méthode qui calcule le paramètre actuel en comparant le nombre de virgules dans la ApplicableToSpan signature au nombre de virgules dans la signature.

    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. Ajoutez un gestionnaire d’événements pour l’événement Changed qui appelle la ComputeCurrentParameter() méthode.

    internal void OnSubjectBufferChanged(object sender, TextContentChangedEventArgs e)
    {
        this.ComputeCurrentParameter();
    }
    
  14. Implémentez la propriété ApplicableToSpan. Cette propriété contient une ITrackingSpan valeur qui correspond à l’étendue du texte dans la mémoire tampon à laquelle la signature s’applique.

    public ITrackingSpan ApplicableToSpan
    {
        get { return (m_applicableToSpan); }
        internal set { m_applicableToSpan = value; }
    }
    
  15. Implémentez les autres paramètres.

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

Implémenter la source d’aide sur la signature

La source d’aide sur la signature est l’ensemble de signatures pour lesquelles vous fournissez des informations.

Pour implémenter la source d’aide sur la signature

  1. Ajoutez une classe nommée TestSignatureHelpSource qui implémente ISignatureHelpSource.

    internal class TestSignatureHelpSource : ISignatureHelpSource
    
  2. Ajoutez une référence à la mémoire tampon de texte.

    private ITextBuffer m_textBuffer;
    
  3. Ajoutez un constructeur qui définit la mémoire tampon de texte et le fournisseur source d’aide de signature.

    public TestSignatureHelpSource(ITextBuffer textBuffer)
    {
        m_textBuffer = textBuffer;
    }
    
  4. Implémentez la méthode AugmentSignatureHelpSession. Dans cet exemple, les signatures sont codées en dur, mais dans une implémentation complète, vous obtenez ces informations à partir de la documentation linguistique.

    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. La méthode CreateSignature() d’assistance est fournie uniquement pour l’illustration.

    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. Implémentez la méthode GetBestMatch. Dans cet exemple, il n’existe que deux signatures, chacune ayant deux paramètres. Par conséquent, cette méthode n’est pas requise. Dans une implémentation plus complète, dans laquelle plusieurs sources d’aide sur la signature sont disponibles, cette méthode est utilisée pour déterminer si la source d’aide de signature la plus prioritaire peut fournir une signature correspondante. Si ce n’est pas le cas, la méthode retourne null et la source de priorité la plus élevée suivante est demandée pour fournir une correspondance.

    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. Implémentez la Dispose() méthode :

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

Implémenter le fournisseur de source d’aide sur la signature

Le fournisseur de source d’aide à la signature est responsable de l’exportation du composant MEF (Managed Extensibility Framework) et de l’instanciation de la source d’aide de signature.

Pour implémenter le fournisseur source d’aide sur la signature

  1. Ajoutez une classe nommée TestSignatureHelpSourceProvider qui implémente ISignatureHelpSourceProvider, puis exportez-la avec un NameAttribute, un ContentTypeAttribute texte et un OrderAttribute before="default ».

    [Export(typeof(ISignatureHelpSourceProvider))]
    [Name("Signature Help source")]
    [Order(Before = "default")]
    [ContentType("text")]
    internal class TestSignatureHelpSourceProvider : ISignatureHelpSourceProvider
    
  2. Implémentez en TryCreateSignatureHelpSource instanciant le TestSignatureHelpSource.

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

Implémenter le gestionnaire de commandes

L’aide de signature est généralement déclenchée par une parenthèse ouvrante « (« caractère » et ignorée par un caractère fermant ) ». Vous pouvez gérer ces séquences de touches en exécutant une IOleCommandTarget session d’aide de signature pour qu’elle déclenche une session d’aide de signature lorsqu’elle reçoit un caractère de parenthèse ouvrant précédée d’un nom de méthode connu et ignore la session lorsqu’elle reçoit un caractère de parenthèse fermante.

Pour implémenter le gestionnaire de commandes

  1. Ajoutez une classe nommée TestSignatureHelpCommand qui implémente IOleCommandTarget.

    internal sealed class TestSignatureHelpCommandHandler : IOleCommandTarget
    
  2. Ajoutez des champs privés pour l’adaptateur IVsTextView (ce qui vous permet d’ajouter le gestionnaire de commandes à la chaîne de gestionnaires de commandes), l’affichage texte, le répartiteur d’aide de signature et la session, a ITextStructureNavigator, et le suivant IOleCommandTarget.

    IOleCommandTarget m_nextCommandHandler;
    ITextView m_textView;
    ISignatureHelpBroker m_broker;
    ISignatureHelpSession m_session;
    ITextStructureNavigator m_navigator;
    
  3. Ajoutez un constructeur pour initialiser ces champs et ajouter le filtre de commandes à la chaîne de filtres de commandes.

    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. Implémentez la Exec méthode pour déclencher la session d’aide de signature lorsque le filtre de commande reçoit un caractère « (« » après l’un des noms de méthode connus, et pour ignorer la session lorsqu’elle reçoit un caractère de parenthèse fermante « ) pendant que la session est toujours active. Dans tous les cas, la commande est transférée.

    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. Implémentez la QueryStatus méthode afin qu’elle transfère toujours la commande.

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

Implémenter le fournisseur de commandes d’aide sur la signature

Vous pouvez fournir la commande d’aide de signature en implémentant le IVsTextViewCreationListener gestionnaire de commandes lors de la création de la vue de texte.

Pour implémenter le fournisseur de commandes d’aide sur la signature

  1. Ajoutez une classe nommée TestSignatureHelpController qui implémente et l’exporte IVsTextViewCreationListener avec le NameAttribute, ContentTypeAttributeet TextViewRoleAttribute.

    [Export(typeof(IVsTextViewCreationListener))]
    [Name("Signature Help controller")]
    [TextViewRole(PredefinedTextViewRoles.Editable)]
    [ContentType("text")]
    internal class TestSignatureHelpCommandProvider : IVsTextViewCreationListener
    
  2. Importez le IVsEditorAdaptersFactoryService (utilisé pour obtenir l’objet ITextView, donné l’objet IVsTextView ), le ITextStructureNavigatorSelectorService (utilisé pour rechercher le mot actuel) et le ISignatureHelpBroker (pour déclencher la session d’aide de signature).

    [Import]
    internal IVsEditorAdaptersFactoryService AdapterService;
    
    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
    [Import]
    internal ISignatureHelpBroker SignatureHelpBroker;
    
  3. Implémentez la VsTextViewCreated méthode en instanciant le 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));
    }
    

Générer et tester le code

Pour tester ce code, générez la solution SignatureHelpTest et exécutez-la dans l’instance expérimentale.

Pour générer et tester la solution SignatureHelpTest

  1. Générez la solution.

  2. Lorsque vous exécutez ce projet dans le débogueur, une deuxième instance de Visual Studio est démarrée.

  3. Créez un fichier texte et tapez du texte incluant le mot « ajouter » ainsi qu’une parenthèse ouvrante.

  4. Une fois que vous avez tapé la parenthèse ouvrante, vous devez voir une info-bulle qui affiche une liste des deux signatures de la add() méthode.