Condividi tramite


Procedura dettagliata: Visualizzare la Guida alla firma

La Guida alla firma (nota anche come Informazioni parametro) visualizza la firma di un metodo in una descrizione comando quando un utente digita il carattere iniziale dell'elenco di parametri (in genere una parentesi di apertura). Come parametro e separatore di parametri (in genere una virgola) vengono digitati, la descrizione comando viene aggiornata per visualizzare il parametro successivo in grassetto. È possibile definire la Guida per le firme nei modi seguenti: nel contesto di un servizio linguistico, definire l'estensione del nome file e il tipo di contenuto e visualizzare la Guida per la firma solo per quel tipo o visualizzare la Guida della firma per un tipo di contenuto esistente ,ad esempio "text". Questa procedura dettagliata illustra come visualizzare la Guida della firma per il tipo di contenuto "text".

La Guida della firma viene in genere attivata digitando un carattere specifico, ad esempio "(" (parentesi di apertura) e ignorato digitando un altro carattere, ad esempio ")" (parentesi di chiusura). Le funzionalità di IntelliSense attivate digitando un carattere possono essere implementate usando un gestore comandi per le sequenze di tasti (l'interfaccia IOleCommandTarget ) e un provider di gestori che implementa l'interfaccia IVsTextViewCreationListener . Per creare l'origine della Guida della firma, ovvero l'elenco delle firme che partecipano alla Guida della firma, implementare l'interfaccia ISignatureHelpSource e un provider di origine che esegue l'interfaccia ISignatureHelpSourceProvider . I provider sono parti del componente MEF (Managed Extensibility Framework) e sono responsabili dell'esportazione delle classi di origine e controller e dell'importazione di servizi e broker, ad esempio , ITextStructureNavigatorSelectorServiceche consente di spostarsi nel buffer di testo e , ISignatureHelpBrokerche attiva la sessione della Guida della Firma.

Questa procedura dettagliata illustra come configurare la Guida della firma per un set di identificatori hardcoded. Nelle implementazioni complete, il linguaggio è responsabile della fornitura di tale contenuto.

Creazione di un progetto MEF

Per creare un progetto MEF

  1. Creare un progetto VSIX C#. (In Finestra di dialogo Nuovo progetto , selezionare Visual C# / Estendibilità e quindi progetto VSIX. Assegnare alla soluzione SignatureHelpTestil nome .

  2. Aggiungere un modello di elemento classificatore dell'editor al progetto. Per altre informazioni, vedere Creare un'estensione con un modello di elemento dell'editor.

  3. Eliminare i file di classe esistenti.

  4. Aggiungere i riferimenti seguenti al progetto e assicurarsi che CopyLocal sia impostato su false:

    Microsoft.VisualStudio.Editor

    Microsoft.VisualStudio.Language.Intellisense

    Microsoft.VisualStudio.OLE.Interop

    Microsoft.VisualStudio.Shell.14.0

    Microsoft.VisualStudio.TextManager.Interop

Implementare firme e parametri della Guida per la firma

L'origine della Guida della firma si basa sulle firme che implementano ISignature, ognuna delle quali contiene parametri che implementano IParameter. In un'implementazione completa queste informazioni vengono ottenute dalla documentazione del linguaggio, ma in questo esempio le firme sono hardcoded.

Per implementare le firme e i parametri della Guida per la firma

  1. Aggiungere un file di classe e assegnargli il nome SignatureHelpSource.

  2. Aggiungere le importazioni seguenti.

    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. Aggiungere una classe denominata TestParameter che implementa IParameter.

    internal class TestParameter : IParameter
    
  4. Aggiungere un costruttore che imposta tutte le proprietà.

    public TestParameter(string documentation, Span locus, string name, ISignature signature)
    {
        Documentation = documentation;
        Locus = locus;
        Name = name;
        Signature = signature;
    }
    
  5. Aggiungere le proprietà di 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. Aggiungere una classe denominata TestSignature che implementa ISignature.

    internal class TestSignature : ISignature
    
  7. Aggiungere alcuni campi privati.

    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. Aggiungere un costruttore che imposta i campi e sottoscrive l'evento 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. Dichiarare un CurrentParameterChanged evento. Questo evento viene generato quando l'utente compila uno dei parametri nella firma.

    public event EventHandler<CurrentParameterChangedEventArgs> CurrentParameterChanged;
    
  10. Implementare la CurrentParameter proprietà in modo che generi l'evento CurrentParameterChanged quando viene modificato il valore della proprietà.

    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. Aggiungere un metodo che genera l'evento CurrentParameterChanged .

    private void RaiseCurrentParameterChanged(IParameter prevCurrentParameter, IParameter newCurrentParameter)
    {
        EventHandler<CurrentParameterChangedEventArgs> tempHandler = this.CurrentParameterChanged;
        if (tempHandler != null)
        {
            tempHandler(this, new CurrentParameterChangedEventArgs(prevCurrentParameter, newCurrentParameter));
        }
    }
    
  12. Aggiungere un metodo che calcola il parametro corrente confrontando il numero di virgole nell'oggetto ApplicableToSpan con il numero di virgole nella firma.

    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. Aggiungere un gestore eventi per l'evento Changed che chiama il ComputeCurrentParameter() metodo .

    internal void OnSubjectBufferChanged(object sender, TextContentChangedEventArgs e)
    {
        this.ComputeCurrentParameter();
    }
    
  14. Implementare la proprietà ApplicableToSpan. Questa proprietà contiene un oggetto ITrackingSpan che corrisponde all'intervallo di testo nel buffer a cui si applica la firma.

    public ITrackingSpan ApplicableToSpan
    {
        get { return (m_applicableToSpan); }
        internal set { m_applicableToSpan = value; }
    }
    
  15. Implementare gli altri parametri.

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

Implementare l'origine della Guida per la firma

L'origine della Guida della firma è il set di firme per cui si forniscono informazioni.

Per implementare l'origine della Guida della firma

  1. Aggiungere una classe denominata TestSignatureHelpSource che implementa ISignatureHelpSource.

    internal class TestSignatureHelpSource : ISignatureHelpSource
    
  2. Aggiungere un riferimento al buffer di testo.

    private ITextBuffer m_textBuffer;
    
  3. Aggiungere un costruttore che imposta il buffer di testo e il provider di origine della Guida della firma.

    public TestSignatureHelpSource(ITextBuffer textBuffer)
    {
        m_textBuffer = textBuffer;
    }
    
  4. Implementa il metodo AugmentSignatureHelpSession. In questo esempio le firme sono hardcoded, ma in un'implementazione completa si otterrebbero queste informazioni dalla documentazione del linguaggio.

    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. Il metodo CreateSignature() helper viene fornito solo per l'illustrazione.

    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. Implementa il metodo GetBestMatch. In questo esempio sono presenti solo due firme, ognuna delle quali ha due parametri. Pertanto, questo metodo non è obbligatorio. In un'implementazione più completa, in cui è disponibile più di un'origine della Guida di firma, questo metodo viene usato per decidere se l'origine della Guida con la firma con priorità più alta può fornire una firma corrispondente. In caso contrario, il metodo restituisce Null e all'origine con priorità più alta successiva viene chiesto di fornire una corrispondenza.

    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. Implementare il Dispose() metodo :

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

Implementare il provider di origine della Guida per la firma

Il provider di origine della Guida per le firme è responsabile dell'esportazione della parte del componente MEF (Managed Extensibility Framework) e della creazione di un'istanza dell'origine della Guida della firma.

Per implementare il provider di origine della Guida della firma

  1. Aggiungere una classe denominata TestSignatureHelpSourceProvider che implementa ISignatureHelpSourceProvidered esportarla con , NameAttributeun ContentTypeAttribute valore di "text" e un OrderAttribute valore before="default".

    [Export(typeof(ISignatureHelpSourceProvider))]
    [Name("Signature Help source")]
    [Order(Before = "default")]
    [ContentType("text")]
    internal class TestSignatureHelpSourceProvider : ISignatureHelpSourceProvider
    
  2. Implementare TryCreateSignatureHelpSource creando un'istanza di TestSignatureHelpSource.

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

Implementare il gestore dei comandi

La Guida della firma viene in genere attivata da una parentesi di apertura "(" carattere e ignorata da una parentesi chiusa ")". È possibile gestire queste sequenze di tasti eseguendo un oggetto IOleCommandTarget in modo che attivi una sessione della Guida della Firma quando riceve una parentesi di apertura preceduta da un nome di metodo noto e chiude la sessione quando riceve una parentesi di chiusura.

Per implementare il gestore dei comandi

  1. Aggiungere una classe denominata TestSignatureHelpCommand che implementa IOleCommandTarget.

    internal sealed class TestSignatureHelpCommandHandler : IOleCommandTarget
    
  2. Aggiungere campi privati per l'adapter IVsTextView (che consente di aggiungere il gestore di comandi alla catena di gestori di comandi), la visualizzazione testo, il gestore della Guida della firma e la sessione, un ITextStructureNavigatore il successivo IOleCommandTarget.

    IOleCommandTarget m_nextCommandHandler;
    ITextView m_textView;
    ISignatureHelpBroker m_broker;
    ISignatureHelpSession m_session;
    ITextStructureNavigator m_navigator;
    
  3. Aggiungere un costruttore per inizializzare questi campi e aggiungere il filtro dei comandi alla catena di filtri dei comandi.

    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. Implementare il Exec metodo per attivare la sessione della Guida della firma quando il filtro dei comandi riceve una parentesi di apertura "(" carattere dopo uno dei nomi di metodi noti e per ignorare la sessione quando riceve una parentesi di chiusura ")" mentre la sessione è ancora attiva. In ogni caso, il comando viene inoltrato.

    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. Implementare il QueryStatus metodo in modo che inoltra sempre il comando .

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

Implementare il provider di comandi della Guida per la firma

È possibile fornire il comando Della Guida per la firma implementando per creare un'istanza IVsTextViewCreationListener del gestore dei comandi quando viene creata la visualizzazione testo.

Per implementare il provider di comandi della Guida per la firma

  1. Aggiungere una classe denominata TestSignatureHelpController che implementa IVsTextViewCreationListener ed esportarla con NameAttribute, ContentTypeAttributee TextViewRoleAttribute.

    [Export(typeof(IVsTextViewCreationListener))]
    [Name("Signature Help controller")]
    [TextViewRole(PredefinedTextViewRoles.Editable)]
    [ContentType("text")]
    internal class TestSignatureHelpCommandProvider : IVsTextViewCreationListener
    
  2. IVsEditorAdaptersFactoryService Importare (usato per ottenere , ITextViewdato l'oggetto IVsTextView ), il ITextStructureNavigatorSelectorService (usato per trovare la parola corrente) e il ISignatureHelpBroker (per attivare la sessione della Guida della firma).

    [Import]
    internal IVsEditorAdaptersFactoryService AdapterService;
    
    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
    [Import]
    internal ISignatureHelpBroker SignatureHelpBroker;
    
  3. Implementare il VsTextViewCreated metodo creando un'istanza di 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));
    }
    

Compilare e testare il codice

Per testare questo codice, compilare la soluzione SignatureHelpTest ed eseguirla nell'istanza sperimentale.

Per compilare e testare la soluzione SignatureHelpTest

  1. Compilare la soluzione.

  2. Quando si esegue questo progetto nel debugger, viene avviata una seconda istanza di Visual Studio.

  3. Creare un file di testo e digitare un testo che includa la parola "add" e una parentesi di apertura.

  4. Dopo aver digitato la parentesi di apertura, verrà visualizzata una descrizione comando che visualizza un elenco delle due firme per il add() metodo .