2023-09-10

rspec.nvim の `RSpecJump` コマンドに force オプションを追加した

ブログ書くのが久しぶりすぎて書き方忘れました。

書きたいネタはちょいちょい思い浮かぶのだけど、なんだかんだ書かずに最後の記事から1年近くが経ちかけようとしていたことに驚き。

RSpecJump の派生として、推測したファイルがいずれも存在しなかった場合に、ファイルの作成をしてくれる force オプション的な機能を実装したいと思っている。

新規でファイルを追加した場合に特に便利なはず!少なくとも自分はほしい。

これは、前回の記事 の最後に書いていたことなのだけど、この機能をやっとこさ実装したのでこれについての紹介を簡単に書こうと思う。以下は GitHub の Release ページヘのリンク。

https://github.com/mogulla3/rspec.nvim/releases/tag/1.3.0

RSpecJump! コマンド

前回の記事時点では bang マークなしの RSpecJump しか使えなかったが、現在の最新版では RSpecJump! というコマンドも使えるようになった。

RSpecJump についてざっくり説明すると、プロダクトコードとテストコード間を命名規則に従ってジャンプするコマンド。しかし、このコマンドはジャンプ先のファイルが見つからなかった場合に以下のような警告を出して中断するようになっていた。

ジャンプ先のファイルが見つからなかったとき
ジャンプ先のファイルが見つからなかったとき

これに対して RSpecJump! コマンドを使うと、ジャンプ先のファイルが存在しなかった場合にファイルを自動で作成した上でそこにジャンプをしてくれる。以下はその様子を示したデモ。

RSpecJump!のデモ

実装のモチベーション

モチベーションは単純で、新規のクラスを作成した場合などテストコードがまだ存在しない場合に楽をしたいためである。また既存のクラスに修正をした場合にテストを書こうと思ったらテストコード自体が存在していなかった...、というようなケースも同じくだ。

これまではこのケースに遭遇してしまった場合は RSpecJump のエラーメッセージに含まれるパスをコピーして、自らコマンドラインから touch /path/to/spec_file のような感じでファイルを作っており、非常に面倒に感じていた。ディレクトリが存在しないケースはさらに面倒で、mkdir -p でディレクトリ作成 → touch でファイル作成、という手順を踏まないといけなかった。

実装時のよもやま話

本機能が入った v1.3.0 と前バージョンの差分は ここ にあるが、diff としては100行にも満たない規模の修正で済んでいる。実際、着想から実現までの時間はかかっているが、着手し始めてからはほぼ1日で実装が完了する程度だった。

実装もあまり難しいことはしていなく、! 付きコマンドの場合は、プラグイン内部で mkdir -p をしてディレクトリを作った上でファイルをオープンしているだけである。

唯一実装に時間がかかったのは、ジャンプ先のファイルが複数推測された場合の処理だった。 例えば、 Rails の Controller ファイルを開いている場合、Request Spec と Controller Spec の両方が候補になる。この先は状況に応じてどちらもあり得るのでユーザに選択してもらいたい。

Vim, Neovim で選択式の UI を提供する方法を調べたところ、inputlist という組み込みの関数を用いる方法を見つけたのでこれを採用することにした。この関数に選択肢の配列を渡すと、ユーザが選択した番号が返り値として受け取れる関数である。

複数の候補が存在する場合のUI
複数の候補が存在する場合のUI

ただかなり素朴な関数であり、上の画像の Choose the path you want to jump. という冒頭のメッセージと選択肢を同じ1つの配列として渡す必要があったり、1. のような番号も自動でつけてくれるわけじゃないなど、やや使いづらさはあった。

また、Escape や q キーを押すと選択を中断できるが、この場合は 0 が返ってくる仕様なので、この考慮や不正な番号を渡された場合のハンドリングなどが必要だった。

次に考えていること

再び RSpecJump コマンド関連で、アプリのディレクトリ構造が命名規則に従っていない場合にもある程度対応できるようにしたいと考えている。

我ながらよく使う機能なので、アプリが特殊なディレクトリ構成になっているだけでこの機能が使えなくなってしまうのはもったいない、というかもはや辛い。実現方法のイメージが湧いていないが、特定の名前のディレクトリを探索時に無視するような動きにすればいけるかもしれない。