カタカタッ ターンッ! RubyのIRBめっちゃ便利ですよね。
IRBは補完機能もすごく便利なんですが、ちょっと気になるところもあります。
- メソッドチェーンすると正しく補完できずありとあらゆるメソッド名が候補に出てしまう
- 正規表現で実装されてるので時々誤作動を起こす
-
array[i].
%s[symbol].
配列のメソッドが候補に出る -
array.map{}.
%w{words}.
HashとProcのメソッドが候補に出る
-
なので、型情報(RBS)とかも使ってもうちょっといい補完候補を出せないかなと 、gem katakata_irb
を試しに作っています。
gemをインストールするとkirb
コマンドが使えるようになります。
変えました。使い方: require 'katakata_irb'
メソッドチェーンしてても、ブロック引数・変数などを使ってても、ある程度正しく型を推測して補完候補を出せたり出せなかったりします。
実装
こういうコードをirbに入力したときに
irb> a = 10
=> 10
irb> a.times.map do |i|
irb> j = i.times.map{}.join
irb> j = a if foobar
irb> puts((j * 2).█
irb> ...
irb> ...
- カーソルより前の文字列を抜き出す
- rubyのコードとして不完全なので、足りていない
)
とend
などを追加したSyntax Validなコードから構文木を作る
sexp = Ripper.sexp(code + "method\n)\nend")
- 補完したい位置に該当する構文木のノード(
(j * 2).method
)を探す - ローカル変数aの中身やrbsの型情報を使って
(j * 2)
が何になり得るかを調べ、この場合はIntegerかStringなのでIntegerとStringのメソッドを補完候補に出す
ということをしています。
※ 不完全なコードのparseには、Ruby3.2から入るRubyVM::AbstractSyntaxTree.parseのerror_tolerantオプションが使えるかもしれない
未実装な部分
- Tuple, Record, Interface型などに対応していない
-
unless a.is_a? Foo
で aからFooが除外されてほしい - TODOコメントを残した箇所がたくさんある
- 直前の解析結果を使いまわせる場合があるのでキャッシュしたい
動機
- irbのruby-lex.rbを読んでいたら自分で書き直したバージョンを作ってみたくなった
- 書き直した結果、閉じられていない括弧・do・quoteなどを調べるメソッドができた
- それを使えば正規表現ではなく構文木から補完候補を出せそう
- 型情報も使うとメソッドチェーンの補完候補も正しく出せそう
- ブロック引数の型も調べたい、型推論っぽいことをしよう
- 気軽に手を出した型推論、めちゃくちゃ大変じゃん(←今ここ)
その他
僕が快適に使いたいからという理由でirb/ruby-lex.rbとrelineにいくつかパッチを当てて読み込んでます。パッチあてるのやめました
(Module#remover_method
Module#prepend
RubyVM::InstructionSequence.load_iseq
などを使って)
ヒアドキュメントのインデントとかが良い感じになってるはず。うまくいけばirbにリファクタリングのプルリクを出したい。
Merry Christmas
Ruby3.2楽しみ