Пошаговое руководство. Отображение подсказок QuickInfo

QuickInfo — это функция IntelliSense, которая отображает сигнатуры и описания методов при перемещении указателя на имя метода. Вы можете реализовать такие функции на основе языка, как QuickInfo, определив идентификаторы, для которых требуется предоставить описания QuickInfo, а затем создав подсказку, в которой будет отображаться содержимое. Вы можете определить QuickInfo в контексте языковой службы или определить собственное расширение имени файла и тип контента и отобразить QuickInfo только для этого типа или отобразить QuickInfo для существующего типа контента (например, "текст"). В этом пошаговом руководстве показано, как отобразить QuickInfo для типа контента "text".

В примере QuickInfo в этом пошаговом руководстве отображаются подсказки при перемещении указателя на имя метода. Для реализации этих четырех интерфейсов требуется:

  • исходный интерфейс

  • интерфейс поставщика источника

  • интерфейс контроллера

  • интерфейс поставщика контроллера

    Поставщики исходных и контроллеров являются компонентами компонента Managed Extensibility Framework (MEF), и отвечают за экспорт классов источника и контроллера и импорта служб и брокеров, таких как ITextBufferFactoryService, который создает текстовый буфер подсказки и IQuickInfoBroker, который активирует сеанс QuickInfo.

    В этом примере источник QuickInfo использует жестко закодированный список имен и описаний методов, но в полных реализациях языковая служба и документация по языку отвечают за предоставление этого содержимого.

Создание проекта MEF

Создание проекта MEF

  1. Создайте проект VSIX на C#. (В Диалоговое окно "Новый проект" , выберите 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. Объявите поставщик источника QuickInfo с именем TestQuickInfoSourceProvider , который реализует IQuickInfoSourceProviderи экспортирует его с NameAttribute помощью источника ToolTip QuickInfo, параметра OrderAttribute Before="default" и ContentTypeAttribute "text".

    [Export(typeof(IQuickInfoSourceProvider))]
    [Name("ToolTip QuickInfo Source")]
    [Order(Before = "Default Quick Info Presenter")]
    [ContentType("text")]
    internal class TestQuickInfoSourceProvider : IQuickInfoSourceProvider
    
  2. Импортируйте два службы ITextStructureNavigatorSelectorService редактора TestQuickInfoSourceProviderи ITextBufferFactoryServiceв качестве свойств.

    [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. Реализуйте метод и DisconnectSubjectBuffer метод как пустые ConnectSubjectBuffer методы для этого примера.

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

Реализация поставщика контроллера QuickInfo

Поставщик контроллера QuickInfo служит главным образом для экспорта в качестве части компонента MEF и создания экземпляра контроллера QuickInfo. Так как это часть компонента MEF, она может импортировать другие компоненты MEF.

Реализация поставщика контроллера QuickInfo

  1. Объявите класс с именем TestQuickInfoControllerProvider , реализующий IIntellisenseControllerProviderи экспортируйте его с NameAttribute помощью контроллера QuickInfo ToolTip и ContentTypeAttribute текстового элемента:

    [Export(typeof(IIntellisenseControllerProvider))]
    [Name("ToolTip QuickInfo Controller")]
    [ContentType("text")]
    internal class TestQuickInfoControllerProvider : IIntellisenseControllerProvider
    
  2. Импортируйте IQuickInfoBroker как свойство.

    [Import]
    internal IQuickInfoBroker QuickInfoBroker { get; set; }
    
  3. TryCreateIntellisenseController Реализуйте метод, создав экземпляр контроллера QuickInfo.

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

Сборка и проверка кода

Чтобы протестировать этот код, создайте решение QuickInfoTest и запустите его в экспериментальном экземпляре.

Создание и тестирование решения QuickInfoTest

  1. Постройте решение.

  2. При запуске этого проекта в отладчике запускается второй экземпляр Visual Studio.

  3. Создайте текстовый файл и введите текст, включающий слова "добавить" и "вычитать".

  4. Переместите указатель на один из вхождения "add". Подпись и описание add метода должны отображаться.