VSCodeのユーザースニペットを使って「やることリスト」を一瞬で作ろう

## スニペットって何？

>切れ端、断片、少し、わずか、取るに足らない人

https://ejje.weblio.jp/content/snippet

断片のこと。

こういう↓補完機能を作りたい。

![ajax-snippet.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356208/bce13d65-92b1-0220-35dc-25aa073e5957.gif)
上記gif画像は公式ドキュメントのもの。

https://code.visualstudio.com/docs/editor/userdefinedsnippets

VSCodeでユーザースニペットを作る方法について記録しておく。

### 作りたいもの

markdownのメモにおいて、箇条書きで書いた「やることリスト」を元に「タスクの詳細化」を自動で作れるようにしたい。

ここまで書いて、
```markdown
* やることリスト
    * タスク1
    * タスク2
    * タスク3
```
↓
↓
↓
以下のようなものを自動で生成する。
```markdown
## タスク1

- 目標
- 期限
- サブタスク

## タスク2

- 目標
- 期限
- サブタスク

## タスク3

- 目標
- 期限
- サブタスク

```

## markdownのスニペット設定をオンにしよう

VScodeではデフォルトでスニペットが無効になっているので、これをオンにするところから始める。

コマンドパレット(Shift+ctrl+P)からSettingと入力し、settings.jsonを開く。以下を追記する。

```markdown
    "[markdown]":  {
        "editor.wordWrap": "on",
        "editor.quickSuggestions": {
            "comments": true,
            "strings": true,
            "other": true
        },
        "editor.acceptSuggestionOnEnter": "off",
        "editor.tabCompletion": "off",
        "editor.snippetSuggestions": "top"
    },
```

この辺を入れておく。

* quickSuggestions
    * 予測変換をつける
    
以下はオマケ。
* wordWrap
    * 画面端折り返し
* tabCompletion
    * tabによる補完が使えるようにする
* acceptSuggestionOnEnter
    * Enterで予測変換候補を決定する。邪魔なので"off"にしておく。
* snippetSuggestions
    * "top"で自作スニペットの優先度を上げる

必要なもの不要なものあると思うので各自調整する。正直[ドキュメント](https://code.visualstudio.com/docs/editor/intellisense)を読んでもよくわからなかった。

## ユーザー設定を作ろう

ユーザースニペットを作成しよう。

File->Preferences->Configure User Snippetsを押す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356208/71492524-f657-0c3b-db3c-9bf70521c140.png)

`new Snippet file for <ディレクトリ名>`でディレクトリ用のスニペットファイルを作る。

### スニペットのルール 基本
以下のようなものを.code-snippetファイルに記載する。
```json
  "sample name": {
    "scope": "markdown",
    "prefix": "@sample",
    "body": [
      "sample1",
      "",
      "sample2",
      ""
    ],
    "description": "ここに説明文を書く"
  }
```

* `"sample name"`の部分はスニペットの名称。名前は自由に変えていい。
* `"scope"`の部分で対象とする言語を指定できる。
* `"prefix"`でスニペットの対象となる文字列を指定する。
* `body`でスニペットで貼られるテキストを作成する。改行は`\n`で書くか配列形式で書くことになるのでちょっと面倒。
* `description`でそのスニペットの説明を書く。

### snippetに使える便利機能
body内ではテキストをベタ書きする以外にも便利な使い方をすることができる。
#### プレイスホルダー
```json
  "test": {
    "prefix": "test",
    "body": ["* ${1:ここが選択状態になる}"]
  }
```
これを打ち込むと選択状態で貼り付けることができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356208/e341d759-6af5-bf0d-d9b2-98093930100b.png)
プレイスホルダーに別の番号を指定するとタブでの切り替えが可能になる。同じ番号でもう一度指定するとその番号と内容が同期する。
```json
    "body": [
      "* ${1:ここが選択状態になる}",
      "* ${2:タブを押すと選択がここに切り替わる}",
      "* ${1:$1の内容を変更するとここも変わる}"
    ]
```


#### スニペットのルール 変数

`${name}`として変数を使うことができる。便利機能として例えば以下の変数が用意されている。

```json
  "variables": {
    "prefix": "var",
    "body": [
      "${TM_SELECTED_TEXT}",
      "${TM_LINE_INDEX}",
      "${CURSOR_INDEX}",
      "${TM_FILENAME}",
      "${CLIPBOARD}",
      "${CURRENT_YEAR}",
      "${CURRENT_MONTH}",
      "${CURRENT_DATE}",
      "${CURRENT_HOUR}",
      "${CURRENT_MINUTE}",
      "${CURRENT_SECOND}",
      "${RANDOM}",
    ]
  }
```

* TM_SELECTED_TEXT
    * 今選択中のテキスト
* TM_LINE_INDEX
    * カーソルの行番号
* CURSOR_INDEX
    * カーソルの位置
* TM_FILENAME
    * ファイル名
* CLIPBOARD
    * クリップボードの内容
* CURRENT_YEAR
    * 年
* CURRENT_MONTH
    * 月
* CURRENT_DATE
    * 日
* CURRENT_HOUR
    * 時
* CURRENT_MINUTE
    * 分
* CURRENT_SECOND
    * 秒
* RANDOM
    * ランダムな6つの数字

#### transform

与えた変数を一定のルールに基づいて変換することができる。

`${変換したいテキスト/変換したいパターン/変換後の文字列/正規表現オプション}`

##### 例
```json
"${TM_FILENAME/[\\.]/_/}"
```

上記例ではファイル名の`.`を`_`に変換する。(`\\.`とバックスラッシュを2つ付けてエスケープしている)
##### 例2
```json
"${TM_FILENAME/[\\.-]/_/g}"
```
ファイル名の`.`もしくは`-`を`_`に変換する。gオプションによって一致する全てを変換する
##### 例3
```json
"${TM_FILENAME/(.*)/${1:/upcase}/}"
```
* `.*`で全ての文字(`.`)を0回以上何度でも繰り返す(`*`)パターン、つまり全ての文字列に一致する。
* `(.*)`としてサブパターンを作っている。\\$0(全体), \\$1(サブパターン1個目), \\$2(サブパターン2個目), ...として変数を引き渡すことができる。
* ${1:/upcase}のように引き渡された変数に対して一定のルールで変換を行うことができる。

ファイル名に含まれる小文字を全て大文字に変換する。

残念ながらtransformで変換した内容を再度transformにかけることや複数のルールによる変換を行うことはできなさそう。

## 作ってみた例

いくつか作ってみた。全てmarkdownのメモで遊ぶためのもの。

### 日付自動入力

`# 2/8`みたいな段落を自動で入力してもらう。

```json
  "date": {
    "scope": "markdown",
    "prefix": "# date",
    "body": ["# ${CURRENT_MONTH}/${CURRENT_DATE}"],
    "description": "日付自動入力"
  }
```
### コイントス
RANDOMで得られる6ケタの数字を`表`もしくは`裏`に変換する。`ifelse`構文を使う。
```json
  "coin toss": {
    "prefix": "## coin",
    "body": ["${RANDOM/[0-4][0-9]{5}/${0:?表:裏}/}"]
  }
```
### タスク自動入力

```json
  "task": {
    "scope": "markdown",
    "prefix": "## task",
    "body": [
      "## ${1:task}",
      "",
      "* ゴール",
      "* 期限",
      "* サブタスク一覧",
    ],
    "description": "タスク設定用のフォーマットです"
  }
```

以下のようなフォーマットでタスク用の段落を作る。

```markdown
## task

* ゴール
* 期限
* サブタスク一覧
```
### やることリストから自動入力

↑のようなタスクを、↓のやることリストから一覧で作ろう。


```markdown:元とするやることリスト
* やることリスト
  * 本を読む
  * ゴミを出す
  * お茶を飲む
```

「transformを2つのルールでかけることはできない」「transformした文字列にtransformをかけることはできない」ようなので、ちょっと苦しいやり方を取る。

上記のリストから以下の部分だけをコピーしてクリップボード内に入れておく。これをCLIPBOARDで取り出す。

```
本を読む
  * ゴミを出す
  * お茶を飲む
```

ここから改行の部分をtransformで変換し、前の部分と後の部分は別途必要な文字列を付け足す。

```json
  "test2": {
    "scope": "markdown",
    "prefix": "## tasks",
    "body": [
      "## ${CLIPBOARD/\n  [\\*-] /\n\n- 目標\n- 期限\n- サブタスク\n\n## /g}\n\n- 目標\n- 期限\n- サブタスク\n\n\n\n"
    ]
  }
```
これで変換した結果は以下のようになる。

```markdown
## 本を読む

- 目標
- 期限
- サブタスク

## ゴミを出す

- 目標
- 期限
- サブタスク

## お茶を飲む


- 目標
- 期限
- サブタスク
```

以上です。