いい感じなgitignoreエディタ作れないかなと思ったので、とりあえずMVPっぽいのを作ってみた

# 起：`.gitignore`のちょっとした悩み

## ことの発端

プロジェクトを`Initial commit`した後におもむろにやる作業として、「[gitignore.io](https://www.gitignore.io)から良さげな`.gitignore`を作る」というのがありますよね？ [^1] [^2]

**ありますよね？**

## 時間経過で.gitignoreは変化する...こともある

最初のまま運用できれば問題ないのですが、

1. あ、この環境用のignoreもつけたい
2. ちょっと特殊なignoreが必要になりそう

ということが出てきたりします。

**出てきたりします。**

(1)のケースだと、もう一回gitignore.ioに行って再生成しないといけなかったりします。

(2)のケースだと、テンプレ生成した`.gitignore`のどこかに記述しないといけなくなるのですが、
こうなってくると(1)の対応のたびに若干の手間を感じてしまったりします。多分


# 承：雑な思いつきと、MVPっぽいの

## 思いつき

そこで「`.gitignore`編集時にいい感じに『言語名』と『言語のignore』をいい感じに連携しつつ簡素に書くエディタ」とか作れないかなと考えてみました。lazygitみたいなUIかっこいいじゃないですか

![こんなの](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6805/cc4ec8d5-55e4-fd03-c081-cf50891dc495.png)

* キーワードを入れると、「そのキーワードを含む言語・環境の候補」が出る
* Tabで候補に移動して右とか押したら、、言語として反映
* 候補がない中でキーワードを選択して右を押したら、パターンとして反映
* 保存時に、勝手にAPIコールしてgitignore.io+パターンで構築

とかできると、派生でignoreしたいパターンなどをまとめて放り込めて便利かなーなんて。

とはいえ、こういう形式でのエディタを作成する能力は今の所ありません。

## 手間を掛けずにそれっぽいの(=MVP?)を作ってみる

超雑に、MVPっぽい超簡易実装を試してみました。
こちら => [vigi = vi gitignore](https://github.com/attakei/vigi)

`vigi` コマンドを実行すると、

```

# ~~~~~~
```

こんな感じのファイルを`vim`で開き、

```
.idea

# ~~~~~~
python
virtualenv
```

こんな感じで編集して`vim`を終了すると、

```
.idea

# ~~~~~~

# https://www.gitignore.io/api/python,virtualenv の中身
```

みたいな状態の`.gitignore`を作ってくれます。

ちなみに、この状態で`vigi`コマンドを実行すると、gitignore.io部分はちゃんと言語のみの情報になります。編集がしやすい……かなと思います。

# 転：中身はこんな感じ

`.gi`という名で仮ファイルを作って編集し、保存されたらそれを`.gitignore`に変換するスタイル

```python
import subprocess
from pathlib import Path
from subprocess import CalledProcessError, PIPE

def main():
    gi_path = Path.cwd() / '.gi'
    gitignore_path = Path.cwd() / '.gitignore'
    try:
        # Convert from ``.gitignore`` to ``.gi``
        if gitignore_path.exists():
            encode_gitignore(gitignore_path, gi_path)
        else:
            with gi_path.open('w') as fp:
                fp.write(dedent(gi_template).strip())
        # Spawn vim
        vim_args = ['/usr/bin/vim', str(gi_path)]
        vim_proc = subprocess.run(vim_args, check=True)
        # Convert from ``.gi`` to ``.gitignore``
        decode_gitignore(gi_path, gitignore_path)
    except CalledProcessError:
        pass
    finally:
        # Remove ``.gi``
        gi_path.unlink()
```

## vimを呼ぶ

`subprocess.run`で`vim`を呼出し、`CalledProcessError`例外がなければOKというざっくりスタイル。楽で良いですね。

## `.gi`は残さない

`try`〜`finally`を使って、最終的に`.gi`ファイルは捨てちゃってます。エラーなら`.gitignore`の編集もしないので環境に配慮されていますね。

## 素材をもとに`.gitignore`を作る

`.gi`ファイルはこの時点でファイルシステム上に普通にあるので、普通にファイルとして開く感じです。
そして、言語側の情報をまとめてgitignore.ioにパス。
HTTPステータスが404の時に限り、通常と同じようにレスポンスを保存してますが、
これは「マスターに存在しない情報を含む」ケースが全て404になるっぽいためです。
唯一と言っていい、ちょっとした工夫点ですね。

```python
def decode_gitignore(src, dest):
    custom = []
    online = []
    with src.open() as fp:
        # .giファイルを開いて、デリミタの前後を分けるなど
    if online:
        url = f'https://www.gitignore.io/api/{",".join(online)}'
        req = Request(url, None, headers={'User-Agent': 'vigi'})
        try:
            resp = urlopen(req)
            custom.append(resp.read().decode())
        except HTTPError as err:
            if err.code == 404:
                custom.append(resp.read().decode())
            raise err
    with dest.open('w') as fp:
        fp.write('\n'.join(custom))
```

# 結：使用感とか

一応、`vigi`プロジェクト内にある`.gitignore`はこのコマンドベースで作りました。

編集時点で表示される行数が格段に減るのはメリットではあるかなと思います。
そういう意味ではMVPとしての仕事はきちんと全うしてくれました。

ただ、言語マスターにどんなパターンが含まれているかが、`.gitignore`への変換を終えないとわからないので、
仮にもうちょっと開発してみようと思ったら、きちんとパターン候補から言語マスターを引っ張り出すようにしないと、
本当に良いものなのかはわからないですね。~~何かの言語のトレーニングには向いてるかも？~~


[^1]: プロジェクトテンプレート作るとかがありますが、一旦忘れてください
[^2]: `dotfiles`で管理するなどもできますが、これも一旦忘れてください