📔

VSCodeでスニペットを作成する

2022/03/13に公開

VSCodeでよく使う文があるならスニペットを使用するのが便利です。
標準で用意されていたり拡張機能をインストールすることで使用できますが、
ユーザー独自でもスニペットを作成できるのでそれについての説明をしていきます。

スニペットファイルを用意する。

VSCode左下の歯車アイコンからユーザースニペットを選択し、スニペットを登録したい言語のjsonファイルを選択すると初期では以下のようなファイルが開かれます。

javascript.json
{
	// Place your snippets for cpp here. Each snippet is defined under a snippet name and has a prefix, body and 
	// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
	// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the 
	// same ids are connected.
	// Example:
	// "Print to console": {
	// 	"prefix": "log",
	// 	"body": [
	// 		"console.log('$1');",
	// 		"$2"
	// 	],
	// 	"description": "Log output to console"
	// }
}

サンプルも記載あるのでサンプルのコメントを外して実際のファイルでどんなふうに動くか確認してみます。

javascript.json
{
  "Print to console": {
  "prefix": "log",
  "body": [
    "console.log('$1');",
    "$2"
   ],
   "description": "Log output to console"
   }
}

コメントアウトして保存しjavascriptのファイルでlogと入力しtabキーを押せば以下のようになります。

sample.js
console.log('');

また展開後のカーソルの位置はjsonファイルで記入した$1の位置に移動します。
文字入力後tabキーを押せば$2の位置に移動します。

スニペットが便利なのはテンプレート文を入力するだけでなく入力位置にカーソルが移動してくれたり、ファイル名や日付を変数を利用して入力したり、正規表現を利用して文字列を加工したりできることです。

構文

"スニペット名":{
  "prefix": "短縮語",      // 複数の場合は["a", "b"]のように入力
  "body": "展開するコード", // 複数行に展開したい場合は["1行目", "2行目", "3行目"...]
  "description": "説明文"  // オプション。なくても構わない。
}

スニペット名は一意の名称を設定します。
サンプルと見比べてみればわかりやすいかもしれません。

スニペットや短縮語、説明文はとくに説明することはありません。
短縮語は複数登録することができるくらいです。

展開するコードについていろいろ機能があるので説明していきます。

基本的な展開

大体のスニペットは基本的な展開でこと足ります。
サンプルを少し改造して以下のようにしてみます。

"Print to console2": {
  "prefix": "log2",
  "body": [
    "console.log('$1');",
    "console.log('$2');",
    "$0"
  ],
  "description": "2回ログを表示する"
}

$1,$2はスニペット展開後のカーソルの位置です。
$1の位置で入力後、またtabを押せば次の$2に移動します。
$0は特別で一番最後の位置となります。
この$Nのことをタブストップといいます。

タブストップについて

タブストップについて機能がいろいろついています。
デフォルト値や選択肢、ファイル名などの変数、正規表現などです。

プレースホルダー

タブストップにはデフォルト値を設定することができます。
${N:デフォルト値}のように記載します。

cpp.json
"for loop":{
  "prefix": "for2",
  "body": [
    "for(${1: size_t} ${2: index} = 0; $2 < ${3: size}; ++$2){",
    "  $0"
    "}"
  ],
  "description": "C++のforループ"
}

上記サンプルで$2が複数あることに気づいたでしょうか?
タブストップは同じ番号が複数あってもかまいません。

また最初の$2だけデフォルト値のindexを記載していますが、展開した場合残りの$2もデフォルト値のindexが入力されています。

展開例
//for2 -> tab
for( size_t  index = 0;  index <  size; ++ index){
  
}

選択肢

タブストップは選択肢から選ぶ形にすることも可能です。
${N|option1,option2,...optionN|}のように記載します。

cpp.json
 "tmplate class": {
    "prefix": "tmpc",
    "body": [
      "template<${1|typename, class|} T>",
      "class ${2:tmp_class}{",
      "  $0",
      "};"
    ]
  },


展開例

変数

ファイル名や現在の日付を入力したい場合変数を使用することも可能です。
$変数名のように記載します。

変数名一覧は以下のようになります。

  • TM_SELECTED_TEXT 現在選択しているテキスト
  • TM_CURRENT_LINE 現在の行の内容
  • TM_CURRENT_WORD 現在のカーソル位置の単語(もしくは空白)
  • TM_LINE_INDEX 最初の行を0行目としたときの現在の行番号
  • TM_LINE_NUMBER 最初の行を1行目としたときの現在の行番号
  • TM_FILENAME 現在のドキュメントのファイル名
  • TM_FILENAME_BASE 現在のドキュメントのファイル名(拡張子なし)
  • TM_DIRECTORY 現在のドキュメントのディレクトリ
  • TM_FILEPATH 現在のドキュメントのフルのファイルパス
  • RELATIVE_FILEPATH 現在のドキュメントの相対(開いているワークスペースやフォルダに対する)ファイルパス
  • CLIPBOARD クリップボードの内容
  • WORKSPACE_NAME 開いているワークスペースまたはフォルダーの名前
  • WORKSPACE_FOLDER 開いているワークスペースまたはフォルダーのパス
  • CURRENT_YEAR 現在の年
  • CURRENT_YEAR_SHORT 現在の年の下2桁
  • CURRENT_MONTH 2桁の月 (例 '02')
  • CURRENT_MONTH_NAME 月の正式名称(例:'July')
  • CURRENT_MONTH_NAME_SHORT 月の略称(例:'Jul')。
  • CURRENT_DATE 2桁の日付(例:'08')
  • CURRENT_DAY_NAME 曜日名 (例: 'Monday')
  • CURRENT_DAY_NAME_SHORT 曜日の略称(例:'Mon')
  • CURRENT_HOUR 24時間表示での現在時刻
  • CURRENT_MINUTE 現在の分を2桁で表示
  • CURRENT_SECOND 現在の秒を2桁で表示
  • CURRENT_SECONDS_UNIX Unixエポックからの秒数
  • RANDOM 10進数6桁の乱数
  • RANDOM_HEX 16進数6桁の乱数
  • UUID バージョン4のUUID
  • BLOCK_COMMENT_START 出力例:C++の場合 /* またはHTMLの場合 <!--
  • BLOCK_COMMENT_END 出力例:C++の場合 */ またはHTMLの場合 -->
  • LINE_COMMENT 出力例:C++の場合 //
  • 19 タブストップ$1$9の内容
  • CURSOR_INDEX マルチカーソルの最初の選択を0とした番号
  • CURSOR_NUMBER マルチカーソルの最初の選択を1とした番号
"day":{
  "prefix": "day",
  "body": "$CURRENT_YEAR/$CURRENT_MONTH/$CURRENT_DATE-$CURRENT_HOUR:$CURRENT_MINUTE:$CURRENT_SECOND",
  "description": "YYYY/MM/DD-HH:MM:SS"
}
sample
2022/03/13-16:57:11

正規表現

変数に対して正規表現で加工することが可能です。
${変数/正規表現/置換/正規表現オプション}

sample
"#name": {
  "prefix": "#name",
  "body": "${TM_FILENAME/(?:(\\w+)(\\.)?)/${1:/upcase}${2:+_}/gi}"
}
header.sample.hpp
HEADER_SAMPLE_HPP

header.sample.hppのファイルで上記スニペットを展開した場合

正規表現及び正規表現オプションはjavascriptの正規表現を確認してください。
スニペット独自の機能として置換後の$1...などに大文字化などのオプションがあります。

上記サンプルで${1: /upcase}としている部分の/upcaseがオプションです。

置換項目のオプション一覧

  • /upcase 大文字にする
  • /downcase 小文字にする
  • /capitalize 文字の先頭を大文字にする
  • /camelcase キャメルケースにする
  • /pascalcase パスカルケースにする
  • + string 正規表現でキャプチャした部分が空白でなければstring
  • ? A: B 正規表現でキャプチャした部分が空白でなければA,空白ならB
  • - string 正規表現でキャプチャした部分が空白であればstring
  • string 正規表現でキャプチャした部分が空白であればstring
キャピタライズなどについて

/capitalizeの変換は最初の一文字しか変換しません・・・キャピタライズとは・・・?
/camelcase,/pascalcaseは空白や記号などを削除はしてくれますが最初の文字以外、大文字を小文字にすることはないようです。
一旦/downcaseで全て小文字にしてからキャメル/パスカルケースにした方が思った通りの変換するかも
複数の変換をかけないのでスニペットは長く見づらくなりますが・・・

ショートカットキーなど

変数一覧に現在の選択範囲があったのを覚えていますか?
選択範囲に対してスニペットを適用する場合は短縮名を入力では対応できません。

コマンドパレットから選択

選択範囲に対してスニペットを適用する方法は2通りあり一つはコマンドパレットからスニペットの挿入を選ぶ方法です。

Ctrl+Shift+P→スニペットの挿入→該当のスニペットを選択

ショートカットキーによる適用

もうひとつはショートカットキーに割り当ててしまう方法です。

こちらは先程のjsonファイルとは異なるjsonファイルに記載します。

ファイル→ユーザー設定→キーボードショートカット→右上のキーボードショートカットを開くのアイコンをクリック

すると以下のようなjsonファイルが開くきます。

keybindings.json
// 既定値を上書きするには、このファイル内にキー バインドを挿入します
[]

そこにショートカットキーとして追加していきます。

keybindings.json
[{
  "key": "cmd+k 1",
  "command": "editor.action.insertSnippet",
  "when": "editorTextFocus",
  "args": {
    "snippet": "`$TM_SELECTED_TEXT`"
  }
}]

すでに言語のjsonファイルでスニペットを登録しており、それを呼び出すだけの場合以下のようにします。

keybindings.json
[{
  "key": "cmd+k 1",
  "command": "editor.action.insertSnippet",
  "when": "editorTextFocus",
  "args": {
    "langId": "cpp",
    "name": "tmplate class"
  }
}]

cpp.jsonで定義されたtemplate classという名前のスニペットをショートカットキーcmd+k、1で挿入します。

ワークスペースのみで使用するスニペットの場合

現在のプロジェクトだけ使用したいスニペットとかもあるでしょう。
そんな場合は以下のようにフォルダ名の新しいスニペットファイルを選択して適当な名前で保存します。

ファイル→ユーザー設定→ユーザースニペット


ファイル名は何でも構いません

sampleという名前で作成するとフォルダの直下に.vscodeフォルダが作成されそこにsample.code-snippetsというファイルができます。

内容はほとんど同じですがjsonファイルのサンプルにscopeという項目が増えています。
scopeはスニペットを適用する言語を指定します。

sample.code-snippets
{
	// Place your Sample ワークスペース snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and 
	// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope 
	// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is 
	// used to trigger the snippet and the body will be expanded and inserted. Possible variables are: 
	// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. 
	// Placeholders with the same ids are connected.
	// Example:
	// "Print to console": {
	// 	"scope": "javascript,typescript",
	// 	"prefix": "log",
	// 	"body": [
	// 		"console.log('$1');",
	// 		"$2"
	// 	],
	// 	"description": "Log output to console"
	// }
}

コメントアウトして保存すればjavascript及びtypescriptのファイルでlog→tabで展開されます。

ちなみにscopeを設定しない場合はワークスペース内のすべてのファイルでスニペットを使用できます。

ファイルテンプレート

スニペットのプロパティにisFileTemplateという項目が追加されていました。
このプロパティをtrueに設定するとそのスニペットがファイルテンプレートとなり、
コマンドパレットのファイルをスニペットで満たすから選択することが出来ます。

既存の何か書いているファイルでこのスニペットを呼び出した場合、書いている項目は全部消えます。
ファイルテンプレートのスニペットにprefixを設定するとうっかり呼び出してしまう可能性があるので設定しない方がいいでしょう。

その他

説明がまとまってない情報に関してはスクラップに投稿しています。
整理できれば記事にしたい

https://zenn.dev/yhsi/scraps/ef82e4f5ee00a1

Discussion