Tutorial: Mostrar ayuda de firma
La Ayuda de firma (también conocida como Información de parámetros) muestra la firma de un método en una información sobre herramientas cuando un usuario escribe el carácter de inicio de la lista de parámetros (normalmente un paréntesis de apertura). Como parámetro y separador de parámetros (normalmente una coma) se escriben, la información sobre herramientas se actualiza para mostrar el siguiente parámetro en negrita. Puede definir la Ayuda de firma de las maneras siguientes: en el contexto de un servicio de lenguaje, defina su propia extensión de nombre de archivo y tipo de contenido y muestre la Ayuda de firma solo para ese tipo, o muestre la Ayuda de firma para un tipo de contenido existente (por ejemplo, "texto"). En este tutorial se muestra cómo mostrar la Ayuda de firma para el tipo de contenido "texto".
La Ayuda de firma se desencadena normalmente escribiendo un carácter específico, por ejemplo, "(" (paréntesis de apertura) y descartado escribiendo otro carácter, por ejemplo, ")" (paréntesis de cierre). Las características de IntelliSense que se desencadenan escribiendo un carácter se pueden implementar mediante un controlador de comandos para las pulsaciones de tecla (la IOleCommandTarget interfaz) y un proveedor de controladores que implementa la IVsTextViewCreationListener interfaz. Para crear el origen de la Ayuda de firma, que es la lista de firmas que participan en la Ayuda de firma, implemente la ISignatureHelpSource interfaz y un proveedor de origen que ejecute la ISignatureHelpSourceProvider interfaz. Los proveedores son componentes de Managed Extensibility Framework (MEF) y son responsables de exportar las clases de origen y controlador e importar servicios y agentes, por ejemplo, , que ITextStructureNavigatorSelectorServicele permite navegar en el búfer de texto y , ISignatureHelpBrokerque desencadena la sesión de Ayuda de firma.
En este tutorial se muestra cómo configurar la Ayuda de firma para un conjunto codificado de identificadores de forma rígida. En implementaciones completas, el lenguaje es responsable de proporcionar ese contenido.
Creación de un proyecto MEF
Para crear un nuevo proyecto de MEF
Cree un proyecto VSIX de C#. (En Cuadro de diálogo Nuevo proyecto , seleccione Visual C# / Extensibilidad y, después , Proyecto VSIX). Asigne un nombre a la solución
SignatureHelpTest
.Agregue una plantilla de elemento clasificador del editor al proyecto. Para obtener más información, vea Creación de una extensión con una plantilla de elemento de editor.
Elimine los archivos de clase existentes.
Agregue las siguientes referencias al proyecto y asegúrese de que CopyLocal esté establecido
false
en :Microsoft.VisualStudio.Editor
Microsoft.VisualStudio.Language.Intellisense
Microsoft.VisualStudio.OLE.Interop
Microsoft.VisualStudio.Shell.14.0
Microsoft.VisualStudio.TextManager.Interop
Implementar firmas y parámetros de la Ayuda de firma
El origen de la Ayuda de firma se basa en firmas que implementan ISignature, cada una de las cuales contiene parámetros que implementan IParameter. En una implementación completa, esta información se obtendría de la documentación del lenguaje, pero en este ejemplo, las firmas están codificadas de forma rígida.
Para implementar las firmas y parámetros de la Ayuda de firma
Agregue un archivo de clase y asígnele el nombre
SignatureHelpSource
.Agregue las importaciones siguientes.
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;
Agregue una clase denominada
TestParameter
que implemente IParameter.Agregue un constructor que establezca todas las propiedades.
Agregue las propiedades de IParameter.
Agregue una clase denominada
TestSignature
que implemente ISignature.Agregue algunos campos privados.
Agregue un constructor que establezca los campos y se suscriba al Changed evento.
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); }
Declare un
CurrentParameterChanged
evento. Este evento se genera cuando el usuario rellena uno de los parámetros de la firma.Implemente la CurrentParameter propiedad para que genere el
CurrentParameterChanged
evento cuando se cambie el valor de la propiedad.Agregue un método que genere el
CurrentParameterChanged
evento.private void RaiseCurrentParameterChanged(IParameter prevCurrentParameter, IParameter newCurrentParameter) { EventHandler<CurrentParameterChangedEventArgs> tempHandler = this.CurrentParameterChanged; if (tempHandler != null) { tempHandler(this, new CurrentParameterChangedEventArgs(prevCurrentParameter, newCurrentParameter)); } }
Agregue un método que calcule el parámetro actual comparando el número de comas en con ApplicableToSpan el número de comas de la firma.
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]; } }
Agregue un controlador de eventos para el Changed evento que llama al
ComputeCurrentParameter()
método .Implemente la propiedad ApplicableToSpan. Esta propiedad contiene un ITrackingSpan que corresponde al intervalo de texto del búfer al que se aplica la firma.
Implemente los demás parámetros.
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; } }
Implementación del origen de la Ayuda de firma
El origen de la Ayuda de firma es el conjunto de firmas para las que se proporciona información.
Para implementar el origen de la Ayuda de firma
Agregue una clase denominada
TestSignatureHelpSource
que implemente ISignatureHelpSource.Agregue una referencia al búfer de texto.
Agregue un constructor que establezca el búfer de texto y el proveedor de origen de la Ayuda de firma.
Implemente el método AugmentSignatureHelpSession. En este ejemplo, las firmas están codificadas de forma rígida, pero en una implementación completa obtendrá esta información de la documentación del lenguaje.
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)); }
El método
CreateSignature()
auxiliar se proporciona solo para la ilustración.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; }
Implemente el método GetBestMatch. En este ejemplo, solo hay dos firmas, cada una de las cuales tiene dos parámetros. Por lo tanto, este método no es necesario. En una implementación más completa, en la que hay disponible más de un origen de ayuda de firma, este método se usa para decidir si el origen de ayuda de firma de prioridad más alta puede proporcionar una firma coincidente. Si no es posible, el método devuelve null y se pide al origen de prioridad más alta que proporcione una coincidencia.
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; }
Implemente el
Dispose()
método :
Implementación del proveedor de origen de la Ayuda de firma
El proveedor de origen de la Ayuda de firma es responsable de exportar la parte del componente Managed Extensibility Framework (MEF) y para crear instancias del origen de la Ayuda de firma.
Para implementar el proveedor de origen de la Ayuda de firma
Agregue una clase denominada
TestSignatureHelpSourceProvider
que implemente ISignatureHelpSourceProvidery expórtela con un NameAttribute, un ContentTypeAttribute de "texto" y un OrderAttribute valor de Before="default".Implemente TryCreateSignatureHelpSource mediante la creación de instancias de
TestSignatureHelpSource
.
Implementación del controlador de comandos
La Ayuda de firma normalmente se desencadena mediante un carácter de paréntesis de apertura "(" y descartado por un carácter de paréntesis de cierre ")". Puede controlar estas pulsaciones de tecla ejecutando para IOleCommandTarget que desencadene una sesión de Ayuda de firma cuando reciba un carácter de paréntesis de apertura precedido por un nombre de método conocido y descarta la sesión cuando recibe un carácter de paréntesis de cierre.
Para implementar el controlador de comandos
Agregue una clase denominada
TestSignatureHelpCommand
que implemente IOleCommandTarget.Agregue campos privados para el IVsTextView adaptador (que le permite agregar el controlador de comandos a la cadena de controladores de comandos), la vista de texto, el agente de ayuda de firma y la sesión, un ITextStructureNavigatory el siguiente IOleCommandTarget.
Agregue un constructor para inicializar estos campos y para agregar el filtro de comandos a la cadena de filtros de comandos.
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); }
Implemente el Exec método para desencadenar la sesión de ayuda de firma cuando el filtro de comandos recibe un paréntesis de apertura "(" carácter después de uno de los nombres de método conocidos y para descartar la sesión cuando recibe un carácter de paréntesis de cierre ")" mientras la sesión sigue activa. En cada caso, el comando se reenvía.
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); }
Implemente el QueryStatus método para que siempre reenvíe el comando.
Implementación del proveedor de comandos de la Ayuda de firma
Puede proporcionar el comando Ayuda de firma implementando para IVsTextViewCreationListener crear una instancia del controlador de comandos cuando se crea la vista de texto.
Para implementar el proveedor de comandos de la Ayuda de firma
Agregue una clase denominada
TestSignatureHelpController
que implementa IVsTextViewCreationListener y exporte con , NameAttributeContentTypeAttributey TextViewRoleAttribute.IVsEditorAdaptersFactoryService Importe (usado para obtener , ITextViewdado el IVsTextView objeto ), ITextStructureNavigatorSelectorService (que se usa para buscar la palabra actual) y el ISignatureHelpBroker objeto (para desencadenar la sesión de Ayuda de firma).
Implemente el método mediante la VsTextViewCreated creación de instancias de
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)); }
Compilación y prueba del código
Para probar este código, compile la solución SignatureHelpTest y ejecútela en la instancia experimental.
Para compilar y probar la solución SignatureHelpTest
Compile la solución.
Al ejecutar este proyecto en el depurador, se inicia una segunda instancia de Visual Studio.
Cree un archivo de texto y escriba algún texto que incluya la palabra "add" más un paréntesis de apertura.
Después de escribir el paréntesis de apertura, debería ver una información sobre herramientas que muestra una lista de las dos firmas para el
add()
método .