共用方式為


逐步解說︰顯示 QuickInfo 工具提示

QuickInfo 是一種 IntelliSense 功能,當使用者將指標移至方法名稱上方時,會顯示方法簽章和描述。 透過定義要為其提供 QuickInfo 描述的識別馬,然後建立用於顯示內容的工具提示,可以實作以語言為基礎的功能 (例如 QuickInfo)。 您可以在語言服務的內容中定義 QuickInfo,也可以定義自己的副檔名和內容類型,並只顯示該類型的 QuickInfo,也可以顯示現有內容類型的 QuickInfo (例如「文字」)。 本逐步解說示範如何顯示「text」內容類型的 QuickInfo。

本逐步解說中的 QuickInfo 範例,會在使用者將指標移至方法名稱上方時顯示工具提示。 此設計需要您實作這四個介面:

  • 來源介面

  • 來源提供者介面

  • 控制器介面

  • 控制器提供者介面

    來源和控制器提供者是 Managed Extensibility Framework (MEF) 元件部分件,負責匯出來源和控制器類別,以及匯入服務與訊息代理程式,例如 ITextBufferFactoryService,它會建立工具提示文字緩衝區,以及 IQuickInfoBroker,它會觸發 QuickInfo 工作階段。

    在此範例中,QuickInfo 來源使用方法名稱和描述的硬式編碼清單,但在完整實作中,語言服務和語言文件要負責提供該內容。

建立 MEF 專案

建立 MEF 專案

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

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

  3. 刪除現有類別檔案。

實作 QuickInfo 來源

QuickInfo 來源負責收集一組識別碼及其描述,並在遇到其中一個識別碼時,將內容新增至工具提示文字緩衝區。 在此範例中,只會在來源建構函式中新增識別碼及其描述。

實作 QuickInfo 來源

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

  2. 新增參考至 Microsoft.VisualStudio.Language.IntelliSense

  3. 新增下列匯入。

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel.Composition;
    using Microsoft.VisualStudio.Language.Intellisense;
    using Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Text.Operations;
    using Microsoft.VisualStudio.Text.Tagging;
    using Microsoft.VisualStudio.Utilities;
    
  4. 宣告實作 IQuickInfoSource 的類別,並將其命名為 TestQuickInfoSource

    internal class TestQuickInfoSource : IQuickInfoSource
    
  5. 新增 QuickInfo 來源提供者、文字緩衝區和一組方法名稱和方法簽章的欄位。 在此範例中,方法名稱和簽章會在 TestQuickInfoSource 建構函式中初始化。

    private TestQuickInfoSourceProvider m_provider;
    private ITextBuffer m_subjectBuffer;
    private Dictionary<string, string> m_dictionary;
    
  6. 新增建構函式,以設定 QuickInfo 來源提供者和文字緩衝區,並填入一組方法名稱,以及方法簽章和描述。

    public TestQuickInfoSource(TestQuickInfoSourceProvider provider, ITextBuffer subjectBuffer)
    {
        m_provider = provider;
        m_subjectBuffer = subjectBuffer;
    
        //these are the method names and their descriptions
        m_dictionary = new Dictionary<string, string>();
        m_dictionary.Add("add", "int add(int firstInt, int secondInt)\nAdds one integer to another.");
        m_dictionary.Add("subtract", "int subtract(int firstInt, int secondInt)\nSubtracts one integer from another.");
        m_dictionary.Add("multiply", "int multiply(int firstInt, int secondInt)\nMultiplies one integer by another.");
        m_dictionary.Add("divide", "int divide(int firstInt, int secondInt)\nDivides one integer by another.");
    }
    
  7. 實作 AugmentQuickInfoSession 方法。 在此範例中,如果游標位於行尾或文字緩衝區,方法會尋找目前單字或上一個單字。 如果該單字是其中一個方法名稱,該方法名稱的描述就會新增至 QuickInfo 內容。

    public void AugmentQuickInfoSession(IQuickInfoSession session, IList<object> qiContent, out ITrackingSpan applicableToSpan)
    {
        // Map the trigger point down to our buffer.
        SnapshotPoint? subjectTriggerPoint = session.GetTriggerPoint(m_subjectBuffer.CurrentSnapshot);
        if (!subjectTriggerPoint.HasValue)
        {
            applicableToSpan = null;
            return;
        }
    
        ITextSnapshot currentSnapshot = subjectTriggerPoint.Value.Snapshot;
        SnapshotSpan querySpan = new SnapshotSpan(subjectTriggerPoint.Value, 0);
    
        //look for occurrences of our QuickInfo words in the span
        ITextStructureNavigator navigator = m_provider.NavigatorService.GetTextStructureNavigator(m_subjectBuffer);
        TextExtent extent = navigator.GetExtentOfWord(subjectTriggerPoint.Value);
        string searchText = extent.Span.GetText();
    
        foreach (string key in m_dictionary.Keys)
        {
            int foundIndex = searchText.IndexOf(key, StringComparison.CurrentCultureIgnoreCase);
            if (foundIndex > -1)
            {
                applicableToSpan = currentSnapshot.CreateTrackingSpan
                    (
                    //querySpan.Start.Add(foundIndex).Position, 9, SpanTrackingMode.EdgeInclusive
                                            extent.Span.Start + foundIndex, key.Length, SpanTrackingMode.EdgeInclusive
                    );
    
                string value;
                m_dictionary.TryGetValue(key, out value);
                if (value != null)
                    qiContent.Add(value);
                else
                    qiContent.Add("");
    
                return;
            }
        }
    
        applicableToSpan = null;
    }
    
  8. 您必須實作 Dispose() 方法,因為 IQuickInfoSource 會實作 IDisposable

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

實作 QuickInfo 來源提供者

QuickInfo 來源的提供者主要是為了將自身匯出為 MEF 元件部分,並具現化 QuickInfo 來源。 因為它是 MEF 元件部分,因此可以匯入其他 MEF 元件部分。

實作 QuickInfo 來源提供者

  1. 宣告實作 IQuickInfoSourceProvider,名為 TestQuickInfoSourceProvider 的 QuickInfo 來源提供者,並使用 “ToolTip QuickInfo Source” 的 NameAttribute、Before=“default” 的 OrderAttribute,和 “text” 的 ContentTypeAttribute 將其匯出。

    [Export(typeof(IQuickInfoSourceProvider))]
    [Name("ToolTip QuickInfo Source")]
    [Order(Before = "Default Quick Info Presenter")]
    [ContentType("text")]
    internal class TestQuickInfoSourceProvider : IQuickInfoSourceProvider
    
  2. 匯入兩個編輯器服務,ITextStructureNavigatorSelectorServiceITextBufferFactoryService,作為 TestQuickInfoSourceProvider 的屬性。

    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
    [Import]
    internal ITextBufferFactoryService TextBufferFactoryService { get; set; }
    
  3. 實作 TryCreateQuickInfoSource 以傳回新的 TestQuickInfoSource

    public IQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer)
    {
        return new TestQuickInfoSource(this, textBuffer);
    }
    

實作 QuickInfo 控制器

QuickInfo 控制器會決定何時顯示 QuickInfo。 在此範例中,當指標位於對應至其中一個方法名稱的單字上方時,就會顯示 QuickInfo。 QuickInfo 控制器實作觸發 QuickInfo 工作階段的滑鼠暫留事件處理常式。

實作 QuickInfo 控制器

  1. 宣告實作 IIntellisenseController 的類別,並將其命名為 TestQuickInfoController

    internal class TestQuickInfoController : IIntellisenseController
    
  2. 為文字檢視、文字檢視中代表的文字緩衝區、QuickInfo 工作階段,以及 QuickInfo 控制器提供者新增私用欄位。

    private ITextView m_textView;
    private IList<ITextBuffer> m_subjectBuffers;
    private TestQuickInfoControllerProvider m_provider;
    private IQuickInfoSession m_session;
    
  3. 新增建構函式,以設定欄位,並新增滑鼠暫留事件處理常式。

    internal TestQuickInfoController(ITextView textView, IList<ITextBuffer> subjectBuffers, TestQuickInfoControllerProvider provider)
    {
        m_textView = textView;
        m_subjectBuffers = subjectBuffers;
        m_provider = provider;
    
        m_textView.MouseHover += this.OnTextViewMouseHover;
    }
    
  4. 新增觸發 QuickInfo 工作階段的滑鼠暫留事件處理常式。

    private void OnTextViewMouseHover(object sender, MouseHoverEventArgs e)
    {
        //find the mouse position by mapping down to the subject buffer
        SnapshotPoint? point = m_textView.BufferGraph.MapDownToFirstMatch
             (new SnapshotPoint(m_textView.TextSnapshot, e.Position),
            PointTrackingMode.Positive,
            snapshot => m_subjectBuffers.Contains(snapshot.TextBuffer),
            PositionAffinity.Predecessor);
    
        if (point != null)
        {
            ITrackingPoint triggerPoint = point.Value.Snapshot.CreateTrackingPoint(point.Value.Position,
            PointTrackingMode.Positive);
    
            if (!m_provider.QuickInfoBroker.IsQuickInfoActive(m_textView))
            {
                m_session = m_provider.QuickInfoBroker.TriggerQuickInfo(m_textView, triggerPoint, true);
            }
        }
    }
    
  5. 實作 Detach 方法,以便在控制器與文字檢視中斷連結時,移除滑鼠暫留事件處理常式。

    public void Detach(ITextView textView)
    {
        if (m_textView == textView)
        {
            m_textView.MouseHover -= this.OnTextViewMouseHover;
            m_textView = null;
        }
    }
    
  6. 實作 ConnectSubjectBuffer 方法和 DisconnectSubjectBuffer 方法做為這個範例的空白方法。

    public void ConnectSubjectBuffer(ITextBuffer subjectBuffer)
    {
    }
    
    public void DisconnectSubjectBuffer(ITextBuffer subjectBuffer)
    {
    }
    

實作 QuickInfo 控制器提供者

QuickInfo 控制器的提供者主要是為了將自身匯出為 MEF 元件部分,並具現化 QuickInfo 控制器。 因為它是 MEF 元件部分,因此可以匯入其他 MEF 元件部分。

實作 QuickInfo 控制器提供者

  1. 宣告實作 IIntellisenseControllerProvider,名為 TestQuickInfoControllerProvider 的類別,並以 “ToolTip QuickInfo Controller” 的 NameAttribute 和 “text” 的 ContentTypeAttribute 將其匯出:

    [Export(typeof(IIntellisenseControllerProvider))]
    [Name("ToolTip QuickInfo Controller")]
    [ContentType("text")]
    internal class TestQuickInfoControllerProvider : IIntellisenseControllerProvider
    
  2. 匯入 IQuickInfoBroker 作為屬性。

    [Import]
    internal IQuickInfoBroker QuickInfoBroker { get; set; }
    
  3. 藉由具現化 QuickInfo 控制器來實作 TryCreateIntellisenseController 方法。

    public IIntellisenseController TryCreateIntellisenseController(ITextView textView, IList<ITextBuffer> subjectBuffers)
    {
        return new TestQuickInfoController(textView, subjectBuffers, this);
    }
    

建置並測試程式碼

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

建置並測試 QuickInfoTest 方案

  1. 建置方案。

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

  3. 建立文字檔,並輸入一些包含 "add" 和 "subtract" 單字的文字。

  4. 將指標移至其中一個出現的 "add"。 應該顯示 add 方法的簽章和描述。