VSCodeで爆速コーディング環境を構築する

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

ショートカット

【Windows版】VS Code キーボードショートカット一覧 (オススメ付き)

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

  • Command + F ファイル内キーワード検索
  • Command + Shift + F プロジェクトフォルダ内キーワード検索
  • Command + Shift + E エクスプローラー表示
  • Command + P ファイル検索
  • Command + Enter 下に行追加
  • Command + Shift + Enter 上に行追加
  • Command + ↓ ファイル末尾
  • Command + ↑ ファイル先頭
  • Alt + → or ← 単語単位移動
  • Command + B サイドバー表示・非表示切り替え
  • Command + Shift + V Markdownファイル(.md)のプレビュー
  • Alt + ↑ or ↓ カーソル行の上下入れ替え
  • Command + N 新規ファイル作成
  • Command + Shift + N 新規ウィンドウを開く

Snippetを作成する

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

スクリーンショット 2017-11-23 16.25.22.png

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

スクリーンショット 2017-11-23 16.25.51.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の設定を上書きします。
基本設定→設定で設定を開きます。

スクリーンショット 2017-11-23 16.52.52.png

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

スクリーンショット 2017-11-23 16.57.48.png

「quickSuggestionsが無効の場合に最適です」と書いてありますが、quickSuggestionsはコード補完なので無効にせず併用します。
"editor.tabCompletion": trueにします。
上記の設定をtrueにすることでTabキーでSnippetコードを展開できるようになります。
"editor.quickSuggestionsDelay": 500に設定します。
この設定は、入力後指定の時間後にコード補完の一覧が表示されます。
今回は500ミリ秒に指定しました。人によってタイピング速度が違うと思うので自分に合った時間を指定してください。
"editor.multiCursorModifier": "ctrlCmd"はMac環境専用の指定です。
cmdキーを押しながら、クリックすることでマルチカーソルで指定カーソル位置から同時編集できます。(同じ変数名などを同時に書き換えたいときに、たまに使えます)
Windowsの場合はこの指定無しの場合はデフォルトctrlキーでマルチカーソルできます。
"javascript.implicitProjectConfig.experimentalDecorators": trueはJavaScriptのdecoratorsの文法を使おうとするとVSCodeが「将来実装される機能です」という警告を出すのでtrueにすることで警告を無視できます。
search.excludeは検索時に除外するファイル、フォルダの設定です。
プロジェクト内検索が遅いなと思ったら余計なファイルまで検索している可能性が高いです。除外しておきましょう。

settings.json
{
    "editor.tabCompletion": true,
    "editor.quickSuggestions": {
        "other": true,
        "comments": false,
        "strings": false
    },
    "editor.quickSuggestionsDelay": 500,
    "editor.multiCursorModifier": "ctrlCmd",
    "javascript.implicitProjectConfig.experimentalDecorators": true,
    "search.exclude": {
        "**/.git": true,
        "**/node_modules": true,
        "**/bower_components": true
    }
}

デモ

タイピングが遅いとコード補完が出てしまうのでSnippetを利用する場合はコード補完が出る前にショートカットキーを打ち切ってTabキーを押すのがコツです。
snippets.gif

Snippet例

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

react.json
{

    // Place your snippets for JavaScript 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:
    "async": {
        "prefix": "asy",
        "body": [
            "async () => {",
            "  const val = await ${2:PromiseFunc}",
            "  return val",
            "}"
        ],
        "description": "非同期待ち"
    },
    "await": {
        "prefix": "aw",
        "body": [
            "await ${2:PromiseFunc}"
        ],
        "description": "非同期待ち"
    },
    "array": {
        "prefix": "arr",
        "body": [
            "const ${1:arr} = []"
        ],
        "description": "配列"
    },
    "const": {
        "prefix": "c",
        "body": [
            "const $1"
        ],
        "description": "定数"
    },
    "console.dir": {
        "prefix": "cd",
        "body": [
            "console.log(${1:obj})"
        ],
        "description": "console.dir"
    },
    "console.log": {
        "prefix": "cl",
        "body": [
            "console.log(${1:val})"
        ],
        "description": "console.log"
    },
    "debugger": {
        "prefix": "de",
        "body": [
            "debugger"
        ],
        "description": "debugger"
    },
    "class": {
        "prefix": "cla",
        "body": [
            "class ${1:name} {",
            "  constructor () {",
            "  }",
            "}"
        ],
        "description": "クラス"
    },
    "export": {
        "prefix": "ex",
        "body": [
            "export ${1:module}"
        ],
        "description": "export"
    },
    "export default": {
        "prefix": "exd",
        "body": [
            "export default ${1:module}"
        ],
        "description": "export default"
    },
    "filter": {
        "prefix": "fil",
        "body": [
            "${1:arr}.filter((${2:val}) => { return ${2:val} === true })"
        ],
        "description": "filter"
    },
    "for in": {
        "prefix": "foi",
        "body": [
            "for (let key in ${1:obj}) {",
            "  const ${2:val} = ${1:obj}[key]",
            "  ",
            "}"
        ],
        "description": "for in"
    },
    "for of": {
        "prefix": "fof",
        "body": [
            "for (let val of ${1:arr}) {",
            "  ",
            "}"
        ],
        "description": "for of"
    },
    "function": {
        "prefix": "fu",
        "body": [
            "function ${1:name}(${2}) {",
            "  ${3:処理}",
            "}"
        ],
        "description": "function"
    },
    "JSON.parse": {
        "prefix": "jsp",
        "body": [
            "JSON.parse(${1:json})"
        ],
        "description": "JSON.parse"
    },
    "JSON.stringify": {
        "prefix": "jss",
        "body": [
            "JSON.stringify(${1:obj})"
        ],
        "description": "JSON.stringify"
    },
    "img": {
        "prefix": "img",
        "body": [
            "https://unsplash.it/${1:300}/${2:300}?random"
        ],
        "description": "ダミーイメージ"      
    },
    "import": {
        "prefix": "imp",
        "body": [
            "import ${1:value} from '${2:module}'"
        ],
        "description": "import"
    },
    "includes": {
        "prefix": "inc",
        "body": [
            "${1:arr}.includes(${2:val})"
        ],
        "description": "includes"
    },
    "let": {
        "prefix": "le",
        "body": [
            "let $1"
        ],
        "description": "変数"
    },
    "map": {
        "prefix": "map",
        "body": [
            "${1:arr}.map((${2:val}) => { ${2:val} })"
        ],
        "description": "map"
    },
    "module.export": {
        "prefix": "mo",
        "body": [
            "module.export = $1"
        ],
        "description": "module.export"
    },
    "object": {
        "prefix": "obj",
        "body": [
            "const ${1:obj} = {}"
        ],
        "description": "オブジェクト"
    },
    "Promise": {
        "prefix": "pr",
        "body": [
            "new Promise((resolve, reject) => {",
            "  $1",
            "})"
        ],
        "description": "Promise"
    },
    "Promise.all": {
        "prefix": "pra",
        "body": [
            "Promise.all([$1])"
        ],
        "description": "Promise.all"
    },
    "return": {
        "prefix": "re",
        "body": [
            "return $1"
        ],
        "description": "return"
    },
    "React": {
        "prefix": "rea",
        "body": [
            "import React from 'react'",
            "",
            "export default class ${1:name} extends React.Component {",
            "  ",
            "  constructor (props) {",
            "    super(props)",
            "    this.state = {}",
            "  }",
            "  ",
            "  componentWillMount() {",
            "    ",
            "  }",
            "  ",
            "  render() {",
            "    ",
            "  }",
            "}"
        ],
        "description": "Reactコンポーネント"
    },
    "reduce": {
        "prefix": "red",
        "body": [
            "${1:arr}.reduce((sum, current) => sum + ${2:current}, 0)"
        ],
        "description": "reduce"
    },
    "require": {
        "prefix": "req",
        "body": [
            "const ${1:module} = require('${1:module}')"
        ],
        "description": "require"
    },
    "switch":{
        "prefix": "sw",
        "body": [
            "switch(${1:val}) {",
            "  case ${2:name}:",
            "    break",
            "  default:",
            "    break",
            "}"
        ],
        "description": "switch"
    },



    "aタグ": {
        "prefix": "a",
        "body": [
            "<a href=\"${1:url}\">${2:link}</a>"
        ],
        "description": "aタグ"
    },
    "audioタグ": {
        "prefix": "au",
        "body": [
            "<audio src=\"${1:url}\" controls></audio>"
        ],
        "description": "audioタグ"
    },
    "brタグ": {
        "prefix": "br",
        "body": [
            "<br/>${1}"
        ],
        "description": "brタグ"
    },
    "buttonタグ": {
        "prefix": "bu",
        "body": [
            "<button>${1:Button}</button>"
        ],
        "description": "buttonタグ"
    },
    "canvasタグ": {
        "prefix": "ca",
        "body": [
            "<canvas id=\"${1:id}\" width=\"${2:640}\" height=\"${3:480}\"></canvas>"
        ],
        "description": "canvasタグ"
    },
    "centerタグ": {
        "prefix": "ce",
        "body": [
            "<center>",
            "  $1",
            "</center>"
        ],
        "description": "canvasタグ"
    },
    "checkタグ": {
        "prefix": "ch",
        "body": [
            "<div>",
            "  <label>${1}</label>",
            "  <div>",
            "  <label for=\"${2:chk}1\"><input id=\"${2:chk}1\" type=\"checkbox\" name=\"${2:chk}\" value=\"${3:value}1\" />${3:value}1</label>",
            "  <label for=\"${2:chk}2\"><input id=\"${2:chk}2\" type=\"checkbox\" name=\"${2:chk}\" value=\"${3:value}2\" />${3:value}2</label>",
            "  <label for=\"${2:chk}3\"><input id=\"${2:chk}3\" type=\"checkbox\" name=\"${2:chk}\" value=\"${3:value}3\" />${3:value}3</label>",
            "  </div>",
            "</div>"
        ],
        "description": "checkタグ"
    },
    "date入力タグ": {
        "prefix": "dat",
        "body": [
            "<input type=\"date\" name=\"${1:name}\" />"
        ],
        "description": "dateタグ"
    },
    "divタグ": {
        "prefix": "d",
        "body": [
            "<div>$1</div>"
        ],
        "description": "divタグ"
    },
    "fileタグ": {
        "prefix": "file",
        "body": [
            "<input type=\"file\" name=\"${1:name}\" accept=\"${2:image/*,video/*,audio/*}\" />"
        ],
        "description": "fileタグ"
    },
    "formタグ": {
        "prefix": "fo",
        "body": [
            "<form action=\"${1:url}\" method=\"post\" enctype=\"multipart/form-data\" accept-charset=\"UTF-8\" >",
            "  <input type=\"submit\" value=\"送信\" />",
            "</form>"
        ],
        "description": "formタグ"
    },
    "inputタグ": {
        "prefix": "inp",
        "body": [
            "<input type=\"${1:text,password}\" name=\"${2:name}\" placeholder=\"${3:入力欄}\" ${4:require} />"
        ],
        "description": "inputタグ"
    },
    "h1〜h6タグ": {
        "prefix": "h",
        "body": [
            "<h${1:1}>${2}</h${1:1}>"
        ],
        "description": "h1〜h6タグ"
    },
    "hrタグ": {
        "prefix": "hr",
        "body": [
            "<hr/>"
        ],
        "description": "hrタグ"
    },
    "imgタグ": {
        "prefix": "img",
        "body": [
            "<img src=\"${1:url}\" />"
        ],
        "description": "imgタグ"
    },
    "iframeタグ": {
        "prefix": "ifr",
        "body": [
            "<iframe src=\"${1:url}\" width=\"${2:640}\" height=\"${3:480}\" seamless sandbox=\"allow-same-origin allow-scripts\" ></iframe>"
        ],
        "description": "iframeタグ"
    },
    "labelタグ": {
        "prefix": "la",
        "body": [
            "<label>${1}</label>"
        ],
        "description": "labelタグ"
    },
    "pタグ": {
        "prefix": "p",
        "body": [
            "<p>$1</p>"
        ],
        "description": "pタグ"
    },
    "textareaタグ": {
        "prefix": "ta",
        "body": [
            "<textarea name=\"${1:name}\"></textarea>"
        ],
        "description": "textareaタグ"
    },
    "ulタグ": {
        "prefix": "ul",
        "body": [
            "<ul>",
            "  <li>${1:項目}</li>",
            "  <li>${1:項目}</li>",
            "  <li>${1:項目}</li>",
            "</ul>"
        ],
        "description": "ulタグ"
    },
    "videoタグ": {
        "prefix": "vi",
        "body": [
            "<video src=\"${1}\" width=\"${2:640}\" height=\"${3:480}\" controls></video>"
        ],
        "description": "videoタグ"
    }
}