はじめに
この記事は、僕が最近自作しているエディタであるMui(無為)でRubyの型定義ファイルである.rbsへ遷移できるようにした際の備忘録をまとめたものです。
実装時の詳細な情報などは個人的に利用しているesaにまとめてあるのですが、ちょうど最近技術ネタの記事をあまり書いていなかったのもあり、記事にしてみました。
ちなみに自作しているエディタのMui(無為)はヘイシャのブログで紹介しているのでそちらをご覧ください。
できたもの
以下のように、自作のエディタ上で動いているTypeProfのLSP経由で、型定義ファイル(.rbs)へ直接遷移できるようにしています。
やったこと
まずは自作のエディタ用のLSPプラグインには型定義ファイルへ遷移するためのコードを追加しました。
def type_definition(file_path:, line:, character:) # Only send to servers that support typeDefinitionProvider text_syncs = text_syncs_for(file_path).select do |ts| ts.client.server_capabilities["typeDefinitionProvider"] end if text_syncs.empty? # Fallback message: check if any server is running but doesn't support typeDefinition any_text_sync = text_syncs_for(file_path).first @editor.message = if any_text_sync "LSP: no server supports typeDefinition for this file" else server_unavailable_message(file_path) end return end uri = TextDocumentSync.path_to_uri(file_path) handler = Handlers::TypeDefinition.new(editor: @editor, client: text_syncs.first.client) # Collect results from all clients results_mutex = Mutex.new pending_count = text_syncs.size all_results = [] text_syncs.each do |text_sync| text_sync.client.type_definition(uri: uri, line: line, character: character) do |result, _error| results_mutex.synchronize do all_results << result if result pending_count -= 1 if pending_count.zero? merged = merge_locations(all_results) handler.handle(merged, nil) end end end end end
ただ、手元の環境でTypeProfを使ってクラスの型定義ファイルへ遷移しようとすると、何故かクラスの定義位置へのみ遷移する挙動をしていました。
エディタ上での動きからして「そもそも型定義ファイルへの遷移は、まだ実装されていないのでは?」と考え、以下のようにRubyのソースコードだった場合は型定義ファイルへ遷移するように変更しました。
def jump_to_type_file(file_path:, line: nil, character: nil) # For Ruby/RBS files, use custom toggle behavior if file_path&.end_with?(".rb", ".rbs") jump_to_ruby_type_file(file_path) else # For other languages, use LSP typeDefinition type_definition(file_path: file_path, line: line, character: character) end end
Rubyのコードだけ特別扱いする形になるので実装としては筋がいいものではありませんが、一旦型情報を参照できるようにしたかったのでこれで妥協することにしました。
ただ、この実装で終わるのも面白くなかったので今回の実装で得た知見を活かして、TypeProf本体にもパッチを投げてみました。
このパッチで以下のことができるようになりました。
- クラス名からクラス自体の型定義ファイルへ遷移できるようになった
foo = Foo.newのようにインスタンスを受け取っている変数からも型定義ファイルへ遷移できるようになった
実際の動作としては以下の動画のようになっています。
動作確認としてはVSCodeとMui(無為)それぞれで確認しています。
なので、このパッチがマージされれば他のエディタ上でも型定義ファイルへの遷移ができるようになると思います。
おわりに
今回の対応で.rbsへの遷移ができるようになりましたが、まだ型情報のホバー表示などには対応できていない状況です。
なので、今後はそのあたりを対応しつつ他の言語のLSPとの連携(たとえばTypeScriptの型情報への遷移とか)を試しつつ、開発を続けていきたいですね。