ruby-lsp 0.17.2 → 0.17.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|