Przewodnik: implementowanie fragmentów kodu

Możesz utworzyć fragmenty kodu i dołączyć je do rozszerzenia edytora, aby użytkownicy rozszerzenia mogli dodać je do własnego kodu.

Fragment kodu jest fragmentem kodu lub innego tekstu, który można włączyć do pliku. Aby wyświetlić wszystkie fragmenty kodu zarejestrowane dla określonych języków programowania, w menu Narzędzia kliknij pozycję Menedżer fragmentów kodu. Aby wstawić fragment kodu w pliku, kliknij prawym przyciskiem myszy miejsce, w którym chcesz umieścić fragment kodu, kliknij polecenie Wstaw fragment kodu lub Umieść za pomocą, znajdź odpowiedni fragment kodu, a następnie kliknij go dwukrotnie. Naciśnij klawisz Tab lub Klawisz Shift+, aby zmodyfikować odpowiednie części fragmentu kodu, a następnie naciśnij klawisz Enter lub Esc, aby go zaakceptować. Aby uzyskać więcej informacji, zobacz Fragmenty kodu.

Fragment kodu znajduje się w pliku XML zawierającym rozszerzenie nazwy pliku .snippet*. Fragment kodu może zawierać pola wyróżnione po wstawieniu fragmentu kodu, aby użytkownik mógł je znaleźć i zmienić. Plik fragmentu kodu zawiera również informacje dotyczące Menedżera fragmentów kodu, dzięki czemu może wyświetlać nazwę fragmentu kodu w odpowiedniej kategorii. Aby uzyskać informacje o schemacie fragmentu kodu, zobacz Dokumentacja schematu fragmentów kodu.

W tym przewodniku przedstawiono sposób wykonywania tych zadań:

  1. Tworzenie i rejestrowanie fragmentów kodu dla określonego języka.

  2. Dodaj polecenie Wstaw fragment kodu do menu skrótów.

  3. Zaimplementuj rozszerzenie fragmentu kodu.

    Ten przewodnik jest oparty na przewodniku: uzupełnianie instrukcji wyświetlania.

Tworzenie i rejestrowanie fragmentów kodu

Zazwyczaj fragmenty kodu są skojarzone z zarejestrowaną usługą językową. Nie trzeba jednak implementować elementu w celu rejestrowania LanguageService fragmentów kodu. Zamiast tego wystarczy określić identyfikator GUID w pliku indeksu fragmentu kodu, a następnie użyć tego samego identyfikatora GUID w ProvideLanguageCodeExpansionAttribute pliku dodanym do projektu.

W poniższych krokach pokazano, jak utworzyć fragmenty kodu i skojarzyć je z określonym identyfikatorem GUID.

  1. Utwórz następującą strukturę katalogów:

    %InstallDir%\TestSnippets\Snippets\1033\

    gdzie %InstallDir% to folder instalacyjny programu Visual Studio. (Chociaż ta ścieżka jest zwykle używana do instalowania fragmentów kodu, można określić dowolną ścieżkę).

  2. W folderze \1033\ utwórz plik XML i nadaj mu nazwę TestSnippets.xml. (Chociaż ta nazwa jest zwykle używana dla pliku indeksu fragmentu kodu, można określić dowolną nazwę, o ile ma rozszerzenie nazwy pliku XML ). Dodaj następujący tekst, a następnie usuń identyfikator GUID symbolu zastępczego i dodaj własny.

    <?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. Utwórz plik w folderze fragmentu kodu, nadaj mu nazwę test.snippet, a następnie dodaj następujący tekst:

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

    W poniższych krokach pokazano, jak zarejestrować fragmenty kodu.

Aby zarejestrować fragmenty kodu dla określonego identyfikatora GUID

  1. Otwórz projekt CompletionTest. Aby uzyskać informacje o sposobie tworzenia tego projektu, zobacz Wskazówki: uzupełnianie instrukcji wyświetlania.

  2. W projekcie dodaj odwołania do następujących zestawów:

    • Microsoft.VisualStudio.TextManager.Interop

    • Microsoft.VisualStudio.TextManager.Interop.8.0

    • microsoft.msxml

  3. W projekcie otwórz plik source.extension.vsixmanifest .

  4. Upewnij się, że karta Zasoby zawiera typ zawartości VsPackage i że program Project jest ustawiony na nazwę projektu.

  5. Wybierz projekt CompletionTest i w okno Właściwości ustaw wartość Generate Pkgdef File (Generuj plik Pkgdef) na true. Zapisz projekt.

  6. Dodaj klasę statyczną SnippetUtilities do projektu.

    static class SnippetUtilities
    
  7. W klasie SnippetUtilities zdefiniuj identyfikator GUID i nadaj mu wartość użytą w pliku SnippetsIndex.xml .

    internal const string LanguageServiceGuidStr = "00000000-0000-0000-0000-00000000";
    
  8. Dodaj element ProvideLanguageCodeExpansionAttribute do TestCompletionHandler klasy . Ten atrybut można dodać do dowolnej publicznej lub wewnętrznej (niestacjonanej) klasy w projekcie. (Może być konieczne dodanie using dyrektywy dla przestrzeni nazw 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. Skompiluj i uruchom projekt. W eksperymentalnym wystąpieniu programu Visual Studio, które rozpoczyna się po uruchomieniu projektu, właśnie zarejestrowany fragment kodu powinien być wyświetlany w Menedżerzefragmentów kodu w języku TestSnippets.

Dodaj polecenie Wstaw fragment kodu do menu skrótów

Polecenie Wstaw fragment kodu nie jest uwzględniane w menu skrótów dla pliku tekstowego. W związku z tym należy włączyć polecenie .

Aby dodać polecenie Wstaw fragment kodu do menu skrótów

  1. TestCompletionCommandHandler Otwórz plik klasy.

    Ponieważ ta klasa implementuje IOleCommandTargetmetodę , możesz aktywować polecenie Wstaw fragment kodu w metodzie QueryStatus . Przed włączeniem polecenia sprawdź, czy ta metoda nie jest wywoływana wewnątrz funkcji automatyzacji, ponieważ po kliknięciu polecenia Wstaw fragment kodu zostanie wyświetlony interfejs użytkownika selektora fragmentów kodu.

    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. Skompiluj i uruchom projekt. W wystąpieniu eksperymentalnym otwórz plik z rozszerzeniem nazwy pliku zzz , a następnie kliknij prawym przyciskiem myszy w dowolnym miejscu w nim. Polecenie Wstaw fragment kodu powinno pojawić się w menu skrótów.

Implementowanie rozszerzenia fragmentu kodu w interfejsie użytkownika selektora fragmentów kodu

W tej sekcji pokazano, jak zaimplementować rozszerzenie fragmentu kodu, aby interfejs użytkownika selektora fragmentów kodu był wyświetlany po kliknięciu wstawka fragmentu kodu w menu skrótów. Fragment kodu jest również rozszerzany, gdy użytkownik wpisze skrót fragmentu kodu, a następnie naciska klawisz Tab.

Aby wyświetlić interfejs użytkownika selektora fragmentów kodu i włączyć akceptację fragmentu kodu nawigacji i po wstawieniu, użyj Exec metody . Sama wstawienie jest obsługiwane przez metodę OnItemChosen .

Implementacja rozszerzenia fragmentu kodu używa starszych Microsoft.VisualStudio.TextManager.Interop interfejsów. Podczas tłumaczenia z bieżących klas edytora na starszy kod należy pamiętać, że starsze interfejsy używają kombinacji numerów wierszy i liczb kolumn do określania lokalizacji w buforze tekstowym, ale bieżące klasy używają jednego indeksu. W związku z tym, jeśli bufor ma trzy wiersze, z których każda ma 10 znaków (plus nowy wiersz, który liczy się jako jeden znak), czwarty znak w trzecim wierszu znajduje się na pozycji 27 w bieżącej implementacji, ale znajduje się w wierszu 2, pozycja 3 w starej implementacji.

Aby zaimplementować rozszerzenie fragmentu kodu

  1. Do pliku zawierającego klasę TestCompletionCommandHandler dodaj następujące using dyrektywy.

    using Microsoft.VisualStudio.Text.Operations;
    using MSXML;
    using System.ComponentModel.Composition;
    
  2. Ustaw klasę TestCompletionCommandHandler na implementację interfejsu IVsExpansionClient .

    internal class TestCompletionCommandHandler : IOleCommandTarget, IVsExpansionClient
    
  3. W klasie zaimportuj TestCompletionCommandHandlerProvider element ITextStructureNavigatorSelectorService.

    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
  4. Dodaj kilka pól prywatnych dla interfejsów rozszerzenia kodu i IVsTextView.

    IVsTextView m_vsTextView;
    IVsExpansionManager m_exManager;
    IVsExpansionSession m_exSession;
    
  5. W konstruktorze TestCompletionCommandHandler klasy ustaw następujące pola.

    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. Aby wyświetlić selektor fragmentów kodu, gdy użytkownik kliknie polecenie Wstaw fragment kodu, dodaj następujący kod do Exec metody . (Aby to wyjaśnienie było bardziej czytelne, Exec()kod używany do uzupełniania instrukcji nie jest wyświetlany; zamiast tego bloki kodu są dodawane do istniejącej metody). Dodaj następujący blok kodu po kodzie, który wyszukuje znak.

    //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. Jeśli fragment kodu zawiera pola, które można nawigować, sesja rozszerzenia jest otwarta do momentu jawnego zaakceptowania rozszerzenia; Jeśli fragment kodu nie zawiera żadnych pól, sesja jest zamknięta i jest zwracana zgodnie null z InvokeInsertionUI metodą . W metodzie Exec po kodzie interfejsu użytkownika selektora fragmentów kodu, który został dodany w poprzednim kroku, dodaj następujący kod, aby obsłużyć nawigację fragmentu kodu (gdy użytkownik naciska klawisz Tab lub Shift+Tab po wstawieniu fragmentu kodu).

    //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. Aby wstawić fragment kodu, gdy użytkownik wpisze odpowiedni skrót, a następnie naciśnie klawisz Tab, dodaj kod do Exec metody . Metoda prywatna, która wstawia fragment kodu, zostanie wyświetlona w późniejszym kroku. Dodaj następujący kod po kodzie nawigacji dodanym w poprzednim kroku.

    //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. Zaimplementuj metody interfejsu IVsExpansionClient . W tej implementacji jedynymi metodami zainteresowania są EndExpansion i OnItemChosen. Inne metody powinny po prostu zwrócić wartość 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. Zaimplementuj metodę OnItemChosen . Metoda pomocnika, która faktycznie wstawia rozszerzenia, jest omówiona w późniejszym kroku. Zawiera TextSpan informacje o wierszach i kolumnach, które można uzyskać z .IVsTextView

    public int OnItemChosen(string pszTitle, string pszPath)
    {
        InsertAnyExpansion(null, pszTitle, pszPath);
        return VSConstants.S_OK;
    }
    
  11. Poniższa metoda prywatna wstawia fragment kodu na podstawie skrótu lub tytułu i ścieżki. Następnie wywołuje metodę InsertNamedExpansion za pomocą fragmentu kodu.

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

Kompilowanie i testowanie rozszerzenia fragmentu kodu

Możesz sprawdzić, czy rozszerzenie fragmentu kodu działa w projekcie.

  1. Stwórz rozwiązanie. Po uruchomieniu tego projektu w debugerze zostanie uruchomione drugie wystąpienie programu Visual Studio.

  2. Otwórz plik tekstowy i wpisz tekst.

  3. Kliknij prawym przyciskiem myszy gdzieś w tekście, a następnie kliknij polecenie Wstaw fragment kodu.

  4. Interfejs użytkownika selektora fragmentów powinien pojawić się z wyskakującym okienkiem z wyświetlonym komunikatem Testowanie pól zastępczych. Kliknij dwukrotnie wyskakujące okienko.

    Należy wstawić poniższy fragment kodu.

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

    Nie naciskaj klawisza Enter ani Esc.

  5. Naciśnij klawisze Tab i Shift+Tab, aby przełączać się między "pierwszym" i "drugim".

  6. Zaakceptuj wstawienie, naciskając klawisz Enter lub Esc.

  7. W innej części tekstu wpisz "test", a następnie naciśnij klawisz Tab. Ponieważ "test" to skrót fragmentu kodu, fragment kodu powinien zostać wstawiony ponownie.