共用方式為


程式碼片段 (受管理的封裝架構) 的支援

程式碼片段是一種插入的原始程式檔的程式碼。 程式碼片段本身是以 XML 為基礎的範本,內含一組欄位。 之後便會插入程式碼片段,且可以有不同的值,用來插入程式碼片段的內容而定,會反白顯示這些欄位。 立即插入程式碼片段後,語言服務可以格式化程式碼片段。

程式碼片段會插入一種特殊的編輯模式,可讓使用 TAB 鍵巡覽至程式碼片段的欄位。 欄位可以支援 IntelliSense 樣式下拉式功鄋瞴。 使用者認可到原始程式檔的程式碼片段輸入 ENTER 或 ESC 鍵。 如果要進一步了解程式碼片段,請參閱程式碼片段

管理套件架構支援程式碼片段

受管理的封裝架構 (MPF) 支援大部份的程式碼片段功能,無法讀取要插入程式碼片段的範本,並啟用特殊編輯模式。 支援透過管理ExpansionProvider類別。

Source類別執行個體化時, CreateExpansionProvider中的方法LanguageService類別呼叫以取得ExpansionProvider物件 (請注意,基底LanguageService類別永遠會傳回新的ExpansionProvider每個物件Source物件)。

MPF 不支援擴充功能。 擴充函式是命名的函式內嵌於程式碼片段範本,並傳回一或多個要放在欄位中的值。 傳回的值所用的語言服務本身透過ExpansionFunction物件。 ExpansionFunction物件必須實作語言服務支援擴充功能。

提供程式碼片段的支援

若要啟用的程式碼片段支援,您必須提供系統,或安裝的程式碼片段,您必須提供使用者可將這些程式碼片段的方法。 有三個步驟來啟用程式碼片段的支援:

  1. 正在安裝的程式碼片段檔案。

  2. 啟用語言服務的程式碼片段。

  3. 叫用ExpansionProvider物件。

安裝程式碼片段檔案

通常一個程式碼片段範本每個檔案時,會以 XML 檔中的範本儲存所有的程式碼片段的語言。 如需使用程式碼片段範本的 XML 結構描述的詳細資訊,請參閱程式碼片段結構描述參考。 每個程式碼片段範本識別語言 id。 此語言 ID 在登錄中指定了,而且會放入Language <Code> 的屬性 在範本中的標記。

通常會有兩個儲存程式碼片段範本檔案的位置: 1) 已安裝您的語言,以及 2) 在 [使用者] 資料夾中。 這些位置新增至登錄所以,Visual Studio 程式碼片段管理員為 true 即可的程式碼片段。 使用者的資料夾是儲存使用者所建立的程式碼片段的位置。

已安裝的程式碼片段的範本檔案的典型資料夾配置看起來像這樣: [InstallRoot]\[TestLanguage]\Snippets\[LCID]\Snippets。

[InstallRoot]是在安裝程式語言的資料夾。

[TestLanguage]是您的語言與資料夾名稱的名稱。

[LCID]的地區設定識別碼。 這是您的程式碼片段的方式當地語系化的版本會儲存。 例如,英文的地區設定識別碼是 1033,所以 [LCID] 已由 1033年。

必須提供一個額外的檔案也就是未索引的檔案,通常會呼叫 SnippetsIndex.xml 或 ExpansionsIndex.xml (您可以使用任何有效的檔名,以.xml 結尾)。 這個檔案通常儲存在 [InstallRoot]\[TestLanguage] 資料夾,並指定程式碼片段資料夾,以及語言識別碼的確切位置的語言服務所使用的程式碼片段的 GUID。 索引檔案的確切路徑會放入登錄中,如稍後所述 〈 安裝登錄項目 〉。 下面是 SnippetsIndex.xml 檔案的範例:

<?xml version="1.0" encoding="utf-8" ?>
<SnippetCollection>
    <Language Lang="Testlanguage" Guid="{b614a40a-80d9-4fac-a6ad-fc2868fff7cd}">
        <SnippetDir>
            <OnOff>On</OnOff>
            <Installed>true</Installed>
            <Locale>1033</Locale>
            <DirPath>%InstallRoot%\TestLanguage\Snippets\%LCID%\Snippets\</DirPath>
            <LocalizedName>Snippets</LocalizedName>
        </SnippetDir>
    </Language>
</SnippetCollection>

<Language> 標記用來指定語言識別碼 ( Lang屬性) 和語言服務的 GUID。

本範例假設您已安裝語言服務在 Visual Studio 的安裝資料夾。 %的 LCID %會被取代的使用者目前的地區設定 id。 多個 <SnippetDir> 標籤可以加入,一個用於每個不同的目錄和地區設定。 此外,程式碼片段資料夾可以包含子資料夾,每一種會 <SnippetSubDir> 用來識別在索引檔案 內嵌於 <SnippetDir> 的標記 標記。

使用者也可以建立自己的程式碼片段,您的語言。 這些通常儲存在使用者的 [設定] 資料夾,例如 [TestDocs]\Code Snippets\[TestLanguage]\Test 程式碼片段,其中 [TestDocs] 是 Visual Studio 的使用者的 [設定] 資料夾的位置。

下列的替代項目可以放入儲存在 <DirPath> 中的路徑 索引檔中的標記。

項目

描述

%LCID%

地區設定 ID。

%Installroot%

Visual Studio,比方說,檔必要 Visual Studio 8 根安裝資料夾。

%Projdir%

包含目前的專案資料夾。

%Projitem%

包含目前的專案項目資料夾。

%Testdocs%

資料夾的使用者設定] 資料夾中,例如 「 C:\Documents 」 和 「 Settings\[使用者名稱]\My Documents\Visual Studio\8。

啟用語言服務的程式碼片段

您可以啟用程式碼片段的語言服務藉由新增ProvideLanguageCodeExpansionAttribute屬性設定為您的 VSPackage (請參閱正在註冊語言服務 (管理的套件架構)如需詳細資訊)。 ShowRootsSearchPaths參數是選擇性的但您必須加上SearchPaths參數名稱,以通知程式碼片段管理員您的程式碼片段的位置。

如何使用這個屬性的範例如下:

[ProvideLanguageCodeExpansion(
         typeof(TestSnippetLanguageService),
         "Test Snippet Language",          // Name of language used as registry key
         0,                               // Resource ID of localized name of language service
         "Test Snippet Language",        // Name of Language attribute in snippet template
         @"%InstallRoot%\Test Snippet Language\Snippets\%LCID%\SnippetsIndex.xml",  // Path to snippets index
         SearchPaths = @"%InstallRoot%\Test Snippet Language\Snippets\%LCID%\")]    // Path to snippets

呼叫擴充提供者

語言服務控制任何程式碼片段中,以及插入會叫用的方式插入的動作。

呼叫程式碼片段的擴充提供者

若要叫用擴充提供者的兩種方式: 使用功能表命令或使用 [完成] 清單中的捷徑。

使用功能表指令來插入程式碼片段

若要使用功能表命令,以顯示程式碼片段的瀏覽器,您加入功能表命令,然後呼叫DisplayExpansionBrowser中的方法ExpansionProvider介面,該功能表命令的回應。

  1. .Vsct 檔案中加入命令和按鈕。 您可以找到在執行動作的指示逐步解說: 使用 Visual Studio 的封裝範本建立功能表命令

  2. 衍生類別從ViewFilter類別並覆寫QueryCommandStatus方法,以指示新的功能表命令的支援。 本範例永遠會啟用功能表指令。

    using Microsoft.VisualStudio.Package;
    
    namespace TestLanguagePackage
    {
        class TestViewFilter : ViewFilter
        {
            public TestViewFilter(CodeWindowManager mgr, IVsTextView view)
                : base(mgr, view)
            {
            }
    
            protected override int QueryCommandStatus(ref Guid guidCmdGroup,
                                                      uint nCmdId)
            {
                int hr = base.QueryCommandStatus(ref guidCmdGroup, nCmdId);
                // If the base class did not recognize the command then
                // see if we can handle the command.
                if (hr == (int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_UNKNOWNGROUP)
                {
                    if (guidCmdGroup == GuidList.guidTestLanguagePackageCmdSet)
                    {
                        if (nCmdId == PkgCmdIDList.InvokeCodeSnippetsBrowser)
                        {
                            hr = (int)(OLECMDF.OLECMDF_SUPPORTED | OLECMDF.OLECMDF_ENABLED);
                        }
                    }
                }
                return hr;
            }
        }
    }
    
  3. 覆寫HandlePreExec中的方法ViewFilter類別,以取得ExpansionProvider物件和呼叫DisplayExpansionBrowser上該物件的方法。

    using Microsoft.VisualStudio.Package;
    
    namespace TestLanguagePackage
    {
        class TestViewFilter : ViewFilter
        {
            public override bool HandlePreExec(ref Guid guidCmdGroup,
                                               uint nCmdId,
                                               uint nCmdexecopt,
                                               IntPtr pvaIn,
                                               IntPtr pvaOut)
            {
                if (base.HandlePreExec(ref guidCmdGroup,
                                       nCmdId,
                                       nCmdexecopt,
                                       pvaIn,
                                       pvaOut))
                {
                    // Base class handled the command.  Do nothing more here.
                    return true;
                }
    
                if (guidCmdGroup == GuidList.guidTestLanguagePackageCmdSet)
                {
                    if (nCmdId == PkgCmdIDList.InvokeCodeSnippetsBrowser)
                    {
                        ExpansionProvider ep = this.GetExpansionProvider();
                        if (this.TextView != null && ep != null)
                        {
                            bool bDisplayed = ep.DisplayExpansionBrowser(
                                this.TextView,
                                "TestLanguagePackage Snippet:",
                                null,
                                false,
                                null,
                                false);
                        }
                        return true;   // Handled the command.
                    }
                }
                return false;   // Did not handle the command.
            }
        }
    }
    

    下列的方法,在ExpansionProvider類別由呼叫 Visual Studio 指定的順序插入程式碼片段的過程:

  4. OnItemChosen

  5. IsValidKind

  6. OnBeforeInsertion

  7. FormatSpan

  8. OnAfterInsertion

    OnAfterInsertion呼叫方法時,在插入程式碼片段和ExpansionProvider物件處於一種特殊的編輯模式,用來修改剛插入的程式碼片段。

使用快速鍵來插入程式碼片段

實作完成清單中是捷徑的更深入比實作功能表命令。 您必須先將程式碼片段的捷徑,加入 IntelliSense 的文字完成清單。 然後您必須偵測已經完成的結果插入程式碼片段捷徑名稱。 最後,您必須取得的程式碼片段的標題和使用的捷徑名稱的路徑,並傳遞該資訊以InsertNamedExpansion上的方法ExpansionProvider方法。

如果要將程式碼片段的捷徑加入至 word 的 [完成] 清單中,將它們以Declarations物件在您AuthoringScope類別。 您必須確定您可以識別為程式碼片段名稱的捷徑。 如需範例,請參閱 逐步解說: 才會列出已安裝的程式碼片段 (受管理的封裝架構)

您可以偵測到所插入的程式碼片段捷徑,在OnAutoComplete方法的Declarations類別。 因為原始程式檔已插入程式碼片段名稱,必須移除擴充插入時。 InsertNamedExpansion方法會將告訴您的程式碼片段 ; 插入點的範圍 如果範圍會包含原始程式檔中的整個程式碼片段名稱,該名稱會取代程式碼片段。

下面是一個版本的Declarations類別會處理程式碼片段插入指定捷徑的名稱。 其他的方法,在Declarations類別已經省略,以利檢視。 請注意,這個類別的建構函式LanguageService物件。 這可以傳入的版本AuthoringScope物件 (例如,您的實作AuthoringScope類別可能要花LanguageService其建構函式中的物件,並傳遞至該物件您TestDeclarations類別建構函式)。

[C#]
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;

namespace TestLanguagePackage
{
    internal class TestDeclarations : Declarations
    {
        private ArrayList       declarations;
        private LanguageService languageService;
        private TextSpan        commitSpan;

        public TestDeclarations(LanguageService langService)
            : base()
        {
            languageService = langService;
            declarations = new ArrayList();
        }

        // This method is used to add declarations to the internal list.
        public void AddDeclaration(TestDeclaration declaration)
        {
            declarations.Add(declaration);
        }

        

        // This method is called to get the string to commit to the source buffer.
        // Note that the initial extent is only what the user has typed so far.
        public override string OnCommit(IVsTextView textView,
                                        string textSoFar,
                                        char commitCharacter,
                                        int index,
                                        ref TextSpan initialExtent)
        {
            // We intercept this call only to get the initial extent
            // of what was committed to the source buffer.
            commitSpan = initialExtent;

            return base.OnCommit(textView,
                                 textSoFar,
                                 commitCharacter,
                                 index,
                                 ref initialExtent);
        }

        // This method is called after the string has been committed to the source buffer.
        public override char OnAutoComplete(IVsTextView textView,
                                            string committedText,
                                            char commitCharacter,
                                            int index)
        {
            TestDeclaration item = declarations[index] as TestDeclaration;
            if (item != null)
            {
                // In this example, TestDeclaration identifies types with a string.
                // You can choose a different approach.
                if (item.Type == "snippet")
                {
                    Source src = languageService.GetSource(textView);
                    if (src != null)
                    {
                        ExpansionProvider ep = src.GetExpansionProvider();
                        if (ep != null)
                        {
                            string title;
                            string path;
                            int commitLength = commitSpan.iEndIndex - commitSpan.iStartIndex;
                            if (commitLength < committedText.Length)
                            {
                                // Replace everything that was inserted
                                // so calculate the span of the full
                                // insertion, taking into account what
                                // was inserted when the commitSpan
                                // was obtained in the first place.
                                commitSpan.iEndIndex += (committedText.Length - commitLength);
                            }

                            if (ep.FindExpansionByShortcut(textView,
                                                           committedText,
                                                           commitSpan,
                                                           true,
                                                           out title,
                                                           out path))
                            {
                                ep.InsertNamedExpansion(textView,
                                                        title,
                                                        path,
                                                        commitSpan,
                                                        false);
                            }
                        }
                    }
                }
            }
            return '\0';
        }
    }
}

當語言服務取得的捷徑名稱時,它會呼叫FindExpansionByShortcut方法,以取得檔名和程式碼片段的標題。 語言服務接著會呼叫InsertNamedExpansion中的方法ExpansionProvider類別來插入程式碼片段。 Visual Studio 會呼叫下列方法中指定的順序ExpansionProvider在過程中插入程式碼片段的類別:

  1. IsValidKind

  2. OnBeforeInsertion

  3. FormatSpan

  4. OnAfterInsertion

如需有關如何取得一份已安裝的程式碼片段的語言服務的詳細資訊,請參閱逐步解說: 才會列出已安裝的程式碼片段 (受管理的封裝架構)

實作 ExpansionFunction 類別

擴充函式是命名的函式內嵌於程式碼片段範本,並傳回一或多個要放在欄位中的值。 若要在您的語言服務支援擴充功能,您必須衍生類別,以從ExpansionFunction類別並實作GetCurrentValue方法。 您必須再覆寫CreateExpansionFunction中的方法LanguageService類別以傳回新的具現化安裝的版本ExpansionFunction您支援的每個擴充函式的類別。 如果您支援的擴充函式的可能值清單時,您也必須覆寫GetIntellisenseList中的方法ExpansionFunction類別以傳回這些值的清單。

擴充函式,函式引數,或是需要存取其他的欄位應該無法編輯的欄位,與相關聯,作為擴充提供者可能無法完全初始化擴充函式被呼叫時。 如此一來,擴充函式不能夠取得它的引數或任何其他的欄位的值。

範例

以下是範例的簡單擴充函式的呼叫GetName ,即可實作。 這個擴充函式附加一個數字基底類別名稱來擴充函式會具現化每次 (其對應到每個時間相關聯的程式碼片段插入)。

using Microsoft.VisualStudio.Package;

namespace TestLanguagePackage
{
    public class TestLanguageService : LanguageService
    {
        private int classNameCounter = 0;

        public override ExpansionFunction CreateExpansionFunction(
            ExpansionProvider provider,
            string functionName)
        {
            ExpansionFunction function = null;
            if (functionName == "GetName")
            {
                ++classNameCounter;
                function = new TestGetNameExpansionFunction(provider, classNameCounter);
            }
            return function;
        }
    }

    internal class TestGetNameExpansionFunction : ExpansionFunction
    {
        private int nameCount;

        TestGetNameExpansionFunction(ExpansionProvider provider, int counter)
            : base(provider)
        {
            nameCount = counter;
        }

        public override string GetCurrentValue()
        {
            string name = "TestClass";
            name += nameCount.ToString();
            return name;
        }
    }
}

請參閱

工作

逐步解說: 才會列出已安裝的程式碼片段 (受管理的封裝架構)

概念

正在註冊語言服務 (管理的套件架構)

其他資源

語言服務功能 (受管理的封裝架構)

程式碼片段