VSCode で不要な空白文字や空白行を削除する

# VSCode で不要な空白文字や空白行を削除する

行末の空白文字やファイルの末に複数ある空白行は不要なものとして削除しておきたい。これを自動的に実行したい。

## ■ VSCode デフォルトによる構成

デフォルトでは、次の 3 つの設定がある。これらは保存時に実行される。

|              設定              | 型 <br>（デフォルト値） | 説明                               |
| :----------------------------: | :---------------------: | :--------------------------------- |
|   `files.insertFinalNewline`   |    真偽値 (`false`)     | ファイルの最終行に空白行を挿入する |
|   `files.trimFinalNewlines`    |    真偽値 (`false`)     | ファイルの最終の空白行を削除する   |
| `files.trimTrailingWhitespace` |    真偽値 (`false`)     | 各行末の半角スペースを削除する     |

ちなみに、`files.insertFinalNewline` と `files.trimFinalNewlines` がともに `true` の場合、ファイルの最終行に空白行が 1 行だけ挿入される。

## ■ 行末の空白文字を削除する

`files.trimTrailingWhitespace` では半角スペースのみしか対応していない。これをすべての空白文字に関して対応させたい。

```regexp
^([\S]*)\s+$
```

ECMAScript では、`\s` は `[ \f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]` <sup>[⧉][s-cClass]</sup> を指している。そのため、`\s` だけで全角スペースなども含まれている。

[s-cClass]: https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes#:~:text=%E3%83%9E%E3%83%83%E3%83%81%E3%81%97%E3%81%BE%E3%81%99%E3%80%82-,%5Cs,-%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E3%80%81%E3%82%BF%E3%83%96%E3%80%81%E6%94%B9

これを `$1` に置換する。Perl ライクに表現すると次のようなものになる。

```perl
s/^([\S]*)\s+$/$1/gm
```

これをキーバインドとして設定するならば、次のようにすると良いだろう。ただし、このキーバインドは検索・置換ウィジェットを表示させるに留まる。これの後に、`Ctrl+Alt+Enter` を押下することで全置換することが出来る。

```json
{
  "key": "",
  "command": "editor.actions.findWithArgs",
  "args": {
    "searchString": "^([\\S]*)\\s+$",
    "replaceString": "$1",
    "isCaseSensitive": false,
    "matchWholeWord": false,
    "isRegex": true,
    "preserveCase": false,
    "findInSelection": false,
  },
  "when": "editorTextFocus && !editorReadonly"
}
```

### ▽ Markdown の強制改行は無視したい

Markdown における強制改行は半角スペース 2 つで表現される。これだけは残すようにしてみたい。

この場合は、次のように正規表現を与えてみると良いだろう。

```regexp
^(\s+|([\S]+)(\s|\s{3,}))$
```

## ■ 連続した空白行を置換で削除する

:::note warn
ここでの空白行とは「文字が何もない行および空白文字のみの行」を指すこととする。
:::

連続した空白行を削除したい場合には、次のような正規表現を用いて置換すると良いだろう。置換先は何もなしで良い。

```regexp
^\s*$\n^\s*$
```

テストすると次のようになる。

https://regex101.com/r/yRvqjJ/1

Perl ライクに表現すると次のようなものになる。

```perl
s/^\s*$\n^\s*$//gm
```

これをキーバインドとして設定するならば、次のようにすると良いだろう。ただし、このキーバインドは検索・置換ウィジェットを表示させるに留まる。これの後に、`Ctrl+Alt+Enter` を押下することで全置換することが出来る。

```json
{
  "key": "",
  "command": "editor.actions.findWithArgs",
  "args": {
    "searchString": "^\\s*$\\n^\\s*$",
    "replaceString": "",
    "isCaseSensitive": false,
    "matchWholeWord": false,
    "isRegex": true,
    "preserveCase": false,
    "findInSelection": false,
  },
  "when": "editorTextFocus && !editorReadonly"
}
```

この置換では、以下のような連続していない空白文字のみの行は無視している。

```python
hello = "Hello World"
　　　
print(hello)
```

# 余談

デフォルトの構成に加えて、ユーザによる置換も提案してみた。正規表現は好きなように変更すると良いだろう。

[multi-command][mc] を利用すると、`editor.actions.findWithArgs` によるシーケンスを上手く構成することが出来る。興味があれば試してみても良いだろう。

[mc]: https://marketplace.visualstudio.com/items?itemName=ryuta46.multi-command

例えば、以下のような感じ。

```jsonc
// settings.json
  "multiCommand.commands": [
    {
      "command": "multiCommand.remove.consecutiveBlankLines",
      "sequence": [
        {
          "command": "editor.actions.findWithArgs",
          "args": {
            "searchString": "^\\s*$\\n^\\s*$",
            "replaceString": "",
            "isRegex": true
          }
        },
        "editor.action.replaceAll",
        "closeFindWidget"
      ],
      "description": "Remove consecutive blank lines"
    }
  ]
```