VSCodeで爆速コーディング環境を構築する(主にReactJS向け設定)

# VSCodeで爆速コーディング環境を構築する
VSCode標準のコード補完とコードスニペットを併用することで爆速にコーディングすることが可能になります。
ReactJSのプロジェクトでコーディングする場合、自分はもっぱらVSCodeを使っていることが多いです。
いくつかのエディタでプラグインを入れることでJSXコード補完をサポートしてくれるのですが、
VSCodeの場合は標準でJSXコード補完をサポートしているのが強みだと思います。
ReactJSはJSXファイルにJavaScriptとHTMLタグを同ファイルに混在する形になります。

# キー、マウスの速度を最速にする
VSCodeに限らず、キーボード速度、トラックパッド（マウス）速度はOSの設定で最速にできます。
システム設定→キーボードの設定からキーのリピートを速い、リピート入力認識までの時間を短いにします。

<img width="669" alt="スクリーンショット 2018-11-05 9.25.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/f20ac688-4a87-eae8-231a-d93e616b11c5.png">

トラックパッド→軌跡の速さを速いにします。
<img width="668" alt="スクリーンショット 2018-11-05 9.27.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/317d9d3d-3d50-d538-8029-404cda2594aa.png">

スクロールの速さはアクセシビリティ→マウスとトラックパッドから変更できます。
<img width="667" alt="スクリーンショット 2018-11-06 14.56.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/bb93ee2f-d532-c07c-84de-1606263e77f9.png">

慣れないうちは酔いますが・・・

# gitのaliasを使いこなす
aliasを使うとコマンドのショートカットを定義できます。
zshを使う場合は[Oh my zsh](https://ohmyz.sh/#install)を入れると深く考えずにzshの色々便利機能が使えるのですが、
gitのaliasは以下のプラグインで定義されています。
[git.plugin.zsh](https://github.com/ohmyzsh/ohmyzsh/blob/master/plugins/git/git.plugin.zsh)

以下よく使うalias

- gl: `git pull` リモートリポジトリから最新版を取得
- gaa: `git add -all` ファイル追加
- gco: `git checkout` ブランチ切り替え
- gcam: `git commit -a -m` コミット
- gd: `git diff` コミットの差分
- grbi: `git rebase -i` リベース
- gsta: `git stash push` 一時保存領域に入れる
- gstp: `git stash pop` 一時保存領域から取得
- ggp: `git push origin` プッシュ
- ggf: `git push --force origin` 強制プッシュ
- gcl: `git clone` リモートリポジトリより全取得
- grh: `git reset` コミットのリセット
- grev: `git revert` リバートコミットの作成
- gcp: `git cherry-pick` cherry-pick 



# ショートカット
[【Windows版】VS Code キーボードショートカット一覧 （オススメ付き）](https://qiita.com/TakahiRoyte/items/cdab6fca64da386a690b)

Macの場合はCtrlをCommandに置き換え、
最低限覚えておきたいもの

* Command + F ファイル内キーワード検索
* Command + Shift + F プロジェクトフォルダ内キーワード検索
* Command + Shift + E エクスプローラー表示
→ この状態でキー入力するとプロジェクト内のファイルのフィルタリングができる
* Command + P ファイル検索
* Command + Shift + O ファイル内関数検索
* Command + Enter 下に行追加
* Command + Shift + Enter 上に行追加
* Command + ↓　ファイル末尾
* Command + ↑　ファイル先頭
* Ctrl + A 行先頭
* Ctrl + E 行末尾
* Ctrl + J 行末尾の改行を消す
* Alt + → or ← 単語単位移動
* Command + B サイドバー表示・非表示切り替え
* Command + Shift + V Markdownファイル（.md）のプレビュー
* Alt + ↑　or ↓ カーソル行の上下入れ替え
* Command + N 新規ファイル作成
* Command + Shift + N 新規ウィンドウを開く
* Alt+Click 変数もしくは関数をAlt+クリックで定義に飛ぶ

# 拡張機能を使う
VSCodeでは拡張プラグインをいれることで作業効率が上がるものが多数あります。
こちらの記事が参考になります。
[VSCodeのオススメ拡張機能 24選 (とTipsを少し)](https://qiita.com/sensuikan1973/items/74cf5383c02dbcd82234)

# Snippetを作成する
Snippetを作成することでテンプレ保存していたショートカットキーワードでコード展開することができます。
メニューから基本設定→ユーザスニペットを選択します。

<img width="550" alt="スクリーンショット 2017-11-23 16.25.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/6e762669-6dc6-a358-912f-f9560d99c5f4.png">

スニペットの言語を指定します。コーディング対象が.jsxファイルの場合はJavaScript React、.jsファイルの場合はJavaScriptを選択します。

<img width="596" alt="スクリーンショット 2017-11-23 16.25.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/0917c744-3e7d-06cd-3bd2-e33f6da58655.png">

例えば、function関数を展開するスニペットは次のように記述します。
`prefix`がSnippetのショートカットキーとなります。
`body`が展開する内容となります。
`${1}`は展開したときの最初のカーソル位置になります。
Tabキーを押すことで`${2}`にカーソル位置が移ります。
`${1:name}`と記述することで展開時にデフォルトで`name`という文字列が入ります。
`description`はコードスニペット内容の説明を記述します。

```javascript.json
{
    "function": {
        "prefix": "fu",
        "body": [
            "function ${1:name}(${2}) {",
            "  ${3:処理}",
            "}"
        ],
        "description": "function"
    }
}
```

# Snippetとコード補完を併用する
Snippetとコード補完を併用するために、VSCodeの設定を上書きします。
基本設定→設定で設定を開きます。

<img width="548" alt="スクリーンショット 2017-11-23 16.52.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/0fc08b2f-db5f-7975-1fd2-3bdd3a5d0b94.png">

右の３点ボタンからsettings.jsonを編集します。
<img width="1448" alt="スクリーンショット 2018-11-02 20.48.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/c5e1b33b-cd0e-05b5-b7f7-260e781c3932.png">


editor.tabCompletionで検索し、鉛筆マークでユーザ定義（settings.json）を開き、上書きの設定を書きます。

<img width="1384" alt="スクリーンショット 2018-11-02 21.00.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/55077/e0004da6-4ad1-75d9-2e17-89d673d61712.png">

suggest、snippetをガリガリ併用で使う場合は
quickSuggestions、quickSuggestionsDelay、tabCompletion、snippetSuggestionsを設定しておくと良いでしょう。タブ補完のほうがsuggestより前に発動するのでタブ補完をsnippet優先にさせ、quickSuggestionsDelayミリ秒以降はsuggestを出すようにします。
tabCompletionはonlySnippetsにしないとsuggestの色々な読み込みで逆に遅くなります。


```settings.json
{
    // 検索から除外
    "search.exclude": {
        "**/.git": true,
        "**/node_modules": true,
    },
    // ファイル保存時にeslintのエラーを自動で直す
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true
    },
    // multiカーソルのキー
    "editor.multiCursorModifier": "ctrlCmd",
    // サジェストの対象の行
    "editor.quickSuggestions": {
        "other": true,
        "comments": false,
        "strings": false
    },
    // サジェストが出るまでの時間
    "editor.quickSuggestionsDelay": 300,
    // tab補完をスニペットのみにする
    "editor.tabCompletion": "onlySnippets",
    // snippetのサジェストを最初に出す。
    "editor.snippetSuggestions": "top",
    // サジェスト表示時にどの項目にカーソルを合わせた状態にするかみたいなやつrecentlyUsedByPrefixは直近で使ったやつ
    "editor.suggestSelection": "recentlyUsedByPrefix",
}
```

# Reactプロジェクト別の拡張設定
webpack.config.jsにて次のようなresolve指定がされている場合に

```webpack.config.js
module.exports = {
  mode: 'development',

  resolve: {
    modules: ['src',  'node_modules'],
    extensions: ['.js', '.json'],
  },
}
```

src/components以下にSomeComponent.jsファイルからSomeComponentがdefault exportされているとして
次のような指定でimportできるようになると思います。
ただし、この場合、Alt+ClickでSomeComponentの定義（ファイル）に飛ぶことができません。

```js

import SomeComponent from 'components/SomeComponent'
```

そこで次のようなjsconfig.json(プロジェクト別に適応される設定ファイル)をプロジェクトフォルダ直下に置くすることでAlt+Clickで飛ぶことができるようになります。
pathsにはresolveを解決したいパスを定義しておきます。

```jsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "components/*": [
        "./src/components/*",
      ]
    },
    "jsx": "react"
  },
  "exclude": [
    "node_modules"
  ]
}
```

# デモ
タイピングが遅いとコード補完が出てしまうのでSnippetを利用する場合はコード補完が出る前にショートカットキーを打ち切ってTabキーを押すのがコツです。
![snippets.gif](https://qiita-image-store.s3.amazonaws.com/0/55077/f4f20075-33c6-bd8d-83f2-ee6e833fbed6.gif)


# Snippet例
私がよく使うJavaScriptコードをSnippetにまとめました。
(javascript.json,javascriptreact.json)

```javascript:react.json
{
	// 変数
	"const": {
		"prefix": "c",
		"body": [
			"const $1"
		],
		"description": "定数"
	},
	"let": {
		"prefix": "l",
		"body": [
			"let $1"
		],
		"description": "変数"
	},
	"var": {
		"prefix": "v",
		"body": [
			"var $1"
		],
		"description": "変数"
	},
	// デバッグ
	"console.log": {
		"prefix": "cl",
		"body": [
			"console.log(${1:val})"
		],
		"description": "console.log"
	},
	"console.dir": {
		"prefix": "cd",
		"body": [
			"console.dir(${1:obj})"
		],
		"description": "console.dir"
	},
	"console.log show all": {
		"prefix": "clj",
		"body": [
			"console.log(JSON.stringify(${1:obj}))"
		],
		"description": "console.log show all"
	},
	"alert": {
		"prefix": "al",
		"body": [
			"alert(${1:message})"
		],
		"description": "alert"
	},
	"debugger": {
		"prefix": "deb",
		"body": [
			"debugger"
		],
		"description": "debugger"
	},
	// ループ
	"for": {
		"prefix": "fr",
		"body": [
			"for (let ${1:i} = 0; ${1:i} < ${2:count}; ++${1:i}) {",
			"  ${3}",
			"}"
		],
		"description": "for in"
	},
	"for in": {
		"prefix": "fi",
		"body": [
			"for (let key in ${1:obj}s) {",
			"  const ${1:obj} = ${1:obj}s[key]",
			"}"
		],
		"description": "for in"
	},
	"for of": {
		"prefix": "fo",
		"body": [
			"for (let val of ${1:arr}) {",
			"  ",
			"}"
		],
		"description": "for of"
	},
	"forEach": {
		"prefix": "fe",
		"body": [
			"Object.keys(${1:obj}).forEach(key => {",
			"  ",
			"})"
		],
		"description": "forEach"
	},
	"Object keys": {
		"prefix": "ok",
		"body": [
			"Object.keys(${1:obj})"
		],
		"description": "Object keys"
	},
	"Object values": {
		"prefix": "ov",
		"body": [
			"Object.values(${1:obj})"
		],
		"description": "Object values"
	},
	"Array from": {
		"prefix": "af",
		"body": [
			"Array.from(${1:arr})"
		],
		"description": "array deepcopy"
	},
	"Object create": {
		"prefix": "oc",
		"body": [
			"Object.create(${1:obj})"
		],
		"description": "object deepcopy"
	},
	"delete": {
		"prefix": "del",
		"body": [
			"delete ",
		],
		"description": "delete"
	},
	"continue": {
		"prefix": "co",
		"body": [
			"continue",
		],
		"description": "continue"
	},
	"break": {
		"prefix": "br",
		"body": [
			"break",
		],
		"description": "break"
	},
	"switch": {
		"prefix": "sw",
		"body": [
			"switch (${1:val}) {",
			"  case ${2:match}:",
			"    break",
			"  default:",
			"}"
		],
		"description": "switch"
	},
	"if": {
		"prefix": "if",
		"body": [
			"if (${1}) {",
			"  ",
			"}",
		],
		"description": "while"
	},
	"while": {
		"prefix": "w",
		"body": [
			"while (${1}) {",
			"  ",
			"}",
		],
		"description": "while"
	},
	"do while": {
		"prefix": "dw",
		"body": [
			"do {",
			"  ",
			"} while (${1})",
		],
		"description": "do while"
	},
	"try catch": {
		"prefix": "tr",
		"body": [
			"try {",
			"  ",
			"} catch (e) {",
			"  ",
			"}",
		],
		"description": "try catch"
	},
	// 配列操作
	"map": {
		"prefix": "ma",
		"body": [
			"${1:[]}.map((${2:val}) => ${2:val})"
		],
		"description": "map"
	},
	"find": {
		"prefix": "fin",
		"body": [
			"${1:[]}.find((${2:val}) => ${2:val} === true)"
		],
		"description": "find"
	},
	"filter": {
		"prefix": "fil",
		"body": [
			"${1:[]}.filter((${2:val}) => ${2:val} === true)"
		],
		"description": "filter"
	},
	"concat": {
		"prefix": "con",
		"body": [
			"${1:[]}.concat(${2:[]})"
		],
		"description": "concat"
	},
	"join": {
		"prefix": "jo",
		"body": [
			"${1:[]}.join('${2:,}')"
		],
		"description": "join"
	},
	"split": {
		"prefix": "sp",
		"body": [
			"${1:''}.split('${2:,}')"
		],
		"description": "split"
	},
	"reduce": {
		"prefix": "red",
		"body": [
			"${1:[]}.reduce((sum, current) => sum + ${2:current}, 0)"
		],
		"description": "reduce"
	},
	"includes": {
		"prefix": "inc",
		"body": [
			"${1:[]}.includes(${2:val})"
		],
		"description": "includes"
	},
	"sort": {
		"prefix": "so",
		"body": [
			"${1:[]}.sort((a, b) => ${2:a > b})"
		],
		"description": "sort"
	},
	"slice": {
		"prefix": "sl",
		"body": [
			"${1:[]}.slice(${2:0, 5})"
		],
		"description": "slice"
	},
	"push": {
		"prefix": "pu",
		"body": [
			"${1:[]}.push(${2:val})"
		],
		"description": "push"
	},
	"pop": {
		"prefix": "po",
		"body": [
			"${1:[]}.pop()"
		],
		"description": "pop"
	},
	"unshift": {
		"prefix": "ush",
		"body": [
			"${1:[]}.unshift(${2:val})"
		],
		"description": "unshift"
	},
	"shift": {
		"prefix": "sh",
		"body": [
			"${1:[]}.shift()"
		],
		"description": "shift"
	},
	// 関数	
	"return": {
		"prefix": "r",
		"body": [
			"return $1"
		],
		"description": "return"
	},
	"async": {
		"prefix": "as",
		"body": [
			"async $1"
		],
		"description": "非同期待ち"
	},
	"arrow function": {
		"prefix": "f",
		"body": [
			"($1) => $2"
		],
		"description": "function"
	},
	"function": {
		"prefix": "fu",
		"body": [
			"function ${1:name}(${2}) {",
			"  $3",
			"}"
		],
		"description": "function"
	},
	"await": {
		"prefix": "aw",
		"body": [
			"await ${1}"
		],
		"description": "非同期待ち"
	},
	"Promise": {
		"prefix": "pr",
		"body": [
			"return new Promise((resolve, reject) => {",
			"  $1",
			"})"
		],
		"description": "Promise"
	},
	"Promise parallel": {
		"prefix": "pra",
		"body": [
			"await Promise.all([${1:PromiseFunc}])",
			"  ${2:.then(([value1, value2]) => {\\}).catch((e) => {\\})}",
		],
		"description": "Promise parallel"
	},
	"Promise sequence": {
		"prefix": "prs",
		"body": [
			"await [${1:{func: PromiseFunc1, param: param1\\},{func: PromiseFunc2, param: param2\\}}]",
    	"  .reduce((promise, current) => {",
      "    return promise.then(async (prev) => {",
      "      return await current.func(current.param, prev)",
    	"    })",
    	"  }, Promise.resolve(null))",
		],
		"description": "Promise sequence"
	},
	// JSON
	"JSON.parse": {
		"prefix": "jsp",
		"body": [
			"JSON.parse(${1:json})"
		],
		"description": "JSON.parse"
	},
	"JSON.stringify": {
		"prefix": "jss",
		"body": [
			"JSON.stringify(${1:obj})"
		],
		"description": "JSON.stringify"
	},
	// インポート・エクスポート
	"export": {
		"prefix": "ex",
		"body": [
			"export $1"
		],
		"description": "export"
	},
	"import": {
		"prefix": "im",
		"body": [
			"import ${1:val} from '${2:module}'"
		],
		"description": "import"
	},
	"module.export": {
		"prefix": "moe",
		"body": [
			"module.exports = $1"
		],
		"description": "module.exports"
	},
	// クラス
	"class": {
		"prefix": "cla",
		"body": [
			"class ${1:name} {",
			"  constructor () {",
			"  }",
			"}"
		],
		"description": "クラス"
	},
	// localStorage
	"localstorage.getItem": {
		"prefix": "lsg",
		"body": [
			"localStorage.getItem('${1}')",
		],
		"description": "LocalStorage取得"
	},
	"localstorage.setItem": {
		"prefix": "lss",
		"body": [
			"localStorage.setItem('${1}', ${1})",
		],
		"description": "LocalStorage保存"
	},
	// 数学
	"Number": {
		"prefix": "num",
		"body": [
			"Number(${1:num})"
		],
		"description": "Number"
	},
	"Math floor": {
		"prefix": "mf",
		"body": [
			"Math.floor(${1:num})"
		],
		"description": "Math floor"
	},
	"Math random": {
		"prefix": "ran",
		"body": [
			"Math.random()"
		],
		"description": "Math random"
	},
	"Number toFixed": {
		"prefix": "tf",
		"body": [
			"${1:num}.toFixed(${2:3})"
		],
		"description": "Number toFixed"
	},
	// 処理制御
	"setTimeout": {
		"prefix": "tm",
		"body": [
			"setTimeout(() => {",
			"  ",
			"}, ${1:1000})",
		],
		"description": "遅延処理"
	},
	"gameloop": {
		"prefix": "ra",
		"body": [
			"function loop () {",
			"",
   		"  requestAnimationFrame(loop)",
			"}",
			"loop()",
		],
		"description": "gameloop"
	},
	"print": {
		"prefix": "pri",
		"body": [
			"window.print()",
		],
		"description": "window.print"
	},

	// タグ
	"div": {
		"prefix": "d",
		"body": [
			"<div>$1</div>"
		],
		"description": "division"
	},
	"a": {
		"prefix": "a",
		"body": [
			"<a href='${1:url}'>${2:link}</a>"
		],
		"description": "anchor link"
	},
	"h1〜h6": {
		"prefix": "h",
		"body": [
			"<h${1:1}>${2}</h${1:1}>"
		],
		"description": "h1〜h6"
	},
	"p": {
		"prefix": "p",
		"body": [
			"<p>$1</p>"
		],
		"description": "paragraph"
	},
	"hr": {
		"prefix": "hr",
		"body": [
			"<hr/>"
		],
		"description": "hr"
	},
	"ol": {
		"prefix": "ol",
		"body": [
			"<ol>",
			"  <li>${1:項目}</li>",
			"  <li>${1:項目}</li>",
			"  <li>${1:項目}</li>",
			"</ul>"
		],
		"description": "順序リスト"
	},
	"ul": {
		"prefix": "ul",
		"body": [
			"<ul style='list-style:none'>",
			"  <li>${1:項目}</li>",
			"  <li>${1:項目}</li>",
			"  <li>${1:項目}</li>",
			"</ul>"
		],
		"description": "順序なしリスト"
	},
	"details": {
		"prefix": "de",
		"body": [
			"<details>",
			"  <summary>${1:メニュー}</summary>",
			"  <dl>",
			"    <dt>${2:項目}</dt>",
			"    <dt>${2:項目}</dt>",
			"    <dt>${2:項目}</dt>",
			"  </dl>",
			"</details>",
		],
		"description": "順序なしリスト"
	},
	// フォーム
	"form": {
		"prefix": "form",
		"body": [
			"<form action='${1:url}' method='post' enctype='multipart/form-data' accept-charset='UTF-8' >",
			"  $2",
			"  <input type='submit' value='送信' />",
			"</form>"
		],
		"description": "form"
	},
	"label": {
		"prefix": "la",
		"body": [
			"<label for='${1:id}'>${2:ラベル}</label>"
		],
		"description": "label"
	},
	"button": {
		"prefix": "bu",
		"body": [
			"<button name='${1:btn}' onclick='${2:alert(\"クリック\")}'>${3:Button}</button>"
		],
		"description": "button"
	},
	"text": {
		"prefix": "txt",
		"body": [
			"<input type='text' name='${1:txt}' placeholder='${2:入力欄}' ${3:require} />"
		],
		"description": "text入力"
	},
	"textarea": {
		"prefix": "txa",
		"body": [
			"<textarea name='${1:txta}'></textarea>"
		],
		"description": "複数行text入力"
	},
	"password": {
		"prefix": "pa",
		"body": [
			"<input type='password' name='${1:pass}' placeholder='${2:パスワード}' require />"
		],
		"description": "password"
	},
	"checkbox": {
		"prefix": "ch",
		"body": [
			"<label for='${1:chk}'><input id='${1:chk}' type='checkbox' name='${1:chk}' value='${2:value}' />${2:value}</label>",
		],
		"description": "checkbox"
	},
	"radio": {
		"prefix": "ra",
		"body": [
	    "<label for='${1:rad}1' class='radio-inline'><input id='${1:rad}1' type='radio' name='${1:rad}' value='${2:value}1' checked />${2:value}1</label>",
      "<label for='${1:rad}2' class='radio-inline'><input id='${1:rad}2' type='radio' name='${1:rad}' value='${2:value}2' />${2:value}2</label>",
      "<label for='${1:rad}3' class='radio-inline'><input id='${1:rad}3' type='radio' name='${1:rad}' value='${2:value}3' />${2:value}3</label>",
		],
		"description": "checkbox"
	},
	"select": {
		"prefix": "sel",
		"body": [
      "<select name='${1:sel}'>",
      "  <option value='${2:value}1'>${3:項目}1</option>",
      "  <option value='${2:value}2'>${3:項目}2</option>",
    	"  <option value='${2:value}3'>${3:項目}3</option>",
      "</select>",
		],
		"description": "select"
	},
	"range": {
		"prefix": "ran",
		"body": [
			"<input type='range' name='${1:range}' min='0' max='100' step='1' />"
		],
		"description": "range picker"
	},
	"file": {
		"prefix": "file",
		"body": [
			"<input type='file' name='${1:file}' accept='${2:image/*,video/*,audio/*}' />"
		],
		"description": "file"
	},
	"color picker": {
		"prefix": "color",
		"body": [
			"<input type='color' name='${1:color}' />"
		],
		"description": "color picker"
	},
	"date picker": {
		"prefix": "dp",
		"body": [
			"<input type='date' name='${1:date}' />"
		],
		"description": "date picker"
	},
	// メディア
	"img": {
		"prefix": "img",
		"body": [
			"<img src='${1:https://picsum.photos/300/300/?random}' />"
		],
		"description": "img"
	},
	"audio": {
		"prefix": "au",
		"body": [
			"<audio src='${2:url}' controls></audio>"
		],
		"description": "audio"
	},
	"video": {
		"prefix": "vi",
		"body": [
			"<video src='${2:url}' width='${3:640}' height='${4:480}' controls></video>"
		],
		"description": "video"
	},
	"canvas": {
		"prefix": "ca",
		"body": [
			"<canvas width='${2:640}' height='${3:480}'></canvas>"
		],
		"description": "canvas"
	},
	"iframe": {
		"prefix": "ifr",
		"body": [
			"<iframe src='${1:url}' width='${2:640}' height='${3:480}' seamless sandbox='allow-same-origin allow-scripts' ></iframe>"
		],
		"description": "iframe"
	},
	"fetch": {
		"prefix": "fet",
		"body": [
			"const data = await fetch('${1:/api}', {",
			"  method: 'POST',",
			"  headers: {'Content-Type': 'application/json'},",
			"  body: JSON.stringify(${2:{data: 'data'\\}}),",
			"}).then(async (response) => {",
			"  return await response.json()",
			"})",
		],
		"description": "fetch"
	},

	// DOM操作
	"element create": {
		"prefix": "ece",
		"body": [
			"document.createElement('${1:div}')"
		],
		"description": "DOM作成"
	},
	"element remove": {
		"prefix": "erc",
		"body": [
			"${1:elem}.parentNode.removeChild(${1:elem})"
		],
		"description": "DOM削除"
	},
	"element copy": {
		"prefix": "eco",
		"body": [
			"${elem}.cloneNode()"
		],
		"description": "DOMのDeepコピー"
	},
	"element class": {
		"prefix": "ecln",
		"body": [
			"${elem}.className"
		],
		"description": "DOMのclass"
	},
	"element style": {
		"prefix": "es",
		"body": [
			"${1:elem}.style.${2:attr}"
		],
		"description": "DOMのstyle"
	},
	"element query": {
		"prefix": "eqs",
		"body": [
			"document.querySelector('${1:[name=\"txt\"]}')"
		],
		"description": "DOM取得"
	},
	"element queryAll": {
		"prefix": "eqsa",
		"body": [
			"document.querySelectorAll('${1:div,img}')"
		],
		"description": "全DOM取得"
	},
	"element append child after": {
		"prefix": "eac",
		"body": [
			"${1:target}.appendChild(${2:elem})"
		],
		"description": "DOM追加(子要素末尾)"
	},
	// イベント
	"element event": {
		"prefix": "eae",
		"body": [
			"${1:elem}.addEventListener('${2:click}', (e) => {",
			"  e.preventDefault()",
    	"  let ${1:elem} = e.target",
			"",
			"})",
		],
		"description": "DOM event"
	},
	"element event monitor": {
		"prefix": "eem",
		"body": [
			"monitorEvents(${1:div,'click'})"
		],
		"description": "DOM event監視"
	},
	"device motion event": {
		"prefix": "edev",
		"body": [
			"window.addEventListener('devicemotion', (event) => {",
    	"  const accel = {x:event.acceleration.x,y:event.acceleration.y,z:event.acceleration.z,alpha:event.rotationRate.alpha,beta:event.rotationRate.beta,gamma:event.rotationRate.gamma}",
			"  ",
			"})",
			"window.addEventListener('deviceorientation', (event) => {",
    	"  const ori = { alpha:event.alpha,beta:event.beta,gamma:event.gamma}",
			"  ",
			"})",
		],
		"description": "deviceのモーションイベント"
	},
	"touch event": {
		"prefix": "etou",
		"body": [
			"${1:document}.addEventListener('touchstart', (e) => {",
    	"  let touches = e.touches",
			"  for (let i = 0; i < touches.length; ++i) {",
			"    const touch = touches[i]",
			"    const pos = {x:touch.clientX,y:touch.clientY}",
			"  }",
			"})",
			"${1:document}.addEventListener('touchmove', (e) => {",
    	"  const touches = e.touches",
    	"  for (let i = 0; i < touches.length; ++i) {",
    	"    const touch = touches[i]",
      "    const pos = {x:touch.clientX,y:touch.clientY}",
    	"  }",
			"})",
			"${1:document}.addEventListener('touchend', (e) => {",
    	"  const touches = e.touches",
    	"  for (let i = 0; i < touches.length; ++i) {",
    	"    const touch = touches[i]",
      "    const pos = {x:touch.clientX,y:touch.clientY}",
    	"  }",
			"})",
		],
		"description": "touchイベント"
	},
	"mouse event": {
		"prefix": "emou",
		"body": [
			"${1:document}.addEventListener('mousedown', (e) => {",
    	"  const pos = {x:e.clientX,y:e.clientY}",
			"  ",
			"})",
			"${1:document}.addEventListener('mousemove', (e) => {",
		  "  const pos = {x:e.clientX,y:e.clientY}",
			"  ",
			"})",
			"${1:document}.addEventListener('mouseup', (e) => {",
    	"  const pos = {x:e.clientX,y:e.clientY}",
			"  ",
			"})",
		],
		"description": "mouseイベント"
	},
	"gamepad event": {
		"prefix": "epad",
		"body": [
			"const gamepads = {}",
			"",
			"function gamepadHandler(event, connecting) {",
		  "  const gamepad = event.gamepad",
			"",
  		"  if (connecting) {",
    	"    gamepads[gamepad.index] = gamepad",
  		"  } else {",
    	"    delete gamepads[gamepad.index]",
  		"  }",
			"}",
			"",
			"window.addEventListener('gamepadconnected', (e) => gamepadHandler(e, true), false)",
			"window.addEventListener('gamepaddisconnected', (e) => gamepadHandler(e, false), false)",
		],
		"description": "gamepadイベント"
	},
	"key event": {
		"prefix": "ekey",
		"body": [
			"document.addEventListener('keyup', (e) => {",
      "  const keycode = e.keyCode",
      "  // Enter",
      "  if (keycode == 13) {}",
      "  // Space",
      "  if (keycode == 32) {}",
      "  // ←,A",
      "  if (keycode == 37 || keycode == 65) {}",
      "  // →,D",
      "  if (keycode == 39 || keycode == 68) {}",
      "  // ↑,W",
      "  if (keycode == 38 || keycode == 87) {}",
      "  // ↓,S",
      "  if (keycode == 40 || keycode == 83) {}",
      "})",
		],
		"description": "keyイベント"
	},
	// DOM animate
	"DOM animate": {
		"prefix": "eani",
		"body": [
			"${1:elem}.animate([",
			"  ${2:{opacity: 0, color: '#fff'\\}, {opacity: 1, color: '#000'\\}},",
			"],",
			"{duration: ${3:1000}, iterations: Infinity})",
		],
		"description": "DOM animate"
	},

	// NodeJS
	"require": {
		"prefix": "re",
		"body": [
			"const ${1:val} = require('${2:module}')"
		],
		"description": "require"
	},
	"node": {
		"prefix": "node",
		"body": [
			"const express = require('express')",
			"const bodyParser = require('body-parser')",
			"const app = express()",
			"",
			"process.on('uncaughtException', (err) => console.error(err))",
			"process.on('unhandledRejection', (err) => console.error(err))",
			"process.on('SIGINT', () => process.exit(1))",
			"",
			"app.use(express.static('dist'))",
			"app.use(bodyParser.urlencoded({extended: true}))",
			"app.use(bodyParser.json())",
			"",
			"app.get('/', (req, res) => {",
			"  res.json('hello world!!')",
			"})",
			"",
			"app.listen(3000, () => {",
  		"  console.log('Access to http://localhost:3000')",
			"})",
		],
		"description": "style"
	},
	// api
	"api": {
		"prefix": "api",
		"body": [
			"const { ${1:path} } = require('./routes')",
			"",
			"app.use(",
  		"  '${2:/api}',",
  		"  ${3:authenticate,}",
  		"  express.Router()",
    	"    .get('/${1:path}', ${1:path}.index)",
    	"    .get('/${1:path}/:id', ${1:path}.show)",
    	"    .post('/${1:path}', ${1:path}.create)",
    	"    .put('/${1:path}/:id', ${1:path}.update)",
      "    .delete('/${1:path}/:id', ${1:path}.remove)",
			")",
		],
		"description": "api"
	},
	"Authenticate": {
		"prefix": "auth",
		"body": [
			"const passport = require('passport')",
			"const BearerStrategy = require('passport-http-bearer')",
			"passport.use(new BearerStrategy(function(token, done) {",
  		"  models.User.findOne({token, deactivate: {$ne: true}}, function(err, user) {",
    	"    if (err) return done(err)",
    	"    if (!user) return done(null, false)",
    	"    return done(null, user)",
  		"  })",
			"}))",
			"const AnonymousStrategy = require('passport-anonymous')",
			"passport.use(new AnonymousStrategy())",
			"const authenticate = passport.authenticate('bearer', {session: false})",
			"const partialAuth = passport.authenticate(['bearer', 'anonymous'], {session: false})",
			"",
			"const crypto = require('crypto')",
			"const generateToken = () => {",
  		"  const length = 32",
  		"  const chars = 'abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789_-'",
		  "  const rnd = crypto.randomBytes(length)",
  		"  const ret = []",
  		"  for (let i = 0; i < length; i++) {",
    	"    ret.push(chars[rnd[i] % chars.length])",
  		"  }",
  		"  return ret.join('')",
			"})",
		],
		"description": "Authenticate"
	},
	"CRUD import": {
		"prefix": "crudi",
		"body": [
			"'use strict'",
			"",
			"require('fs').readdirSync(__dirname).forEach(e => {",
  		"  const name = /^([a-z]+)\\.js$/i.test(e) && RegExp.\\$1",
  		"  if (name && name !== 'index') {",
    	"    const route = require('./' + name)",
    	"    module.exports[name] = route",
  		"  }",
			"})",
		],
		"description": "crudi"
	},
	"CRUD": {
		"prefix": "crud",
		"body": [
			"const { ${1:Model} } = require('../models')",
			"",
			"module.exports = {",
			"  index,",
			"  create,",
			"  show,",
			"  update,",
			"  remove,",
			"}",
			"",
			"async function index(req, res) {",
			"  const ${2:model}s = await ${1:Model}.find()",
			"  res.json(${2:model}s)",
			"}",
			"",
			"async function create(req, res) {",
			"  const exist = await ${1:Model}.findOne({${3:name}: req.body.${3:name}})",
			"  if (exist) return res.status(400).json({message: 'already exist'})",
			"  const ${2:model} = await ${1:Model}.create(req.body)",
			"  res.json(${2:model})",
			"}",
			"",
			"async function show(req, res) {",
			"  const ${2:model} = await ${1:Model}.findOne({_id: req.params.id})",
			"  if (${2:model} === null) return res.status(404).json({message: 'not found'})",
			"  res.json(${2:model})",
			"}",
			"",
			"async function update(req, res) {",
			"  let ${2:model} = await ${1:Model}.findOne({_id: req.params.id})",
			"  if (${2:model} === null) return res.status(404).json({message: 'not found'})",
			"  ${2:model} = await ${1:Model}.findByIdAndUpdate(${2:model}.id, {\\$set: req.body})",
			"  res.json(${2:model})",
			"}",
			"",
			"async function remove(req, res) {",
			"  let ${2:model} = await ${1:Model}.findOne({_id: req.params.id})",
			"  if (${2:model} === null) return res.status(404).json({message: 'not found'})",
  		"  ${2:model} = await ${1:Model}.findByIdAndRemove(${2:model}.id)",
			"  res.json(${2:model})",
			"}",
		],
		"description": "Create Read Update Delete"
	},
	// WebSocket
	"WebSocket Server": {
		"prefix": "wss",
		"body": [
			"const io = require('socket.io')(9000)",
			"",
			"io.on('connection', (socket) => {",
		  "  socket.on('event', (msg) => {",
		 	"    io.emit('event', msg)",
		  "  })",
			"})",
		],
		"description": "WebSocket Server"
	},
	"WebSocket Client script": {
		"prefix": "jswsc",
		"body": [
			"<script src=\"node_modules/socket.io-client/dist/socket.io.js\"></script>",
		],
		"description": "WebSocket Client script"
	},
	"WebSocket Client import": {
		"prefix": "imwsc",
		"body": [
			"import io from 'socket.io-client'",
		],
		"description": "WebSocket Client script"
	},
	"WebSocket Client": {
		"prefix": "wsc",
		"body": [
			"const socket = io('http://localhost:9000')",
      "socket.on('connect', () => {",
      "  socket.on('event', (data) => {",
      "  console.log(data)",
      " })",
      "  socket.emit('event', {msg: 'メッセージ'})",
      "})",
		],
		"description": "WebSocket Client"
	},
	// axios
	"Axios script": {
		"prefix": "jsax",
		"body": [
			"<script src=\"https://unpkg.com/axios/dist/axios.min.js\"></script>",
		],
		"description": "axios script"
	},
	"Axios import": {
		"prefix": "imax",
		"body": [
			"import axios from 'axios'",
		],
		"description": "axios import"
	},
	"Axios": {
		"prefix": "ax",
		"body": [
			"axios.${1:get}('${2:https://randomuser.me/api/}')",
		],
		"description": "axios"
	},
	"Axios interceptors": {
		"prefix": "axin",
		"body": [
			"axios.interceptors.request.use(req => {",
	  	"  const token = localStorage.getItem('token')",
			"",
		  "  if (token) {",
    	"    // ユーザ認証トークン付与",
		  "    req.headers.Authorization = `Bearer ${token}`",
		  "  }",
  		"  return req",
			"}, err => Promise.reject(err))",
			"",
			"axios.interceptors.response.use(res => res, err => {",
  		"  if (axios.isCancel(err)) {",
    	"    return Promise.reject({code: 999, message: 'cancel'})",
  		"  }",
  		"  if (err.response.status && err.response.status === 401) {",
    	"    localStorage.setItem('token', '')",
  		"  }",
  		"  return Promise.reject(err.response || {})",
			"})",
			"",
			"// ユーザ登録時認証トークン保存",
    	"//localStorage.setItem('token', token)",
		],
		"description": "axios interceptors"
	},
	// mongodb
	"MongoDB": {
		"prefix": "mongo",
		"body": [
			"const mongoose = require('mongoose')",
			"mongoose.Promise = global.Promise",
  		"mongoose.connect('mongodb://localhost/${1:dbname}')",
  		"const models = require('./models')",
		],
		"description": "MongoDB"
	},
	// mongoose models
	"Mongoose Models": {
		"prefix": "mongoi",
		"body": [
			"'use strict'",
			"",
			"require('fs').readdirSync(__dirname).forEach(e => {",
  		"  const name = /^([a-z]+)\\.js$/i.test(e) && RegExp.\\$1",
  		"  if (name && name !== 'index') {",
    	"    const model = require('./' + name)",
    	"    module.exports[model.modelName] = model",
  		"  }",
			"})",
		],
		"description": "Mongoose Models"
	},
	// mongoose model
	"Mongoose Model": {
		"prefix": "model",
		"body": [
			"const mongoose = require('mongoose')",
			"const Schema = mongoose.Schema",
			"const mongooseLeanVirtuals = require('mongoose-lean-virtuals')",
			"",
			"const schema = new Schema({",
			"  ${1:releation: {type: Schema.Types.ObjectId, ref: 'OtherSchema'\\},}",
			"}, {",
			"  timestamps: true,",
			"  toObject: {",
    	"    virtuals: true,",
  		"  },",
  		"  toJSON: {",
      "    virtuals: true,",
    	"    transform: (doc, m) => {",
      "      delete m.__v",
      "      return m",
      "    },",
  		"  },",
			"})",
			"",
			"schema.pre('update', async function(next) {",
		  "  this.options.runValidators = true",
  		"  return next()",
			"})",
			"schema.pre('findOneAndUpdate', async function(next) {",
			"  this.options.runValidators = true",
		  "  this.options.new = true",
  		"  return next()",
			"})",
			"",
			"schema.plugin(mongooseLeanVirtuals)",
			"",
			"module.exports = mongoose.model('${2:SchemaName}', schema)",
		],
		"description": "Mongoose Model"
	},
	"Mongo model index": {
		"prefix": "mmi",
		"body": [
			"schema.index({${1:loc: '2dsphere'}})",
		],
		"description": "Mongo model index"
	},
	"Mongo model method": {
		"prefix": "mmm",
		"body": [
			"schema.method('${1:name}', function(${2:arg}) {",
			"",
			"})",
		],
		"description": "Mongo model method"
	},
	"Mongo model static method": {
		"prefix": "mms",
		"body": [
			"schema.static('${1:name}', function(${2:arg}) {",
			"",
			")",
		],
		"description": "Mongo model static method"
	},
	"Mongo model virtual": {
		"prefix": "mmv",
		"body": [
			"schema.virtual('${1:field}').get(function() {",
			"",
			"})",
		],
		"description": "Mongo model virtual"
	},
	// mongoose find
	"Mongo find": {
		"prefix": "mfi",
		"body": [
			"${1:Model}.find({${2}})",
		],
		"description": "Mongo find"
	},
	"Mongo findOne": {
		"prefix": "mfio",
		"body": [
			"${1:Model}.findOne({${2}})",
		],
		"description": "Mongo findOne"
	},
	"Mongo find select": {
		"prefix": "mfs",
		"body": [
			".select('${1}')",
		],
		"description": "Mongo find select"
	},
	"Mongo find populate": {
		"prefix": "mfp",
		"body": [
			".populate({",
			"  path: '${1:model}',",
			"  select: '${2}',",
			"})",
		],
		"description": "Mongo find populate"
	},
	"Mongo find sort": {
		"prefix": "mfso",
		"body": [
			".sort('-${1:field}')",
		],
		"description": "Mongo find sort"
	},
	"Mongo find skip": {
		"prefix": "mfsk",
		"body": [
			".skip(${1:num})",
		],
		"description": "Mongo find skip"
	},
	"Mongo find lean": {
		"prefix": "mfl",
		"body": [
			".lean(${1:{ virtuals: ['${field}'] \\}})",
		],
		"description": "Mongo find lean"
	},
	// mongoose aggregate
	"Mongo aggregate": {
		"prefix": "ma",
		"body": [
			"${1:Model}.aggregate([",
			"  ${2}",
			"])",
		],
		"description": "Mongo aggregate"
	},
	"Mongo aggregate match": {
		"prefix": "mam",
		"body": [
			"{ \\$match: { ${1} } },",
		],
		"description": "Mongo aggregate match"
	},
	"Mongo aggregate project": {
		"prefix": "map",
		"body": [
			"{ \\$project: { ${1} } },",
		],
		"description": "Mongo aggregate project"
	},
	"Mongo aggregate group": {
		"prefix": "mag",
		"body": [
			"{ \\$group: { _id: '\\$${1:field}', count: { \\$sum: 1 } } },",
		],
		"description": "Mongo aggregate group"
	},
	"Mongo aggregate lookup": {
		"prefix": "mal",
		"body": [
			"{ \\$lookup: {",
			"  from: '${1:collection}',",
      "  localField: '${2:field}',",
      "  foreignField: '_id',",
      "  as: '${2:field}',",
			"}},",
		],
		"description": "Mongo aggregate lookup"
	},
	"Mongo aggregate lookup pipeline": {
		"prefix": "malp",
		"body": [
			"{ \\$lookup: {",
			"  from: '${1:collection}',",
      "  let: { ${2:field}: '\\$${2:field}' },",
      "  pipeline: [",
      "    {",
      "      \\$match: {",
      "        \\$expr: {",
      "          \\$eq: ['\\$_id', '\\$\\$${2:field}'],",
      "        },",
      "      },",
      "    },",
      "  ],",
      "  as: '${2:field}',",
			"}},",
		],
		"description": "Mongo aggregate lookup select"
	},
	"Mongo aggregate sort": {
		"prefix": "mas",
		"body": [
	    "{\\$sort: { ${1:count}: -1 }},",
		],
		"description": "Mongo aggregate sort"
	},
	"Mongo update": {
		"prefix": "mu",
		"body": [
			".update(${1:findCondition}, ${2:updateData})",
		],
		"description": "Mongo update"
	},
	"Mongo findOneAndUpdate": {
		"prefix": "mu",
		"body": [
			".findOneAndUpdate(${1:findCondition}, ${2:updateData}, {upsert: true, new: true, runValidators: true})",
		],
		"description": "Mongo update"
	},
	"Mongo findOneAndRemove": {
		"prefix": "mfr",
		"body": [
			".findOneAndRemove(${1:findCondition})",
		],
		"description": "Mongo remove"
	},

	// React
	"style": {
		"prefix": "st",
		"body": [
			"style={{$1}}",
		],
		"description": "style"
	},
	"className": {
		"prefix": "cln",
		"body": [
			"className={$1}",
		],
		"description": "className"
	},
	"ReactComponent HOC": {
		"prefix": "hoc",
		"body": [
			"import React from 'react'",
			"import { connect } from 'react-redux'",
			"import { reduxForm } from 'redux-form'",
			"",
			"export default () => {",
  		"  return (WrappedComponent) => {",
	    "    return connect(",
      "      // propsに受け取るreducerのstate",
      "      () => ({}),",
      "      // propsに付与するactions",
      "      {}",
      "    )(reduxForm({",
			"      form: 'formName',",
      "      validate: values => {",
      "        const errors = {}",
      "        return errors",
      "      },",
			"    })(class extends React.Component {",
			"",
			"      componentDidMount() {}",
			"",
			"      handleSubmit = (values) => {}",
			"",
      "      render () {",
      "        const { handleSubmit } = this.props",
    	"        return <WrappedComponent {...this.props} onSubmit={handleSubmit(this.handleSubmit)} />",
      "      }",
    	"    }))",
  		"  }",
			"}",
		],
		"description": "React HOCコンポーネント"
	},
	"ReactComponent": {
		"prefix": "rea",
		"body": [
			"import React from 'react'",
			"",
			"export default class ${1:name} extends React.Component {",
		  "",
  		"  componentDidMount () {}",
			"",
  		"  render () {",
    	"    return <div/>",
  		"  }",
			"}",
		],
		"description": "Reactコンポーネント"
	},
	"StatelessReactComponent": {
		"prefix": "srea",
		"body": [
			"import React from 'react'",
			"",
			"const ${1:name} = (${2:props}) => {",
		  "  return ${3:null}",
			"}",
			"",
			"export default ${1:name}",
		],
		"description": "Stateless Functinal Reactコンポーネント"
	},
	"ref": {
		"prefix": "ref",
		"body": [
			"ref={${1:node} => this.${1:node} = ${1:node} }",
		],
		"description": "ref"
	},
	// Redux
	"Redux Reducer": {
		"prefix": "redux",
		"body": [
			"import { SubmissionError } from 'redux-form'",
			"",
			"const INDEX = '${1:model}/INDEX'",
			"const SHOW = '${1:model}/SHOW'",
			"const CREATE = '${1:model}/CREATE'",
			"const UPDATE = '${1:model}/UPDATE'",
			"",
			"const initialState = {",
			"  ${1:model}s: [],",
			"  ${1:model}: null,",
			"}",
			"",
			"export default function reducer(state = initialState, action = {}) {",
			"  switch (action.type) {",
			"    case INDEX:",
			"      return {",
			"        ...state,",
			"        ${1:model}s: action.${1:model}s || state.${1:model}s,",
			"      }",
			"    case SHOW:",
			"      return {",
			"        ...state,",
			"        ${1:model}: action.${1:model} || state.${1:model},",
			"      }",
			"    case CREATE:",
			"      return {",
			"        ...state,",
			"        ${1:model}: action.${1:model} || state.${1:model},",
			"      }",
			"    case UPDATE:",
			"      return {",
			"        ...state,",
			"        ${1:model}: action.${1:model} || state.${1:model},",
			"      }",
			"    default:",
			"      return state",
			"  }",
			"}",
			"",
			"export function index(page) {",
			"  return (dispatch, getState, client) => {",
			"    return client",
			"      .get(`/api/${1:model}${page ? '?page=' + page : ''}`)",
			"      .then(res => res.data)",
      "      .then(${1:model}s => {",
			"        dispatch({type: INDEX, ${1:model}s})",
			"        return ${1:model}s",
			"      })",
			"  }",
			"}",
			"",
			"export function show(id) {",
			"  return (dispatch, getState, client) => {",
			"    return client",
			"      .get(`/api/${1:model}/\\${id}`)",
			"      .then(res => res.data)",
      "      .then(${1:model} => {",
			"        dispatch({type: SHOW, ${1:model}})",
			"        return ${1:model}",
			"      })",
			"  }",
			"}",
			"",
			"export function create(data) {",
			"  return (dispatch, getState, client) => {",
			"    return client",
			"      .post('/api/${1:model}', data)",
			"      .then(res => res.data)",
      "      .then(${1:model} => {",
			"        dispatch({type: CREATE, ${1:model}})",
			"        return ${1:model}",
			"      })",
			"      .catch(err => {",
      "        throw new SubmissionError({",
      "          _error: err && err.data ? err.data.message : 'エラーが発生しました',",
      "          status: err && err.data ? err.data.status : null,",
      "        })",
      "      })",
			"  }",
			"}",
			"",
			"export function update(id, data) {",
			"  return (dispatch, getState, client) => {",
			"    return client",
			"      .put(`/api/${1:model}/\\${id}`, data)",
			"      .then(res => res.data)",
      "      .then(${1:model} => {",
			"        dispatch({type: UPDATE, ${1:model}})",
			"        return ${1:model}",
			"      })",
			"      .catch(err => {",
      "        throw new SubmissionError({",
      "          _error: err && err.data ? err.data.message : 'エラーが発生しました',",
      "          status: err && err.data ? err.data.status : null,",
      "        })",
      "      })",
			"  }",
			"}",
			"",
			"export function remove(id) {",
			"  return (dispatch, getState, client) => {",
			"    return client.delete(`/api/${1:model}/\\${id}`)",
			"  }",
			"}",
		],
		"description": "Redux Reducer"
	},
	// connect
	"React Redux connect": {
		"prefix": "dco",
		"body": [
			"@connect(",
      "  state => ({}),",
      "  {}",
      ")"
		],
		"description": "React Redux connect"
	},
	"React Redux connect import": {
		"prefix": "imco",
		"body": [
			"import { connect } from 'react-redux'",
		],
		"description": "React Redux connect import"
	},
	// React Router
	"React Router import": {
		"prefix": "imrou",
		"body": [
			"import { withRouter } from 'react-router'",
			"",
			"@withRouter",
		],
		"description": "React Router import"
	},
	"React Router DOM Link": {
		"prefix": "lin",
		"body": [
			"<Link to='${1:/}'>${2:リンク}</Link>",
		],
		"description": "React Router DOM Link"
	},
	"React Router DOM Link import": {
		"prefix": "imlin",
		"body": [
			"import { Link } from 'react-router-dom'",
		],
		"description": "React Router DOM Link import"
	},
	// Material-UI
	"classes": {
		"prefix": "cls",
		"body": [
			"classes={$1}",
		],
		"description": "classes"
	},
	"withStyles": {
		"prefix": "wst",
		"body": [
			"withStyles(theme => ({",
			"  ",
			"}), {withTheme: true})(${1:props => <div/>})",
		],
		"description": "withStyles"
	},
	"withStyles decorators": {
		"prefix": "dwst",
		"body": [
			"@withStyles(theme => ({",
			"  ",
			"}), {withTheme: true})",
		],
		"description": "withStyles decorators"
	},
	"withStyles import": {
		"prefix": "imwst",
		"body": [
			"import { withStyles } from '@material-ui/core'",
		],
		"description": "withStyles import"
	},
	"AppBar import": {
		"prefix": "imapp",
		"body": [
			"import AppBar from '@material-ui/core/AppBar'",
			"import Toolbar from '@material-ui/core/Toolbar'",
		],
		"description": "AppBar iport"
	},
	"AppBar": {
		"prefix": "app",
		"body": [
			"<AppBar position='static'>",
			"  <Toolbar>",
			"    ${1:タイトル}",
			"  </Toolbar>",
			"</AppBar>"
		],
		"description": "AppBar"
	},
	"Avatar import": {
		"prefix": "imav",
		"body": [
			"import Avatar from '@material-ui/core/Avatar'"
		],
		"description": "Avatar import"
	},
	"Avatar": {
		"prefix": "av",
		"body": [
			"<Avatar src='${1:https://unsplash.it/48/48?random}' />"
		],
		"description": "Avatar"
	},
	"Badge import": {
		"prefix": "imba",
		"body": [
			"import Badge from '@material-ui/core/Badge'"
		],
		"description": "Badge import"
	},
	"Badge": {
		"prefix": "ba",
		"body": [
			"<Badge badgeContent={${1:1}} color='primary'>$2</Badge>"
		],
		"description": "Badge"
	},
	"BottomNavigation import": {
		"prefix": "imbot",
		"body": [
			"import BottomNavigation from '@material-ui/core/BottomNavigation'",
			"import BottomNavigationAction from '@material-ui/core/BottomNavigationAction'",
			"import FavoriteIcon from '@material-ui/icons/Favorite'",
		],
		"description": "BottomNavigation import"
	},
	"BottomNavigation": {
		"prefix": "bot",
		"body": [
			"<BottomNavigation",
      "  value={this.state.selected || 0}",
      "  onChange={(event, selected) => this.setState({selected})}",
      "  showLabels",
      ">",
      "  <BottomNavigationAction label='${1:Favorites}' icon={<FavoriteIcon />} />",
      "  <BottomNavigationAction label='${1:Favorites}' icon={<FavoriteIcon />} />",
      "  <BottomNavigationAction label='${1:Favorites}' icon={<FavoriteIcon />} />",
      "</BottomNavigation>",
		],
		"description": "BottomNavigation"
	},
	"Button import": {
		"prefix": "imbu",
		"body": [
			"import Button from '@material-ui/core/Button'"
		],
		"description": "Button import"
	},
	"Button": {
		"prefix": "bu",
		"body": [
			"<Button size='medium' variant='contained' color='primary' onClick={() => {}}>${1:Button}</Button>"
		],
		"description": "Button"
	},
	"Card import": {
		"prefix": "imca",
		"body": [
			"import Card from '@material-ui/core/Card'",
			"import CardActions from '@material-ui/core/CardActions'",
			"import CardContent from '@material-ui/core/CardContent'",
			"import CardMedia from '@material-ui/core/CardMedia'",
		],
		"description": "Card import"
	},
	"Card": {
		"prefix": "ca",
		"body": [
			"<Card style={{maxWidth: 240}}>",
			"  <CardMedia style={{height: 120}} image='https://unsplash.it/240/120?random' />",
			"  <CardContent></CardContent>",
			"  <CardActions><Button size='small' variant='contained' color='primary' onClick={() => {}}>${1:Button}</Button></CardActions>",
			"</Card>"
		],
		"description": "Card"
	},
	"Chip import": {
		"prefix": "imchi",
		"body": [
			"import Chip from '@material-ui/core/Chip'"
		],
		"description": "Chip"
	},
	"Chip": {
		"prefix": "chi",
		"body": [
			"<Chip label='${2:テキスト}' onClick={() => {}} onDelete={() => {}} />"
		],
		"description": "Chip"
	},
	"Dialog import": {
		"prefix": "imdia",
		"body": [
			"import Dialog from '@material-ui/core/Dialog'",
			"import DialogTitle from '@material-ui/core/DialogTitle'",
			"import DialogContent from '@material-ui/core/DialogContent'",
			"import DialogActions from '@material-ui/core/DialogActions'",
		],
		"description": "Dialog import"
	},
	"Dialog": {
		"prefix": "dia",
		"body": [
			"<Dialog open={!!this.state.${1:open}} onClose={() => this.setState({${1:open}: false})}>",
			"  <DialogTitle>${2:タイトル}</DialogTitle>",
			"  <DialogContent></DialogContent>",
			"  <DialogActions><Button size='small' variant='contained' color='primary' onClick={() => this.setState({${1:open}: false})}>${3:Button}</Button></DialogActions>",
			"</Dialog>"
		],
		"description": "Dialog"
	},
	"Drawer import": {
		"prefix": "imdra",
		"body": [
			"import Drawer from '@material-ui/core/Drawer'",
		],
		"description": "Drawer import"
	},
	"Drawer": {
		"prefix": "dra",
		"body": [
			"<Button size='small' onClick={() => this.setState({${1:open}: true})}>メニュー</Button>",
			"<Drawer anchor='bottom' open={this.state.${1:open}} onClose={() => this.setState({${1:open}: false})}>",
      "  <div",
      "    tabIndex={0}",
      "    role='button'",
      "    onClick={() => this.setState({${1:open}: false})}>",
			"    <List>",
			"      {",
			"        ${2:[1, 2, 3]}.map(v =>",
			"          <ListItem button divider key={v} onClick={() => this.setState({${1:open}: false})}>",
			"            <ListItemText primary={`項目\\${v}`} secondary={`項目\\${v} サブ`} />",
			"          </ListItem>",
			"        )",
			"      }",
			"    </List>",
      "  </div>",
      "</Drawer>",
		],
		"description": "Drawer"
	},
	"GridList import": {
		"prefix": "imgr",
		"body": [
			"import GridList from '@material-ui/core/GridList'",
			"import GridListTile from '@material-ui/core/GridListTile'",
		],
		"description": "GridList import"
	},
	"GridList": {
		"prefix": "gr",
		"body": [
			"<GridList cellHeight={160} cols={3}>",
      "  {[",
      "    {img: 'https://unsplash.it/320/160?random&1', cols: 2},",
      "    {img: 'https://unsplash.it/160/160?random&2', cols: 1},",
      "    {img: 'https://unsplash.it/160/160?random&3', cols: 1},",
      "    {img: 'https://unsplash.it/320/160?random&4', cols: 2},",
      "  ].map((tile, idx) => (",
      "    <GridListTile key={idx} cols={tile.cols || 1}>",
      "      <img src={tile.img} />",
      "    </GridListTile>",
      "  ))}",
      "</GridList>",
		],
		"description": "GridList"
	},
	"ExpansionPanel import": {
		"prefix": "imexp",
		"body": [
			"import ExpansionPanel from '@material-ui/core/ExpansionPanel'",
			"import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary'",
			"import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails'",
			"import ExpandMoreIcon from '@material-ui/icons/ExpandMore'",
		],
		"description": "ExpansionPanel import"
	},
	"ExpansionPanel": {
		"prefix": "exp",
		"body": [
			"<ExpansionPanel",
      "  classes={{expanded: {margin: 0}}}>",
      "  <ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}>${1:サマリ}</ExpansionPanelSummary>",
			"  <ExpansionPanelDetails>${2:詳細}</ExpansionPanelDetails>",
      "</ExpansionPanel>",
		],
		"description": "ExpansionPanel"
	},
	"List import": {
		"prefix": "imli",
		"body": [
			"import List from '@material-ui/core/List'",
			"import ListItem from '@material-ui/core/ListItem'",
			"import ListItemText from '@material-ui/core/ListItemText'",
		],
		"description": "List import"
	},
	"List": {
		"prefix": "li",
		"body": [
			"<List>",
			"  {",
			"    ${1:[1, 2, 3]}.map(v =>",
			"      <ListItem button divider key={v} onClick={() => {}}>",
			"        <ListItemText primary={`項目\\${v}`} secondary={`項目\\${v} サブ`} />",
			"      </ListItem>",
			"    )",
			"  }",
			"</List>",
		],
		"description": "List"
	},
	"Menu import": {
		"prefix": "imme",
		"body": [
			"import Menu from '@material-ui/core/Menu'",
			"import MenuItem from '@material-ui/core/MenuItem'",
		],
		"description": "Menu"
	},
	"Menu": {
		"prefix": "me",
		"body": [
			"<Button size='small' onClick={e => this.setState({anchor: e.currentTarget})}>${1:メニュー}</Button>",
			"<Menu",
			"  open={!!this.state.anchor}",
			"  anchorEl={this.state.anchor}",
			"  onClose={() => this.setState({anchor: null})}",
			"  MenuListProps={{style: {padding: 0}}}",
			">",
			"  {",
			"    ${2:[1, 2, 3]}.map(v =>",
			"      <MenuItem key={v} onClick={() => {}}>{`項目\\${v}`}</MenuItem>",
			"    )",
			"  }",
			"</Menu>",
		],
		"description": "Menu"
	},
	"Paper import": {
		"prefix": "impa",
		"body": [
			"import Paper from '@material-ui/core/Paper'"
		],
		"description": "Paper import"
	},
	"Paper": {
		"prefix": "pa",
		"body": [
			"<Paper elevation={4}>$1</Paper>"
		],
		"description": "Paper"
	},
	"Datetime Picker": {
		"prefix": "dtpi",
		"body": [
			"<TextField type='datetime-local' defaultValue={new Date().toISOString().slice(0, 16)} InputLabelProps={{shrink: true}}/>"
		],
		"description": "Datetime Picker"
	},
	"CircularProgress import": {
		"prefix": "imcpg",
		"body": [
			"import CircularProgress from '@material-ui/core/CircularProgress'",
		],
		"description": "CircularProgress import"
	},
	"CircularProgress": {
		"prefix": "cpg",
		"body": [
			"<CircularProgress size={${1:30}} thickness={${2:3}} />",
		],
		"description": "CircularProgress"
	},
	"LinearProgress import": {
		"prefix": "imlpg",
		"body": [
			"import LinearProgress from '@material-ui/core/LinearProgress'",
		],
		"description": "LinearProgress import"
	},
	"LinearProgress": {
		"prefix": "lpg",
		"body": [
			"<LinearProgress />",
		],
		"description": "LinearProgress"
	},
	"Checkbox import": {
		"prefix": "imch",
		"body": [
			"import Checkbox from '@material-ui/core/Checkbox'",
			"import FormControlLabel from '@material-ui/core/FormControlLabel'",
		],
		"description": "Checkbox"
	},
	"Checkbox": {
		"prefix": "ch",
		"body": [
			"<FormControlLabel",
      "  control={",
			"    <Checkbox",
			"      checked={this.state.${1:checked}}",
			"      onChange={(e) => this.setState({${1:checked}: e.target.checked})} />",
			"  }",
			"  label='${2:ラベル}'",
			"/>",
		],
		"description": "Checkbox"
	},
	"Radio import": {
		"prefix": "imra",
		"body": [
			"import Radio from '@material-ui/core/Radio'",
			"import FormControlLabel from '@material-ui/core/FormControlLabel'",
		],
		"description": "Radio"
	},
	"Radio": {
		"prefix": "ra",
		"body": [
			"<FormControlLabel value='${1:value}' control={<Radio />} label='${2:ラベル}' />",
		],
		"description": "Radio"
	},
	"Switch import": {
		"prefix": "imswi",
		"body": [
			"import Switch from '@material-ui/core/Switch'",
			"import FormControlLabel from '@material-ui/core/FormControlLabel'",
		],
		"description": "Switch"
	},
	"Switch": {
		"prefix": "swi",
		"body": [
			"<FormControlLabel",
			"  control={",
			"    <Switch",
			"      checked={this.state.${1:checked}}",
			"      onChange={(e) => this.setState({${1:checked}: e.target.checked})}",
			"    />",
      "  }",
      "  label='${2:ラベル}'",
      "/>",
		],
		"description": "Switch"
	},
	"Select import": {
		"prefix": "imse",
		"body": [
			"import FormControl from '@material-ui/core/FormControl'",			
			"import InputLabel from '@material-ui/core/InputLabel'",
			"import MenuItem from '@material-ui/core/MenuItem'",
			"import Select from '@material-ui/core/Select'",
		],
		"description": "Select"
	},
	"Select": {
		"prefix": "se",
		"body": [
			"<FormControl>",
			"  <InputLabel htmlFor='${1:inputLabel}'>選択項目</InputLabel>",
			"  <Select",
			"    value={this.state.${2:count} || ''}",
			"    onChange={(e) => this.setState({${2:count}: e.target.value})}",
			"    inputProps={{",
			"      id: '${1:inputLabel}',",
			"    }}",
			"  >",
      "    <MenuItem value={1}>選択肢１</MenuItem>",
      "    <MenuItem value={2}>選択肢２</MenuItem>",
      "    <MenuItem value={3}>選択肢３</MenuItem>",
      "  </Select>",
      "</FormControl>",
		],
		"description": "Select"
	},
	"Snackbar import": {
		"prefix": "imsna",
		"body": [
			"import Snackbar from '@material-ui/core/Snackbar'",
		],
		"description": "Snackbar import"
	},
	"Snackbar": {
		"prefix": "sna",
		"body": [
			"<Snackbar",
			"  open={!!this.state.${1:msg}}",
			"  anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}",
			"  onClose={() => this.setState({${1:msg}: null})}",
			"  autoHideDuration={2000}",
			"  message={this.state.${1:msg}} />",
		],
		"description": "Snackbar"
	},
	"Stepper import": {
		"prefix": "imstep",
		"body": [
			"import Stepper from '@material-ui/core/Stepper'",
			"import Step from '@material-ui/core/Step'",
			"import StepLabel from '@material-ui/core/StepLabel'",
		],
		"description": "Stepper import"
	},
	"Stepper": {
		"prefix": "step",
		"body": [
				"<div>",
        "  <Stepper activeStep={this.state.activeStep || 0} ${1:orientation='vertical'}>",
        "    { ${2:['項目１', '項目２', '項目３']}.map(label => (",
        "      <Step key={label}>",
        "        <StepLabel>{label}</StepLabel>",
        "      </Step>",
        "    ))}",
        "  </Stepper>",
        "  <Button size='medium' variant='raised' color='primary' onClick={() => this.setState({activeStep: (this.state.activeStep || 0) - 1})} disabled={(this.state.activeStep || 0) === 0}>前へ</Button>",
        "  <Button size='medium' variant='raised' color='primary' onClick={() => this.setState({activeStep: (this.state.activeStep || 0) + 1 })} disabled={(this.state.activeStep || 0) === ${2:['項目１', '項目２', '項目３']}.length - 1}>次へ</Button>",
				"</div>",
		],
		"description": "Stepper"
	},
	"MobileStepper import": {
		"prefix": "immstep",
		"body": [
			"import MobileStepper from '@material-ui/core/MobileStepper'",
			"import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'",
			"import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'",
		],
		"description": "MobileStepper import"
	},
	"MobileStepper": {
		"prefix": "mstep",
		"body": [
			"<MobileStepper",
      "  steps={${1:3}}",
      "  position='static'",
      "  activeStep={this.state.activeStep || 0}",
      "  nextButton={",
      "    <Button size='small' onClick={() => this.setState({activeStep: (this.state.activeStep || 0) + 1})} disabled={(this.state.activeStep || 0) === ${1:3} - 1}>",
      "      次へ",
      "      <KeyboardArrowRight />",
      "    </Button>",
      "  }",
      "  backButton={",
      "    <Button size='small' onClick={() => this.setState({activeStep: (this.state.activeStep || 0) - 1})} disabled={(this.state.activeStep || 0) === 0}>",
      "      <KeyboardArrowLeft />",
      "      前へ",
      "    </Button>",
      "  }",
      "/>",
		],
		"description": "MobileStepper"
	},
	"Table import": {
		"prefix": "imta",
		"body": [
			"import Table from '@material-ui/core/Table'",
			"import TableBody from '@material-ui/core/TableBody'",
			"import TableCell from '@material-ui/core/TableCell'",
			"import TableHead from '@material-ui/core/TableHead'",
			"import TableRow from '@material-ui/core/TableRow'",
		],
		"description": "Table import"
	},
	"Table": {
		"prefix": "ta",
		"body": [
			"<Table>",
      "  <TableHead>",
      "    <TableRow>",
      "      <TableCell>名前</TableCell>",
      "      <TableCell>年齢</TableCell>",
      "      <TableCell>身長</TableCell>",
      "    </TableRow>",
      "  </TableHead>",
      "  <TableBody>",
      "    {[",
      "      {name: '太郎', age: 20, height: '180cm'},",
      "      {name: '次郎', age: 18, height: '170cm'},",
      "      {name: '花子', age: 16, height: '160cm'},",
      "    ].map(((n, idx) => (",
      "      <TableRow key={idx}>",
      "        <TableCell component='th' scope='row'>{n.name}</TableCell>",
      "        <TableCell>{n.age}</TableCell>",
      "        <TableCell>{n.height}</TableCell>",
      "      </TableRow>",
      "    )",
      "    )}",
      "  </TableBody>",
      "</Table>",
		],
		"description": "Table"
	},
	"TextField import": {
		"prefix": "imtx",
		"body": [
			"import TextField from '@material-ui/core/TextField'",
		],
		"description": "TextField import"
	},
	"TextField": {
		"prefix": "tx",
		"body": [
			"<TextField label='${1:入力欄}' margin='normal' ${2:multiline rows='4'} ${3:error} />",
		],
		"description": "TextField"
	},
	"Tab import": {
		"prefix": "imtab",
		"body": [
			"import Tabs from '@material-ui/core/Tabs'",
			"import Tab from '@material-ui/core/Tab'",
		],
		"description": "Tab import"
	},
	"Tab": {
		"prefix": "tab",
		"body": [
			"<div>",
			"  <AppBar position='static'>",
			"    <Tabs value={this.state.${1:selected}} onChange={(e, ${1:selected}) => this.setState({${1:selected}})} fullWidth >",
			"      <Tab label='項目1' />",
			"      <Tab label='項目2' />",
			"      <Tab label='項目3' />",
			"    </Tabs>",
			"  </AppBar>",
			"  {this.state.${1:selected} === 0 && <div>項目1 コンテンツ</div>}",
			"  {this.state.${1:selected} === 1 && <div>項目2 コンテンツ</div>}",
			"  {this.state.${1:selected} === 2 && <div>項目3 コンテンツ</div>}",
			"</div>",
		],
		"description": "Tab"
	},
	"Tooltip import": {
		"prefix": "imtip",
		"body": [
			"import Tooltip from '@material-ui/core/Tooltip'",
		],
		"description": "Tooltip import"
	},
	"Tooltip": {
		"prefix": "tip",
		"body": [
			"<Tooltip title='${1:説明}'>",
			"  <Button size='small' onClick={() => {}}>ボタン</Button>",
			"</Tooltip>",
		],
		"description": "Tooltip"
	},
	// Material-Icon
	"Material Icon import": {
		"prefix": "imic",
		"body": [
			"import ${1:Name} from '@material-ui/icons/${1:Name}'",
		],
		"description": "Material Icon import"
	},
	// Redux Form
	"ReduxForm decorators": {
		"prefix": "drfo",
		"body": [
			"@reduxForm({",
			"  form: '${1:formName}',",
			"  validate: values => {",
      "    const errors = {}",
      "    return errors",
      "  },",
			"})",
		],
		"description": "ReduxForm decorators"
	},
	"ReduxForm import": {
		"prefix": "imrfo",
		"body": [
			"import { reduxForm } from 'redux-form'",
		],
		"description": "ReduxForm import"
	},
	"Redux form": {
		"prefix": "rfo",
		"body": [
			"<form onSubmit={this.props.handleSubmit(${1:(values) => null})}>",
			"</form>",
		],
		"description": "Redux form"
	},
	"Field": {
		"prefix": "rfi",
		"body": [
			"<Field name='${1:name}' component={${2:component}} input meta />",
		],
		"description": "Field"
	},
	"Field import": {
		"prefix": "imrfi",
		"body": [
			"import { Field } from 'redux-form'",
		],
		"description": "Field import"
	},
	"FieldArray": {
		"prefix": "rfia",
		"body": [
			"<FieldArray name='${1:name}' component={${2:component}} />",
		],
		"description": "FieldArray"
	},
	"FieldArray import": {
		"prefix": "rfia",
		"body": [
			"import { FieldArray } from 'redux-form'",
		],
		"description": "FieldArray import"
	},
	// Styled Components
	"Styled Components import": {
		"prefix": "sc",
		"body": [
			"import styled from 'styled-components'",
		],
		"description": "Styled Components import"
	},
	"Styled Components": {
		"prefix": "sc",
		"body": [
			"const Styled${1:MuiComponent} = styled(${1:MuiComponent})`",
  		"  && {",
    	"    ${1:style}",
  		"  }",
			"`",
		],
		"description": "Styled Components"
	},
	// レイアウト
	"flexbox": {
		"prefix": "fl",
		"body": [
			"display: flex;",
			"flex-direction: ${1:row,row-reverse,column,column-reverse};",
    	"justify-content: ${2:flex-start,flex-end,space-between,space-around};",
			"align-items: ${3:stretch,flex-start,flex-end,center,base-line};",
			"flex-wrap: ${4:nowrap,wrap,wrap-reverse};",
		],
		"description": "flexbox"
	},
	"grid": {
		"prefix": "gr",
		"body": [
			"display: inline-grid;",
			"grid-template-rows: ${1:100px 50px};",
    	"grid-template-columns: ${2:50px 100px 50px};",
		],
		"description": "grid"
	},
	"grid-item": {
		"prefix": "gri",
		"body": [
			"grid-row: ${1:1 / 2};",
    	"grid-column: ${2:2 / 3};",
		],
		"description": "grid-item"
	},
	// レスポンシブ
	"media query": {
		"prefix": "me",
		"body": [
			"@media screen and (max-width: 768px) {",
    	"  $1",
			"}",
		],
		"description": "レスポンシブレイアウト"
	},
	// スティッキー要素
	"sticky": {
		"prefix": "sti",
		"body": [
			"position: sticky;",
		],
		"description": "スティッキー"
	},
	// マウスオーバー
	"hover": {
		"prefix": "ho",
		"body": [
			"&:hover {",
    	"  $1",
			"}",
		],
		"description": "マウスオーバー"
	},
	// 余白
	"padding": {
		"prefix": "p",
		"body": [
			"padding: ${1:1rem};",
		],
		"description": "パディング"
	},
	"margin": {
		"prefix": "m",
		"body": [
			"margin: ${1:1rem};",
		],
		"description": "マージン"
	},
	// 背景
	"background-color": {
		"prefix": "bgc",
		"body": [
			"background-color: ${1:#888};",
		],
		"description": "背景色"
	},
	"background-image": {
		"prefix": "bgi",
		"body": [
			"background: url('${1:https://unsplash.it/300/300?random}');",
	  	"background-size: ${1:100}px ${2:50}px;",
		  "width: ${1:100}px;",
  		"height: ${2:50}px;",
		],
		"description": "背景画像"
	},
	"background-blend-mode": {
		"prefix": "bgb",
		"body": [
			"background: url('${1:https://unsplash.it/300/300?random=1}'),url('${2:https://unsplash.it/300/300?random=2}');",
			"background-blend-mode: ${1:multiply,darken,lighten,screen,overlay,difference};",
		],
		"description": "背景画像ブレンディング"
	},
	"linear-gradient": {
		"prefix": "grl",
		"body": [
			"background: linear-gradient(${1:45}deg,${2:red,orange,yellow,green,blue,purple});",
		],
		"description": "線形グラデーション"
	},
	"radial-gradient": {
		"prefix": "grc",
		"body": [
			"background: radial-gradient(center,circle,${1:red,orange,yellow,green,blue,purple});",
		],
		"description": "円形グラデーション"
	},
	// 位置、サイズ
	"left": {
		"prefix": "le",
		"body": [
			"left: ${1:4px,0.01vw};",
		],
		"description": "横位置"
	},
	"top": {
		"prefix": "to",
		"body": [
			"top: ${1:4px,0.01vh};",
		],
		"description": "縦位置"
	},
	"width": {
		"prefix": "w",
		"body": [
			"width: ${1:30%};",
		],
		"description": "幅"
	},
	"height": {
		"prefix": "h",
		"body": [
			"height: ${1:30%};",
		],
		"description": "高さ"
	},
	// 文字
	"color": {
		"prefix": "c",
		"body": [
			"color: ${1:#888};",
		],
		"description": "文字色"
	},
	"font-size": {
		"prefix": "fs",
		"body": [
			"font-size: ${1:1.2rem};",
		],
		"description": "文字サイズ"
	},
	"font-weight": {
		"prefix": "fw",
		"body": [
			"font-weight: ${1:normal, bold, 500};",
		],
		"description": "文字サイズ"
	},
	"text-decoration": {
		"prefix": "td",
		"body": [
			"text-decoration: ${1:none,overline,underline,line-through};",
		],
		"description": "文字装飾"
	},
	"WebFont": {
		"prefix": "wf",
		"body": [
			"@font-face {",
			"  src: url('${1:fontname}.ttf') format('truetype');",
			"  font-family: '${1:fontname}';",
			"}",
		],
		"description": "WebFont"
	},
	// 影
	"text-shadow": {
		"prefix": "ts",
		"body": [
			"text-shadow: ${1:1rem 1rem 1rem rgba(0,0,0,1)};",
		],
		"description": "テキスト影"
	},
	"box-shadow:": {
		"prefix": "bs",
		"body": [
			"text-shadow: ${1:1rem 1rem 1rem 1rem rgba(0,0,0,0.8)};",
		],
		"description": "DOM影"
	},
	// 領域内表示方法
	"overflow": {
		"prefix": "o",
		"body": [
			"overflow: ${1:visible,hidden,scroll,auto};",
		],
		"description": "領域外表示"
	},
	"overflow-wrap": {
		"prefix": "ow",
		"body": [
			"word-wrap: ${1:normal,break-word};",
			"overflow-wrap: ${1:normal,break-word};",
		],
		"description": "領域外の折り返し表示"
	},
	"white-space": {
		"prefix": "ws",
		"body": [
			"white-space: ${1:normal,pre,no-wrap};",
		],
		"description": "改行文字解釈"
	},
	// ボーダー
	"border": {
		"prefix": "b",
		"body": [
			"border: '${1:solid,dashed,groovy,ridge,inset,outset} ${2:0.1rem} ${3:black}';",
		],
		"description": "ボーダー"
	},
	"border-radius": {
		"prefix": "br",
		"body": [
			"border-radius: ${1:1rem};",
		],
		"description": "角丸ボーダー"
	},
	// クリッピング
	"clip-path": {
		"prefix": "cli",
		"body": [
			"clip-path: ${1:circle(50% at 50% 50%),polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%),polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%),polygon(0% 0%, 100% 0%, 100% 75%, 75% 75%, 75% 100%, 50% 75%, 0% 75%)};",
		],
		"description": "クリッピング(円、星、六角形、吹き出し)"
	},
	// フィルター
	"css filter": {
		"prefix": "fil",
		"body": [
			"filter: ${1:grayscale(50%),blur(50%),,brightness(50%),contrast(50%),invert(50%),saturate(50%),sepia(50%)};",
		],
		"description": "CSSフィルター"
	},
	// 幾何変換
	"transform": {
		"prefix": "trf",
		"body": [
			"transform: ${1:translate(0rem, 0rem) rotate(0deg) scale(1.0)};",
		],
		"description": "幾何変換"
	},
	"transform3d": {
		"prefix": "trf",
		"body": [
			"transform: ${1:translate3d(0rem,0rem,0rem) rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale3d(${1:1.0},${1:1.0},${1:1.0})};",
		],
		"description": "3D幾何変換"
	},
	// 透明度
	"opacity": {
		"prefix": "op",
		"body": [
			"opacity:@val;",
		],
		"description": "透明度"
	},
	// トランジション
	"transition": {
		"prefix": "tr",
		"body": [
			"transition: all ${1:linear,ease,ease-in,ease-out,ease-in-out,steps(5,start)} ${2:1}s;",
		],
		"description": "CSS動的変化時のアニメーション"
	},
	// キーフレームアニメーション
	"keyframe animation": {
		"prefix": "ani",
		"body": [
			"@keyframes ${1:name} {",
			"  0% {}",
			"  100% {}",
			"}",
			"animation: ${1:name} ${2:1}s ${3:linear,ease,ease-in,ease-out,ease-in-out,steps(5,start)} ${4:infinite} ${5:alternative};",
		],
		"description": "キーフレームアニメーション"
	},
}
```