IRB Reline Changes
Past IRB Reline Changes
2025/01/01
Readline vi emacs などには C-v (もしくは C-q ) に続いてキーを打つと制御文字も含めて任意の文字を入力できる機能があるんですが、
Relineでは C-c C-z C-\ だけは入力することができませんでした。(null文字も入力できないがこれはReadlineの仕様に合わせてる)
これらのキー入力では通常SIGINT SIGTSTP SIGQUITが送られるからですが、最近のRelineの変更により、C-v の直後の文字を読むときだけinterruptなしのrawモードに切り替えることができるようになって直りました。
2025/01/04
README.mdの整理です。ContributingセクションをCONTRIBUTING.mdに分ける・不要な(古くて間違ってる)説明を消す、など
LineEditorに入力されたキーの情報が渡った後、どう処理するかの部分のリファクタリングです。
@waiting_proc (次のキー入力をどう処理するかを上書きする機構)
@vi_waiting_operator (viモードの時、カーソル移動操作の後、移動した範囲に対して特定の操作(削除・コピー・etc)をすることを予約するための機構)
などが設定されているときの特別処理が重複・分散していたのをまとめています。
2025/01/05
ターミナル画面の高さよりも行数の多い入力をペーストした時のバグを修正しています。
ペースト中判定(stdinのバッファに読み込めるデータが残っているか)のチェックタイミングの問題
ペースト中判定の時はレンダリング及びカーソルの位置が画面内に入るよう入力行スクロールする処理をスキップしている
スクロールスキップしたせいでカーソルの座標が画面外にあるという状態をレンダリングした時のバグ
などが絡んでいました。
テストコード中に "\M-[char]" "\M-\C-[char]" という形で不正なバイト列のUTF-8文字列を使っていたのをやめました。なお、Rubyの文字列リテラル "\M-a" は "\M-a".bytes == ['a'.ord | 0x80] と8bit目を強制的に立てた1byteの文字になります。
ターミナルエミュレータでAltキー(Metaキー)を押しながら他のキーを入力した時、"\e" + key がSTDINに入力されるんですが、テストコード中では "\M-[char]" を使いつつ、invalid byte sequence を\eに戻すような処理をしていました。
古い端末では、ASCII文字しか使われていない前提で、Metaキー入力は8bit目を立てた文字コードが入力されたらしいのですが、Relineは元々そんな入力に対応していません。
UTF-8として不正なバイト列を使うことは不便なだけでなく、以下の問題があります。
"\M-C\M-\C-A" == "Á" は Meta+C Meta+Ctrl+A なのか マルチバイト文字 Á の入力なのか判別がつかないのでMetaキーの表現方法として不完全
実際の入力 "\e" + key と異なるのでテストとして不適切
2025/01/12
複数行入力で、最終行以外にカーソルがあるときにENTERを押すと入力の確定になっていたのを、改行文字が挿入されるようになりました。
以前から改行文字の挿入は Option(Alt)+Enter でできたんですが、便利なのに知っている人が少なく多分あまり使われてなかった
Option+Enterを今まで使っていた自分にとってはまだちょっと慣れない
Unicode のバージョンが 16.0.0 に上がりました。
これにより、ターミナル上で表示されるであろう横幅の計算の結果が幾つかの文字で少し変化します。
irbrcでコマンドのエイリアス設定を間違えた場合、そのエイリアスを使ったときに警告メッセージが出るようになりました。
CI上での端末のE2Eテストの設定のリファクタリング
IRBのバグと思われるエラーが起きた時のメッセージを改善してruby/irbリポジトリのissueに書くよう案内しています。
IRB.conf[:SAVE_HISTORY] 履歴保存件数の設定に以前は true false が設定可能だったものの今はエラーになるらしく、
bool値を設定できそうな設定項目に見えることもあって実際に使っている人がいたため、true(デフォルト1000) false(無効) を以前同様に受け付けるようにしました。
RDoc コメントのescape (定数と同名の文字列がリンクにならないようにするためのbackslash) のうち、今は不要になったものを消しています
Privateメソッドを private def ではなく privateの後ろに def で定義するようなリファクタリング
ppがBasicObjectを扱えなかったため、IRBの方で特別扱いしていたのですが、pp 0.6.2 で修正されたため、特別扱いをやめつつ pp 0.6.2 をdependencyに追加しています。
2025/01/15
shareable_constant_valueコメントがある場合に補完ができないケースがある問題を修正しました。
定数への代入がShareableConstantNodeで包まれるんですが、このnodeの対応が抜けていました。
ブロッックパラメーターのitをネストしていた場合の補完バグ修正です。
:outer.tap { it; 'inner'.tap{it}; it. の補完でinnerの補完候補が混ざっていました。
it(内部では _1 の名前でローカル変数テーブルに入れている)の変数テーブルを内側のblockで作り忘れていたせい。
CIの設定のリファクタリングです。bundler-cache: true を指定している場合 bundle install を明示的に実行する必要はないらしい
tap{||} を含んだコードで補完が出ない問題の修正です。
BlockParameterNode はあるものの、そのparametersがnilになっているレアケース
IRBでのドキュメント表示のために、ReplTypeCompletorは、今選択中の補完候補は "Class.method" ですよ、というのを返しています。
補完対象のオブジェクトが無名クラスのインスタンスの場合、".method" を返してしまい、このケースでRDocが遅くなる問題がありました。(その名前のメソッドがあるドキュメントを全て探すため?)
無名クラスだった場合にsuperclassを辿って名前のあるクラス名を使うようにしてます。
a[&b]=c a[**b]=c などがあったケースの対応をやめています。3.4でSyntaxErrorになることに変わったため。
以前は x[**(a=A.new)]=1; a. や x[&(a=A.new)]=1; a. などが補完されていましたがテストコードも含め消しています。
Prism側のAPIもそのうち変わり得る(IndexOrWriteNodeのblockメソッドが消える・パースされ方が変わるなど)
補完対象のAST nodeを調べる処理で、nodeのオブジェクトとして完全一致を調べるためにHashに__id__を入れていたところ、
{}.compare_by_identity で済みそうだった(書いたときは存在を知らなかった)ので書き換えています
backref ($& $1 $2 など)がカラーリングされるようになりました。
$~ $! などはtokenとしてはgvarなんですがbackrefは違うんですね、(どちらも普通のグローバル変数とはちょっと違う特殊なやつ)
2025/01/22
copyコマンドが実装されました。copy 10.times.map{rand} などで実行結果をpretty_printsした結果をコピーすることと
copy で直前の実行結果をコピーすることができます。
コピーには pbcopy xclip -selection clipboard もしくは環境変数IRB_COPY_COMMANDなどで設定したコマンドを使います。
文字列コピーのエスケープシーケンスも使いたいが(例えばfallbackとして)、あれは使える端末が限られるからどうしたものか...
1000000.times.to_a はpretty_printに10秒以上かかりますが、最初の1ページ目のプレビューを0.1秒で表示できるようになりました。
pretty_printはストリームでoutputするため、これの単純な実装方法はpagerのコマンドにストリームで流し込むだけで良いです(Pryがこの実装)。
が、世の中にはpretty_print中に標準出力にログを書き出すRubyアプリ(RailsのActiveRecord)が沢山あるため、
どのタイミングでログが出力されても画面が崩れないよう・またログが後から見れるようにするために1ページ目のみプレビュー方式にしました。
コピーコマンドについてドキュメント追加
RDocの仕様として、ドキュメント中のIRB Array Hashなどの文字列(定数が存在するものと同名のもの)は勝手にリンクになるのですが、
それを抑制するために \ でエスケープしていました。最新のRDocにオートリンクから除外する単語リストを指定できる機能が追加されたため、このエスケープを外しています。
IRB 1.15.0 released 🎉
ReplTypeCompletor 0.1.10 released 🎉
2025/01/24
IRBとRelineがdefault gemからbundled gemになったようです
2025/01/27
IRB/RelineがBundled gemになった対応シリーズ
gemspec の spec.files の指定方法を変えています。bundled gemの何か(テストかinstallか)の時、Dir.pwdが異なることがあることへの対応で Dir.chdir で囲っています。
IRBの方では、外部コマンドとしてIRBをテストする場合に、irb/lib reline/libのpathを渡すような変更も入れています。
ruby/rubyのbundled gemのテストではGemfile無しでテストされるようで、テスト対象とは違うreline・irbを読み込んでしまうことを防ぐためのようです。
Bundled gemになったので、default gemだった頃のruby/rubyで行われるものと同じテストをするworkflowを削除しています。
Gemfileにreadlineを追加しています。
RelineがGNU Readlineと互換性があるかどうかをテストするため、readline-extのテストをRelineで行うCIがあるんですが、
Readlineもbundled gemになったので、Gemfileにreadlineを追加しないとテストできなくなっていました。
irb_infoコマンドを実行すると文字幅が環境で異なる文字の文字幅計測が始まってしまうのですが、コマンドのテストではそれが起きないようにしました。
ruby/rubyのbundled gemのテストが止まってしまうなどの問題がありました。
(Process.spawn(command, pgroup: true) の中でSTDIN.rawを呼ぶ・stty rawコマンドを呼ぶとRubyのプロセスが応答しなくなるが理由がわからない Ruby無関係な気がするが...)
IRBを irb --readline irb --singleline で起動した時、require 'readline' をするんですが、これが失敗しうるようになりました。Readlineがdefault gemではなくなったため。
その場合、Relineを使うようにしました。
(とりあえずこう対処したけど、最初からRelineを使っても良いのかもしれない)
Pagerのプレビュー表示部分を実装した結果、winsize=[0,0]の環境でIRBがinspectに失敗するようになった問題を修正しています。
docker run -it | cat などで発生する問題だと思うんですが、IRBに限らずemacs や vim や git show(で使われるpager)なども表示が崩れて正常に動かないので、環境の方を見直した方が良いはずです。
undo redo 周りのリファクタリングです。
最近の別のリファクタリングにより、Relineに対するキー操作は(ペーストも含め)全てinput_keyメソッドで実行されるようになったため、undo redoバッファに操作を積むのもinput_keyメソッド内で完結するようになりました。
不要になったインスタンス変数(ローカル変数で済むため)の削除やメソッドの整理など。
消し忘れの未使用の定数 Reline::ANSI::CAPNAME_KEY_BINDINGS を削除しました。
RelineがTerminfoを使っていた頃の、Terminfoのどの名前のkeyでRelineのどの動作をするか定義していた定数でした。