Passo a passo: exibir preenchimento de declaração
Você pode implementar a conclusão de instrução baseada em idioma definindo os identificadores para os quais deseja fornecer conclusão e, em seguida, disparando uma sessão de conclusão. Você pode definir a conclusão da instrução no contexto de um serviço de idioma, definir sua própria extensão de nome de arquivo e tipo de conteúdo e, em seguida, exibir a conclusão apenas para esse tipo. Ou, você pode disparar a conclusão para um tipo de conteúdo existente — por exemplo, "texto sem formatação". Este passo a passo mostra como disparar a conclusão de instrução para o tipo de conteúdo "texto sem formatação", que é o tipo de conteúdo dos arquivos de texto. O tipo de conteúdo "texto" é o ancestral de todos os outros tipos de conteúdo, incluindo código e arquivos XML.
Normalmente, a conclusão da instrução é acionada digitando determinados caracteres, por exemplo, digitando o início de um identificador, como "usando". Normalmente, ele é descartado pressionando a tecla Barra de espaço, Tab ou Enter para confirmar uma seleção. Você pode implementar os recursos do IntelliSense que são acionados ao digitar um caractere 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 fonte de conclusão, que é a lista de identificadores que participam da conclusão, implemente a interface e um provedor de origem de conclusão (a ICompletionSource ICompletionSourceProvider interface). Os provedores são partes do componente MEF (Managed Extensibility Framework). Eles são responsáveis por exportar as classes de origem e controlador e importar serviços e brokers — por exemplo, o , que permite a navegação no buffer de texto e o ITextStructureNavigatorSelectorServiceICompletionBroker, que aciona a sessão de conclusão.
Este passo a passo mostra como implementar a conclusão de instrução para um conjunto embutido de identificadores embutido em código. Em implementações completas, o serviço de idioma e a documentação de idioma são responsáveis por fornecer esse conteúdo.
Criar 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
CompletionTest
.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 certifique-se de que CopyLocal está definido como
false
:Microsoft.VisualStudio.Editor
Microsoft.VisualStudio.Language.Intellisense
Microsoft.VisualStudio.OLE.Interop
Microsoft.VisualStudio.Shell.15.0
Microsoft.VisualStudio.Shell.Immutable.10.0
Microsoft.VisualStudio.TextManager.Interop
Implementar a origem de conclusão
A fonte de conclusão é responsável por coletar o conjunto de identificadores e adicionar o conteúdo à janela de conclusão quando um usuário digita um gatilho de conclusão, como as primeiras letras de um identificador. Neste exemplo, os identificadores e suas descrições são embutidos no AugmentCompletionSession método. Na maioria dos usos do mundo real, você usaria o analisador do seu idioma para obter os tokens para preencher a lista de conclusão.
Para implementar a fonte de conclusão
Adicione um arquivo de classe e nomeie-o
TestCompletionSource
.Adicione estas importações:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel.Composition; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Utilities;
Modifique a declaração de classe para
TestCompletionSource
que ela implemente ICompletionSource:Adicione campos privados para o provedor de origem, o buffer de texto e uma lista de objetos (que correspondem aos identificadores que participarão da sessão de Completion conclusão):
Adicione um construtor que defina o provedor de origem e o buffer. A
TestCompletionSourceProvider
classe é definida em etapas posteriores:Implemente o AugmentCompletionSession método adicionando um conjunto de conclusão que contém as conclusões que você deseja fornecer no contexto. Cada conjunto de conclusão contém um conjunto de conclusões e corresponde a uma guia da janela de Completion conclusão. (Em projetos do Visual Basic, as guias da janela de conclusão são nomeadas Comum e Tudo.) O
FindTokenSpanAtPosition
método é definido na próxima etapa.void ICompletionSource.AugmentCompletionSession(ICompletionSession session, IList<CompletionSet> completionSets) { List<string> strList = new List<string>(); strList.Add("addition"); strList.Add("adaptation"); strList.Add("subtraction"); strList.Add("summation"); m_compList = new List<Completion>(); foreach (string str in strList) m_compList.Add(new Completion(str, str, str, null, null)); completionSets.Add(new CompletionSet( "Tokens", //the non-localized title of the tab "Tokens", //the display title of the tab FindTokenSpanAtPosition(session.GetTriggerPoint(m_textBuffer), session), m_compList, null)); }
O método a seguir é usado para localizar a palavra atual a partir da posição do cursor:
private ITrackingSpan FindTokenSpanAtPosition(ITrackingPoint point, ICompletionSession session) { SnapshotPoint currentPoint = (session.TextView.Caret.Position.BufferPosition) - 1; ITextStructureNavigator navigator = m_sourceProvider.NavigatorService.GetTextStructureNavigator(m_textBuffer); TextExtent extent = navigator.GetExtentOfWord(currentPoint); return currentPoint.Snapshot.CreateTrackingSpan(extent.Span, SpanTrackingMode.EdgeInclusive); }
Implemente o
Dispose()
método:
Implementar o provedor de origem de conclusão
O provedor de origem de conclusão é a parte do componente MEF que instancia a fonte de conclusão.
Para implementar o provedor de origem de conclusão
Adicione uma classe chamada
TestCompletionSourceProvider
que implementa ICompletionSourceProvidero . Exporte essa classe com um de "texto sem formatação" e um ContentTypeAttribute NameAttribute de "conclusão de teste".Importe um ITextStructureNavigatorSelectorService, que localiza a palavra atual na fonte de conclusão.
Implemente o TryCreateCompletionSource método para instanciar a origem de conclusão.
Implementar o provedor de manipulador de comandos de conclusão
O provedor do manipulador de comandos de conclusão é derivado de um , que escuta um evento de criação de modo de exibição de texto e converte o modo de exibição de um — que permite a adição do comando à cadeia de comandos do shell do Visual Studio — em um IVsTextViewCreationListenerIVsTextViewITextView. Como essa classe é uma exportação MEF, você também pode usá-la para importar os serviços que são exigidos pelo próprio manipulador de comandos.
Para implementar o provedor de manipulador de comandos de conclusão
Adicione um arquivo chamado
TestCompletionCommandHandler
.Adicione estes usando diretivas:
using System; using System.ComponentModel.Composition; using System.Runtime.InteropServices; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Utilities;
Adicione uma classe chamada
TestCompletionHandlerProvider
que implementa IVsTextViewCreationListenero . Exporte essa classe com um de "manipulador de conclusão de token", um de "texto sem formatação" e um NameAttribute ContentTypeAttribute TextViewRoleAttribute de .EditableImporte o , que permite a conversão de um para um , um e um SVsServiceProvider IVsTextView ITextViewICompletionBrokerque permite o IVsEditorAdaptersFactoryServiceacesso aos serviços padrão do Visual Studio.
Implemente o método para instanciar o VsTextViewCreated manipulador de comandos.
public void VsTextViewCreated(IVsTextView textViewAdapter) { ITextView textView = AdapterService.GetWpfTextView(textViewAdapter); if (textView == null) return; Func<TestCompletionCommandHandler> createCommandHandler = delegate() { return new TestCompletionCommandHandler(textViewAdapter, textView, this); }; textView.Properties.GetOrCreateSingletonProperty(createCommandHandler); }
Implementar o manipulador de comandos de conclusão
Como a conclusão da instrução é acionada por pressionamentos de tecla, você deve implementar a interface para receber e processar os pressionamentos de tecla que disparam, confirmam e descartam a IOleCommandTarget sessão de conclusão.
Para implementar o manipulador de comandos de conclusão
Adicione uma classe chamada
TestCompletionCommandHandler
que implementa IOleCommandTarget:Adicione campos particulares para o próximo manipulador de comandos (para o qual você passa o comando), o modo de exibição de texto, o provedor do manipulador de comandos (que permite o acesso a vários serviços) e uma sessão de conclusão:
Adicione um construtor que defina o modo de exibição de texto e os campos do provedor e adicione o comando à cadeia de comandos:
Implemente o método passando o QueryStatus comando junto:
Implementar o método de Exec . Quando esse método recebe um pressionamento de tecla, ele deve fazer uma destas coisas:
Permita que o caractere seja gravado no buffer e, em seguida, dispare ou filtre a conclusão. (A impressão de caracteres faz isso.)
Confirme a conclusão, mas não permita que o caractere seja gravado no buffer. (Espaço em branco, Tab e Enter fazem isso quando uma sessão de conclusão é exibida.)
Permita que o comando seja passado para o próximo manipulador. (Todos os outros comandos.)
Como esse método pode exibir a interface do usuário, chame IsInAutomationFunction para certificar-se de que ele não é chamado em um contexto de automação:
public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { if (VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider)) { return m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); } //make a copy of this so we can look at it after forwarding some commands uint commandID = nCmdID; char typedChar = char.MinValue; //make sure the input is a char before getting it if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR) { typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); } //check for a commit character if (nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN || nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB || (char.IsWhiteSpace(typedChar) || char.IsPunctuation(typedChar))) { //check for a selection if (m_session != null && !m_session.IsDismissed) { //if the selection is fully selected, commit the current session if (m_session.SelectedCompletionSet.SelectionStatus.IsSelected) { m_session.Commit(); //also, don't add the character to the buffer return VSConstants.S_OK; } else { //if there is no selection, dismiss the session m_session.Dismiss(); } } } //pass along the command so the char is added to the buffer int retVal = m_nextCommandHandler.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); bool handled = false; if (!typedChar.Equals(char.MinValue) && char.IsLetterOrDigit(typedChar)) { if (m_session == null || m_session.IsDismissed) // If there is no active session, bring up completion { this.TriggerCompletion(); m_session.Filter(); } else //the completion session is already active, so just filter { m_session.Filter(); } handled = true; } else if (commandID == (uint)VSConstants.VSStd2KCmdID.BACKSPACE //redo the filter if there is a deletion || commandID == (uint)VSConstants.VSStd2KCmdID.DELETE) { if (m_session != null && !m_session.IsDismissed) m_session.Filter(); handled = true; } if (handled) return VSConstants.S_OK; return retVal; }
Esse código é um método privado que dispara a sessão de conclusão:
private bool TriggerCompletion() { //the caret must be in a non-projection location SnapshotPoint? caretPoint = m_textView.Caret.Position.Point.GetPoint( textBuffer => (!textBuffer.ContentType.IsOfType("projection")), PositionAffinity.Predecessor); if (!caretPoint.HasValue) { return false; } m_session = m_provider.CompletionBroker.CreateCompletionSession (m_textView, caretPoint.Value.Snapshot.CreateTrackingPoint(caretPoint.Value.Position, PointTrackingMode.Positive), true); //subscribe to the Dismissed event on the session m_session.Dismissed += this.OnSessionDismissed; m_session.Start(); return true; }
O próximo exemplo é um método privado que cancela a inscrição do Dismissed evento:
Compilar e testar o código
Para testar esse código, compile a solução CompletionTest e execute-a na instância experimental.
Para criar e testar a solução CompletionTest
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".
Ao digitar primeiro "a" e depois "d", uma lista que contém "adição" e "adaptação" deve aparecer. Observe que a adição está selecionada. Quando você digita outro "d", a lista deve conter apenas "adição", que agora está selecionada. Você pode confirmar "adição" pressionando a tecla Barra de espaço, Tab ou Enter, ou descartar a lista digitando Esc ou qualquer outra tecla.