shuhelohelo’s blog

Xamarin.Forms多めです.

Visual Studioのコードスニペットの作り方

youtu.be

こちらの動画を参考にした.

基本的な形はこちら.

<?xml version="1.0" encoding="utf-8"?>

<!-- <?xml ?>の下は1行空けないと読み込めない -->
<CodeSnippets xmlns="http://schema.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <!-- Titleのみ必須 -->
            <Title></Title>
            <Author></Author>
            <Description></Description>
            <!-- 呼び出すときの名前 -->
            <Shortcut></Shortcut>
        </Header>
        <Snippet>
            <!-- 言語を指定.C#の場合は"CSharp" -->
            <Code Language="">
                <![CDATA[<!-- ここにコードを書く -->]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

シンプルな例として,C#Console.ReadLine();コードスニペットはこちら.crで呼び出す設定.

<?xml version="1.0" encoding="utf-8"?>

<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>Console Read Line</Title>
            <Author>Shuhei Nishizawa</Author>
            <Description>Creates a Console.ReadLine() statement</Description>
            <Shortcut>cr</Shortcut>
        </Header>
        <Snippet>
            <Code Language="CSharp">
                <![CDATA[Console.ReadLine();]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

これをファイルに保存して,Visual Studioから読み込む.

Visual Studioコードスニペットを追加する

Tools -> Code Snippets Managerコードスニペットマネージャを開く.

f:id:shuhelohelo:20200109112556p:plain

ドロップダウンから対象の言語を選択する.

今回はCSharpを選択する.

f:id:shuhelohelo:20200109112810p:plain

左下のImportをクリックする.

f:id:shuhelohelo:20200109112932p:plain

ファイル選択ダイアログが開くので,先程保存したコードスニペットのファイルを選択する.

f:id:shuhelohelo:20200109113101p:plain

コードスニペットの保存先を訊かれるので,保存先を選択してFinishを押して登録完了.

f:id:shuhelohelo:20200109113251p:plain

もっと便利なスニペットの作り方

先程のコードスニペットマネージャですが,デフォルト,自分で追加したものを問わず登録されているスニペットのファイルの場所を知ることができます.

f:id:shuhelohelo:20200109114354p:plain

これで既存のスニペットの設定,書き方を参考にすると,もっと便利なスニペットを作ることができると思います.

例えばConsole.WriteLine();コードスニペットを観てみましょう.

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>cw</Title>
            <Shortcut>cw</Shortcut>
            <Description>Code snippet for Console.WriteLine</Description>
            <Author>Microsoft Corporation</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal Editable="false">
                    <ID>SystemConsole</ID>
                    <Function>SimpleTypeName(global::System.Console)</Function>
                </Literal>
            </Declarations>
            <Code Language="csharp"><![CDATA[$SystemConsole$.WriteLine($end$);]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

WriteLineのスニペットを実行すると,スニペットが挿入された後のカーソルの位置が()の中に移動します.

これを指定しているのが$end$です.この位置にカーソルが移動します.

ここでよくみてみると,CDATAの中のコードが$SystemConsole$.WriteLine($end$);となっていて,上で作成したReadLineのスニペットとは異なっています.

ReadLineのスニペットではConsole.ReadLine()と固定の文字列でしたが,WriteLineのほうはクラス名が変数で指定されています.

この違いがどのように影響するかというと,System名前空間が追加されていないときにそれぞれcw,crスニペットを挿入するとわかります.

f:id:shuhelohelo:20200109115838p:plain

WriteLineの方はSystem.Console.WriteLine();となりますが,ReadLineの方はConsole.ReadLine();となり,名前空間が不足しているのでエラーが出ます.

そこで,WriteLineのほうは名前空間を含むクラス名をプロパティとして定義しておき,それによって置換されるようになっています.

名前空間とクラス名の取得はSimpleTypeNameという関数を使っています.関数については公式サイトが詳しいです.

docs.microsoft.com

以下の部分がプロパティを定義している部分です.

         <Declarations>
                <Literal Editable="false">
                    <ID>SystemConsole</ID>
                    <Function>SimpleTypeName(global::System.Console)</Function>
                </Literal>
            </Declarations>

<ID>要素で指定した名前を使って,スニペット挿入時に値を取得しています.

プロパティの使い方は$プロパティ名$です.

ではReadLineのスニペットの方にもこの方法を導入しましょう.

<?xml version="1.0" encoding="utf-8"?>

<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>Console Read Line</Title>
      <Author>Shuhei Nishizawa</Author>
      <Description>Creates a Console.ReadLine() statement</Description>
      <Shortcut>cr</Shortcut>
    </Header>
    <Snippet>
      <Declarations>
        <Literal Editable="false">
          <ID>SystemConsole</ID>
          <Function>SimpleTypeName(global::System.Console)</Function>
        </Literal>
      </Declarations>
      <Code Language="CSharp">
        <![CDATA[$SystemConsole$.ReadLine();]]>
      </Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

これを改めてスニペットマネージャで上書き登録して,crスニペットを使用してみます.

以下のようにSystem名前空間が導入されていない状況下で実行すると,System.Console.ReadLine();と挿入されるようになりました.

f:id:shuhelohelo:20200109120813p:plain

もっともっと便利なスニペットを作る

propスニペットを使うとわかりますが,プロパティの型,プロパティ名を少ない手間で変更できるようになっています.

propの定義をみてみましょう.

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>prop</Title>
            <Shortcut>prop</Shortcut>
            <Description>Code snippet for an automatically implemented property
Language Version: C# 3.0 or higher</Description>
            <Author>Microsoft Corporation</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>type</ID>
                    <ToolTip>Property type</ToolTip>
                    <Default>int</Default>
                </Literal>
                <Literal>
                    <ID>property</ID>
                    <ToolTip>Property name</ToolTip>
                    <Default>MyProperty</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp"><![CDATA[public $type$ $property$ { get; set; }$end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

Literal要素の中にWriteLineのときと同じようにプロパティが定義されています.

WriteLineのときとの大きな違いはEditable="false"がついていないことです.

これがついていない場合はデフォルトで変更可能な状態となり,propスニペットのようにプロパティの箇所をタブで移動して変更できる動作になります.

上記のスニペットはこちら

github.com