ruby-lsp 0.17.2 → 0.17.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/VERSION +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +280 -74
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +102 -102
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +234 -56
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +147 -0
- data/lib/ruby_indexer/ruby_indexer.rb +1 -0
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +49 -2
- data/lib/ruby_indexer/test/configuration_test.rb +1 -1
- data/lib/ruby_indexer/test/constant_test.rb +1 -1
- data/lib/ruby_indexer/test/index_test.rb +702 -71
- data/lib/ruby_indexer/test/instance_variables_test.rb +84 -7
- data/lib/ruby_indexer/test/method_test.rb +74 -24
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +67 -0
- data/lib/ruby_indexer/test/test_case.rb +7 -0
- data/lib/ruby_lsp/document.rb +37 -8
- data/lib/ruby_lsp/global_state.rb +43 -18
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listeners/code_lens.rb +2 -2
- data/lib/ruby_lsp/listeners/completion.rb +53 -14
- data/lib/ruby_lsp/listeners/definition.rb +11 -7
- data/lib/ruby_lsp/listeners/hover.rb +14 -7
- data/lib/ruby_lsp/listeners/signature_help.rb +5 -2
- data/lib/ruby_lsp/node_context.rb +6 -1
- data/lib/ruby_lsp/requests/completion.rb +5 -4
- data/lib/ruby_lsp/requests/completion_resolve.rb +8 -0
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +88 -0
- data/lib/ruby_lsp/requests/support/common.rb +19 -1
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -4
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +91 -0
- data/lib/ruby_lsp/requests/workspace_symbol.rb +1 -21
- data/lib/ruby_lsp/requests.rb +2 -0
- data/lib/ruby_lsp/server.rb +54 -4
- data/lib/ruby_lsp/test_helper.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +86 -0
- metadata +29 -4
@@ -0,0 +1,147 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RubyIndexer
|
5
|
+
class RBSIndexer
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
sig { params(index: Index).void }
|
9
|
+
def initialize(index)
|
10
|
+
@index = index
|
11
|
+
end
|
12
|
+
|
13
|
+
sig { void }
|
14
|
+
def index_ruby_core
|
15
|
+
loader = RBS::EnvironmentLoader.new
|
16
|
+
RBS::Environment.from_loader(loader).resolve_type_names
|
17
|
+
|
18
|
+
loader.each_signature do |source, pathname, _buffer, declarations, _directives|
|
19
|
+
process_signature(source, pathname, declarations)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
sig { params(source: T.untyped, pathname: Pathname, declarations: T::Array[RBS::AST::Declarations::Base]).void }
|
26
|
+
def process_signature(source, pathname, declarations)
|
27
|
+
declarations.each do |declaration|
|
28
|
+
process_declaration(declaration, pathname)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
sig { params(declaration: RBS::AST::Declarations::Base, pathname: Pathname).void }
|
33
|
+
def process_declaration(declaration, pathname)
|
34
|
+
case declaration
|
35
|
+
when RBS::AST::Declarations::Class
|
36
|
+
handle_class_declaration(declaration, pathname)
|
37
|
+
when RBS::AST::Declarations::Module
|
38
|
+
handle_module_declaration(declaration, pathname)
|
39
|
+
else # rubocop:disable Style/EmptyElse
|
40
|
+
# Other kinds not yet handled
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
sig { params(declaration: RBS::AST::Declarations::Class, pathname: Pathname).void }
|
45
|
+
def handle_class_declaration(declaration, pathname)
|
46
|
+
nesting = [declaration.name.name.to_s]
|
47
|
+
file_path = pathname.to_s
|
48
|
+
location = to_ruby_indexer_location(declaration.location)
|
49
|
+
comments = Array(declaration.comment&.string)
|
50
|
+
parent_class = declaration.super_class&.name&.name&.to_s
|
51
|
+
class_entry = Entry::Class.new(nesting, file_path, location, comments, parent_class)
|
52
|
+
add_declaration_mixins_to_entry(declaration, class_entry)
|
53
|
+
@index.add(class_entry)
|
54
|
+
declaration.members.each do |member|
|
55
|
+
next unless member.is_a?(RBS::AST::Members::MethodDefinition)
|
56
|
+
|
57
|
+
handle_method(member, class_entry)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
sig { params(declaration: RBS::AST::Declarations::Module, pathname: Pathname).void }
|
62
|
+
def handle_module_declaration(declaration, pathname)
|
63
|
+
nesting = [declaration.name.name.to_s]
|
64
|
+
file_path = pathname.to_s
|
65
|
+
location = to_ruby_indexer_location(declaration.location)
|
66
|
+
comments = Array(declaration.comment&.string)
|
67
|
+
module_entry = Entry::Module.new(nesting, file_path, location, comments)
|
68
|
+
add_declaration_mixins_to_entry(declaration, module_entry)
|
69
|
+
@index.add(module_entry)
|
70
|
+
declaration.members.each do |member|
|
71
|
+
next unless member.is_a?(RBS::AST::Members::MethodDefinition)
|
72
|
+
|
73
|
+
handle_method(member, module_entry)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
sig { params(rbs_location: RBS::Location).returns(RubyIndexer::Location) }
|
78
|
+
def to_ruby_indexer_location(rbs_location)
|
79
|
+
RubyIndexer::Location.new(
|
80
|
+
rbs_location.start_line,
|
81
|
+
rbs_location.end_line,
|
82
|
+
rbs_location.start_column,
|
83
|
+
rbs_location.end_column,
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
sig do
|
88
|
+
params(
|
89
|
+
declaration: T.any(RBS::AST::Declarations::Class, RBS::AST::Declarations::Module),
|
90
|
+
entry: Entry::Namespace,
|
91
|
+
).void
|
92
|
+
end
|
93
|
+
def add_declaration_mixins_to_entry(declaration, entry)
|
94
|
+
declaration.each_mixin do |mixin|
|
95
|
+
name = mixin.name.name.to_s
|
96
|
+
mixin_operation =
|
97
|
+
case mixin
|
98
|
+
when RBS::AST::Members::Include
|
99
|
+
Entry::Include.new(name)
|
100
|
+
when RBS::AST::Members::Extend
|
101
|
+
Entry::Extend.new(name)
|
102
|
+
when RBS::AST::Members::Prepend
|
103
|
+
Entry::Prepend.new(name)
|
104
|
+
end
|
105
|
+
entry.mixin_operations << mixin_operation if mixin_operation
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
sig { params(member: RBS::AST::Members::MethodDefinition, owner: Entry::Namespace).void }
|
110
|
+
def handle_method(member, owner)
|
111
|
+
name = member.name.name
|
112
|
+
file_path = member.location.buffer.name
|
113
|
+
location = to_ruby_indexer_location(member.location)
|
114
|
+
comments = Array(member.comment&.string)
|
115
|
+
|
116
|
+
visibility = case member.visibility
|
117
|
+
when :private
|
118
|
+
Entry::Visibility::PRIVATE
|
119
|
+
when :protected
|
120
|
+
Entry::Visibility::PROTECTED
|
121
|
+
else
|
122
|
+
Entry::Visibility::PUBLIC
|
123
|
+
end
|
124
|
+
|
125
|
+
real_owner = member.singleton? ? existing_or_new_singleton_klass(owner) : owner
|
126
|
+
@index.add(Entry::Method.new(name, file_path, location, comments, [], visibility, real_owner))
|
127
|
+
end
|
128
|
+
|
129
|
+
sig { params(owner: Entry::Namespace).returns(T.nilable(Entry::Class)) }
|
130
|
+
def existing_or_new_singleton_klass(owner)
|
131
|
+
*_parts, name = owner.name.split("::")
|
132
|
+
|
133
|
+
# Return the existing singleton class if available
|
134
|
+
singleton_entries = T.cast(
|
135
|
+
@index["#{owner.name}::<Class:#{name}>"],
|
136
|
+
T.nilable(T::Array[Entry::SingletonClass]),
|
137
|
+
)
|
138
|
+
return singleton_entries.first if singleton_entries
|
139
|
+
|
140
|
+
# If not available, create the singleton class lazily
|
141
|
+
nesting = owner.nesting + ["<Class:#{name}>"]
|
142
|
+
entry = Entry::SingletonClass.new(nesting, owner.file_path, owner.location, [], nil)
|
143
|
+
@index.add(entry, skip_prefix_tree: true)
|
144
|
+
entry
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -11,6 +11,7 @@ require "ruby_indexer/lib/ruby_indexer/entry"
|
|
11
11
|
require "ruby_indexer/lib/ruby_indexer/configuration"
|
12
12
|
require "ruby_indexer/lib/ruby_indexer/prefix_tree"
|
13
13
|
require "ruby_indexer/lib/ruby_indexer/location"
|
14
|
+
require "ruby_indexer/lib/ruby_indexer/rbs_indexer"
|
14
15
|
|
15
16
|
module RubyIndexer
|
16
17
|
@configuration = T.let(Configuration.new, Configuration)
|
@@ -191,7 +191,8 @@ module RubyIndexer
|
|
191
191
|
|
192
192
|
@index.delete(IndexablePath.new(nil, "/fake/path/foo.rb"))
|
193
193
|
refute_entry("Foo")
|
194
|
-
|
194
|
+
|
195
|
+
assert_no_indexed_entries
|
195
196
|
end
|
196
197
|
|
197
198
|
def test_comments_can_be_attached_to_a_class
|
@@ -323,7 +324,7 @@ module RubyIndexer
|
|
323
324
|
assert_equal("Bar", foo.parent_class)
|
324
325
|
|
325
326
|
baz = T.must(@index["Baz"].first)
|
326
|
-
|
327
|
+
assert_equal("::Object", baz.parent_class)
|
327
328
|
|
328
329
|
qux = T.must(@index["Something::Qux"].first)
|
329
330
|
assert_equal("::Baz", qux.parent_class)
|
@@ -469,5 +470,51 @@ module RubyIndexer
|
|
469
470
|
constant_path_references = T.must(@index["ConstantPathReferences"][0])
|
470
471
|
assert_equal(["Foo::Bar", "Foo::Bar2"], constant_path_references.mixin_operation_module_names)
|
471
472
|
end
|
473
|
+
|
474
|
+
def test_tracking_singleton_classes
|
475
|
+
index(<<~RUBY)
|
476
|
+
class Foo; end
|
477
|
+
class Foo
|
478
|
+
# Some extra comments
|
479
|
+
class << self
|
480
|
+
end
|
481
|
+
end
|
482
|
+
RUBY
|
483
|
+
|
484
|
+
foo = T.must(@index["Foo::<Class:Foo>"].first)
|
485
|
+
assert_equal(4, foo.location.start_line)
|
486
|
+
assert_equal("Some extra comments", foo.comments.join("\n"))
|
487
|
+
end
|
488
|
+
|
489
|
+
def test_dynamic_singleton_class_blocks
|
490
|
+
index(<<~RUBY)
|
491
|
+
class Foo
|
492
|
+
# Some extra comments
|
493
|
+
class << bar
|
494
|
+
end
|
495
|
+
end
|
496
|
+
RUBY
|
497
|
+
|
498
|
+
singleton = T.must(@index["Foo::<Class:bar>"].first)
|
499
|
+
|
500
|
+
# Even though this is not correct, we consider any dynamic singleton class block as a regular singleton class.
|
501
|
+
# That pattern cannot be properly analyzed statically and assuming that it's always a regular singleton simplifies
|
502
|
+
# the implementation considerably.
|
503
|
+
assert_equal(3, singleton.location.start_line)
|
504
|
+
assert_equal("Some extra comments", singleton.comments.join("\n"))
|
505
|
+
end
|
506
|
+
|
507
|
+
def test_namespaces_inside_singleton_blocks
|
508
|
+
index(<<~RUBY)
|
509
|
+
class Foo
|
510
|
+
class << self
|
511
|
+
class Bar
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
RUBY
|
516
|
+
|
517
|
+
assert_entry("Foo::<Class:Foo>::Bar", Entry::Class, "/fake/path/foo.rb:2-4:3-7")
|
518
|
+
end
|
472
519
|
end
|
473
520
|
end
|
@@ -54,7 +54,7 @@ module RubyIndexer
|
|
54
54
|
|
55
55
|
assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb")
|
56
56
|
assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/ipaddr.rb")
|
57
|
-
assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/
|
57
|
+
assert_includes(indexables, "#{RbConfig::CONFIG["rubylibdir"]}/erb.rb")
|
58
58
|
end
|
59
59
|
|
60
60
|
def test_indexables_includes_project_files
|