Поделиться через


Пошаговое руководство. Справка по отображению подписи

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

Справка по сигнатуре обычно активируется путем ввода определенного символа, например "(" (открытие скобки) и увольнения путем ввода другого символа, например "" (закрывающий скобки). Функции IntelliSense, которые активируются путем ввода символа, можно реализовать с помощью обработчика команд для нажатий клавиш ( IOleCommandTarget интерфейса) и поставщика обработчика, реализующего IVsTextViewCreationListener интерфейс. Чтобы создать источник справки по подписи, который является списком подписей, участвующих в справке по подписям, реализуйте ISignatureHelpSource интерфейс и поставщик источника, который запускает ISignatureHelpSourceProvider интерфейс. Поставщики являются компонентами компонента Managed Extensibility Framework (MEF) и отвечают за экспорт классов исходного и контроллера и импорта служб и брокеров, например, то, ITextStructureNavigatorSelectorServiceчто позволяет перемещаться в текстовом буфере, а также ISignatureHelpBrokerзапускать сеанс справки по подписи.

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

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

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

  1. Создайте проект VSIX на C#. (В Диалоговое окно "Новый проект" , выберите 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. Добавьте обработчик событий для Changed события, вызывающего ComputeCurrentParameter() метод.

    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. Добавьте класс с именем TestSignatureHelpSourceProvider , реализующий ISignatureHelpSourceProviderи экспортируйте его с NameAttributeпомощью типа ContentTypeAttribute "text" и " OrderAttribute Before=" по умолчанию.

    [Export(typeof(ISignatureHelpSourceProvider))]
    [Name("Signature Help source")]
    [Order(Before = "default")]
    [ContentType("text")]
    internal class TestSignatureHelpSourceProvider : ISignatureHelpSourceProvider
    
  2. Реализация TryCreateSignatureHelpSource путем создания экземпляра TestSignatureHelpSource.

    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. Добавьте класс с именем TestSignatureHelpController , который реализует IVsTextViewCreationListener и экспортирует его с NameAttributeпомощью , ContentTypeAttributeи TextViewRoleAttribute.

    [Export(typeof(IVsTextViewCreationListener))]
    [Name("Signature Help controller")]
    [TextViewRole(PredefinedTextViewRoles.Editable)]
    [ContentType("text")]
    internal class TestSignatureHelpCommandProvider : IVsTextViewCreationListener
    
  2. IVsEditorAdaptersFactoryService Импортируйте (используется для получения ITextViewIVsTextView объекта), ITextStructureNavigatorSelectorService (используется для поиска текущего слова) и ISignatureHelpBroker (для активации сеанса справки сигнатуры).

    [Import]
    internal IVsEditorAdaptersFactoryService AdapterService;
    
    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
    [Import]
    internal ISignatureHelpBroker SignatureHelpBroker;
    
  3. VsTextViewCreated Реализуйте метод путем создания экземпляра 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));
    }
    

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

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

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

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

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

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

  4. После ввода открывающей скобки появится подсказка, отображающая список двух подписей для add() метода.