RubyのLSP実装の簡易比較
RubyのLSP実装の機能の簡易の比較をしてみた。対象は以下。
- solargraph 0.47.2
- sorbet 0.5.9204
- ruby-lsp 0.3.6
- typeprof 0.21.3
- steep 1.3.0
現時点ではSorbetはRuby 2.7までしかサポートしていなくて、typeprofのLSPはRuby 3しかサポートしていない。そのためtypeprof以外をRuby 2.7で、typeprofを Ruby 3.1で動作させる。
# Gemfile.lsp27
gem 'solargraph', require: false
gem 'ruby-lsp', require: false
gem 'steep', require: false
gem 'sorbet'
gem 'sorbet-runtime'
gem 'tapioca', require: false
# Gemfile.lsp3
gem 'typeprof', require: false
# lsp_test.rb
require "language_server-protocol"
name = ARGV.shift
cmd = case name
when "solargraph"
%w(solargraph stdio)
when "steep"
%w(steep langserver)
when "sorbet"
%w(srb typecheck --lsp --disable-watchman)
when "ruby-lsp"
%w(ruby-lsp)
when "typeprof"
%w(typeprof --lsp --stdio)
end
io = IO.popen(["bundle", "exec", *cmd], "w+")
LSP = LanguageServer::Protocol
writer = LSP::Transport::Io::Writer.new(io)
reader = LSP::Transport::Io::Reader.new(io)
begin
writer.write(
id: 0,
method: :initialize,
params: {
processId: $$,
rootUri: "file:///dummy",
capabilities: {},
initializationOptions: { # ruby-lspはこれがないとNoMethodErrorでコケる
enabledFeatures: [
:documentSymbols,
:documentLink,
:hover,
:foldingRanges,
#:semanticHighlighting, # これを入れるとNameErrorでコケる
:diagnostics,
:onTypeFormatting,
:inlayHint,
:selectionRanges,
:formatting,
:documentHighlights,
:codeActions,
],
},
}
)
puts "[#{name}]"
reader.read do |res|
if res[:error]
puts "Error"
pp res
else
pp res.dig(:result, :capabilities)
end
break
end
writer.write(id: 1, method: :shutdown)
reader.read do |res|
# pp res
break
end
ensure
io.close_write
end
実行結果:
$ rbenv shell 2.7.6
$ export BUNDLE_GEMFILE=Gemfile.lsp27
$ bundle install
...(略)...
$ bundle exec tapioca init
create sorbet/config
...(略)...
$ bundle exec steep init
Writing Steepfile...
$ for name in solargraph sorbet ruby-lsp steep; do ruby lsp_test.rb "$name" 2>/dev/null; done
[solargraph]
{:textDocumentSync=>2,
:workspace=>
{:workspaceFolders=>{:supported=>true, :changeNotifications=>true}},
:completionProvider=>
{:resolveProvider=>true, :triggerCharacters=>[".", ":", "@"]},
:signatureHelpProvider=>{:triggerCharacters=>["(", ","]},
:hoverProvider=>true,
:documentSymbolProvider=>true,
:definitionProvider=>true,
:renameProvider=>{:prepareProvider=>true},
:referencesProvider=>true,
:workspaceSymbolProvider=>true,
:foldingRangeProvider=>true,
:documentHighlightProvider=>true}
[sorbet]
{:textDocumentSync=>1,
:hoverProvider=>true,
:completionProvider=>{:triggerCharacters=>[".", ":", "@", "#"]},
:definitionProvider=>true,
:typeDefinitionProvider=>true,
:implementationProvider=>true,
:referencesProvider=>true,
:documentHighlightProvider=>false,
:documentSymbolProvider=>false,
:workspaceSymbolProvider=>true,
:codeActionProvider=>
{:codeActionKinds=>["quickfix", "source.fixAll.sorbet", "refactor.extract"]},
:documentFormattingProvider=>false,
:renameProvider=>{:prepareProvider=>true},
:sorbetShowSymbolProvider=>true}
[ruby-lsp]
{:textDocumentSync=>{:openClose=>true, :change=>2},
:hoverProvider=>{},
:documentHighlightProvider=>true,
:documentSymbolProvider=>
{:symbolKind=>
{:value_set=>
[1,
...(略)...,
26]},
:hierarchicalDocumentSymbolSupport=>true},
:codeActionProvider=>true,
:documentLinkProvider=>{},
:documentFormattingProvider=>true,
:documentOnTypeFormattingProvider=>
{:firstTriggerCharacter=>"{", :moreTriggerCharacter=>["\n", "|"]},
:foldingRangeProvider=>{:lineFoldingOnly=>true},
:selectionRangeProvider=>true,
:inlayHintProvider=>{},
:diagnosticProvider=>
{:interFileDependencies=>false, :workspaceDiagnostics=>false}}
[steep]
{:textDocumentSync=>{:openClose=>true, :change=>2, :save=>{}},
:completionProvider=>
{:workDoneProgress=>true, :triggerCharacters=>[".", "@", ":"]},
:hoverProvider=>
{:workDoneProgress=>true, :partialResults=>true, :partialResult=>true},
:definitionProvider=>true,
:implementationProvider=>true,
:workspaceSymbolProvider=>true}
$ rbenv shell 3.1.3
$ export BUNDLE_GEMFILE=Gemfile.lsp3
$ bundle install
$ ruby lsp_test.rb typeprof
[typeprof]
TypeProf for IDE is started successfully
{:textDocumentSync=>{:openClose=>true, :change=>2},
:completionProvider=>{:triggerCharacters=>["."]},
:signatureHelpProvider=>{:triggerCharacters=>["(", ","]},
:codeLensProvider=>{:resolveProvider=>true},
:executeCommandProvider=>
{:commands=>["typeprof.createPrototypeRBS", "typeprof.enableSignature", "typeprof.disableSignature"]},
:definitionProvider=>true,
:typeDefinitionProvider=>true,
:referencesProvider=>true}