Compartilhar via


Demonstra Passo a passo: Implementar trechos de código

Você pode criar trechos de código e incluí-los em uma extensão de editor para que os usuários da extensão possam adicioná-los ao seu próprio código.

Um trecho de código é um fragmento de código ou outro texto que pode ser incorporado em um arquivo. Para exibir todos os trechos que foram registrados para linguagens de programação específicas, no menu Ferramentas , clique em Gerenciador de trechos de código. Para inserir um trecho em um arquivo, clique com o botão direito do mouse no local desejado, clique em Inserir Trecho ou Cercar Com, localize o trecho desejado e clique duas vezes nele. Pressione Tab ou Shift+Tab para modificar as partes relevantes do trecho e pressione Enter ou Esc para aceitá-lo. Para obter mais informações, consulte Trechos de código.

Um trecho de código está contido em um arquivo XML que tem a extensão de nome de arquivo .snippet*. Um trecho pode conter campos que são realçados depois que o trecho é inserido para que o usuário possa localizá-los e alterá-los. Um arquivo de trecho também fornece informações para o Gerenciador de trechos de código para que ele possa exibir o nome do trecho na categoria correta. Para obter informações sobre o esquema de trechos, consulte Referência de esquema de trechos de código.

Este passo a passo ensina como realizar estas tarefas:

  1. Crie e registre trechos de código para um idioma específico.

  2. Adicione o comando Inserir trecho a um menu de atalho.

  3. Implementar expansão de trechos.

    Este passo a passo é baseado em Passo a passo: conclusão da instrução de exibição.

Criar e registrar trechos de código

Normalmente, os trechos de código são associados a um serviço de idioma registrado. No entanto, você não precisa implementar um para registrar trechos de LanguageService código. Em vez disso, basta especificar um GUID no arquivo de índice de trechos e, em seguida, usar o ProvideLanguageCodeExpansionAttribute mesmo GUID no que você adicionar ao seu projeto.

As etapas a seguir demonstram como criar trechos de código e associá-los a um GUID específico.

  1. Crie a seguinte estrutura de diretórios:

    %InstallDir%\TestSnippets\Snippets\1033\

    onde %InstallDir% é a pasta de instalação do Visual Studio. (Embora esse caminho seja normalmente usado para instalar trechos de código, você pode especificar qualquer caminho.)

  2. Na pasta \1033\, crie um arquivo .xml e nomeie-o TestSnippets.xml. (Embora esse nome seja normalmente usado para um arquivo de índice de trechos, você pode especificar qualquer nome, desde que ele tenha uma extensão de nome de arquivo .xml .) Adicione o texto a seguir e, em seguida, exclua o GUID de espaço reservado e adicione o seu próprio.

    <?xml version="1.0" encoding="utf-8" ?>
    <SnippetCollection>
        <Language Lang="TestSnippets" Guid="{00000000-0000-0000-0000-000000000000}">
            <SnippetDir>
                <OnOff>On</OnOff>
                <Installed>true</Installed>
                <Locale>1033</Locale>
                <DirPath>%InstallRoot%\TestSnippets\Snippets\%LCID%\</DirPath>
                <LocalizedName>Snippets</LocalizedName>
            </SnippetDir>
        </Language>
    </SnippetCollection>
    
  3. Crie um arquivo na pasta de trecho, nomeie-o teste.snippet e, em seguida, adicione o seguinte texto:

    <?xml version="1.0" encoding="utf-8" ?>
    <CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
        <CodeSnippet Format="1.0.0">
            <Header>
                <Title>Test replacement fields</Title>
                <Shortcut>test</Shortcut>
                <Description>Code snippet for testing replacement fields</Description>
                <Author>MSIT</Author>
                <SnippetTypes>
                    <SnippetType>Expansion</SnippetType>
                </SnippetTypes>
            </Header>
            <Snippet>
                <Declarations>
                    <Literal>
                      <ID>param1</ID>
                        <ToolTip>First field</ToolTip>
                        <Default>first</Default>
                    </Literal>
                    <Literal>
                        <ID>param2</ID>
                        <ToolTip>Second field</ToolTip>
                        <Default>second</Default>
                    </Literal>
                </Declarations>
                <References>
                   <Reference>
                       <Assembly>System.Windows.Forms.dll</Assembly>
                   </Reference>
                </References>
                <Code Language="TestSnippets">
                    <![CDATA[MessageBox.Show("$param1$");
         MessageBox.Show("$param2$");]]>
                </Code>
            </Snippet>
        </CodeSnippet>
    </CodeSnippets>
    

    As etapas a seguir mostram como registrar os trechos de código.

Para registrar trechos de código para um GUID específico

  1. Abra o projeto CompletionTest . Para obter informações sobre como criar este projeto, consulte Demonstra Passo a passo: conclusão da instrução de exibição.

  2. No projeto, adicione referências aos seguintes assemblies:

    • Microsoft.VisualStudio.TextManager.Interop

    • Microsoft.VisualStudio.TextManager.Interop.8.0

    • Microsoft.MSXML

  3. No projeto, abra o arquivo source.extension.vsixmanifest .

  4. Verifique se a guia Ativos contém um tipo de conteúdo VsPackage e se Project está definido como o nome do projeto.

  5. Selecione o projeto CompletionTest e, na janela Propriedades, defina Generate Pkgdef File como true. Salve o projeto.

  6. Adicione uma classe estática SnippetUtilities ao projeto.

    static class SnippetUtilities
    
  7. Na classe SnippetUtilities, defina um GUID e atribua-lhe o valor que você usou no arquivo SnippetsIndex.xml .

    internal const string LanguageServiceGuidStr = "00000000-0000-0000-0000-00000000";
    
  8. Adicione o ProvideLanguageCodeExpansionAttribute à TestCompletionHandler classe. Esse atributo pode ser adicionado a qualquer classe pública ou interna (não estática) no projeto. (Talvez seja necessário adicionar uma using diretiva para o namespace Microsoft.VisualStudio.Shell.)

    [ProvideLanguageCodeExpansion(
    SnippetUtilities.LanguageServiceGuidStr,
    "TestSnippets", //the language name
    0,              //the resource id of the language
    "TestSnippets", //the language ID used in the .snippet files
    @"%InstallRoot%\TestSnippets\Snippets\%LCID%\TestSnippets.xml",
        //the path of the index file
    SearchPaths = @"%InstallRoot%\TestSnippets\Snippets\%LCID%\",
    ForceCreateDirs = @"%InstallRoot%\TestSnippets\Snippets\%LCID%\")]
    internal class TestCompletionCommandHandler : IOleCommandTarget
    
  9. Compile e execute o projeto. Na instância experimental do Visual Studio que inicia quando o projeto é executado, o trecho que você acabou de registrar deve ser exibido no Gerenciador de trechos de código na linguagem TestSnippets .

Adicionar o comando Inserir trecho de informação ao menu de atalho

O comando Inserir trecho não está incluído no menu de atalho de um arquivo de texto. Portanto, você deve habilitar o comando.

Para adicionar o comando Inserir trecho de interesse ao menu de atalho

  1. Abra o arquivo de TestCompletionCommandHandler classe.

    Como essa classe implementa IOleCommandTarget, você pode ativar o comando Inserir trecho no QueryStatus método. Antes de habilitar o comando, verifique se esse método não está sendo chamado dentro de uma função de automação, pois quando o comando Inserir trecho é clicado, ele exibe a interface do usuário (UI) do seletor de trechos.

    public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
    {
        if (!VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider))
        {
            if (pguidCmdGroup == VSConstants.VSStd2K && cCmds > 0)
            {
                // make the Insert Snippet command appear on the context menu 
                if ((uint)prgCmds[0].cmdID == (uint)VSConstants.VSStd2KCmdID.INSERTSNIPPET)
                {
                    prgCmds[0].cmdf = (int)Constants.MSOCMDF_ENABLED | (int)Constants.MSOCMDF_SUPPORTED;
                    return VSConstants.S_OK;
                }
            }
        }
    
        return m_nextCommandHandler.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
    }
    
  2. Compile e execute o projeto. Na instância experimental, abra um arquivo que tenha a extensão de nome de arquivo .zzz e, em seguida, clique com o botão direito do mouse em qualquer lugar nele. O comando Inserir trecho deve aparecer no menu de atalho.

Implementar a expansão de trechos na interface do usuário do Seletor de trechos

Esta seção mostra como implementar a expansão de trechos de código para que a interface do usuário do seletor de trechos seja exibida quando Inserir trecho é clicado no menu de atalho. Um trecho de código também é expandido quando um usuário digita o atalho de trecho de código e pressiona Tab.

Para exibir a interface do usuário do seletor de trechos e habilitar a navegação e a aceitação do trecho pós-inserção, use o Exec método. A inserção em si é manipulada OnItemChosen pelo método.

A implementação da expansão de trechos de código usa interfaces herdadas Microsoft.VisualStudio.TextManager.Interop . Ao traduzir das classes do editor atual para o código herdado, lembre-se de que as interfaces herdadas usam uma combinação de números de linha e números de coluna para especificar locais em um buffer de texto, mas as classes atuais usam um índice. Portanto, se um buffer tiver três linhas, cada uma com 10 caracteres (mais uma nova linha, que conta como um caractere), o quarto caractere na terceira linha estará na posição 27 na implementação atual, mas estará na linha 2, posição 3 na implementação antiga.

Para implementar a expansão de trechos

  1. Para o arquivo que contém a TestCompletionCommandHandler classe, adicione as seguintes using diretivas.

    using Microsoft.VisualStudio.Text.Operations;
    using MSXML;
    using System.ComponentModel.Composition;
    
  2. Faça a classe implementar a TestCompletionCommandHandlerIVsExpansionClient interface.

    internal class TestCompletionCommandHandler : IOleCommandTarget, IVsExpansionClient
    
  3. Na classe, importe TestCompletionCommandHandlerProvider o ITextStructureNavigatorSelectorServicearquivo .

    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
  4. Adicione alguns campos privados para as interfaces de expansão de código e o IVsTextView.

    IVsTextView m_vsTextView;
    IVsExpansionManager m_exManager;
    IVsExpansionSession m_exSession;
    
  5. No construtor da TestCompletionCommandHandler classe, defina os seguintes campos.

    internal TestCompletionCommandHandler(IVsTextView textViewAdapter, ITextView textView, TestCompletionHandlerProvider provider)
    {
        this.m_textView = textView;
        m_vsTextView = textViewAdapter;
        m_provider = provider;
        //get the text manager from the service provider
        IVsTextManager2 textManager = (IVsTextManager2)m_provider.ServiceProvider.GetService(typeof(SVsTextManager));
        textManager.GetExpansionManager(out m_exManager);
        m_exSession = null;
    
        //add the command to the command chain
        textViewAdapter.AddCommandFilter(this, out m_nextCommandHandler);
    }
    
  6. Para exibir o seletor de trechos quando o usuário clicar no comando Inserir trecho , adicione o seguinte código ao Exec método. (Para tornar essa explicação mais legível, o Exec()código usado para conclusão de instrução não é mostrado; em vez disso, blocos de código são adicionados ao método existente.) Adicione o seguinte bloco de código após o código que verifica um caractere.

    //code previously written for Exec
    if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR)
    {
        typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn);
    }
    //the snippet picker code starts here
    if (nCmdID == (uint)VSConstants.VSStd2KCmdID.INSERTSNIPPET)
    {
        IVsTextManager2 textManager = (IVsTextManager2)m_provider.ServiceProvider.GetService(typeof(SVsTextManager));
    
        textManager.GetExpansionManager(out m_exManager);
    
        m_exManager.InvokeInsertionUI(
            m_vsTextView,
            this,      //the expansion client
            new Guid(SnippetUtilities.LanguageServiceGuidStr),
            null,       //use all snippet types
            0,          //number of types (0 for all)
            0,          //ignored if iCountTypes == 0
            null,       //use all snippet kinds
            0,          //use all snippet kinds
            0,          //ignored if iCountTypes == 0
            "TestSnippets", //the text to show in the prompt
            string.Empty);  //only the ENTER key causes insert 
    
        return VSConstants.S_OK;
    }
    
  7. Se um trecho tiver campos que possam ser navegados, a sessão de expansão será mantida aberta até que a expansão seja explicitamente aceita; Se o trecho não tiver campos, a sessão será fechada e retornada como null pelo InvokeInsertionUI método. Exec No método, após o código da interface do usuário do seletor de trechos que você adicionou na etapa anterior, adicione o código a seguir para manipular a navegação de trechos (quando o usuário pressiona Tab ou Shift+Tab após a inserção do trecho).

    //the expansion insertion is handled in OnItemChosen
    //if the expansion session is still active, handle tab/backtab/return/cancel
    if (m_exSession != null)
    {
        if (nCmdID == (uint)VSConstants.VSStd2KCmdID.BACKTAB)
        {
            m_exSession.GoToPreviousExpansionField();
            return VSConstants.S_OK;
        }
        else if (nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB)
        {
    
            m_exSession.GoToNextExpansionField(0); //false to support cycling through all the fields
            return VSConstants.S_OK;
        }
        else if (nCmdID == (uint)VSConstants.VSStd2KCmdID.RETURN || nCmdID == (uint)VSConstants.VSStd2KCmdID.CANCEL)
        {
            if (m_exSession.EndCurrentExpansion(0) == VSConstants.S_OK)
            {
                m_exSession = null;
                return VSConstants.S_OK;
            }
        }
    }
    
  8. Para inserir o trecho de código quando o usuário digita o atalho correspondente e pressiona Tab, adicione código ao Exec método. O método private que insere o snippet será mostrado em uma etapa posterior. Adicione o seguinte código após o código de navegação que você adicionou na etapa anterior.

    //neither an expansion session nor a completion session is open, but we got a tab, so check whether the last word typed is a snippet shortcut 
    if (m_session == null && m_exSession == null && nCmdID == (uint)VSConstants.VSStd2KCmdID.TAB)
    {
        //get the word that was just added 
        CaretPosition pos = m_textView.Caret.Position;
        TextExtent word = m_provider.NavigatorService.GetTextStructureNavigator(m_textView.TextBuffer).GetExtentOfWord(pos.BufferPosition - 1); //use the position 1 space back
        string textString = word.Span.GetText(); //the word that was just added
        //if it is a code snippet, insert it, otherwise carry on
        if (InsertAnyExpansion(textString, null, null))
            return VSConstants.S_OK;
    }
    
  9. Implemente os IVsExpansionClient métodos da interface. Nesta implementação, os únicos métodos de interesse são EndExpansion e OnItemChosen. Os outros métodos devem apenas retornar S_OK.

    public int EndExpansion()
    {
        m_exSession = null;
        return VSConstants.S_OK;
    }
    
    public int FormatSpan(IVsTextLines pBuffer, TextSpan[] ts)
    {
        return VSConstants.S_OK;
    }
    
    public int GetExpansionFunction(IXMLDOMNode xmlFunctionNode, string bstrFieldName, out IVsExpansionFunction pFunc)
    {
        pFunc = null;
        return VSConstants.S_OK;
    }
    
    public int IsValidKind(IVsTextLines pBuffer, TextSpan[] ts, string bstrKind, out int pfIsValidKind)
    {
        pfIsValidKind = 1;
        return VSConstants.S_OK;
    }
    
    public int IsValidType(IVsTextLines pBuffer, TextSpan[] ts, string[] rgTypes, int iCountTypes, out int pfIsValidType)
    {
        pfIsValidType = 1;
        return VSConstants.S_OK;
    }
    
    public int OnAfterInsertion(IVsExpansionSession pSession)
    {
        return VSConstants.S_OK;
    }
    
    public int OnBeforeInsertion(IVsExpansionSession pSession)
    {
        return VSConstants.S_OK;
    }
    
    public int PositionCaretForEditing(IVsTextLines pBuffer, TextSpan[] ts)
    {
        return VSConstants.S_OK;
    }
    
  10. Implementar o método de OnItemChosen . O método auxiliar que realmente insere as expansões é abordado em uma etapa posterior. O TextSpan fornece informações de linha e coluna, que você pode obter do IVsTextView.

    public int OnItemChosen(string pszTitle, string pszPath)
    {
        InsertAnyExpansion(null, pszTitle, pszPath);
        return VSConstants.S_OK;
    }
    
  11. O método private a seguir insere um trecho de código, com base no atalho ou no título e no caminho. Em seguida, chama o método com o InsertNamedExpansion trecho.

    private bool InsertAnyExpansion(string shortcut, string title, string path)
    {
        //first get the location of the caret, and set up a TextSpan
        int endColumn, startLine;
        //get the column number from  the IVsTextView, not the ITextView
        m_vsTextView.GetCaretPos(out startLine, out endColumn);
    
        TextSpan addSpan = new TextSpan();
        addSpan.iStartIndex = endColumn;
        addSpan.iEndIndex = endColumn;
        addSpan.iStartLine = startLine;
        addSpan.iEndLine = startLine;
    
        if (shortcut != null) //get the expansion from the shortcut
        {
            //reset the TextSpan to the width of the shortcut, 
            //because we're going to replace the shortcut with the expansion
            addSpan.iStartIndex = addSpan.iEndIndex - shortcut.Length;
    
            m_exManager.GetExpansionByShortcut(
                this,
                new Guid(SnippetUtilities.LanguageServiceGuidStr),
                shortcut,
                m_vsTextView,
                new TextSpan[] { addSpan },
                0,
                out path,
                out title);
    
        }
        if (title != null && path != null)
        {
            IVsTextLines textLines;
            m_vsTextView.GetBuffer(out textLines);
            IVsExpansion bufferExpansion = (IVsExpansion)textLines;
    
            if (bufferExpansion != null)
            {
                int hr = bufferExpansion.InsertNamedExpansion(
                    title,
                    path,
                    addSpan,
                    this,
                    new Guid(SnippetUtilities.LanguageServiceGuidStr),
                    0,
                   out m_exSession);
                if (VSConstants.S_OK == hr)
                {
                    return true;
                }
            }
        }
        return false;
    }
    

Criar e testar a expansão de trechos de código

Você pode testar se a expansão de trechos funciona em seu projeto.

  1. Compile a solução. Quando você executa esse projeto no depurador, uma segunda instância do Visual Studio é iniciada.

  2. Abra um arquivo de texto e digite algum texto.

  3. Clique com o botão direito do mouse em algum lugar no texto e clique em Inserir Trecho.

  4. A interface do usuário do seletor de trechos deve aparecer com um pop-up que diz Testar campos de substituição. Clique duas vezes no pop-up.

    O seguinte trecho deve ser inserido.

    MessageBox.Show("first");
    MessageBox.Show("second");
    

    Não pressione Enter ou Esc.

  5. Pressione Tab e Shift+Tab para alternar entre "primeiro" e "segundo".

  6. Aceite a inserção pressionando Enter ou Esc.

  7. Em uma parte diferente do texto, digite "test" e pressione Tab. Como "teste" é o atalho do trecho de código, o trecho deve ser inserido novamente.