Neovim内でのRSpec実行環境をneotermで簡単に導入する

# 前書き
Vim(Neovim)で開発をしている方、RSpecの実行どうしてますか？
参考までに私の試行錯誤遍歴を🙇‍♂️
- iterm2の画面分割で常にRSpec用の画面を確保しておいてRSpec実行時にウインドウ移動
常に画面領域を圧迫して邪魔(他にログ表示用やrails console用の領域を使いたいならなおさら)
VSCodeみたいに使いたいときにだけ表示したい！

- `C-z` でVimから抜けてターミナルでRSpecを実行する
一応使いたい時だけ表示することになるので画面は圧迫しないが都度Vimから抜けるのはストレス
再度Vimに戻った後にRSpecの実行結果をまた見たい時はまた `C-z` しないと見れないのが面倒

- Neovimのターミナルモードを使う
Neovimだと `:terminal` コマンドでターミナルモードに入れるのでVSCodeに近い感じには出来るが、どうも操作性が悪い
設定を結構ゴニョゴニョしないと使い物にはならないなという印象(設定弄る前に次に紹介するneotermを知ってしまった)

- **neotermを使う**
色々試して行き着いたのが[kassio/neoterm](https://github.com/kassio/neoterm)です
これは上記の他の案の欠点をカバー出来る上、中身は`:terminal`コマンドのラッパープラグインなので導入・設定も簡単！
![fe459572-34b1-11e5-93c9-c3b6f3b44719.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542581/f50caa61-c58d-8149-c626-bc30f2705e90.gif)

本記事は、このneotermを活用して簡単にNeovim内でRSpec実行を完結させられるようにします

# neotermのインストールと設定
早速インストールしましょう
といってもdeinでプラグインを管理している方は`dein.toml`に下記をコピペして`:call dein#install()`したら終わりです
※ hook_add以下は`.vimrc`や`init.vim`に書いてもOKです、お好みで使い分けてください

```toml:dein.toml
[[plugins]]
repo = 'kassio/neoterm'
hook_add = '''
  let g:neoterm_default_mod='belowright'
  let g:neoterm_size=25
  let g:neoterm_autoscroll=1

  let g:neoterm_rspec_cmd='bundle exec rspec'
  function! RSpecFile()
    let l:filename = expand('%')
    call neoterm#do({ 'cmd': join([g:neoterm_rspec_cmd, l:filename]) })
  endfunction
  function! RSpecLine()
      let l:filename = expand('%')
      let l:opt = join([l:filename, line('.')], ':')
      call neoterm#do({ 'cmd': join([g:neoterm_rspec_cmd, l:opt]) })
  endfunction
  command! RSpecAll call neoterm#do({ 'cmd': g:neoterm_rspec_cmd })
  command! RSpecFile call RSpecFile()
  command! RSpecLine call RSpecLine()

  nnoremap <silent> <C-t><C-t> :Ttoggle<CR>zz
  tnoremap <silent> <C-t><C-t> <C-\><C-n>:Ttoggle<CR>zz
  tnoremap <silent> <C-w> <C-\><C-n>
  nnoremap <silent> <C-t>a :Topen<CR><C-w><C-j>i<C-l><C-\><C-n><C-w><C-k>:RSpecAll<CR>
  nnoremap <silent> <C-t>f :Topen<CR><C-w><C-j>i<C-l><C-\><C-n><C-w><C-k>:RSpecFile<CR>
  nnoremap <silent> <C-t>l :Topen<CR><C-w><C-j>i<C-l><C-\><C-n><C-w><C-k>:RSpecLine<CR>
'''
```
はい終わり！とっても簡単✌️
![20220706.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542581/f8ad9c62-be2e-ba58-4665-6c78ae441ca4.gif)
設定したキーマッピング`<C-t>l`で`:RSpecLine`を実行して現在行のRSpecを実行してます（Spec超適当ですみません🙏）

オプション等の細かい説明は次の項目で解説します

# 解説
### オプション
**`let g:neoterm_default_mod='belowright'`**
ターミナルウインドウの表示位置を下部表示（水平分割）にする設定
これを指定しないとターミナルウインドウは新しいバッファで開いてしまうので必須です
※ 右に表示（垂直分割）の場合は`'vertical belowright'`

**`let g:neoterm_size=25`**
ターミナルウインドウの表示サイズを設定
私は使う時だけの表示できればいい用途なのでそこそこ大きい数字に設定してます
常時表示したいとかでもっと狭いスペースで良かったら`10`にするとか各自調節してください

**`let g:neoterm_autoscroll=1`**
ターミナルの自動スクロール設定
テストログが流れてくRSpec実行には必須ですね

**`let g:neoterm_rspec_cmd='bundle exec rspec'`**
オプションではないですが下記のRSpecスクリプト関連で必要な設定
デフォルトの`rspec`コマンドを設定します
各自の環境に合わせて設定してください

[補足]
私は無くて平気でしたが、環境によっては
`let g:neoterm_shell='$SHELL -l'`で起動するshellの指定が必要かも
動かなかったら試してみてください

### スクリプト＆コマンド
`function! RSpecFile()`
〜
`command! RSpecLine call RSpecLine()`
までがRSpec関連のスクリプトやらコマンドです

スクリプトは [uji/neoterm-rspec.vim](https://github.com/uji/neoterm-rspec.vim) を参考に必要な機能だけ頂戴して少しだけカスタマイズしました
もっと楽するためにコマンドは下記でキーマッピングに割り当てます

#### キーマッピング
**`nnoremap <silent> <C-t><C-t> :Ttoggle<CR>zz`**
**`tnoremap <silent> <C-t><C-t> <C-\><C-n>:Ttoggle<CR>zz`**
ターミナルの開閉のキーマッピング
最後に`zz`としてるのはターミナル開閉時に元ウインドウのカーソル位置が真ん中に来なくて気持ち悪かったのでカーソル位置の中央表示をするようにしてます

**`tnoremap <silent> <C-w> <C-\><C-n>`**
ターミナルモードから抜ける際のキーマッピングです
いちいち`<C-\><C-n>`なんて打たなくて良くなります
※ ターミナルモードから抜ける？って概念については後ほど [ターミナルモードの操作について](#ターミナルモードの操作について) で説明します

ネット上ではこのキーマッピングを`<ESC>`にしてる人が多いように感じましたが、私はターミナルモードから抜ける時はウインドウ移動したいことが多いので`<C-w>`にしてます
つまりターミナルモード中にウインドウ移動したいときは`<C-w><C-w><C-w>`でいけてわかりやすい（好み分かれるとは思いますが）

**`nnoremap <silent> <C-t>a :Topen<CR><C-w><C-j>i<C-l><C-\><C-n><C-w><C-k>:RSpecAll<CR>`**
**`nnoremap <silent> <C-t>f :Topen<CR><C-w><C-j>i<C-l><C-\><C-n><C-w><C-k>:RSpecFile<CR>`**
**`nnoremap <silent> <C-t>l :Topen<CR><C-w><C-j>i<C-l><C-\><C-n><C-w><C-k>:RSpecLine<CR>`**
RSpecスクリプト実行用のキーマップです

コマンド実行前に色々ごちゃごちゃ書いてるので一応解説します
`:Topen<CR>`　ターミナルウインドウを表示(表示してたらそのまま)
`<C-w><C-j>i`　ターミナルウインドウに移動してターミナルモードへ
`<C-l><C-\><C-n>`　ターミナルをクリアしてターミナルモードから抜ける（`:Tclear`意図しない動作になったためこのようにしてます）
`<C-w><C-k>`　元のSpecファイルのウインドウへ戻る
`:RSpecAll<CR>`　RSpecスクリプト実行

なぜこんなことしてるのかと言うと、
RSpec実行時に
1）ターミナルウインドウを(表示してなかったら)自動で開いて
2）ターミナルをクリアして（テスト実行ログがわかりやすいように）
3）アクティブウインドウはSpecファイルのウインドウのままにして実行
という一連の動作をしたいからです

:::note info
わざわざウインドウ移動してコマンド打たなくても
`let g:neoterm_autoinsert=1`か`g:neoterm_autojump=1`オプションを設定していれば
ターミナルを開いたら自動でターミナルウインドウに移動してくれます
ただ、これを設定するとターミナル開閉のキーマップ時にもウインドウ移動してしまうのです

私は実行したいターミナルコマンドがあったら`:T hogehoge`とそのままノーマルモードで実行しています([番外編参照](#番外編))
ターミナルウインドウ開閉をする時はターミナルウインドウをチラっと確認したいだけの場合が多いのでこの方が都合良いのです
:::

他のオプションが知りたかったら`:h neoterm`コマンドでヘルプが引けます
細かい設定オプションがいっぱいあるので見てみると楽しいですよ

# ターミナルモードの操作について
Newvimのターミナルモードの操作はちょっとクセがあります
通常のVim操作と同じくモードの概念があるので最初は少し戸惑うかもしれません

- ノーマルモード
最初の状態
通常のNORMALモードと同じくVISUALモードへの移行や文字列のヤンクも出来ます
当然ですが編集(INSERTモードへの移行)は出来ません

- ターミナルモード
INSERTモードに移行できない代わりに`i`キーを押すとターミナルモードに移行します
ターミナルモードになって初めてターミナル操作が出来るようになります（分かりづらいッ）
さらにややこしいのはターミナルモードからノーマルモードに戻る時のコマンド
`<C-\><C-n>`です（使いづらいのでさきほどキーマッピングしましたね）

通常のターミナルと違ってモードの概念があるので最初は面食らうと思います
まあでも慣れるので頑張って操作して慣れましょう
Vimから抜けなくて良くなるリターンの方が遥かに大きいです！！

特に`<C-t>f`と`<C-t>l`にマッピングした`:RSpecFile`と`:RSpecLine`の威力は絶大です
おかげで今開いてるSpecファイル名を気にしたり何行目か目視で確認する手間が省けました
控えめに言って最高です


# 番外編
ターミナルウインドウに移動しなくても`:T hogehoge`　でそのまま実行できます
もしよくコマンドだったらキーマップ割り当てるのもアリです

例1）
**`nnoremap <silent> <C-t>t :Topen<CR>:T tig<CR><C-w>j<C-w>_i`**
`tig`はターミナル上で使えるgitのGUIです( [jonas/tig](https://github.com/jonas/tig) )
私はコミットやプッシュ、差分確認などは大体これを使ってます（これについても今度記事書きたい）
`tig`は全画面で操作出来た方が見やすいので画面最大化(`<C-w>_`)するように設定してます
また`tig`を起動する時は操作したい時なので`i`でそのままターミナルモードに移行するようにしてます

例2）
**`nnoremap <silent> <C-t>c :T rails c<CR>:Topen<CR><C-w>ji`**
`rails console`を開きます
これも起動する時はすぐに操作したいのでそのままターミナルモードに移行するようにしてます

:::note warn
RSpec実行と違って自動的に終了しないので上記2つを開いたままRSpecキーマップを実行するとちゃんと動作しないので注意！
使い終わったら`tig`の場合は`q`、`rails console`の場合は`exit`で終了するのを忘れずに
:::

こんな感じでまだまだ結構色々な用途に使えそうです🤔
様子見つつオススメ見つけたらまた共有します

# 最後に
なんだかんだでVimに入門して半年以上経ちました！

だんだんと操作がVim内で完結してきたのでVim引きこもりがどんどん加速していってますねw
世間のVSCodeの波に負けずにこれからもVimに引きこもって開発していこうと思います

それではみなさん良きVimライフを😇😇😇

### 他のVim関連記事も良かったら見てみてください🙇‍♂️
[Vimの複雑なコマンドを理解する](https://qiita.com/rakudalms/items/b1c8c226ea7be26084b9)
[[VimとEmacsの共存] VimでEmacsキーバインドを使えるようにしてみた](https://qiita.com/rakudalms/items/da0271562dd3cfdf2bdf)