Demonstra Passo a passo: Ajuda da Assinatura de Vídeo
A Ajuda de Assinatura (também conhecida como Informações de Parâmetro) exibe a assinatura de um método em uma dica de ferramenta quando um usuário digita o caractere de início da lista de parâmetros (normalmente um parêntese de abertura). À medida que um parâmetro e um separador de parâmetros (normalmente uma vírgula) são digitados, a dica de ferramenta é atualizada para mostrar o próximo parâmetro em negrito. Você pode definir a Ajuda de Assinatura das seguintes maneiras: no contexto de um serviço de idioma, defina sua própria extensão de nome de arquivo e tipo de conteúdo e exiba a Ajuda de Assinatura apenas para esse tipo ou exiba a Ajuda de Assinatura para um tipo de conteúdo existente (por exemplo, "texto"). Este passo a passo mostra como exibir a Ajuda de assinatura para o tipo de conteúdo "texto".
A Ajuda de Assinatura é normalmente acionada digitando um caractere específico, por exemplo, "(" (parêntese de abertura), e descartada digitando outro caractere, por exemplo, ")" (parêntese de fechamento). Os recursos do IntelliSense que são acionados digitando um caractere podem ser implementados usando um manipulador de comandos para os pressionamentos de tecla (a interface) e um provedor de manipulador que implementa a IOleCommandTarget IVsTextViewCreationListener interface. Para criar a origem da Ajuda de Assinatura, que é a lista de assinaturas que participam da Ajuda de Assinatura, implemente a interface e um provedor de origem que execute a ISignatureHelpSource ISignatureHelpSourceProvider interface. Os provedores são partes do componente MEF (Managed Extensibility Framework) e são responsáveis por exportar as classes de origem e controlador e importar serviços e agentes, por exemplo, o , que permite navegar no buffer de texto e o ITextStructureNavigatorSelectorServiceISignatureHelpBroker, que aciona a sessão de Ajuda de Assinatura.
Este passo a passo mostra como configurar a Ajuda de assinatura para um conjunto codificado de identificadores. Em implementações completas, a linguagem é responsável por fornecer esse conteúdo.
Criando um projeto MEF
Para criar um projeto MEF
Crie um projeto C# VSIX. (No Caixa de diálogo Novo Projeto, selecione Visual C# / Extensibilidade e, em seguida, Projeto VSIX.) Nomeie a solução
SignatureHelpTest
.Adicione um modelo de item Editor Classificador ao projeto. Para obter mais informações, consulte Criar uma extensão com um modelo de item do editor.
Exclua os arquivos de classe existentes.
Adicione as seguintes referências ao projeto e verifique se CopyLocal está definido como
false
:Microsoft.VisualStudio.Editor
Microsoft.VisualStudio.Language.Intellisense
Microsoft.VisualStudio.OLE.Interop
Microsoft.VisualStudio.Shell.14.0
Microsoft.VisualStudio.TextManager.Interop
Implementar assinaturas e parâmetros da Ajuda de Assinatura
A origem da Ajuda de Assinatura é baseada em assinaturas que implementam , cada uma das quais contém parâmetros que implementam ISignatureIParametero . Em uma implementação completa, essas informações seriam obtidas da documentação do idioma, mas neste exemplo, as assinaturas são codificadas.
Para implementar as assinaturas e os parâmetros da Ajuda da Assinatura
Adicione um arquivo de classe e nomeie-o
SignatureHelpSource
.Adicione as seguintes importações.
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;
Adicione uma classe chamada
TestParameter
que implementa IParametero .Adicione um construtor que defina todas as propriedades.
Adicione as propriedades de IParameter.
Adicione uma classe chamada
TestSignature
que implementa ISignatureo .Adicione alguns campos privados.
Adicione um construtor que defina os campos e se inscreva no 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 um
CurrentParameterChanged
evento. Esse evento é gerado quando o usuário preenche um dos parâmetros na assinatura.Implemente a propriedade para que ela gere CurrentParameter o evento quando o
CurrentParameterChanged
valor da propriedade for alterado.Adicione um método que gera o
CurrentParameterChanged
evento.private void RaiseCurrentParameterChanged(IParameter prevCurrentParameter, IParameter newCurrentParameter) { EventHandler<CurrentParameterChangedEventArgs> tempHandler = this.CurrentParameterChanged; if (tempHandler != null) { tempHandler(this, new CurrentParameterChangedEventArgs(prevCurrentParameter, newCurrentParameter)); } }
Adicione um método que calcule o parâmetro atual comparando o número de vírgulas no com ApplicableToSpan o número de vírgulas na assinatura.
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]; } }
Adicione um manipulador de eventos para o evento que chama o Changed
ComputeCurrentParameter()
método.Implemente a propriedade ApplicableToSpan. Essa propriedade contém um ITrackingSpan que corresponde à extensão do texto no buffer ao qual a assinatura se aplica.
Implemente os outros 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; } }
Implementar a fonte da Ajuda de Assinatura
A fonte da Ajuda de Assinatura é o conjunto de assinaturas para o qual você fornece informações.
Para implementar a fonte da Ajuda de Assinatura
Adicione uma classe chamada
TestSignatureHelpSource
que implementa ISignatureHelpSourceo .Adicione uma referência ao buffer de texto.
Adicione um construtor que defina o buffer de texto e o provedor de origem da Ajuda de Assinatura.
Implementar o método de AugmentSignatureHelpSession . Neste exemplo, as assinaturas são codificadas, mas em uma implementação completa você obteria essas informações da documentação do idioma.
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)); }
O método
CreateSignature()
auxiliar é fornecido apenas para ilustração.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; }
Implementar o método de GetBestMatch . Neste exemplo, há apenas duas assinaturas, cada uma com dois parâmetros. Portanto, esse método não é necessário. Em uma implementação mais completa, na qual mais de uma fonte da Ajuda de Assinatura está disponível, esse método é usado para decidir se a fonte da Ajuda de Assinatura de prioridade mais alta pode fornecer uma assinatura correspondente. Se não for possível, o método retornará null e a próxima fonte de prioridade mais alta será solicitada a fornecer uma correspondência.
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 o
Dispose()
método:
Implementar o provedor de origem da Ajuda de Assinatura
O provedor de origem da Ajuda de Assinatura é responsável por exportar a parte do componente MEF (Managed Extensibility Framework) e por instanciar a fonte da Ajuda de Assinatura.
Para implementar o provedor de origem da Ajuda de Assinatura
Adicione uma classe chamada
TestSignatureHelpSourceProvider
que implementa ISignatureHelpSourceProvidero e exporte-a com um , a ContentTypeAttribute de "text" e um NameAttributeOrderAttribute de Before="default".Implemente TryCreateSignatureHelpSource instanciando o
TestSignatureHelpSource
.
Implementar o manipulador de comandos
A Ajuda de Assinatura é normalmente acionada por um caractere "(" de parêntese de abertura e descartada por um caractere de parêntese de fechamento ")". Você pode manipular esses pressionamentos de tecla executando um para que ele acione uma sessão de Ajuda de Assinatura quando ele recebe um caractere de parêntese de abertura precedido por um nome de método conhecido e descarta a sessão quando recebe um IOleCommandTarget caractere de parêntese de fechamento.
Para implementar o manipulador de comandos
Adicione uma classe chamada
TestSignatureHelpCommand
que implementa IOleCommandTargeto .Adicione campos particulares para o adaptador (que permite adicionar o manipulador de comandos à cadeia de manipuladores de comandos), o modo de exibição de texto, o agente e a sessão da Ajuda de Assinatura, um ITextStructureNavigatore o IVsTextView próximo IOleCommandTarget.
Adicione um construtor para inicializar esses campos e adicionar o filtro de comando à cadeia de filtros de comando.
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 o método para disparar a sessão de Ajuda de Assinatura quando o filtro de comando receber um caractere de parêntese de abertura "(" após um dos nomes de método conhecidos e para descartar a sessão quando receber um caractere de parêntese Exec de fechamento ")" enquanto a sessão ainda estiver ativa. Em todos os casos, o comando é encaminhado.
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 o método para que ele sempre encaminhe o QueryStatus comando.
Implementar o provedor de comandos Ajuda de Assinatura
Você pode fornecer o comando Ajuda de Assinatura implementando o para instanciar o manipulador de comandos quando o IVsTextViewCreationListener modo de exibição de texto for criado.
Para implementar o provedor de comandos Ajuda de Assinatura
Adicione uma classe chamada
TestSignatureHelpController
que implementa IVsTextViewCreationListener e exporta-a com o NameAttribute, ContentTypeAttributee TextViewRoleAttribute.Importe o (usado para obter o , dado o objeto), o (usado para localizar a palavra atual) e o IVsEditorAdaptersFactoryService ITextStructureNavigatorSelectorService ITextViewIVsTextView ISignatureHelpBroker (para acionar a sessão da Ajuda de Assinatura).
Implemente o método instanciando o 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)); }
Compilar e testar o código
Para testar esse código, crie a solução SignatureHelpTest e execute-a na instância experimental.
Para criar e testar a solução SignatureHelpTest
Compile a solução.
Quando você executa esse projeto no depurador, uma segunda instância do Visual Studio é iniciada.
Crie um arquivo de texto e digite algum texto que inclua a palavra "adicionar" mais um parêntese de abertura.
Depois de digitar o parêntese de abertura, você verá uma dica de ferramenta que exibe uma lista das duas assinaturas do
add()
método.