逐步解說︰顯示簽章說明

簽章說明 (也稱為 參數資訊) 會在使用者輸入參數清單開始字元 (通常是左括號) 時,在工具提示中顯示方法的簽章。 輸入參數和參數分隔符號 (通常是逗號) 時,工具提示會更新,以粗體顯示下一個參數。 可以使用下列方式定義簽章說明:在語言服務的內容中,定義您自己的副檔名和內容類型,並只顯示該類型的簽章說明,或顯示現有內容類型的簽章說明 (例如,「文字」)。 本逐步解說示範如何顯示「文字」內容類型的簽章說明。

簽章說明通常是由輸入特定字元來觸發,例如「(」(左括號),並由輸入另一個字元來關閉,例如「)」(右括號)。 藉由輸入字元所觸發的 IntelliSense 功能,可以使用按鍵命令處理常式 (IOleCommandTarget 介面) 和實作 IVsTextViewCreationListener 介面的處理常式提供者實作。 若要建立簽章說明來源,即參與簽章說明的簽章清單,請實作 ISignatureHelpSource 介面和執行 ISignatureHelpSourceProvider 介面的來源提供者。 提供者是 Managed Extensibility Framework (MEF) 元件部分,且負責匯出來源和控制器類別,以及匯入服務和訊息代理程式,例如:ITextStructureNavigatorSelectorService (可讓您在文字緩衝區中瀏覽),和 ISignatureHelpBroker (觸發簽章說明工作階段)。

本逐步解說示範如何為一組硬式編碼識別碼設定簽章說明。 在完整實作中,是由語言負責提供該內容。

建立 MEF 專案

建立 MEF 專案

  1. 建立 C# VXIS 專案。 (在新增專案對話框中,選取 Visual C# /擴充性,然後選取 VSIX 專案) 。命名解決方案 SignatureHelpTest

  2. 將編輯器分類器項目範本新增至專案。 如需詳細資訊,請參閱 使用編輯器項目範本建立擴充功能

  3. 刪除現有類別檔案。

  4. 將下列參考加入專案中,並確定將 CopyLocal 設定為 false

    Microsoft.VisualStudio.Editor

    Microsoft.VisualStudio.Language.Intellisense

    Microsoft.VisualStudio.OLE.Interop

    Microsoft.VisualStudio.Shell.14.0

    Microsoft.VisualStudio.TextManager.Interop

實作簽章說明簽章和參數

簽章說明來源是以實作 ISignature 的簽章為基礎,每個簽章都包含實作 IParameter 的參數。 在完整實作中,此資訊由語言文件提供,但此範例中的簽章是硬式編碼。

實作簽章說明簽章和參數

  1. 加入類別檔案,並將它命名為 SignatureHelpSource

  2. 新增下列匯入。

    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. 新增名為 TestParameter 且實作 IParameter 的類別。

    internal class TestParameter : IParameter
    
  4. 新增可設定所有屬性的建構函式。

    public TestParameter(string documentation, Span locus, string name, ISignature signature)
    {
        Documentation = documentation;
        Locus = locus;
        Name = name;
        Signature = signature;
    }
    
  5. 新增 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. 新增名為 TestSignature 且實作 ISignature 的類別。

    internal class TestSignature : ISignature
    
  7. 新增一些私用欄位。

    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. 新增設定欄位和訂閱 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. 宣告 CurrentParameterChanged事件。 當使用者填入簽章中的其中一個參數時,就會引發此事件。

    public event EventHandler<CurrentParameterChangedEventArgs> CurrentParameterChanged;
    
  10. 實作 CurrentParameter 屬性,以便在屬性值變更時引發 CurrentParameterChanged 事件。

    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. 新增引發 CurrentParameterChanged 事件的方法。

    private void RaiseCurrentParameterChanged(IParameter prevCurrentParameter, IParameter newCurrentParameter)
    {
        EventHandler<CurrentParameterChangedEventArgs> tempHandler = this.CurrentParameterChanged;
        if (tempHandler != null)
        {
            tempHandler(this, new CurrentParameterChangedEventArgs(prevCurrentParameter, newCurrentParameter));
        }
    }
    
  12. 新增透過比較 ApplicableToSpan 中的逗號數量與簽章的逗號數量,來計算目前參數的方法。

    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. 為呼叫 ComputeCurrentParameter() 方法的 Changed 事件,新增事件處理常式。

    internal void OnSubjectBufferChanged(object sender, TextContentChangedEventArgs e)
    {
        this.ComputeCurrentParameter();
    }
    
  14. 實作 ApplicableToSpan 屬性。 這個屬性會保留對應至套用簽章之緩衝區中的文字範圍的 ITrackingSpan

    public ITrackingSpan ApplicableToSpan
    {
        get { return (m_applicableToSpan); }
        internal set { m_applicableToSpan = value; }
    }
    
  15. 實作其他參數。

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

實作簽章說明來源

簽章說明來源是您提供資訊的一組簽章。

實作簽章說明來源

  1. 新增名為 TestSignatureHelpSource 且實作 ISignatureHelpSource 的類別。

    internal class TestSignatureHelpSource : ISignatureHelpSource
    
  2. 新增文字緩衝區的參考。

    private ITextBuffer m_textBuffer;
    
  3. 新增建構函式,以設定文字緩衝區和簽章說明來源提供者。

    public TestSignatureHelpSource(ITextBuffer textBuffer)
    {
        m_textBuffer = textBuffer;
    }
    
  4. 實作 AugmentSignatureHelpSession 方法。 此範例中的簽章是硬式編碼,但在完整實作中,此資訊將由語言文件提供。

    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. 在此提供的協助程式方法 CreateSignature() 僅供說明。

    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. 實作 GetBestMatch 方法。 在此範例中,只有兩個簽章,每個簽章都有兩個參數。 因此,不需要此方法。 在提供多個簽章說明來源的更完整實作中,這個方法可用來決定最高優先順序的簽章說明來源,是否可以提供相符的簽章。 如果無法提供,此方法會傳回 null,且要求下一個最高優先順序的來源提供相符的簽章。

    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. 實作 Dispose() 方法:

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

實作簽章說明來源提供者

簽章說明來源提供者負責匯出 Managed Extensibility Framework (MEF) 元件部分,以及具現化簽章說明來源。

實作簽章說明來源提供者

  1. 新增實作 ISignatureHelpSourceProvider,名為 TestSignatureHelpSourceProvider 的類別,並以 NameAttribute、“text” 的 ContentTypeAttribute 和 Before=“default” 的 OrderAttribute 來將其匯出。

    [Export(typeof(ISignatureHelpSourceProvider))]
    [Name("Signature Help source")]
    [Order(Before = "default")]
    [ContentType("text")]
    internal class TestSignatureHelpSourceProvider : ISignatureHelpSourceProvider
    
  2. 藉由具現化 TestSignatureHelpSource 來實作 TryCreateSignatureHelpSource

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

實作命令處理常式

簽章說明通常是由左括號「(」字元觸發,並由右括號「)」字元關閉。 您可以執行 IOleCommandTarget 來處理這些鍵輸入,以便在其收到已知方法名稱之前的左括號字元時觸發簽章說明工作階段,並在收到右括號字元時關閉工作階段。

實作命令處理常式

  1. 新增名為 TestSignatureHelpCommand 且實作 IOleCommandTarget 的類別。

    internal sealed class TestSignatureHelpCommandHandler : IOleCommandTarget
    
  2. IVsTextView 配接器新增私用欄位 (可讓您將命令處理常式新增至命令處理常式鏈結)、文字檢視、簽章說明訊息代理程式和工作階段、ITextStructureNavigator,以及下一個 IOleCommandTarget

    IOleCommandTarget m_nextCommandHandler;
    ITextView m_textView;
    ISignatureHelpBroker m_broker;
    ISignatureHelpSession m_session;
    ITextStructureNavigator m_navigator;
    
  3. 新增建構函式來初始化這些欄位,並將命令篩選新增至命令篩選的鏈結。

    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. 當命令篩選在已知的方法名稱之後收到左括號「(」字元時觸發簽章說明工作階段,並在工作階段仍在作用中時時收到右括號「)」字元時解除工作階段時,實作 Exec 以觸發簽章說明工作階段。 在每種情況下,都會轉寄命令。

    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. 實作 QueryStatus 方法,所以總是轉寄命令。

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

實作簽章說明命令提供者

您可以在建立文字檢視時實作 IVsTextViewCreationListener 來具現化命令處理常式,以提供簽章說明命令。

實作簽章說明命令提供者

  1. 新增實作 IVsTextViewCreationListener,名為 TestSignatureHelpController 的類別,並使用 NameAttributeContentTypeAttribute,和 TextViewRoleAttribute 來將其匯出。

    [Export(typeof(IVsTextViewCreationListener))]
    [Name("Signature Help controller")]
    [TextViewRole(PredefinedTextViewRoles.Editable)]
    [ContentType("text")]
    internal class TestSignatureHelpCommandProvider : IVsTextViewCreationListener
    
  2. 匯入 IVsEditorAdaptersFactoryService (用來取得 ITextView,指定 IVsTextView 物件)、ITextStructureNavigatorSelectorService (用來尋找目前單字) 和 ISignatureHelpBroker (觸發簽章說明工作階段)。

    [Import]
    internal IVsEditorAdaptersFactoryService AdapterService;
    
    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
    [Import]
    internal ISignatureHelpBroker SignatureHelpBroker;
    
  3. 藉由具現化 TestSignatureCommandHandler 來實作 VsTextViewCreated 方法。

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

建置並測試程式碼

若要測試此程式碼,請建置 SignatureHelpTest 方案,並在實驗執行個體中執行它。

建置並測試 SignatureHelpTest 方案

  1. 建置方案。

  2. 當您在偵錯工具中執行這個專案時,會啟動第二個 Visual Studio 執行個體。

  3. 建立文字檔,並輸入一些文字,其中包含「add」單字加上左括號。

  4. 輸入左括號之後,您應該會看到顯示 add() 方法的兩個簽章清單的工具提示。