Partager via


Procédure pas à pas : implémenter des extraits de code

Vous pouvez créer des extraits de code et les inclure dans une extension d’éditeur afin que les utilisateurs de l’extension puissent les ajouter à leur propre code.

Un extrait de code est un fragment de code ou d’un autre texte qui peut être incorporé dans un fichier. Pour afficher tous les extraits de code inscrits pour des langages de programmation particuliers, dans le menu Outils , cliquez sur Code Snippet Manager. Pour insérer un extrait de code dans un fichier, cliquez avec le bouton droit sur l’emplacement souhaité, cliquez sur Insérer un extrait de code ou Entourer, recherchez l’extrait de code souhaité, puis double-cliquez dessus. Appuyez sur Tab ou Maj+pour modifier les parties pertinentes de l’extrait de code, puis appuyez sur Entrée ou Échap pour l’accepter. Pour plus d’informations, consultez extraits de code.

Un extrait de code est contenu dans un fichier XML qui a l’extension de nom de fichier .snippet*. Un extrait de code peut contenir des champs mis en surbrillance après l’insertion de l’extrait de code afin que l’utilisateur puisse les rechercher et les modifier. Un fichier d’extrait de code fournit également des informations pour le Gestionnaire d’extraits de code afin qu’il puisse afficher le nom de l’extrait de code dans la catégorie correcte. Pour plus d’informations sur le schéma d’extrait de code, consultez la référence de schéma des extraits de code.

Cette procédure pas à pas explique comment effectuer ces tâches :

  1. Créez et inscrivez des extraits de code pour une langue spécifique.

  2. Ajoutez la commande Insérer un extrait de code à un menu contextuel.

  3. Implémentez l’extension de l’extrait de code.

    Cette procédure pas à pas est basée sur la procédure pas à pas : saisie semi-automatique de l’instruction Display.

Créer et inscrire des extraits de code

En règle générale, les extraits de code sont associés à un service de langage inscrit. Toutefois, vous n’avez pas besoin d’implémenter un LanguageService pour inscrire des extraits de code. Au lieu de cela, spécifiez simplement un GUID dans le fichier d’index d’extrait de code, puis utilisez le même GUID dans le ProvideLanguageCodeExpansionAttribute fichier d’index que vous ajoutez à votre projet.

Les étapes suivantes montrent comment créer des extraits de code et les associer à un GUID spécifique.

  1. Créez la structure de répertoires suivante :

    %InstallDir%\TestSnippets\Snippets\1033\

    %InstallDir% est le dossier d’installation de Visual Studio. (Bien que ce chemin d’accès soit généralement utilisé pour installer des extraits de code, vous pouvez spécifier n’importe quel chemin d’accès.)

  2. Dans le dossier \1033\, créez un fichier .xml et nommez-le TestSnippets.xml. (Bien que ce nom soit généralement utilisé pour un fichier d’index d’extrait de code, vous pouvez spécifier n’importe quel nom tant qu’il a une extension de nom de fichier .xml .) Ajoutez le texte suivant, puis supprimez le GUID de l’espace réservé et ajoutez-le.

    <?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. Créez un fichier dans le dossier d’extraits de code, nommez-le.snippet, puis ajoutez le texte suivant :

    <?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>
    

    Les étapes suivantes montrent comment inscrire les extraits de code.

Pour inscrire des extraits de code pour un GUID spécifique

  1. Ouvrez le projet CompletionTest . Pour plus d’informations sur la création de ce projet, consultez Procédure pas à pas : saisie semi-automatique des instructions.

  2. Dans le projet, ajoutez des références aux assemblys suivants :

    • Microsoft.VisualStudio.TextManager.Interop

    • Microsoft.VisualStudio.TextManager.Interop.8.0

    • microsoft.msxml

  3. Dans le projet, ouvrez le fichier source.extension.vsixmanifest .

  4. Vérifiez que l’onglet Ressources contient un type de contenu VsPackage et que Project est défini sur le nom du projet.

  5. Sélectionnez le projet CompletionTest et, dans le Fenêtre Propriétés définissez Générer le fichier Pkgdef sur true. Enregistrez le projet.

  6. Ajoutez une classe statique SnippetUtilities au projet.

    static class SnippetUtilities
    
  7. Dans la classe SnippetUtilities, définissez un GUID et attribuez-lui la valeur que vous avez utilisée dans le fichier SnippetsIndex.xml .

    internal const string LanguageServiceGuidStr = "00000000-0000-0000-0000-00000000";
    
  8. Ajoutez la ProvideLanguageCodeExpansionAttributeTestCompletionHandler classe. Cet attribut peut être ajouté à n’importe quelle classe publique ou interne (non statique) dans le projet. (Vous devrez peut-être ajouter une using directive pour l’espace de noms 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. Générez et exécutez le projet. Dans l’instance expérimentale de Visual Studio qui démarre lors de l’exécution du projet, l’extrait de code que vous venez d’inscrire doit être affiché dans le Gestionnaire d’extraits de code sous le langage TestSnippets .

Ajouter la commande Insérer un extrait de code au menu contextuel

La commande Insérer un extrait de code n’est pas incluse dans le menu contextuel d’un fichier texte. Par conséquent, vous devez activer la commande.

Pour ajouter la commande Insérer un extrait de code au menu contextuel

  1. Ouvrez le TestCompletionCommandHandler fichier de classe.

    Étant donné que cette classe implémente IOleCommandTarget, vous pouvez activer la commande Insert Snippet dans la QueryStatus méthode. Avant d’activer la commande, case activée que cette méthode n’est pas appelée à l’intérieur d’une fonction Automation, car lorsque la commande Insérer un extrait de code est cliqué, elle affiche l’interface utilisateur du sélecteur d’extraits de code(UI).

    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. Générez et exécutez le projet. Dans l’instance expérimentale, ouvrez un fichier qui a l’extension de nom de fichier .zzz, puis cliquez avec le bouton droit n’importe où dans celui-ci. La commande Insérer un extrait de code doit apparaître dans le menu contextuel.

Implémenter l’extension d’extrait de code dans l’interface utilisateur du sélecteur d’extraits de code

Cette section montre comment implémenter l’expansion de l’extrait de code afin que l’interface utilisateur du sélecteur d’extraits de code s’affiche lorsque l’extrait de code est cliqué sur le menu contextuel. Un extrait de code est également développé lorsqu’un utilisateur tape le raccourci de l’extrait de code, puis appuie sur Tab.

Pour afficher l’interface utilisateur du sélecteur d’extraits de code et activer la navigation et l’acceptation de l’extrait de code après insertion, utilisez la Exec méthode. L’insertion elle-même est gérée par la OnItemChosen méthode.

L’implémentation de l’extension d’extrait de code utilise des interfaces héritées Microsoft.VisualStudio.TextManager.Interop . Lorsque vous traduisez des classes d’éditeur actuelles vers le code hérité, n’oubliez pas que les interfaces héritées utilisent une combinaison de numéros de ligne et de numéros de colonne pour spécifier des emplacements dans une mémoire tampon de texte, mais les classes actuelles utilisent un index. Par conséquent, si une mémoire tampon comporte trois lignes dont chacune a 10 caractères (plus une nouvelle ligne, qui compte comme un caractère), le quatrième caractère de la troisième ligne est à la position 27 dans l’implémentation actuelle, mais il se trouve à la ligne 2, position 3 dans l’ancienne implémentation.

Pour implémenter l’extension de l’extrait de code

  1. Dans le fichier qui contient la TestCompletionCommandHandler classe, ajoutez les directives suivantes using .

    using Microsoft.VisualStudio.Text.Operations;
    using MSXML;
    using System.ComponentModel.Composition;
    
  2. Créez l’implémentation de l’interface par la TestCompletionCommandHandlerIVsExpansionClient classe.

    internal class TestCompletionCommandHandler : IOleCommandTarget, IVsExpansionClient
    
  3. Dans la TestCompletionCommandHandlerProvider classe, importez le ITextStructureNavigatorSelectorService.

    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
  4. Ajoutez des champs privés pour les interfaces d’extension de code et le IVsTextView.

    IVsTextView m_vsTextView;
    IVsExpansionManager m_exManager;
    IVsExpansionSession m_exSession;
    
  5. Dans le constructeur de la TestCompletionCommandHandler classe, définissez les champs suivants.

    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. Pour afficher le sélecteur d’extraits de code lorsque l’utilisateur clique sur la commande Insérer un extrait de code, ajoutez le code suivant à la Exec méthode. (Pour rendre cette explication plus lisible, le Exec()code utilisé pour la saisie semi-automatique des instructions n’est pas affiché ; au lieu de cela, les blocs de code sont ajoutés à la méthode existante.) Ajoutez le bloc de code suivant après le code qui case activée s pour un caractère.

    //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. Si un extrait de code contient des champs qui peuvent être parcourus, la session d’extension est conservée ouverte jusqu’à ce que l’extension soit explicitement acceptée ; si l’extrait de code n’a aucun champ, la session est fermée et est retournée comme null par la InvokeInsertionUI méthode. Dans la Exec méthode, après le code de l’interface utilisateur du sélecteur d’extraits de code que vous avez ajouté à l’étape précédente, ajoutez le code suivant pour gérer la navigation des extraits de code (lorsque l’utilisateur appuie sur Tab ou Maj+Tab après l’insertion de l’extrait de code).

    //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. Pour insérer l’extrait de code lorsque l’utilisateur tape le raccourci correspondant, puis appuie sur Tab, ajoutez du code à la Exec méthode. La méthode privée qui insère l’extrait de code s’affiche dans une étape ultérieure. Ajoutez le code suivant après le code de navigation que vous avez ajouté à l’étape précédente.

    //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. Implémentez les méthodes de l’interface IVsExpansionClient . Dans cette implémentation, les seules méthodes d’intérêt sont EndExpansion et OnItemChosen. Les autres méthodes doivent simplement retourner 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. Implémentez la méthode OnItemChosen. La méthode d’assistance qui insère réellement les extensions est abordée dans une étape ultérieure. Fournit TextSpan des informations de ligne et de colonne, que vous pouvez obtenir à partir du IVsTextView.

    public int OnItemChosen(string pszTitle, string pszPath)
    {
        InsertAnyExpansion(null, pszTitle, pszPath);
        return VSConstants.S_OK;
    }
    
  11. La méthode privée suivante insère un extrait de code, en fonction du raccourci ou du titre et du chemin. Il appelle ensuite la InsertNamedExpansion méthode avec l’extrait de code.

    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;
    }
    

Développement d’extraits de code de build et de test

Vous pouvez tester si l’extension d’extrait de code fonctionne dans votre projet.

  1. Générez la solution. Lorsque vous exécutez ce projet dans le débogueur, une deuxième instance de Visual Studio est démarrée.

  2. Ouvrez un fichier texte et tapez du texte.

  3. Cliquez avec le bouton droit dans le texte, puis cliquez sur Insérer un extrait de code.

  4. L’interface utilisateur du sélecteur d’extraits de code doit apparaître avec une fenêtre contextuelle indiquant les champs de remplacement de test. Double-cliquez sur la fenêtre contextuelle.

    L’extrait de code suivant doit être inséré.

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

    N’appuyez pas sur Entrée ou Échap.

  5. Appuyez sur Tab et Maj tab+ pour basculer entre « first » et « second ».

  6. Acceptez l’insertion en appuyant sur Entrée ou Échap.

  7. Dans une autre partie du texte, tapez « test », puis appuyez sur Tab. Étant donné que « test » est le raccourci d’extrait de code, l’extrait de code doit être inséré à nouveau.