Freigeben über


Exemplarische Vorgehensweise: Implementieren von Codeausschnitten

Sie können Codeausschnitte erstellen und in eine Editorerweiterung einschließen, damit Benutzer der Erweiterung sie ihrem eigenen Code hinzufügen können.

Ein Codeausschnitt ist ein Codefragment oder ein anderer Text, der in eine Datei integriert werden kann. Klicken Sie im Menü "Extras" auf "Codeausschnitt-Manager", um alle Codeausschnitte anzuzeigen, die für bestimmte Programmiersprachen registriert wurden. Wenn Sie einen Codeausschnitt in eine Datei einfügen möchten, klicken Sie mit der rechten Maustaste auf die gewünschte Stelle, klicken Sie auf "Codeausschnitt einfügen" oder "Surround With", suchen Sie den gewünschten Codeausschnitt, und doppelklicken Sie dann darauf. Drücken Sie TAB oder UMSCHALT TAB+, um die relevanten Teile des Codeausschnitts zu ändern, und drücken Sie dann die EINGABETASTE oder ESC, um sie anzunehmen. Weitere Informationen finden Sie unter Codeausschnitte.

Ein Codeausschnitt ist in einer XML-Datei enthalten, die die Dateinamenerweiterung .snippet* aufweist. Ein Codeausschnitt kann Felder enthalten, die nach dem Einfügen des Codeausschnitts hervorgehoben werden, damit der Benutzer sie finden und ändern kann. Eine Codeausschnittdatei stellt außerdem Informationen für den Codeausschnitt-Manager bereit, damit der Codeausschnittname in der richtigen Kategorie angezeigt werden kann. Informationen zum Codeausschnittschema finden Sie unter Codeausschnittschemareferenz.

In dieser exemplarischen Vorgehensweise erfahren Sie, wie Sie diese Aufgaben ausführen:

  1. Erstellen und Registrieren von Codeausschnitten für eine bestimmte Sprache.

  2. Fügen Sie dem Kontextmenü den Befehl "Codeausschnitt einfügen" hinzu.

  3. Implementieren Der Codeausschnitterweiterung.

    Diese exemplarische Vorgehensweise basiert auf walkthrough : Display statement completion.

Erstellen und Registrieren von Codeausschnitten

Codeausschnitte sind in der Regel einem registrierten Sprachdienst zugeordnet. Sie müssen jedoch keinen Codeausschnitt implementieren LanguageService , um Codeausschnitte zu registrieren. Geben Sie stattdessen einfach eine GUID in der Codeausschnittindexdatei an, und verwenden Sie dann dieselbe GUID in dem ProvideLanguageCodeExpansionAttribute , den Sie Ihrem Projekt hinzufügen.

Die folgenden Schritte veranschaulichen, wie Codeausschnitte erstellt und einer bestimmten GUID zugeordnet werden.

  1. Erstellen Sie die folgende Verzeichnisstruktur:

    %InstallDir%\TestSnippets\Snippets\1033\

    dabei ist %InstallDir% der Visual Studio-Installationsordner. (Obwohl dieser Pfad normalerweise zum Installieren von Codeausschnitten verwendet wird, können Sie einen beliebigen Pfad angeben.)

  2. Erstellen Sie im Ordner \1033\ eine XML-Datei , und nennen Sie sie "TestSnippets.xml". (Obwohl dieser Name in der Regel für eine Codeausschnittindexdatei verwendet wird, können Sie einen beliebigen Namen angeben, solange er über die Dateinamenerweiterung .XML verfügt.) Fügen Sie den folgenden Text hinzu, und löschen Sie dann die Platzhalter-GUID, und fügen Sie Eigene hinzu.

    <?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. Erstellen Sie eine Datei im Codeausschnittordner, benennen Sie sie ,.snippet und fügen Sie dann den folgenden Text hinzu:

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

    Die folgenden Schritte zeigen, wie Sie die Codeausschnitte registrieren.

So registrieren Sie Codeausschnitte für eine bestimmte GUID

  1. Öffnen Sie das Projekt "CompletionTest ". Informationen zum Erstellen dieses Projekts finden Sie unter Walkthrough: Display statement completion.

  2. Fügen Sie im Projekt Verweise auf die folgenden Assemblys hinzu:

    • Microsoft.VisualStudio.TextManager.Interop

    • Microsoft.VisualStudio.TextManager.Interop.8.0

    • microsoft.msxml

  3. Öffnen Sie im Projekt die Datei "source.extension.vsixmanifest ".

  4. Stellen Sie sicher, dass die Registerkarte "Assets" einen VsPackage-Inhaltstyp enthält und project auf den Namen des Projekts festgelegt ist.

  5. Wählen Sie das CompletionTest-Projekt aus, und legen Sie im Eigenschaftenfenster "Pkgdef-Datei generieren" auf "true" fest. Speichern Sie das Projekt.

  6. Fügen Sie dem Projekt eine statische SnippetUtilities Klasse hinzu.

    static class SnippetUtilities
    
  7. Definieren Sie in der SnippetUtilities-Klasse eine GUID, und geben Sie ihm den Wert, den Sie in der Datei SnippetsIndex.xml verwendet haben.

    internal const string LanguageServiceGuidStr = "00000000-0000-0000-0000-00000000";
    
  8. Fügen Sie die ProvideLanguageCodeExpansionAttribute Klasse hinzu TestCompletionHandler . Dieses Attribut kann jeder öffentlichen oder internen (nicht statischen) Klasse im Projekt hinzugefügt werden. (Möglicherweise müssen Sie eine using Direktive für den Namespace "Microsoft.VisualStudio.Shell" hinzufügen.)

    [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. Erstellen Sie das Projekt, und führen Sie es aus. In der experimentellen Instanz von Visual Studio, die beim Ausführen des Projekts beginnt, sollte der soeben registrierte Codeausschnitt im Codeausschnitt-Manager unter der Sprache "TestSnippets " angezeigt werden.

Hinzufügen des Befehls "Codeausschnitt einfügen" zum Kontextmenü

Der Befehl "Codeausschnitt einfügen" ist nicht im Kontextmenü für eine Textdatei enthalten. Daher müssen Sie den Befehl aktivieren.

So fügen Sie dem Kontextmenü den Befehl "Codeausschnitt einfügen" hinzu

  1. Öffnen Sie die TestCompletionCommandHandler Klassendatei.

    Da diese Klasse implementiert wird IOleCommandTarget, können Sie den Befehl "Codeausschnitt einfügen" in der QueryStatus Methode aktivieren. Bevor Sie den Befehl aktivieren, überprüfen Sie, ob diese Methode nicht innerhalb einer Automatisierungsfunktion aufgerufen wird, da beim Klicken auf den Befehl "Codeausschnitt einfügen" die Codeausschnittauswahl-Benutzeroberfläche angezeigt wird.

    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. Erstellen Sie das Projekt, und führen Sie es aus. Öffnen Sie in der experimentellen Instanz eine Datei mit der Dateinamenerweiterung Zzz , und klicken Sie dann mit der rechten Maustaste darauf. Der Befehl "Codeausschnitt einfügen" sollte im Kontextmenü angezeigt werden.

Implementieren der Codeausschnitterweiterung in der Codeausschnittauswahl-Benutzeroberfläche

In diesem Abschnitt wird gezeigt, wie Codeausschnitterweiterung implementiert wird, sodass die Codeausschnittauswahl-UI angezeigt wird, wenn im Kontextmenü auf "Codeausschnitt einfügen" geklickt wird. Ein Codeausschnitt wird auch erweitert, wenn ein Benutzer die Codeausschnittverknüpfung eingibt und dann die TAB-TASTE drückt.

Um die Codeausschnittauswahl-UI anzuzeigen und die Akzeptanz von Navigations- und Post-Einfügeausschnitten zu ermöglichen, verwenden Sie die Exec Methode. Die Einfügung selbst wird von der OnItemChosen Methode behandelt.

Die Implementierung der Codeausschnitterweiterung verwendet legacyschnittstellen Microsoft.VisualStudio.TextManager.Interop . Wenn Sie aus den aktuellen Editorklassen in den Legacycode übersetzen, denken Sie daran, dass die älteren Schnittstellen eine Kombination aus Zeilennummern und Spaltennummern verwenden, um Speicherorte in einem Textpuffer anzugeben, aber die aktuellen Klassen verwenden einen Index. Wenn ein Puffer daher drei Zeilen enthält, die jeweils 10 Zeichen enthalten (plus eine Neue zeile, die als ein Zeichen zählt), befindet sich das vierte Zeichen in der dritten Zeile an Position 27 in der aktuellen Implementierung, aber in Zeile 2, Position 3 in der alten Implementierung.

So implementieren Sie codeausschnitterweiterung

  1. Fügen Sie der Datei, die die TestCompletionCommandHandler Klasse enthält, die folgenden using Direktiven hinzu.

    using Microsoft.VisualStudio.Text.Operations;
    using MSXML;
    using System.ComponentModel.Composition;
    
  2. Implementieren Sie die Schnittstelle durch die TestCompletionCommandHandlerIVsExpansionClient Klasse.

    internal class TestCompletionCommandHandler : IOleCommandTarget, IVsExpansionClient
    
  3. Importieren Sie in der TestCompletionCommandHandlerProvider Klasse die ITextStructureNavigatorSelectorService.

    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
  4. Fügen Sie einige private Felder für die Codeerweiterungsschnittstellen und die IVsTextView.

    IVsTextView m_vsTextView;
    IVsExpansionManager m_exManager;
    IVsExpansionSession m_exSession;
    
  5. Legen Sie im Konstruktor der TestCompletionCommandHandler Klasse die folgenden Felder fest.

    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. Um die Codeausschnittauswahl anzuzeigen, wenn der Benutzer auf den Befehl "Codeausschnitt einfügen" klickt, fügen Sie der Methode den folgenden Code hinzu Exec . (Um diese Erklärung besser lesbar zu machen, wird der Code, der für die Exec()Anweisungsabschluss verwendet wird, nicht angezeigt. Stattdessen werden Codeblöcke der vorhandenen Methode hinzugefügt.) Fügen Sie den folgenden Codeblock nach dem Code hinzu, der nach einem Zeichen sucht.

    //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. Wenn ein Codeausschnitt Felder enthält, die navigiert werden können, wird die Erweiterungssitzung geöffnet, bis die Erweiterung explizit akzeptiert wird. wenn der Codeausschnitt keine Felder enthält, wird die Sitzung geschlossen und wie null von der InvokeInsertionUI Methode zurückgegeben. Fügen Sie in der Exec Methode nach dem codeausschnittauswahl-UI-Code, den Sie im vorherigen Schritt hinzugefügt haben, den folgenden Code zum Behandeln der Codeausschnittnavigation hinzu (wenn der Benutzer die TAB-TASTE oder UMSCHALT-TAB+ nach dem Einfügen des Codeausschnitts drückt).

    //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. Wenn Sie den Codeausschnitt einfügen möchten, wenn der Benutzer die entsprechende Verknüpfung eingibt und dann die TAB-TASTE drückt, fügen Sie der Exec Methode Code hinzu. Die private Methode, die den Codeausschnitt einfügt, wird in einem späteren Schritt angezeigt. Fügen Sie den folgenden Code nach dem Navigationscode hinzu, den Sie im vorherigen Schritt hinzugefügt haben.

    //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. Implementieren Sie die Methoden der IVsExpansionClient Schnittstelle. In dieser Implementierung sind und sind EndExpansion die einzigen Interessanten Methoden und OnItemChosen. Die anderen Methoden sollten nur zurückgegeben werden 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. Implementieren Sie die OnItemChosen-Methode. Die Hilfsmethode, die die Erweiterungen tatsächlich einfügt, wird in einem späteren Schritt behandelt. Es TextSpan werden Zeilen- und Spalteninformationen bereitgestellt, die Sie aus der IVsTextViewDatei abrufen können.

    public int OnItemChosen(string pszTitle, string pszPath)
    {
        InsertAnyExpansion(null, pszTitle, pszPath);
        return VSConstants.S_OK;
    }
    
  11. Mit der folgenden privaten Methode wird ein Codeausschnitt eingefügt, der entweder auf der Verknüpfung oder auf dem Titel und pfad basiert. Anschließend wird die InsertNamedExpansion Methode mit dem Codeausschnitt aufgerufen.

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

Erstellen und Testen der Codeausschnitterweiterung

Sie können testen, ob die Codeausschnitterweiterung in Ihrem Projekt funktioniert.

  1. Erstellen Sie die Projektmappe. Wenn Sie dieses Projekt im Debugger ausführen, wird eine zweite Instanz von Visual Studio gestartet.

  2. Öffnen Sie eine Textdatei, und geben Sie Text ein.

  3. Klicken Sie mit der rechten Maustaste auf eine beliebige Stelle im Text, und klicken Sie dann auf "Codeausschnitt einfügen".

  4. Die Benutzeroberfläche für die Codeausschnittauswahl sollte mit einem Popup angezeigt werden, das "Testersetzungsfelder" enthält. Doppelklicken Sie auf das Popup.

    Der folgende Codeausschnitt sollte eingefügt werden.

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

    Drücken Sie nicht die EINGABETASTE oder ESC.

  5. Drücken Sie TAB und UMSCHALT-TAB+, um zwischen "erster" und "sekunde" umzuschalten.

  6. Akzeptieren Sie die Einfügemarke, indem Sie die EINGABETASTE oder ESC drücken.

  7. Geben Sie in einem anderen Teil des Texts "test" ein, und drücken Sie dann die TAB-TASTE. Da "test" die Codeausschnittverknüpfung ist, sollte der Codeausschnitt erneut eingefügt werden.