VSCode 拡張のエントリーポイント
VSCode 拡張としての実装は vscode ディレクトリ以下に格納されている。実装は TypeScript 。今インストールしている VSCode 拡張のバージョンが v0.9.7 なのでそのタグで確認していく。
https://github.com/Shopify/ruby-lsp/tree/vscode-ruby-lsp-v0.9.7/vscode
VSCode のドキュメントも確認した。
https://code.visualstudio.com/api/get-started/your-first-extension
vscode/src/extension.ts の activate メソッドがエントリーポイントということで良さそう。
https://github.com/Shopify/ruby-lsp/blob/vscode-ruby-lsp-v0.9.7/vscode/src/extension.ts#L11
RubyLsp クラスを new して activate() をコールしている。
extension = new RubyLsp(context, logger); await extension.activate();
https://github.com/Shopify/ruby-lsp/blob/vscode-ruby-lsp-v0.9.7/vscode/src/extension.ts#L45-L46
RubyLSP クラス
The RubyLsp class represents an instance of the entire extension.
とのこと。拡張機能のメインクラスということでヨシ。
https://github.com/Shopify/ruby-lsp/blob/vscode-ruby-lsp-v0.9.7/vscode/src/rubyLsp.ts#L22-L25
activate() → activateWorkspace() → runActivation() の順番にコールして、Workspace クラスを new して activate() → start() をコールしている。
await workspace.activate();
await workspace.start();
https://github.com/Shopify/ruby-lsp/blob/vscode-ruby-lsp-v0.9.7/vscode/src/rubyLsp.ts#L233-L234
Workspace クラス start() メソッド
https://github.com/Shopify/ruby-lsp/blob/vscode-ruby-lsp-v0.9.7/vscode/src/workspace.ts#L106
Client クラスを new して start() をコールしている。この時に Ruby LSP 拡張が起動する際に VSCode に表示される「Initializing Ruby LSP」というメッセージも一緒に設定しているみたい。Ruby LSP 拡張の初期化処理の本丸ということで良さそう。
https://github.com/Shopify/ruby-lsp/blob/vscode-ruby-lsp-v0.9.7/vscode/src/workspace.ts#L169-L178
Client クラス
Client クラスには start() メソッドの定義がない。なのでこれは親クラスの LanguageClient から継承されたメソッド。
https://github.com/Shopify/ruby-lsp/blob/vscode-ruby-lsp-v0.9.7/vscode/src/client.ts#L345
注目すべきは親クラスコンストラクタの2番目の引数値を取得する getLspExecutables() メソッド。
super(
LSP_NAME,
getLspExecutables(workspaceFolder, ruby.env),
Language Server の起動コマンドが計算されている。
run = {
command: "bundle",
args: ["exec", "ruby-lsp"],
options: executableOptions,
};
https://github.com/Shopify/ruby-lsp/blob/vscode-ruby-lsp-v0.9.7/vscode/src/client.ts#L116-L120
念の為 LanguageClient の実装も確認する。
ServerOptions 型で合っている。
ruby-lsp コマンド
ここからは Ruby 実装。 v0.23.11 タグで確認していく。
まず gemspec の executables を確認。
s.bindir = "exe" s.executables = ["ruby-lsp", "ruby-lsp-check", "ruby-lsp-launcher"]
https://github.com/Shopify/ruby-lsp/blob/v0.23.11/ruby-lsp.gemspec#L17-L18
exe/ruby-lsp スクリプトが該当し、最終行で RubyLsp::Server を起動している。
RubyLsp::Server.new.start
https://github.com/Shopify/ruby-lsp/blob/v0.23.11/exe/ruby-lsp#L154
RubyLsp::Server クラス、 RubyLsp::BaseServer クラス
start メソッドは定義されていないので親クラスの BaseServer を確認する。
https://github.com/Shopify/ruby-lsp/blob/v0.23.11/lib/ruby_lsp/server.rb#L5
@reader.read のブロックがループしてそう。
def start
@reader.read do |message|
https://github.com/Shopify/ruby-lsp/blob/v0.23.11/lib/ruby_lsp/base_server.rb#L45-L46
この @reader が何かと言うと、Transport::Stdio::Reader のインスタンス。language_server-protocol-ruby gem のクラスで README に書いてある通りの使い方をしているといった感じか。
https://github.com/mtsmfm/language_server-protocol-ruby?tab=readme-ov-file#usage
クライアントからリクエストが来たら処理され、@incoming_queue に message を格納。
https://github.com/Shopify/ruby-lsp/blob/v0.23.11/lib/ruby_lsp/base_server.rb#L106
ワーカースレッドでデキューして process_message() で処理をすると。
https://github.com/Shopify/ruby-lsp/blob/v0.23.11/lib/ruby_lsp/base_server.rb#L150-L166
process_message() で各リクエストに対する分岐が書かれている。
https://github.com/Shopify/ruby-lsp/blob/v0.23.11/lib/ruby_lsp/server.rb#L13
個別の処理はここから追いかけていけばOKになった。
次回
クライアントから hover リクエストが飛んできた時のサーバーの処理の流れを追いかけてみよう。