[搬運(yùn)] 將 Visual Studio 的代碼片段導(dǎo)出到 VS Code

原文 : A Visual Studio to Visual Studio Code Snippet Converter
作者 : Rick Strahl
譯者 : 張蘅水

導(dǎo)語(yǔ)

和原文作者一樣察绷,水弟我現(xiàn)在也是使用 VS Code 和 Rider 作為主力開發(fā)工具恬砂,尤其是 VS Code 可以跨平臺(tái)蚜点,又有豐富的插件支持和多種編程語(yǔ)言支持衬衬。當(dāng)我從 VS 轉(zhuǎn)移到以 VS Code 的開發(fā)過程中轧抗,遇到的最大問題就是代碼提示的不完善(被 VS 和 R# 調(diào)教壞了,總想按 tab 鍵)县忌。當(dāng)我看到原文作者通過從 VS 中導(dǎo)出代碼片段到 VS Code 時(shí)筷畦,瞬間被吸引到了。 雖然在不知不覺中用了 VS 自帶的代碼片段黍判,但我從來沒有想過要自定義專屬的代碼片段豫尽,最多也是制作用于項(xiàng)目的模板(方便創(chuàng)建特定的類型文件)。雖然導(dǎo)出到 Rider 不是很完美样悟,但 Rider 自帶了 R#拂募,對(duì)這方面的需求還是很少的庭猩。


譯文:

Visual Studio 內(nèi)置了非常好用的代碼片段工具窟她,多年來我一直在使用它來創(chuàng)建大量有用的擴(kuò)展片段,使我的日常開發(fā)更容易蔼水。我有很多 C# 代碼片段震糖,但更多的是用于 HTML 、自定義的 Bootstrap 代碼片段趴腋,乃至復(fù)雜的 HTML 控件代碼段吊说。偶爾也會(huì)用到 JavaScript 、XAML 甚至Powershell 优炬。

在過去的幾年里颁井,我越來越多地使用其他工具與 Visual Studio 結(jié)合使用。特別是 Visual Studio Code JetBrains Rider蠢护。

在多年的使用 Visual Studio 中雅宾,我已經(jīng)累積了 130 多個(gè)代碼片段。每當(dāng)我在其他開發(fā)環(huán)境中工作時(shí) ( VS Code 或者 Rider)葵硕,我真的很需要他們眉抬,特別是要寫一大段 HTML的時(shí)候贯吓,總是要去痛苦地去對(duì)應(yīng)的文檔站點(diǎn)查找。使用代碼片段功能蜀变,只需幾次擊鍵就會(huì)自動(dòng)填充我自定義的特定代碼悄谐,每天可節(jié)省大量時(shí)間。

所以我很需要代碼片段功能库北,有時(shí)我打開 Visual Studio 只是為了找到需要的 HTML 的代碼片段爬舰,然后將它們粘貼回 VS Code 或 Rider。雖然繁瑣寒瓦,但是仍然從文檔網(wǎng)站中復(fù)制代碼洼专,然后手動(dòng)修改代碼來得便捷。如果能在每個(gè)對(duì)應(yīng)的開發(fā)環(huán)境中直接執(zhí)行代碼片段的功能孵构,那就太好了屁商!

因此,在過去的幾個(gè)周末颈墅,我做了一個(gè)將 Visual Studio 中的代碼片段導(dǎo)出到 VS Code 中的小工具蜡镶,同時(shí)盡量能導(dǎo)出到 JetBrains Rider 。

如果你感興趣恤筛,可以在GitHub上找到代碼:

另外說一句官还,這還只是一個(gè)菜鳥項(xiàng)目,并不能保證它支持所有類型的的代碼片段毒坛。只是我自己擁有的 137 個(gè)代碼片段都完美地移植到 VS Code望伦,并且能夠運(yùn)行。同時(shí)我還可以重新導(dǎo)出煎殷, 輕松地導(dǎo)出新創(chuàng)建的代碼片段屯伞,這樣就可以對(duì)比和更新了。

對(duì)于 Rider 而言豪直,操作起來更為復(fù)雜劣摇,因?yàn)?Rider 有一種瘋狂的機(jī)制,可以將模板存儲(chǔ)在內(nèi)部的單個(gè)配置文件中弓乙。它還為 .NET相關(guān)的片段 (C#末融、VB、F#暇韧、Razor勾习、ASP.NET )和 基于 Web ( html、css懈玻、js 等)的代碼片段使用了多個(gè)完全不同的存儲(chǔ)引擎巧婶。所以工具目前僅支持一次性導(dǎo)出 .NET 相關(guān)代碼段,因?yàn)?Rider 中基于 GUID 的密鑰系統(tǒng)不允許在沒有 GUID 的情況下查找現(xiàn)有代碼段。后面我們?cè)僭敿?xì)介紹粹舵。

代碼片段轉(zhuǎn)換器

你可以通過借助 .NET 全局工具 (.NET Global SDK Tool )钮孵,使用 Nuget 下載和運(yùn)行代碼片段轉(zhuǎn)換器:

dotnet tool install --global dotnet-snippetconverter

如果您不想安裝并只運(yùn)行該工具,您可以克隆或下載Github倉(cāng)庫(kù)眼滤,然后:

cd .\SnippetConverter\
dotnet run

安裝后巴席, 可以通過指向文件夾或單個(gè)文件將 Visual Studio 中的代碼片段批量或單獨(dú)轉(zhuǎn)換為 VS Code 支持的代碼片段。

snippetconverter ~2017 -r -d

或者诅需,您可以像下面這張屏幕截圖那樣指定輸出文件:

image

有幾個(gè)選項(xiàng)可用于轉(zhuǎn)換單個(gè)片段和文件夾漾唉,使用前綴,遞歸文件夾堰塌,輸出生成文件的路徑等:

Syntax:
-------
SnippetConverter <sourceFileOrDirectory> -o <outputFile> 
                 --mode --prefix --recurse --display

Commands:
---------
HELP || /?          This help display           

Options:
--------
sourceFileOrDirectory  Either an individual snippet file, or a source folder
                       Optional special start syntax using `~` to point at User Code Snippets folder:
                       ~      -  Visual Studio User Code Snippets folder (latest version installed)
                       ~2017  -  Visual Studio User Code Snippets folder (specific VS version 2019-2012)                       

-o <outputFile>        Output file where VS Code snippets are generated into (ignored by Rider)   
                       Optional special start syntax using `~` to point at User Code Snippets folder:
                       %APPDATA%\Code\User\snippets\ww-my-codesnippets.code-snippets
                       ~\ww-my-codesnippets.code-snippets
                       if omitted generates `~\exported-visualstudio.code-snippets`
                       
-m,--mode              vs-vscode  (default)
                       vs-rider   experimental - (C#,VB.NET,html only)
-d                     display the target file in Explorer
-r                     if specifying a source folder recurses into child folders
-p,--prefix            snippet prefix generate for all snippets exported
                       Example: `ww-` on a snippet called `ifempty` produces `ww-ifempty`

Examples:
---------
# vs-vscode: Individual Visual Studio Snippet
SnippetConverter "~2017\Visual C#\My Code Snippets\proIPC.snippet" 
                 -o "~\ww-csharp.code-snippets" -d

# vs-vscode: All snippets in a folder user VS Snippets and in recursive child folers
SnippetConverter "~2017\Visual C#\My Code Snippets" -o "~\ww-csharp.code-snippets" -r -d

# vs-vscode: All the user VS Snippets and in recursive child folders
SnippetConverter ~2017\ -o "~\ww-all.code-snippets" -r -d

# vs-vscode: All defaults: Latest version of VS, all snippets export to  ~\visualstudio-export.code-snippets
SnippetConverter ~ -r -d --prefix ww-

# vs-rider: Individual VS Snippet
SnippetConverter "~2017\proIPC.snippet" -m vs-rider -d

# vs-rider: All VS Snippets in a folder
SnippetConverter "~2017\Visual C#\My Code Snippets" -m vs-rider -d

上面的用例應(yīng)該足夠說明用途了赵刑。如果還想要了解更多信息,請(qǐng)接著往下看......

什么是 VS Code 的代碼片段

如果您不熟悉或不使用代碼片段场刑,那您并不是少數(shù)人般此。它們?cè)?Visual Studio 中幾乎是一個(gè)隱藏的功能,這是一個(gè)恥辱牵现,因?yàn)樗鼈兪欠浅S杏玫纳a(chǎn)力工具铐懊。不幸的是,Visual Studio 沒有任何有用的內(nèi)置UI來創(chuàng)建這些片段瞎疼,因此大多數(shù)開發(fā)人員都沒有充分利用此功能科乎。Visual Studio 只能蹩腳地點(diǎn)擊 ** 工具 - > 代碼片段管理器 ** 菜單 ,除了一個(gè)查看器之外贼急,它沒有其他管理功能茅茂,僅僅是查看哪些片段是可用的,沒有內(nèi)置的方法來創(chuàng)建或編輯片段太抓,甚至跳轉(zhuǎn)到并查看代碼片段空闲。

但是,代碼片段僅僅只是位于用戶目錄的 Documents 文件夾下的 XML 文件腻异。它們非常容易創(chuàng)建和更新进副,僅僅是原始的 XML 文件,用 VS Code 等文本編輯器去做代碼片段和高亮實(shí)在是非常簡(jiǎn)單悔常。盡管在 Visual Studio 中有一些提供 UI 操作的劣質(zhì)插件,但它們往往比原始的代碼片段文件更麻煩给赞。

創(chuàng)建新代碼段的最佳方法是復(fù)制現(xiàn)有代碼段并對(duì)其進(jìn)行修改以滿足您的需求机打。

一般來說,代碼片段位于 (水弟我是直接用 Everything搜索的):

<Documents>\Visual Studio 2017\Code Snippets

每種語(yǔ)言技術(shù)都有自己的子文件夾進(jìn)行分組片迅,但僅僅是文件夾上的區(qū)分而已残邀。代碼片段實(shí)際上通過 XML中的 Language 屬性確定它們適用的語(yǔ)言。

Visual Studio在此位置附帶了許多代碼段,您可以使用這些代碼段作為新代碼段的模板進(jìn)行學(xué)習(xí)芥挣。

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>Property with INotifyPropertyChange raised</Title>
      <Description>Control Property with Attributes</Description>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
      <Shortcut>proIPC</Shortcut>
    </Header>
    <Snippet>
      <References />
      <Imports />
      <Declarations>
        <Literal Editable="true">
          <ID>name</ID>
          <Type></Type>
          <ToolTip>Property Name</ToolTip>
          <Default>MyProperty</Default>
          <Function></Function>
        </Literal>        
        <Literal Editable="true">
          <ID>type</ID>
          <Type></Type>
          <ToolTip>Property Type</ToolTip>
          <Default>string</Default>
          <Function></Function>
        </Literal>
      </Declarations>
      <Code Language="csharp" Kind="method decl" Delimiter="$"><![CDATA[public $type$ $name$
{
    get { return _$name$; }
    set
    {
        if (value == _$name$) return;
        _$name$ = value;
        OnPropertyChanged(nameof($name$));
    }
}        
private $type$ _$name$;
]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

一旦文件存在或更新了驱闷,Visual Studio 無需重啟,就能立即發(fā)現(xiàn)并使用空免。在相關(guān)的 (如 C#) 編輯器中空另,立馬就能看到智能提示中的代碼段:

智能提示

它插入對(duì)應(yīng)的模板并允許您編輯在模板中聲明的 $expr$ 占位符:

EditSnippetInVS.png

這個(gè) C# 代碼片段示例, 是 VS 中最常見的語(yǔ)言蹋砚。如您所見扼菠, XML文件中的 <Code> 節(jié)點(diǎn)定義模板文本,Shortcut 節(jié)點(diǎn)定義觸發(fā)提示的按鍵坝咐,你可以使用在 <Declaration> 節(jié)點(diǎn)中使用類似 $txt$ 占位符來定義參數(shù)循榆,同樣的占位符在多個(gè)地方出現(xiàn)也能同步更改。

對(duì)于我來說墨坚,最有用和最常用的代碼片段時(shí)用于插入 HTML 代碼秧饮,特別是在定義 Bootstrap 結(jié)構(gòu)或其他很難記住語(yǔ)法的自定義控件。我喜歡在瀏覽文檔網(wǎng)站后創(chuàng)建一個(gè)對(duì)應(yīng)的片段泽篮,這樣就很方便使用浦楣。用多幾次,省下來的時(shí)間就賺翻了咪辱≌窭停花費(fèi)幾分鐘設(shè)置模板可以節(jié)省大量時(shí)間去輸入重復(fù)代碼,尤其是您每次都要浪費(fèi)時(shí)間查找相同的 Bootstrap 代碼時(shí)油狂。??

前綴以及代碼片段包

Visual Studio Marketplace中還有許多可用的代碼片段历恐,您可以安裝使用一整套預(yù)設(shè)的代碼片段。例如专筷,Bootstrap Snippet 包就內(nèi)置了一堆以 bs- 為前綴的代碼片段弱贼。

代碼片段包

即使您自己有專屬的代碼片段,最好為您的代碼片段創(chuàng)建一個(gè)前綴磷蛹,以便您可以在智能提示的海洋中輕松地找到它們吮旅。我一般使用 ww- 作為大多數(shù)代碼片段的前綴。不幸的是味咳,我自己沒有很好得遵循這個(gè)建議庇勃,還是有不少代碼片段沒有這么做。

構(gòu)建轉(zhuǎn)換器

因?yàn)槲以?Visual Studio 大量使用了代碼片段槽驶,所以我做了一個(gè)將 Visual Studio 中的代碼片段遷移到 VS Code 中的小工具责嚷,同時(shí)盡量能遷移到 JetBrains Rider 。

我想可能還有其他人需要用到掂铐,所以我把它作為 .NET Global Tool 控制臺(tái)應(yīng)用程序發(fā)布罕拂,以便快速安裝:

dotnet tool install dotnet-snippetconverter

您需要 .NET Core 2.1 SDK或更高版本 才能運(yùn)行它揍异。

以下示例命令將代碼片段從 Visual Studio 遷移到 VS Code,稍后再討論遷移到 Rider 的事

安裝后爆班,您可以使用以下命令快速將所有 Visual Studio 代碼片段轉(zhuǎn)換為 VS Code 可以接受的格式衷掷。

snippetconverter ~ -r -d 

這將轉(zhuǎn)換最新安裝的 Visual Studio 版本(2017,2019等)中的所有代碼片段,并在位于%appdata%\Code\User\snippets 路徑的 VS Code 的代碼文件夾中創(chuàng)建單獨(dú)的 visualstudio-exported.code-snippets 文件夾柿菩。

您還可以導(dǎo)出特定 VS 版本的代碼片段:

snippetconverter ~2017 -r -d

或特定文件夾:

snippetconverter "~2017\Visual C#\My Code Snippets" -r -d -o "~\ww-csharp.code-snippets"

其中輸入和輸出文件夾選項(xiàng)中的路徑都是可選的戚嗅,示例中的~ 是物理片段路徑的占位符,會(huì)指向 Visual Studio(%Documents%\Visual Studio <year>\Code Snippets)和 VS Code(%appdata%\Code\User\Snippets\)中存放代碼片段的基本位置碗旅,因此您不必每次都指定完整路徑渡处。您高興的話,也可以使用合格的全路徑祟辟。

最后医瘫,您還可以導(dǎo)出單個(gè)文件:

snippetconverter "~2017\Visual C#\My Code Snippets\proIPC.snippet" -d -o "~\ww-csharp.code-snippets"

如果 VS Code 中已存在該代碼片段,則會(huì)覆蓋更新旧困,所以每次重新運(yùn)行都會(huì)更新對(duì)應(yīng)的代碼片段醇份。

運(yùn)行遷移工具后,在VS Code 中通過前綴或者快捷方式就可以立即使用:

在 Visual Studio 中多個(gè)占位符輸入也是支持的:

同步代碼片段

目前只支持從Visual Studio 單向 遷移到到 VS Code吼具。這意味著如果要保持 Visual Studio 和 VS Code 之間的代碼段同步僚纷,最好是在 Visual Studio 中創(chuàng)建代碼片段,然后通過此工具將它們遷移到 VS Code拗盒。

VS Code 中的代碼片段

我之前討論過 Visual Studio Snippets 的代碼片段格式怖竭,現(xiàn)在讓我們看看 VS Code 中又是什么樣的。

  • 存放在 %AppData\Code\User\snippets
  • 使用 JSON 格式化
  • 命名為 lang.json
  • 或者是 <name>.code-snippet 的命名格式
  • 可以包含一個(gè)或者多個(gè)代碼片段

轉(zhuǎn)換器之所以導(dǎo)出為 .code-snippet 文件格式陡蝇,是因?yàn)槭褂?lang.json 很容易造成命名沖突痊臭。如果默認(rèn)的 visualstudio-export.code-snippets 不能使用,則使用 -o 來指定輸出文件登夫。

VS Code 代碼片段文件是 JSON广匙,它們看起來像:

{
  "proipc": {
    "prefix": "proipc",
    "scope": "csharp",
    "body": [
      "public ${2:string} ${1:MyProperty}",
      "{",
      "    get { return _${1:MyProperty}; }",
      "    set",
      "    {",
      "        if (value == _${1:MyProperty}) return;",
      "        _${1:MyProperty} = value;",
      "        OnPropertyChanged(nameof(${1:MyProperty}));",
      "    }",
      "}        ",
      "private ${2:string} _${1:MyProperty};",
      ""
    ],
    "description": "Control Property with Attributes"
  },
  "commandbase-object-declaration": {
    "prefix": "commandbase",
    "scope": "csharp",
    "body": [
      "        public CommandBase ${1:CommandName}Command { get; set;  }",
      "",
      "        void Command_${1:CommandName}()",
      "        {",
      "            ${1:CommandName}Command = new CommandBase((parameter, command) =>",
      "            {",
      "              $0",
      "            }, (p, c) => true);",
      "        }",
      ""
    ],
    "description": "Create a CommandBase implementation and declaration"
  } 
}

VS Code 的代碼模板在概念上更簡(jiǎn)單,只有模板恼策,前綴和范圍鸦致,以及使用字符串插值和約定來確定如何定義占位符。當(dāng)然還有其他字段可以填充涣楷,但大多數(shù)值是可選的分唾,對(duì)于從 Visual Studio 轉(zhuǎn)換過來的代碼片段用不到。

您可以在此處找到Visual Studio代碼段模板文檔:

但是實(shí)際上总棵,自己手動(dòng)創(chuàng)建模板鳍寂,定義 JSON中的 body 屬性還是有難度的,因?yàn)樽址赡苤皇且粋€(gè)字符串?dāng)?shù)組(yuk)情龄,也可能是一個(gè)可以輸入的類型迄汛。好在只是從 Visual Studio 代碼片段轉(zhuǎn)換,還是很容易生成對(duì)應(yīng)的模板...

咦骤视?導(dǎo)出到 Rider

轉(zhuǎn)換器某種程度上可以適配到 Rider鞍爱,但功能有限。因?yàn)镽ider 使用令人抓狂的模式來存儲(chǔ)代碼片段专酗,使用 GUID 來標(biāo)識(shí)的 XML 文件。

%USERPROFILE%\.Rider2018.2\config\resharper-host\GlobalSettingsStorage.DotSettings

讓我們看看幾個(gè)導(dǎo)出的模板效果:

<root>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Reformat/@EntryValue">True</s:Boolean>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Shortcut/@EntryValue">proipc</s:String>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/ShortenQualifiedReferences/@EntryValue">True</s:Boolean>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Scope/=C3001E7C0DA78E4487072B7E050D86C5/@KeyIndexDefined">True</s:Boolean>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Scope/=C3001E7C0DA78E4487072B7E050D86C5/Type/@EntryValue">InCSharpFile</s:String>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Text/@EntryValue">public $type$ $name$
{
    get { return _$name$; }
    set
    {
        if (value == _$name$) return;
        _$name$ = value;
        OnPropertyChanged(nameof($name$));
    }
}        
private $type$ _$name$;
    </s:String>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=name/@KeyIndexDefined">True</s:Boolean>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=name/Expression/@EntryValue">complete()</s:String>
    <s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=name/Order/@EntryValue">0</s:Int64>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=type/@KeyIndexDefined">True</s:Boolean>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=type/Expression/@EntryValue">complete()</s:String>
    <s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=720E28E0ECFD4CA0B80F10DC82149BD4/Field/=type/Order/@EntryValue">1</s:Int64>

    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/@KeyIndexDefined">True</s:Boolean>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Applicability/=Live/@EntryIndexedValue">True</s:Boolean>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Reformat/@EntryValue">True</s:Boolean>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Shortcut/@EntryValue">seterror</s:String>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/ShortenQualifiedReferences/@EntryValue">True</s:Boolean>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Scope/=C3001E7C0DA78E4487072B7E050D86C5/@KeyIndexDefined">True</s:Boolean>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Scope/=C3001E7C0DA78E4487072B7E050D86C5/Type/@EntryValue">InCSharpFile</s:String>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Text/@EntryValue">      
        public string ErrorMessage {get; set; }

        protected void SetError()
        {
            this.SetError("CLEAR");
        }

        protected void SetError(string message)
        {
            if (message == null || message=="CLEAR")
            {
                this.ErrorMessage = string.Empty;
                return;
            }
            this.ErrorMessage += message;
        }

        protected void SetError(Exception ex, bool checkInner = false)
        {
            if (ex == null)
                this.ErrorMessage = string.Empty;

            Exception e = ex;
            if (checkInner)
                e = e.GetBaseException();

            ErrorMessage = e.Message;
        }
    </s:String>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=busObject/@KeyIndexDefined">True</s:Boolean>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=busObject/Expression/@EntryValue">complete()</s:String>
    <s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=busObject/Order/@EntryValue">0</s:Int64>
    <s:Boolean x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=NewLiteral/@KeyIndexDefined">True</s:Boolean>
    <s:String x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=NewLiteral/Expression/@EntryValue">complete()</s:String>
    <s:Int64 x:Key="/Default/PatternsAndTemplates/LiveTemplates/Template/=E88A906D39C741C0A3B8095C5063DADE/Field/=NewLiteral/Order/@EntryValue">1</s:Int64>
</root>    

使用這種瘋狂的格式,無法分辨一組代碼片段的開始和結(jié)束的位置佑笋。每個(gè)代碼片段都有多個(gè) Key翼闹,加上 GUID 標(biāo)識(shí)蒋纬,這使得匹配現(xiàn)有的代碼段來判斷是否存在的目的幾乎不可能實(shí)現(xiàn)。

據(jù)我所知蜀备,沒有找到任何相關(guān)鍵值配置的文檔关摇,也沒有如何存儲(chǔ)的文檔。很有可能存在其他存儲(chǔ)選項(xiàng)碾阁,但看起來 Rider 并沒有為代碼片段設(shè)置編輯功能。如果您有更好的開發(fā)人員文檔宪睹,請(qǐng)發(fā)表評(píng)論艰猬。

出于這個(gè)原因,Rider 導(dǎo)入是一次性的冠桃,如果您導(dǎo)出兩次相同的片段,它們就會(huì)翻倍胸蛛。

為了測(cè)試樱报,我在 Rider 的導(dǎo)出文件中添加了一個(gè)標(biāo)記鍵。然后民珍,在我導(dǎo)入相同的代碼片段時(shí),我會(huì)刪除了之前添加的代碼片段嚷量。很簡(jiǎn)陋蝶溶,也只是測(cè)試階段。如果相關(guān)的配置發(fā)生了變化抖所,則可能會(huì)失效田轧。

此格式僅適用于 Rider 支持的 .NET 特定代碼類型:.NET Languages暴匠,Razor 和包含 HTML 模板的 WebForms涯鲁。其他格式( JavaScript抹腿、HTML 、CSS)則使用完全獨(dú)立的格式警绩,我沒有精力在實(shí)現(xiàn)相關(guān)的功能。對(duì)于 Rider后室,我主要關(guān)心的是 C# 和 HTML 模板混狠,能正常運(yùn)行就好了。

只需導(dǎo)出特定文件夾贡避,如 C# 文件夾或 HTML 代碼段予弧,而不是批量導(dǎo)出整個(gè)代碼片段文件夾刮吧。

SnippetConverter "~2017\Visual C#\My Code Snippets" -m vs-rider -d
SnippetConverter "~2017\Code Snippets\Visual Web Developer\My HTML Snippets" -m vs-rider -d

摘要

正如我前面提到的掖蛤,所有這些都是非常簡(jiǎn)陋,但對(duì)于將我全部的代碼片段從 Visual Studio 導(dǎo)出到 Visual Studio Code 是完全夠用的致讥。對(duì)于 Rider, C# 和 HTML 代碼片段導(dǎo)出也可以做到蝇恶,但是其他類型(如 JavaScript惶桐、CSS)會(huì)出現(xiàn)異常潘懊。我只是當(dāng)作個(gè)人工具,如果哪天有足夠的興趣的話救恨,我會(huì)接著完善释树,但是很大程度是需要另外搞一個(gè)完全獨(dú)立的轉(zhuǎn)換器奢啥。

我沒有測(cè)試所有的 Visual Studio 支持的文件類型,即使是VS 內(nèi)置的代碼片段也可能存在某些問題寂纪。保險(xiǎn)一點(diǎn)赌结,請(qǐng)不要批量導(dǎo)出所有代碼段,而是單獨(dú)導(dǎo)出每種類型的代碼片段拟杉。

我還是強(qiáng)烈建議使用前綴量承,因?yàn)榭梢愿菀椎卣业侥愕拇a片段,并保持它們不受影響焕梅。

現(xiàn)在這個(gè)工具對(duì)于我來說已經(jīng)足夠了卦洽,但是我很想知道我是否是少數(shù)幾個(gè)投身到代碼片段轉(zhuǎn)換的人之一??

相關(guān)資源


本文采用 知識(shí)共享署名-非商業(yè)性使用-相同方式共享 3.0 中國(guó)大陸許可協(xié)議
轉(zhuǎn)載請(qǐng)注明來源:張蘅水

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末阀蒂,一起剝皮案震驚了整個(gè)濱河市弟蚀,隨后出現(xiàn)的幾起案子酗失,更是在濱河造成了極大的恐慌规肴,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件删壮,死亡現(xiàn)場(chǎng)離奇詭異兑牡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)亿虽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門洛勉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來墩朦,“玉大人,你說我怎么就攤上這事牛哺±头停” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵淳附,是天一觀的道長(zhǎng)奴曙。 經(jīng)常有香客問我草讶,道長(zhǎng),這世上最難降的妖魔是什么坤溃? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮祠饺,結(jié)果婚禮上汁政,老公的妹妹穿的比我還像新娘。我一直安慰自己试疙,他們只是感情好抠蚣,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布嘶窄。 她就那樣靜靜地躺著距贷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪现横。 梳的紋絲不亂的頭發(fā)上阁最,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天速种,我揣著相機(jī)與錄音,去河邊找鬼馏颂。 笑死棋傍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的亿絮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼葱绒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼斗锭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起帮毁,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤烈疚,失蹤者是張志新(化名)和其女友劉穎聪轿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灯抛,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡音瓷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年绳慎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片靡砌。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乏奥,死狀恐怖亥曹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情骗炉,我是刑警寧澤蛇受,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站乍丈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏忆矛。R本人自食惡果不足惜请垛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一宗收、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧混稽,春花似錦荚坞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至璃俗,卻和暖如春悉默,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背唱星。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工间聊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抵拘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓尚蝌,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親衣形。 傳聞我的和親對(duì)象是個(gè)殘疾皇子热凹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容