Ruby 3.2 のIRBの新機能

これは[Rubyアドベントカレンダー2022](https://qiita.com/advent-calendar/2022/ruby)の22日目の記事です。

# binding.irb のすすめ

8年前に [今更聞けないpryの使い方と便利プラグイン集](https://qiita.com/k0kubun/items/b118e9ccaef8707c4d9f) という記事を書き、そこから長い間 `binding.pry` を愛用していた。`binding.irb` も `binding.pry` も使ったことがないよ、という人はご一読をお勧めしたい。

当時PryにあったIRBに対する優位性のうち、時間が経っても常に使い続けた機能といえば `binding.pry`の存在、`$` (`show-source`)、`@` (`whereami`)、`ls` とその `-G` (grep) オプション、デバッグ機能 (要pry-byebugプラグイン) という感じで、他はまあたまに使うかもねくらいの感じになったのだが、なんとこれらはRuby 3.2ではIRBで全て使えるようになった。その上、Gemfileに何も書かなくてもデフォルトで使えるところがPryよりも便利。

# Ruby 2.4~3.1 のIRBの機能
なので [Pryはもう古い、時代はIRB](https://k0kubun.hatenablog.com/entry/2021/04/02/211455) という感じなのだが、これはRuby 3.1のIRBを開発していた去年出した記事で、Ruby 2.4でbinding.irbが追加されてからのIRBの進化について触れた。そこでは僕自身が開発したか個人的によく使うものだけしか書かなかったので、ここでそれ以外のものも含めた概要をおさらいとしてまとめておく。

## Ruby 2.4
* binding.irb 追加 (nobu)
  * これを書いた場所がデバッガのブレークポイントになるような感じでIRBを起動できる。IRBが標準ライブラリなのでGemfile記述がなくても使える。この時点ではrequireが必要で、周辺のコードの表示もなく、やや使いにくい状態。

## Ruby 2.5

* binding.irb
  * require不要化 (nobu)
    * preludeでbinding.irbが定義され、そこでIRBが遅延ロードされるようになった。Gemfileに書かなくても使えることと合わせて、これはPryには難しいIRBの強みになった。
  * 周辺のコード表示を実装 (k0kubun)

## Ruby 2.6

* default gem化 (hsbt)
  * Ruby 2.6以降からは、IRBがgemとしてリリースされていればアップデート可能になる。ただしGemfileにirbを書かない状態でbundle execをすると、Rubyと一緒に配布されているIRBが使われるので、Ruby本体のバージョンを上げた方がIRBが便利に使えることには変わりない。

## Ruby 2.7

* 複数行エディタ (aycabta)
  * このバージョンからreadline実装がreline.gemに差し変わる。
* タブで補完、タブ2回でドキュメント表示 (aycabta)
* シンタックスハイライト (k0kubun)
  * 周辺コードや入力コードに色がつく。pry-coollineなどのプラグインなしで入力行がインクリメンタルハイライトされるのはIRBの方が便利。
* IRB上での式の評価結果の色つけ (k0kubun)

## Ruby 3.0

* ペースト高速化 (aycabta)
  * Ruby 2.7でのrelineの最初のリリースではコピペが遅い問題があったが、3.0でこれが解消される。
* `measure` コマンド (aycabta)
  * 入力した式の実行速度が簡単に計測できる。

## Ruby 3.1

* `ls`, `show_source`, `whereami` コマンド (k0kubun)
  * Pryにある奴と基本的には同じ。
* 補完ウィンドウ (aycabta)

# Ruby 3.1のIRBに足りなかったもの
Ruby 3.1のIRBでも既に便利に使えていたのだが、2つ心残りがないでもなかった。

今年、[Pryが依然としてIRBよりも人気な"デバッガ"であるというツイート](https://twitter.com/th1agofm/status/1562424593746657283)や、そのスレッドでのIRBに不足していた (僕の秘伝の.irbrcには実装されていた) 機能に関する議論に触発され、Ruby 3.2を開発していた今年、その2つの問題を解消するに至った。

## デバッガ機能
pryはpry-byebugというプラグインをインストールするとステップ実行ができ、実際にデバッガとして使用することができる。IRBには同じことをやるコマンドがなかったため、我慢するしかなかった。そういう(僕は把握していない)IRBプラグインはあったかもしれないが、`binding.irb`の便利なところはGemfileに書かなくても使えるところなので、Gemfileの修正が必要になると本末転倒感がある。

pry-byebugは僕が昔業務で[10倍速くした](https://speakerdeck.com/k0kubun/how-i-debugged-debugger-number-rubykaigi-1)プラグインで、思い入れもあるのだが、Ruby 3.1から[debug.gem](https://github.com/ruby/debug)というよりモダンなデバッガが標準添付され始めた影響か、byebugおよびpry-byebugはもう1年以上コミットされていないプロジェクトとなった。民業圧迫。従ってIRBにもdebug.gem連携が欲しいという感じだったのだが、Ruby 3.2でそれができるようになった。

## Rubyとして有効でない入力のサポート
Ruby 3.1までのIRBではREPL上で受け付ける入力はRubyの式として成立するものだけであった。従って、Pryで便利に使っていた `ls` の `-G` (grep) オプションや、`$` (`show-source`)、`@` (`whereami`)、`show-source` といったコマンド名が使えなかったり、`show_source`の引数を文字列としてクオートする必要があったりした。

Ruby 3.2のIRBではRubyの入力として有効でないコマンドも受け付けて良いという合意が得られ、実装された。`ls -G`は動くし、`@`や`$`といったエイリアスは標準装備だし、`show_source`の引数にクオートは不要になった。

Rubyとして有効でない入力のサポートは[onkさんも欲しがっていた](https://twitter.com/onk/status/1579340655163498496)機能なのだが、[彼の記事](https://onk.hatenablog.jp/entry/2022/10/10/003542)でそれが必要な`--super`というオプションの要望が出ていたことは今この記事を書いていて思い出した…。流石に3.2に入れるには手遅れという感じなので、これは3.3になりそう。

# Ruby 3.2 のIRBの新機能
もう割と新機能紹介した感あるけど、やっとこれで本題。3.2で入った主要な機能を紹介しておく。

実は英語では既に [What's new in Ruby 3.2's IRB?](https://st0012.dev/whats-new-in-ruby-3-2-irb) という記事をStan ([st0012](https://github.com/st0012)) が書いているのだけど、紹介されている機能のうち半分は彼が入れた機能、残りは僕が入れたみたいな感じなので、まあ僕が日本語版を書くくらいでバランスがいいのではないかなと思う。

新機能の概要としてはこんな感じ:

* 新コマンド
  * `debug` (st0012)
    * Gemfileになくてもdebug.gemを強制ロード (k0kubun)
  * `break`, `catch`, `next`, `delete`, `step`, `continue`, `finish`, `backtrace`, `info` (k0kubun)
  * `edit` (st0012)
  * `show_cmds` (st0012)
* 既存コマンドの改善
  * `ls`: `-g`/`-G` オプションの追加 (k0kubun)
  * `show_source`: エイリアス `$` の追加、引数のクオート不要化 (k0kubun)
  * `whereami`: エイリアス `@` の追加 (k0kubun)
  * `help`: エイリアス `show_doc` の追加、引数のクオート不要化 (st0012)
* 環境変数
  * `IRB_USE_AUTOCOMPLETE` (st0012)

以下でもう少し詳細に解説する。

## debug.gem連携
というわけでdebug.gem連携が入った。`debug`コマンドで明示的にdebug.gemモードにREPLをすり替えるか、`break`, `catch`, `next`, `delete`, `step`, `continue`, `finish`, `backtrace`, `info`などのコマンドを叩くことで暗黙的にdebug.gemモードに切り替えてデバッガ機能を使うことができる。

```rb
From: test.rb @ line 2 :

    1: a = 1
 => 2: binding.irb
    3: b = 2
    4: c = 3

irb(main)[01:0]> next
(rdbg:irb) next
[1, 4] in test.rb
     1| a = 1
     2| binding.irb
=>   3| b = 2
     4| c = 3
=>#0    <main> at test.rb:3
(rdbg)
```

debug.gem自体にIRBの機能がどんどんコピーされているため、debug.gemが普通に使える環境ではそもそも `binding.irb` ではなく `debugger` やVSCode拡張からdebug.gemを直接起動してしまった方が便利な場合もあるが、Gemfileなしで使える default gem (要するに標準ライブラリ)であるIRBと違い、debug.gem は bundled gem (単にデフォルトでgem installされているだけ) なので、Gemfileに書かないと使えないという問題があった。

ところが、これらのdebug.gem連携用コマンドにはGemfileにdebug.gemが書いてなくても`$LOAD_PATH`にインストール済み最新のdebug.gemのパスを動的に追加してロードしてくるハックがあるため、ひとたび`binding.irb`を起動してこれらのデバッガ用コマンドを使うと、Gemfileにirbもdebugも何も書いてないプロジェクトでもいきなりデバッガが使えるようになっている。

debug.gemにはIRBの機能が大分コピーされている一方、continueではなくC-dでREPLを抜けようとすると `Really quit? [Y/n]` というプロンプトに妨害されたり、Ruby 3.2のIRBにいれたエイリアスやオプションとかがdebug.gem側にはまだないため、暗黙的にdebug.gemモードに突入してしまうことには不便な側面もある。これは現状意図的というよりは単にその方が実装が楽だったからという側面が強く、Ruby 3.3ではIRBを抜けずにdebug.gemをライブラリとして使役する感じで(`debug`自体以外の)デバッグコマンドを実現したいねという話をStanとしている。

## Pry風の機能: edit, ls -G, @, $, クオートなし入力
`edit`は[今更聞けないpryの使い方と便利プラグイン集](https://qiita.com/k0kubun/items/b118e9ccaef8707c4d9f)でも紹介した機能で、現在のコンテキストのソースコードをエディタで開くことができる。環境変数`EDITOR`でエディタを設定できる。

`ls -G`に関しては↑のpryの記事では`ls --grep`と紹介しているが、`ls -G`の方が短いのでそれを使っている。要は`ls`の結果を絞りこむオプション。僕はクラスやオブジェクトに生えてるメソッドを探す時に大体`ls`か`ls -G`だけを使って探している。

`@` (`whereami`のエイリアス)は個人的にPryでよく使っていた機能で、何かコマンドをいろいろ使ってログが流れてしまった後、どこに`binding.irb`置いたんだっけというのを再度表示するのに使う。

`$` (`show_source` のエイリアス)はありとあらゆるもののソースコードを探す時に便利なやつで、たとえば `$ Foo` でクラスのソースを表示したり、クオートなし入力が可能になったことで `$ Foo#bar` みたいなのもできるようになった。Moduleのincludeがガンガンに使われていてメソッドがどこに定義されてるか全然わからないコード(Railsとかのこと)を読む時に、それを呼ぶところで `binding.irb` で止めて `$ foo` とかするとコードリーディングに便利。

## help: show_doc, show_cmds
pry, byebug, debug.gemなど、IRB以外のgemで実装されているREPLでは大体helpコマンドはそのREPLで使えるコマンド一覧を出すのに使えるのだが、IRBではドキュメントを出す機能に既に使われていて、Stanはこれを地上げしてコマンド一覧に差し替えたいという話をしており、僕も賛同している。

が、これを思いついたのが3.2.0-rc1が出た後とかで、長年存在していたコマンドの中身を差し替える破壊的変更をいれるには遅すぎたので、Ruby 3.2のIRBでは別名でコマンド一覧を出すようにし、かつ現在のhelpにも別名のエイリアスを用意した。

その前者が`show_cmds`で、後者が`show_doc`である。`show_cmds`は以下のように動く。

```
irb(main)[01:0]> show_cmds
IRB
  cwws           Show the current workspace.
  chws           Change the current workspace to an object.
  workspaces     Show workspaces.
  pushws         Push an object to the workspace stack.
  popws          Pop a workspace from the workspace stack.
  irb_load       Load a Ruby file.
  irb_require    Require a Ruby file.
  source         Loads a given file in the current session.
  irb            Start a child IRB.
  jobs           List of current sessions.
  fg             Switches to the session of the given number.
  kill           Kills the session with the given number.
  irb_info       Show information about IRB.
  show_cmds      List all available commands and their description.

Debugging
  debug          Start the debugger of debug.gem.
  break          Start the debugger of debug.gem and run its `break` command.
  catch          Start the debugger of debug.gem and run its `catch` command.
  next           Start the debugger of debug.gem and run its `next` command.
  delete         Start the debugger of debug.gem and run its `delete` command.
  step           Start the debugger of debug.gem and run its `step` command.
  continue       Start the debugger of debug.gem and run its `continue` command.
  finish         Start the debugger of debug.gem and run its `finish` command.
  backtrace      Start the debugger of debug.gem and run its `backtrace` command.
  info           Start the debugger of debug.gem and run its `info` command.

Misc
  edit           Open a file with the editor command defined with `ENV["EDITOR"]`.
  measure        `measure` enables the mode to measure processing time. `measure :off` disables it.

Context
  show_doc       Enter the mode to look up RI documents.
  ls             Show methods, constants, and variables. `-g [query]` or `-G [query]` allows you to filter out the output.
  show_source    Show the source code of a given method or constant.
  whereami       Show the source code around binding.irb again.
```

`help`は`show_doc`としても使えるようになり、また`show_source`とかと同じく、クオート不要で引数を取れるようになった。上記の背景から、今後はこの機能は`help`ではなく`show_doc`として使う方が安心そう。(多分3.3向けにそのうちチケットが生えるので、反対意見はそちらにどうぞ)

## IRB_USE_AUTOCOMPLETE
Ruby 3.1から入った補完ウィンドウだが、リリース後以下のような問題が散見された。

1. サーバー上でリモート実行していると、補完ウィンドウが帯域をより多く消費し、遅い
2. 補完のウィンドウの色が見づらい
3. REPLが画面の下の方にあると、補完ウィンドウのサイズに応じて入力行の位置がどんどんズレて見づらい

(2)は僕個人的にはまあ別にいいかという感じなのだが、これが気になってる人は多いようで、チケットや開発者ミーティング上で何度も議論が交わされた。が、名前問題に収束してしまったようで、Ruby 3.2ではこの問題は特に進展がなかった。

(3)が僕はあまりにも気になるので`~/.irbrc`に `IRB.conf[:USE_AUTOCOMPLETE] = false` を入れて切っていたのだが、切ったことで僕自身が困らなくなったのでこれもRuby 3.2では特に進展しなかった。

今回入った`IRB_USE_AUTOCOMPLETE`という環境変数は(1)の問題を避けるために便利な機能で、`~/.irbrc`を設置するのは難しいが環境変数ならセットしやすいという状況向けに、環境変数でもこの機能が切れるようになった。Railsのheadでは、(1)を避けるためにRAILS_ENV=productionでは`IRB_USE_AUTOCOMPLETE=false`がrails c向けにセットされるようになった。 https://github.com/rails/rails/pull/46656

# まとめ
いかがでしたか? Ruby 3.2ではIRBがRuby 3.1より便利になっていることがわかりました!