Поделиться через


Пошаговое руководство: Создание фрагментов кода

Можно создать фрагменты кода и включить их в расширении редактор, чтобы пользователи могли добавлять их к их расширения своего кода.

Фрагмент кода фрагмент кода или другого текста, который можно включить в файл. Чтобы просмотреть все фрагменты, которые были зарегистрированы для конкретных языков программирования, в меню Сервис нажмите кнопку Диспетчер фрагментов кода. Чтобы вставить фрагмент в файл, щелкните правой кнопкой мыши где требуется фрагмент, щелкните Вставить фрагмент или Разместить во фрагменте, обнаруживают расположение фрагмента, а затем дважды щелкните их. Нажмите клавишу TAB или SHIFT+TAB для изменения соответствующие части фрагмента, а затем нажмите клавишу ввод или ESC, чтобы принять его. Дополнительные сведения см. в разделе Фрагменты кода.

Фрагмент кода содержится в XML-файл, который имеет расширение имени файла с расширением .snippet. Фрагмент может содержать поля, выбранных после вставки фрагмента, чтобы пользователь мог находить и изменять их. Файл фрагмента также предоставляет сведения для Диспетчер фрагментов кода таким образом, чтобы он мог указать имя фрагмента в правильную категории. Дополнительные сведения о схеме фрагмента см. в разделе Справочник по схеме фрагментов кода.

В этом пошаговом руководстве назначения, как выполнять эти задачи.

  1. Создайте и зарегистрируйте фрагменты кода для указанного языка.

  2. Добавьте команду Вставить фрагмент в контекстное меню.

  3. Реализуйте расширение фрагмента.

В этом пошаговом руководстве основано на Пошаговое руководство: Завершение операторов отображение.

Обязательные компоненты

Чтобы выполнить это пошаговое руководство, необходимо установить пакет SDK Visual Studio 2010. Дополнительные сведения о пакет Visual Studio SDK и загрузить его см. в Центр разработчиков расширяемости Visual Studio разделе на веб-сайте MSDN.

Создание и регистрация фрагменты кода

Обычно фрагменты кода связаны с зарегистрированной службой языка. Однако не следует реализовывать LanguageService для регистрации фрагментов кода. Вместо этого просто укажите идентификатор GUID в файле индекса фрагмента, а затем используйте один и тот же идентификатор GUID в ProvideLanguageCodeExpansionAttribute, добавляемые в проект.

Следующие шаги демонстрируют, как создать фрагменты кода и связать их с определенным идентификатором GUID.

Создание фрагментов кода

  1. Создайте следующую структуру каталогов.

    %InstallDir%\TestSnippets\Snippets\1033\

    где %InstallDir% — это папка установки Visual Studio. (Хотя этот путь обычно используется для задания фрагменты кода, можно указать любой путь).

  2. В папке \1033\ создайте XML-файл и задайте для него имя SnippetIndex.xml. (Хотя это имя, как правило, используется для файла индекса фрагмента, можно задать любое имя, если оно имеет расширение имени файла XML). Добавьте следующий текст, а затем удалите GUID заполнителя и добавьте необходимые требования.

    <?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. Создайте файл в папке фрагментов, назовите его тестом .snippet, а затем добавьте следующий текст:

    <?xml version="1.0" encoding="utf-8" ?>
    <CodeSnippets  xmlns="https://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>
    

Следующие шаги показывают, как регистрировать фрагментов кода.

Регистрация фрагменты кода для конкретного идентификатора GUID

  1. Открытие проекта CompletionTest. Сведения о том, как создать этот проект, см. в разделе Пошаговое руководство: Завершение операторов отображение.

  2. В проекте добавьте ссылки на следующие сборки:

    • Microsoft.VisualStudio.TextManager.Interop

    • Microsoft.VisualStudio.TextManager.Interop.8.0

    • microsoft.msxml

  3. Чтобы зарегистрировать фрагменты кода, проект должен выводить файл .pkgdef. Дополнительные сведения см. в разделе Registering VSPackages.

    В проекте откройте файл source.extension.vsixmanifest.

  4. В разделе Содержимое нажмите кнопку Добавление содержимого выберите тип содержимого Пакет VS выберите параметр Проект и выберите TestCompletion в раскрывающемся списке.

    Следующая линия должна появиться в файл.

    TestCompletion;PkgdefProjectOutputGroup|
    
  5. Сохраните файл и закройте его. source.extension.vsixmanifest

  6. Добавьте статический класс SnippetUtilities в проект.

    Module SnippetUtilities
    
    static class SnippetUtilities
    
  7. В классе SnippetUtilities укажите идентификатор GUID и присвойте ему значение, выбранное в файле SnippetsIndex.xml.

    Friend Const LanguageServiceGuidStr As String = "00000000-0000-0000-0000-00000000"
    
    internal const string LanguageServiceGuidStr = "00000000-0000-0000-0000-00000000";
    
  8. Добавьте в класс ProvideLanguageCodeExpansionAttributeTestCompletionHandler. Этот атрибут можно добавить к любому пользователю предоставлено или нестатическими внутреннему классу () в проекте. (Можно добавить оператор using для пространства имен Microsoft.VisualStudio.Shell).

    <ProvideLanguageCodeExpansion(
    SnippetUtilities.LanguageServiceGuidStr,
    "TestSnippets",
    0,
    "TestSnippets",
    "%InstallRoot%\TestSnippets\Snippets\%LCID%\SnippetsIndex.xml",
    SearchPaths:="%InstallRoot%\TestSnippets\Snippets\%LCID%\",
    ForceCreateDirs:="%InstallRoot%\TestSnippets\Snippets\%LCID%\")>
    Friend Class TestCompletionCommandHandler
        Implements IOleCommandTarget
    
    [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%\SnippetsIndex.xml",
        //the path of the index file
    SearchPaths = @"%InstallRoot%\TestSnippets\Snippets\%LCID%\",
    ForceCreateDirs = @"%InstallRoot%\TestSnippets\Snippets\%LCID%\")]
    internal class TestCompletionCommandHandler : IOleCommandTarget
    
  9. Выполните построение и запуск проекта. В экспериментальном экземпляре Visual Studio, которое запускается при запуске проекта фрагмент просто регистрации должен отображаться в Диспетчер фрагментов кода с языком TestSnippets.

Добавление команды вставить фрагмент в контекстное меню

Команда Вставить фрагмент не включается в контекстном меню для текстового файла. Поэтому необходимо включить команды.

Добавление команды вставить фрагмент в контекстное меню

  1. Откройте файл класса TestCompletionCommandHandler.

    Поскольку этот класс реализует IOleCommandTarget, можно активировать команды Вставить фрагмент в методе QueryStatus. Прежде чем включать команду, убедитесь, что этот метод не вызывается внутри функции автоматизации, поскольку если команда Вставить фрагмент ", она будет отображать пользовательский интерфейс выбора фрагмента (пользовательский интерфейс).

    Public Function QueryStatus(ByRef pguidCmdGroup As Guid, ByVal cCmds As UInteger, ByVal prgCmds As OLECMD(), ByVal pCmdText As IntPtr) As Integer Implements IOleCommandTarget.QueryStatus
        If Not VsShellUtilities.IsInAutomationFunction(m_provider.ServiceProvider) Then 
            If pguidCmdGroup = VSConstants.VSStd2K AndAlso cCmds > 0 Then 
                ' make the Insert Snippet command appear on the context menu  
                If CUInt(prgCmds(0).cmdID) = CUInt(VSConstants.VSStd2KCmdID.INSERTSNIPPET) Then
                    prgCmds(0).cmdf = CInt(Constants.MSOCMDF_ENABLED) Or CInt(Constants.MSOCMDF_SUPPORTED)
                    Return VSConstants.S_OK
                End If 
            End If 
        End If 
    
        Return m_nextCommandHandler.QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText)
    End Function
    
    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. Выполните построение и запуск проекта. В экспериментальном экземпляре, откройте файл с расширением имени файла и щелкните правой кнопкой мыши в любом месте .zzz их. Команда Вставить фрагмент должна появиться в контекстном меню.

Реализация расширения фрагмента в пользовательском интерфейсе snippet picker

В этом подразделе показано, как реализовать расширение фрагмента кода, так что пользовательский интерфейс выбора фрагмента отображается при Вставить фрагмент будет нажата кнопка в контекстном меню. Фрагмент кода также развернут когда пользователь вводит ярлык код-фрагмента и нажмите клавишу TAB.

Для отображения пользовательского интерфейса и snippet picker, чтобы обеспечить принятие фрагмента навигации и POST-вставки, используйте метод Exec. Вставка OnItemChosen сама обрабатывается методом.

Реализация расширения фрагмента кода использует устаревшие интерфейсы Microsoft.VisualStudio.TextManager.Interop. Если переводите от текущих классов редактора к устаревшим кодом, помните, что устаревшие интерфейсы используются номера линии сочетание и номера столбца для определения расположения в текстовом буфере, но существующие классы используют один индекс. Поэтому, если буфер содержит 3 линии, каждый из которых имеет 10 символов (плюс новой строки, который считается 1 символов), четвертый символ в третьей линии в позиции 27 в текущей реализации, но на линии 2, то положение 3 в старой реализации.

К расширению фрагмента "

  1. К файлу, который содержит класс TestCompletionCommandHandler добавьте следующие using выписки.

    Imports Microsoft.VisualStudio.Text.Operations
    Imports MSXML
    
    using Microsoft.VisualStudio.Text.Operations;
    using MSXML;
    using System.ComponentModel.Composition;
    
  2. Сделайте класс TestCompletionCommandHandler реализовать интерфейс IVsExpansionClient.

    <ProvideLanguageCodeExpansion(
    SnippetUtilities.LanguageServiceGuidStr,
    "TestSnippets",
    0,
    "TestSnippets",
    "%InstallRoot%\TestSnippets\Snippets\%LCID%\SnippetsIndex.xml",
    SearchPaths:="%InstallRoot%\TestSnippets\Snippets\%LCID%\",
    ForceCreateDirs:="%InstallRoot%\TestSnippets\Snippets\%LCID%\")>
    Friend Class TestCompletionCommandHandler
        Implements IOleCommandTarget
        Implements IVsExpansionClient
    
    internal class TestCompletionCommandHandler : IOleCommandTarget, IVsExpansionClient
    
  3. В классе TestCompletionCommandHandlerProvider, импортировать ITextStructureNavigatorSelectorService.

    <Import()>
    Friend Property NavigatorService As ITextStructureNavigatorSelectorService
    
    [Import]
    internal ITextStructureNavigatorSelectorService NavigatorService { get; set; }
    
  4. Добавьте закрытые поля для некоторых интерфейсов модулей кода и IVsTextView.

    Dim m_vsTextView As IVsTextView
    Dim m_exManager As IVsExpansionManager
    Dim m_exSession As IVsExpansionSession
    
    IVsTextView m_vsTextView;
    IVsExpansionManager m_exManager;
    IVsExpansionSession m_exSession;
    
  5. В конструкторе класса TestCompletionCommandHandler установите следующие поля.

    Friend Sub New(ByVal textViewAdapter As IVsTextView, ByVal textView As ITextView, ByVal provider As TestCompletionHandlerProvider)
        Me.m_textView = textView
        Me.m_provider = provider
        Me.m_vsTextView = textViewAdapter
    
        Dim textManager As IVsTextManager2 = DirectCast(m_provider.ServiceProvider.GetService(GetType(SVsTextManager)), IVsTextManager2)
        textManager.GetExpansionManager(m_exManager)
        m_exSession = Nothing 
    
        'add the command to the command chain
        textViewAdapter.AddCommandFilter(Me, m_nextCommandHandler)
    End Sub
    
    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. Snippet picker для отображения, когда пользователь выбирает соответствующую команду Вставить фрагмент добавьте следующий код в метод Exec. (Это сделать объяснение более четким, не показан код exec (), используемый для завершения выписки; вместо этого блоки кода добавляются к существующему методу). Добавьте следующий блок кода после кода, который проверяет наличие знака.

    'code previously written for Exec 
    If pguidCmdGroup = VSConstants.VSStd2K AndAlso nCmdID = CUInt(VSConstants.VSStd2KCmdID.TYPECHAR) Then
        typedChar = ChrW(CUShort(Marshal.GetObjectForNativeVariant(pvaIn)))
    End If 
    'the snippet picker code starts here 
    If nCmdID = CUInt(VSConstants.VSStd2KCmdID.INSERTSNIPPET) Then 
        Dim textManager As IVsTextManager2 = DirectCast(m_provider.ServiceProvider.GetService(GetType(SVsTextManager)), IVsTextManager2)
    
        textManager.GetExpansionManager(m_exManager)
        m_exManager.InvokeInsertionUI(
            m_vsTextView,
            Me,
            New Guid(SnippetUtilities.LanguageServiceGuidStr),
            Nothing,
            0,
            0,
            Nothing,
            0,
            0,
            "TestSnippets",
            String.Empty)
        Return VSConstants.S_OK
    End If
    
    //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. Если фрагмент содержит поля, которые можно перейти, сеанс расширения быть открыто до тех пор, пока расширение явно не принимаются. если фрагмент не содержит поля, сеанс закрыт и возвращается как null методом InvokeInsertionUI. В методе Exec, after code snippet picker пользовательского интерфейса, созданного на предыдущем этапе, добавьте следующий код к переходов фрагмента маркера (если пользователь отожмет TAB или SHIFT+TAB после вставки фрагмента).

    'the expansion insertion is handled in OnItemChosen 
    'if the expansion session is still active, handle tab/backtab/return/cancel 
    If m_exSession IsNot Nothing Then 
        If nCmdID = CUInt(VSConstants.VSStd2KCmdID.BACKTAB) Then
            m_exSession.GoToPreviousExpansionField()
            Return VSConstants.S_OK
        ElseIf nCmdID = CUInt(VSConstants.VSStd2KCmdID.TAB) Then
            m_exSession.GoToNextExpansionField(0)
            'false to support cycling through all the fields 
            Return VSConstants.S_OK
        ElseIf nCmdID = CUInt(VSConstants.VSStd2KCmdID.[RETURN]) OrElse nCmdID = CUInt(VSConstants.VSStd2KCmdID.CANCEL) Then 
            If m_exSession.EndCurrentExpansion(0) = VSConstants.S_OK Then
                m_exSession = Nothing 
                Return VSConstants.S_OK
            End If 
        End If 
    End If
    
    //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. Чтобы вставить фрагмент кода, когда пользователь вводит соответствующий ярлык, а затем нажмите клавишу TAB, добавить код в метод Exec. Закрытый метод, который представляет фрагмент будет отображаться на более позднем шаге. Добавьте следующий код после кода навигации, созданного на предыдущем шаге.

    '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 Is Nothing AndAlso m_exSession Is Nothing AndAlso nCmdID = CUInt(VSConstants.VSStd2KCmdID.TAB) Then 
        'get the word that was just added  
        Dim pos As CaretPosition = m_textView.Caret.Position
        Dim word As TextExtent = m_provider.NavigatorService.GetTextStructureNavigator(m_textView.TextBuffer).GetExtentOfWord(pos.BufferPosition - 1)
        Dim textString As String = word.Span.GetText()
        'if it is a code snippet, insert it, otherwise carry on 
        If InsertAnyExpansion(textString, Nothing, Nothing) Then 
            Return VSConstants.S_OK
        End If 
    End If
    
    //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. Реализуйте методы интерфейса IVsExpansionClient. В данной реализации EndExpansion только методы, представляющие интерес, а OnItemChosen. Другие методы должны просто возвращать [F:Microsoft.VisualStudio.VSConstants.S_ОК].

    Public Function EndExpansion() As Integer Implements IVsExpansionClient.EndExpansion
        m_exSession = Nothing 
        Return VSConstants.S_OK
    End Function 
    
    Public Function FormatSpan(ByVal pBuffer As IVsTextLines, ByVal ts As TextSpan()) As Integer Implements IVsExpansionClient.FormatSpan
        Return VSConstants.S_OK
    End Function 
    
    Public Function GetExpansionFunction(ByVal xmlFunctionNode As IXMLDOMNode, ByVal bstrFieldName As String, ByRef pFunc As IVsExpansionFunction) As Integer Implements IVsExpansionClient.GetExpansionFunction
        pFunc = Nothing 
        Return VSConstants.S_OK
    End Function 
    
    Public Function IsValidKind(ByVal pBuffer As IVsTextLines, ByVal ts As TextSpan(), ByVal bstrKind As String, ByRef pfIsValidKind As Integer) As Integer Implements IVsExpansionClient.IsValidKind
        pfIsValidKind = 1
        Return VSConstants.S_OK
    End Function 
    
    Public Function IsValidType(ByVal pBuffer As IVsTextLines, ByVal ts() As TextSpan, ByVal rgTypes() As String, ByVal iCountTypes As Integer, ByRef pfIsValidType As Integer) As Integer Implements IVsExpansionClient.IsValidType
        pfIsValidType = 1
        Return VSConstants.S_OK
    End Function 
    
    Public Function OnAfterInsertion(ByVal pSession As IVsExpansionSession) As Integer Implements IVsExpansionClient.OnAfterInsertion
        Return VSConstants.S_OK
    End Function 
    
    Public Function OnBeforeInsertion(ByVal pSession As IVsExpansionSession) As Integer Implements IVsExpansionClient.OnBeforeInsertion
        Return VSConstants.S_OK
    End Function 
    
    Public Function PositionCaretForEditing(ByVal pBuffer As IVsTextLines, ByVal ts As TextSpan()) As Integer Implements IVsExpansionClient.PositionCaretForEditing
        Return VSConstants.S_OK
    End Function
    
    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. Реализуйте метод OnItemChosen. Вспомогательный метод, который фактически будет предусматриван вставки расширений в последующем шаге. TextSpan предоставляет сведения о линии и столбца, которые можно получить из IVsTextView.

    Public Function OnItemChosen(ByVal pszTitle As String, ByVal pszPath As String) As Integer Implements IVsExpansionClient.OnItemChosen
        InsertAnyExpansion(Nothing, pszTitle, pszPath)
        Return VSConstants.S_OK
    End Function
    
    public int OnItemChosen(string pszTitle, string pszPath)
    {
        InsertAnyExpansion(null, pszTitle, pszPath);
        return VSConstants.S_OK;
    }
    
  11. Закрытый метод вставляет следующий фрагмент кода, основанный на ярлыке или либо на заголовке и пути. Затем он вызывает метод InsertNamedExpansion с фрагментом.

    Private Function InsertAnyExpansion(ByVal shortcut As String, ByVal title As String, ByVal path As String) As Boolean 
        Dim endColumn As Integer, startLine As Integer 
        'get the column number from  the IVsTextView, not the ITextView
        m_vsTextView.GetCaretPos(startLine, endColumn)
    
        Dim addSpan As New TextSpan()
        addSpan.iStartIndex = endColumn
        addSpan.iEndIndex = endColumn
        addSpan.iStartLine = startLine
        addSpan.iEndLine = startLine
    
        'get the expansion from the shortcut 
        If shortcut IsNot Nothing Then 
            '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(
                Me,
                New Guid(SnippetUtilities.LanguageServiceGuidStr),
                shortcut,
                m_vsTextView,
                New TextSpan() {addSpan},
                0,
                path,
                title)
        End If 
        If title IsNot Nothing AndAlso path IsNot Nothing Then 
            Dim textLines As IVsTextLines = Nothing
            m_vsTextView.GetBuffer(textLines)
            Dim bufferExpansion As IVsExpansion = DirectCast(textLines, IVsExpansion)
    
            If bufferExpansion IsNot Nothing Then 
                Dim hr As Integer = bufferExpansion.InsertNamedExpansion(
                    title,
                    path,
                    addSpan,
                    Me,
                    New Guid(SnippetUtilities.LanguageServiceGuidStr),
                    0,
                    m_exSession)
                If VSConstants.S_OK = hr Then 
                    Return True 
                End If 
            End If 
        End If 
        Return False 
    End Function
    
    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;
    }
    

Построение и тестирование расширения фрагмента кода

Можно проверить, работает ли расширение фрагмента в проекте.

Построение и расширение фрагмента кода теста

  1. Выполните построение решения. Если запустить этот проект в отладчике, второй экземпляр Visual Studio создается.

  2. Откройте текстовый файл и введите текст.

  3. Щелкните правой кнопкой мыши где-то в текст и выберите пункт Вставить фрагмент.

  4. Пользовательский интерфейс выбора фрагмента должен присутствовать и показаться строка замены «поля». Дважды щелкните эту строку.

    Следующий фрагмент должен быть вставлен.

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

    Не нажимайте клавишу ВВОД или ESC.

  5. Нажмите клавишу TAB, SHIFT+TAB для переключения между «first» и «далее».

  6. Примите insert, нажав клавиши или ВВОД или ESC.

  7. В другую часть текста, введите «тест» и нажмите клавишу TAB. Поскольку «тест» ярлык код-фрагмента фрагмент должен быть вставлен попытку.