Slide 1

Slide 1 text

RubyLSPのマルチバイト文字対応 2024/11/07 - Omotesando.rb#103 Iori IKEDA @NotFounds8080

Slide 2

Slide 2 text

自己紹介 Iori IKEDA ● 株式会社YAMAP ● Webエンジニア ● 壁と山を登るのが好きです ● Twitter: @NotFounds8080 ● GitHub: @NotFounds

Slide 3

Slide 3 text

RubyLSPについて

Slide 4

Slide 4 text

RubyLSPとは ● Rubyの言語サーバー ● 2022年ごろからShopifyが開発 ● 定義ジャンプやセマンティックハイライト、RobocopによるLintなど ● プラグイン機構があり、RailsやRSpecの拡張 ● ブログ等で比較的よく紹介されている(気がする)

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

何かがおかしい...?

Slide 9

Slide 9 text

RubyLSPに潜む問題 ● Definition jumps are not possible with files containing Japanese characters. · Issue #1347 · Shopify/ruby-lsp · GitHub ● 意訳: 日本語が含まれていると動かない ● これじゃん!!! ● どうやらメンテナも認識しているようだが未対応 ● でもどうやって直せばいいんだ...

Slide 10

Slide 10 text

RubyLSPの問題

Slide 11

Slide 11 text

RubyLSPの問題 マルチバイト文字の位置計算をいい感じに する(意訳) Prismが位置計算を行うときにエン コーディングを考慮していないから どうにかする必要があるっぽい

Slide 12

Slide 12 text

RubyLSPの問題 いい感じのAPI生えたし 対応が進みそう!

Slide 13

Slide 13 text

RubyLSPの仕組み

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

class Hoge
 def fuga
 puts "Hello"
 end
 end
 
 Hoge.new.fuga RubyLSPの仕組み 1. エディタ上で定義ジャンプ {
 "method": "textDocument/definition",
 "params": {
 "textDocument": {
 "uri": "file://a.rb"
 },
 "position": {
 "line": 6,
 "character": 9
 }
 }
 }


Slide 17

Slide 17 text

2. positionを先頭から何文字目かに変換 元のファイルでは 6行 9文字目 'class Hoge\ndef fuga\nputs "Hello"\nend\nend\n\nHoge.new.fuga\n' 
 class Hoge
 def fuga
 puts "Hello"
 end
 end
 
 Hoge.new.fuga RubyLSPの仕組み 53文字目

Slide 18

Slide 18 text

3. 先頭からの何文字数からノードを取得 抽象構文木(AST)を辿り、53文字目のノードを探す class Hoge
 def fuga
 puts "Hello"
 end
 end
 
 Hoge.new.fuga RubyLSPの仕組み

Slide 19

Slide 19 text

3. 先頭からの何文字数からノードを取得 ASTを辿り、53文字目のノードを探す class Hoge
 def fuga
 puts "Hello"
 end
 end
 
 Hoge.new.fuga RubyLSPの仕組み 0~42文字目

Slide 20

Slide 20 text

3. 先頭からの何文字数からノードを取得 ASTを辿り、53文字目のノードを探す class Hoge
 def fuga
 puts "Hello"
 end
 end
 
 Hoge.new.fuga RubyLSPの仕組み 13~38文字目

Slide 21

Slide 21 text

3. 先頭からの何文字数からノードを取得 ASTを辿り、53文字目のノードを探す class Hoge
 def fuga
 puts "Hello"
 end
 end
 
 Hoge.new.fuga RubyLSPの仕組み 22~34文字目

Slide 22

Slide 22 text

3. 先頭からの何文字数からノードを取得 ASTを辿り、53文字目のノードを探す class Hoge
 def fuga
 puts "Hello"
 end
 end
 
 Hoge.new.fuga RubyLSPの仕組み 44~57文字目

Slide 23

Slide 23 text

3. 先頭からの何文字数からノードを取得 ASTを辿り、53文字目のノードを探す class Hoge
 def fuga
 puts "Hello"
 end
 end
 
 Hoge.new.fuga RubyLSPの仕組み 44~48文字目

Slide 24

Slide 24 text

3. 先頭からの何文字数からノードを取得 ASTを辿り、53文字目のノードを探す class Hoge
 def fuga
 puts "Hello"
 end
 end
 
 Hoge.new.fuga RubyLSPの仕組み 53~57文字目

Slide 25

Slide 25 text

class Hoge
 def fuga
 puts "Hello"
 end
 end
 
 Hoge.new.fuga RubyLSPの仕組み 4. 事前に作成したコードのIndex(辞書)から対象のノードを探す ノードが見つかったらLSPに結果を返す {
 "uri": "file://a.rb",
 "range": {
 "start": {
 "line": 1,
 "character": 2
 },
 "end": {
 "line": 3,
 "character": 5
 }
 }
 }


Slide 26

Slide 26 text

RubyLSPの問題(再掲) マルチバイト文字の位置計算をいい感じに する(意訳) Prismが位置計算を行うときにエン コーディングを考慮していないから どうにかする必要があるっぽい

Slide 27

Slide 27 text

RubyLSPの問題 位置の計算をしているところが問題 ● インデックス作成 ● 対象ノードを取得 インデックスの位置計算が不正確 ⇒ノードの正しい場所を返せない 対象ノードの取得が不正確 ⇒関係ないノードの場所を返す

Slide 28

Slide 28 text

RubyLSPの修正

Slide 29

Slide 29 text

RubyLSPの修正 ● 主な修正内容 ○ LSPを初期化時にエディタから受け取ったencodingを設定 ○ 位置計算を行っている箇所でPrismの新しいAPIを利用

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

無事動くようになった🎉🎉🎉 Ruby LSP v0.19.2~ 🚀 latest: v0.21.3

Slide 32

Slide 32 text

まとめ ● RubyLSPはRubyの言語サーバー ● RubyLSPの仕組みザックリ解説 ● 学び ○ ファイルや文字列を扱うときはエンコーディングに注意 ぜひRubyLSPを試してみてください!

Slide 33

Slide 33 text

参考文献・資料 ● Code indexing: How language servers understand our code ● Language Server Protocol の仕様 及び実装方法 ● Ruby LSP | An opinionated language server for Ruby. Batteries included! ● https://github.com/Shopify/ruby-lsp/pull/2619 ● https://github.com/Shopify/ruby-lsp/pull/2669