Пошаговое руководство. Справка по отображению подписи
Справка сигнатуры (также известная как сведения о параметрах) отображает подпись метода в подсказке, когда пользователь вводит начальный символ списка параметров (обычно открывающая скобка). В качестве разделителя параметров и параметров (обычно запятой) введите подсказку, чтобы отобразить следующий параметр полужирным шрифтом. Справку сигнатур можно определить следующим образом: в контексте языковой службы определите собственное расширение имени файла и тип контента и отобразите справку по подписи только для этого типа или отобразить справку по подписи для существующего типа контента (например, "текст"). В этом пошаговом руководстве показано, как отобразить справку подписи для типа контента "text".
Справка по сигнатуре обычно активируется путем ввода определенного символа, например "(" (открытие скобки) и увольнения путем ввода другого символа, например "" (закрывающий скобки). Функции IntelliSense, которые активируются путем ввода символа, можно реализовать с помощью обработчика команд для нажатий клавиш ( IOleCommandTarget интерфейса) и поставщика обработчика, реализующего IVsTextViewCreationListener интерфейс. Чтобы создать источник справки по подписи, который является списком подписей, участвующих в справке по подписям, реализуйте ISignatureHelpSource интерфейс и поставщик источника, который запускает ISignatureHelpSourceProvider интерфейс. Поставщики являются компонентами компонента Managed Extensibility Framework (MEF) и отвечают за экспорт классов исходного и контроллера и импорта служб и брокеров, например, то, ITextStructureNavigatorSelectorServiceчто позволяет перемещаться в текстовом буфере, а также ISignatureHelpBrokerзапускать сеанс справки по подписи.
В этом пошаговом руководстве показано, как настроить справку подписи для жестко закодированного набора идентификаторов. В полных реализациях язык отвечает за предоставление этого содержимого.
Создание проекта MEF
Создание проекта MEF
Создайте проект VSIX на C#. (В Диалоговое окно "Новый проект" , выберите Visual C# / Расширяемость, а затем ПРОЕКТ VSIX.) Назовите решение
SignatureHelpTest
.Добавьте в проект шаблон элемента классификатора редактора. Дополнительные сведения: Создание расширения с помощью шаблона элемента редактора.
Удалите файлы существующих классов.
Добавьте следующие ссылки в проект и убедитесь, что copyLocal имеет значение
false
:Microsoft.VisualStudio.Editor
Microsoft.VisualStudio.Language.Intellisense
Microsoft.VisualStudio.OLE.Interop
Microsoft.VisualStudio.Shell.14.0
Microsoft.VisualStudio.TextManager.Interop
Реализация подписей справки и параметров
Источник справки подписи основан на сигнатурах, реализующих ISignatureкаждое из которых содержит параметры, реализующие IParameter. В полной реализации эти сведения будут получены из языковой документации, но в этом примере подписи жестко закодируются.
Реализация подписей справки и параметров
Добавьте файл класса с именем
SignatureHelpSource
.Добавьте приведенные ниже импортированные данные.
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;
Добавьте класс с именем
TestParameter
, который реализует IParameter.Добавьте конструктор, который задает все свойства.
Добавьте свойства IParameter.
Добавьте класс с именем
TestSignature
, который реализует ISignature.Добавьте некоторые частные поля.
Добавьте конструктор, который задает поля и подписывается на 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); }
Объявите
CurrentParameterChanged
событие. Это событие возникает, когда пользователь заполняет один из параметров в сигнатуре.CurrentParameter Реализуйте свойство, чтобы оно вызвало
CurrentParameterChanged
событие при изменении значения свойства.Добавьте метод, который вызывает
CurrentParameterChanged
событие.private void RaiseCurrentParameterChanged(IParameter prevCurrentParameter, IParameter newCurrentParameter) { EventHandler<CurrentParameterChangedEventArgs> tempHandler = this.CurrentParameterChanged; if (tempHandler != null) { tempHandler(this, new CurrentParameterChangedEventArgs(prevCurrentParameter, newCurrentParameter)); } }
Добавьте метод, вычисляющий текущий параметр, сравнивая число запятых в 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]; } }
Добавьте обработчик событий для Changed события, вызывающего
ComputeCurrentParameter()
метод.Реализуйте свойство ApplicableToSpan. Это свойство содержит ITrackingSpan диапазон текста в буфере, к которому применяется подпись.
Реализуйте другие параметры.
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; } }
Реализация источника справки по подписи
Источник справки подписи — это набор подписей, для которых предоставляются сведения.
Реализация источника справки сигнатуры
Добавьте класс с именем
TestSignatureHelpSource
, который реализует ISignatureHelpSource.Добавьте ссылку на текстовый буфер.
Добавьте конструктор, который задает текстовый буфер и поставщик справки по сигнатуре.
Реализуйте метод 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)); }
Вспомогательный метод
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; }
Реализуйте метод 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; }
Dispose()
Реализуйте метод:
Реализация поставщика источника справки по подписи
Поставщик справки по подписям отвечает за экспорт части компонента Managed Extensibility Framework (MEF) и создания экземпляра источника справки сигнатуры.
Реализация поставщика источника справки по подписи
Добавьте класс с именем
TestSignatureHelpSourceProvider
, реализующий ISignatureHelpSourceProviderи экспортируйте его с NameAttributeпомощью типа ContentTypeAttribute "text" и " OrderAttribute Before=" по умолчанию.Реализация TryCreateSignatureHelpSource путем создания экземпляра
TestSignatureHelpSource
.
Реализация обработчика команд
Справка по подписи обычно активируется открывающей скобкой "(" и закрывается закрывающей скобкой ") символом. Вы можете обработать эти нажатия клавиш, выполнив IOleCommandTarget так, чтобы он активировал сеанс справки подписи при получении открывающего скобки символа, предшествующего известному имени метода, и закрывает сеанс при получении закрывающего скобки символа.
Реализация обработчика команд
Добавьте класс с именем
TestSignatureHelpCommand
, который реализует IOleCommandTarget.Добавьте частные поля для IVsTextView адаптера (который позволяет добавить обработчик команд в цепочку обработчиков команд), текстовое представление, брокер справки подписи и сеанс, ITextStructureNavigatorа также следующее IOleCommandTarget.
Добавьте конструктор для инициализации этих полей и добавьте фильтр команд в цепочку фильтров команд.
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); }
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); }
QueryStatus Реализуйте метод таким образом, чтобы он всегда перенаправил команду.
Реализация поставщика команд справки подписи
Вы можете предоставить команду справки по подписи, реализуя IVsTextViewCreationListener экземпляр обработчика команд при создании текстового представления.
Реализация поставщика команд справки подписи
Добавьте класс с именем
TestSignatureHelpController
, который реализует IVsTextViewCreationListener и экспортирует его с NameAttributeпомощью , ContentTypeAttributeи TextViewRoleAttribute.IVsEditorAdaptersFactoryService Импортируйте (используется для получения ITextViewIVsTextView объекта), ITextStructureNavigatorSelectorService (используется для поиска текущего слова) и ISignatureHelpBroker (для активации сеанса справки сигнатуры).
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
Постройте решение.
При запуске этого проекта в отладчике запускается второй экземпляр Visual Studio.
Создайте текстовый файл и введите текст, включающий слово "добавить" и открывающую скобку.
После ввода открывающей скобки появится подсказка, отображающая список двух подписей для
add()
метода.