Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Index core classes using RBS #2132

Merged
merged 1 commit into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ PATH
ruby-lsp (0.17.2)
language_server-protocol (~> 3.17.0)
prism (>= 0.29.0, < 0.30)
rbs (>= 3, < 4)
sorbet-runtime (>= 0.5.10782)

GEM
Expand All @@ -31,6 +32,7 @@ GEM
reline (>= 0.4.2)
json (2.7.2)
language_server-protocol (3.17.0.3)
logger (1.6.0)
minitest (5.23.1)
minitest-reporters (1.6.1)
ansi
Expand All @@ -54,6 +56,8 @@ GEM
rbi (0.1.13)
prism (>= 0.18.0, < 1.0.0)
sorbet-runtime (>= 0.5.9204)
rbs (3.5.1)
logger
regexp_parser (2.9.2)
reline (0.5.0)
io-console (~> 0.5)
Expand Down
63 changes: 63 additions & 0 deletions lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# typed: strict
# frozen_string_literal: true

module RubyIndexer
class RBSIndexer
extend T::Sig

sig { params(index: Index).void }
def initialize(index)
@index = index
end

sig { void }
def index_core_classes
loader = RBS::EnvironmentLoader.new
RBS::Environment.from_loader(loader).resolve_type_names

loader.each_signature do |source, pathname, _buffer, declarations, _directives|
process_signature(source, pathname, declarations)
end
end

private

sig { params(source: T.untyped, pathname: Pathname, declarations: T::Array[RBS::AST::Declarations::Base]).void }
def process_signature(source, pathname, declarations)
declarations.each do |declaration|
process_declaration(declaration, pathname)
end
end

sig { params(declaration: RBS::AST::Declarations::Base, pathname: Pathname).void }
def process_declaration(declaration, pathname)
case declaration
when RBS::AST::Declarations::Class
handle_class_declaration(declaration, pathname)
else # rubocop:disable Style/EmptyElse
# Other kinds not yet handled
end
end

sig { params(declaration: RBS::AST::Declarations::Class, pathname: Pathname).void }
def handle_class_declaration(declaration, pathname)
nesting = [declaration.name.name.to_s]
file_path = pathname.to_s
location = to_ruby_indexer_location(declaration.location)
comments = Array(declaration.comment&.string)
parent_class = declaration.super_class&.name&.name&.to_s
class_entry = Entry::Class.new(nesting, file_path, location, comments, parent_class)
@index << class_entry
end

sig { params(rbs_location: RBS::Location).returns(RubyIndexer::Location) }
def to_ruby_indexer_location(rbs_location)
RubyIndexer::Location.new(
rbs_location.start_line,
rbs_location.end_line,
rbs_location.start_column,
rbs_location.end_column,
)
end
end
end
1 change: 1 addition & 0 deletions lib/ruby_indexer/ruby_indexer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require "ruby_indexer/lib/ruby_indexer/configuration"
require "ruby_indexer/lib/ruby_indexer/prefix_tree"
require "ruby_indexer/lib/ruby_indexer/location"
require "ruby_indexer/lib/ruby_indexer/rbs_indexer"

module RubyIndexer
@configuration = T.let(Configuration.new, Configuration)
Expand Down
2 changes: 1 addition & 1 deletion lib/ruby_indexer/test/configuration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def test_indexables_includes_default_gems

assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb")
assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/ipaddr.rb")
assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/abbrev.rb")
assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/erb.rb")
end

def test_indexables_includes_project_files
Expand Down
29 changes: 29 additions & 0 deletions lib/ruby_indexer/test/rbs_indexer_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# typed: true
# frozen_string_literal: true

require_relative "test_case"

module RubyIndexer
class RBSIndexerTest < TestCase
def setup
@index = RubyIndexer::Index.new
RBSIndexer.new(@index).index_core_classes
end

def test_index_core_classes
entries = @index["Array"]
refute_nil(entries)
assert_equal(1, entries.length)
entry = entries.first
assert_match(%r{/gems/rbs-.*/core/array.rbs}, entry.file_path)
assert_equal("array.rbs", entry.file_name)
assert_equal("Object", entry.parent_class)

# Using fixed positions would be fragile, so let's just check some basics.
assert_operator(entry.location.start_line, :>, 0)
assert_operator(entry.location.end_line, :>, entry.location.start_line)
assert_equal(0, entry.location.start_column)
assert_operator(entry.location.end_column, :>, 0)
end
end
end
1 change: 1 addition & 0 deletions lib/ruby_lsp/internal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
require "prism"
require "prism/visitor"
require "language_server-protocol"
require "rbs"

require "ruby-lsp"
require "ruby_lsp/base_server"
Expand Down
11 changes: 11 additions & 0 deletions lib/ruby_lsp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,17 @@ def shutdown

sig { params(config_hash: T::Hash[String, T.untyped]).void }
def perform_initial_indexing(config_hash)
index_core_classes
index_ruby_code(config_hash)
end

sig { void }
def index_core_classes
RubyIndexer::RBSIndexer.new(@global_state.index).index_core_classes
end

sig { params(config_hash: T::Hash[String, T.untyped]).void }
def index_ruby_code(config_hash)
# The begin progress invocation happens during `initialize`, so that the notification is sent before we are
# stuck indexing files
RubyIndexer.configuration.apply_config(config_hash)
Expand Down
1 change: 1 addition & 0 deletions ruby-lsp.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Gem::Specification.new do |s|

s.add_dependency("language_server-protocol", "~> 3.17.0")
s.add_dependency("prism", ">= 0.29.0", "< 0.30")
s.add_dependency("rbs", ">= 3", "< 4")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified it on 3.0.0. With 2.0.0 it fails because "each_signature" isn't defined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I'm open to removing the upper constraint)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep the upper constraint. We can't guarantee that a major version won't break the LSP.

s.add_dependency("sorbet-runtime", ">= 0.5.10782")

s.required_ruby_version = ">= 3.0"
Expand Down
Loading
Loading