Skip to content

Commit

Permalink
Use RBS for indexing Core classes
Browse files Browse the repository at this point in the history
  • Loading branch information
andyw8 committed Jun 7, 2024
1 parent 385641d commit 0f078ed
Show file tree
Hide file tree
Showing 9 changed files with 6,756 additions and 1 deletion.
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ PATH
ruby-lsp (0.17.2)
language_server-protocol (~> 3.17.0)
prism (>= 0.29.0, < 0.30)
rbs
sorbet-runtime (>= 0.5.10782)

GEM
remote: https://rubygems.org/
specs:
abbrev (0.1.2)
ansi (1.5.0)
ast (2.4.2)
builder (3.2.4)
Expand Down Expand Up @@ -54,6 +56,8 @@ GEM
rbi (0.1.13)
prism (>= 0.18.0, < 1.0.0)
sorbet-runtime (>= 0.5.9204)
rbs (3.4.4)
abbrev
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&.lines)
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
14 changes: 14 additions & 0 deletions lib/ruby_lsp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,20 @@ 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
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
RubyIndexer::RBSIndexer.new(@global_state.index).index_core_classes
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
$stderr.puts("Indexed Ruby core classes in #{(end_time - start_time).round(2)} seconds")
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")
s.add_dependency("sorbet-runtime", ">= 0.5.10782")

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

0 comments on commit 0f078ed

Please sign in to comment.