ruby-lsp 0.20.1 → 0.22.1
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 -2
- data/VERSION +1 -1
- data/exe/ruby-lsp +19 -4
- data/exe/ruby-lsp-launcher +124 -0
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +6 -0
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +233 -59
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +34 -16
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +1 -1
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +15 -15
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +4 -4
- data/lib/ruby_indexer/test/configuration_test.rb +10 -0
- data/lib/ruby_indexer/test/constant_test.rb +8 -8
- data/lib/ruby_indexer/test/enhancements_test.rb +169 -41
- data/lib/ruby_indexer/test/index_test.rb +41 -2
- data/lib/ruby_indexer/test/instance_variables_test.rb +1 -1
- data/lib/ruby_indexer/test/method_test.rb +139 -0
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +1 -1
- data/lib/ruby_lsp/addon.rb +9 -2
- data/lib/ruby_lsp/base_server.rb +14 -5
- data/lib/ruby_lsp/client_capabilities.rb +67 -0
- data/lib/ruby_lsp/document.rb +1 -1
- data/lib/ruby_lsp/global_state.rb +33 -20
- data/lib/ruby_lsp/internal.rb +3 -0
- data/lib/ruby_lsp/listeners/completion.rb +62 -0
- data/lib/ruby_lsp/listeners/definition.rb +48 -13
- data/lib/ruby_lsp/listeners/document_highlight.rb +91 -4
- data/lib/ruby_lsp/listeners/document_symbol.rb +37 -4
- data/lib/ruby_lsp/listeners/hover.rb +52 -0
- data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/completion.rb +7 -1
- data/lib/ruby_lsp/requests/completion_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/definition.rb +28 -11
- data/lib/ruby_lsp/requests/document_highlight.rb +7 -1
- data/lib/ruby_lsp/requests/document_symbol.rb +2 -1
- data/lib/ruby_lsp/requests/hover.rb +26 -6
- data/lib/ruby_lsp/requests/rename.rb +1 -1
- data/lib/ruby_lsp/requests/request.rb +1 -1
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +12 -1
- data/lib/ruby_lsp/scripts/compose_bundle.rb +20 -0
- data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +8 -0
- data/lib/ruby_lsp/server.rb +85 -55
- data/lib/ruby_lsp/setup_bundler.rb +154 -47
- data/lib/ruby_lsp/store.rb +0 -4
- data/lib/ruby_lsp/utils.rb +63 -0
- metadata +8 -3
@@ -646,7 +646,7 @@ module RubyIndexer
|
|
646
646
|
(positionals.empty? && forwarding_arguments.any?) ||
|
647
647
|
(
|
648
648
|
# Check if positional arguments match. This includes required, optional, rest arguments. We also need to
|
649
|
-
# verify if there's a trailing
|
649
|
+
# verify if there's a trailing forwarding argument, like `def foo(a, ...); end`
|
650
650
|
positional_arguments_match?(positionals, forwarding_arguments, keyword_args, min_pos, max_pos) &&
|
651
651
|
# If the positional arguments match, we move on to checking keyword, optional keyword and keyword rest
|
652
652
|
# arguments. If there's a forward argument, then it will always match. If the method accepts a keyword rest
|
@@ -7,6 +7,7 @@ module RubyIndexer
|
|
7
7
|
|
8
8
|
class UnresolvableAliasError < StandardError; end
|
9
9
|
class NonExistingNamespaceError < StandardError; end
|
10
|
+
class IndexNotEmptyError < StandardError; end
|
10
11
|
|
11
12
|
# The minimum Jaro-Winkler similarity score for an entry to be considered a match for a given fuzzy search query
|
12
13
|
ENTRY_SIMILARITY_THRESHOLD = 0.7
|
@@ -39,9 +40,6 @@ module RubyIndexer
|
|
39
40
|
# Holds the linearized ancestors list for every namespace
|
40
41
|
@ancestors = T.let({}, T::Hash[String, T::Array[String]])
|
41
42
|
|
42
|
-
# List of classes that are enhancing the index
|
43
|
-
@enhancements = T.let([], T::Array[Enhancement])
|
44
|
-
|
45
43
|
# Map of module name to included hooks that have to be executed when we include the given module
|
46
44
|
@included_hooks = T.let(
|
47
45
|
{},
|
@@ -51,12 +49,6 @@ module RubyIndexer
|
|
51
49
|
@configuration = T.let(RubyIndexer::Configuration.new, Configuration)
|
52
50
|
end
|
53
51
|
|
54
|
-
# Register an enhancement to the index. Enhancements must conform to the `Enhancement` interface
|
55
|
-
sig { params(enhancement: Enhancement).void }
|
56
|
-
def register_enhancement(enhancement)
|
57
|
-
@enhancements << enhancement
|
58
|
-
end
|
59
|
-
|
60
52
|
# Register an included `hook` that will be executed when `module_name` is included into any namespace
|
61
53
|
sig { params(module_name: String, hook: T.proc.params(index: Index, base: Entry::Namespace).void).void }
|
62
54
|
def register_included_hook(module_name, &hook)
|
@@ -360,6 +352,15 @@ module RubyIndexer
|
|
360
352
|
).void
|
361
353
|
end
|
362
354
|
def index_all(indexable_paths: @configuration.indexables, &block)
|
355
|
+
# When troubleshooting an indexing issue, e.g. through irb, it's not obvious that `index_all` will augment the
|
356
|
+
# existing index values, meaning it may contain 'stale' entries. This check ensures that the user is aware of this
|
357
|
+
# behavior and can take appropriate action.
|
358
|
+
# binding.break
|
359
|
+
if @entries.any?
|
360
|
+
raise IndexNotEmptyError,
|
361
|
+
"The index is not empty. To prevent invalid entries, `index_all` can only be called once."
|
362
|
+
end
|
363
|
+
|
363
364
|
RBSIndexer.new(self).index_ruby_core
|
364
365
|
# Calculate how many paths are worth 1% of progress
|
365
366
|
progress_step = (indexable_paths.length / 100.0).ceil
|
@@ -386,7 +387,6 @@ module RubyIndexer
|
|
386
387
|
result,
|
387
388
|
indexable_path.full_path,
|
388
389
|
collect_comments: collect_comments,
|
389
|
-
enhancements: @enhancements,
|
390
390
|
)
|
391
391
|
dispatcher.dispatch(result.value)
|
392
392
|
|
@@ -784,7 +784,7 @@ module RubyIndexer
|
|
784
784
|
singleton_levels
|
785
785
|
)
|
786
786
|
# Find the first class entry that has a parent class. Notice that if the developer makes a mistake and inherits
|
787
|
-
# from two
|
787
|
+
# from two different classes in different files, we simply ignore it
|
788
788
|
superclass = T.cast(
|
789
789
|
if singleton_levels > 0
|
790
790
|
self[attached_class_name]&.find { |n| n.is_a?(Entry::Class) && n.parent_class }
|
@@ -974,10 +974,10 @@ module RubyIndexer
|
|
974
974
|
[]
|
975
975
|
end
|
976
976
|
|
977
|
-
# Removes
|
978
|
-
# of the ["A", "B"] nesting, then we should not concatenate the nesting with the name or else we'll end up
|
979
|
-
# `A::B::A::B::Foo`. This method will remove any redundant parts from the final name based on the reference and
|
980
|
-
# nesting
|
977
|
+
# Removes redundancy from a constant reference's full name. For example, if we find a reference to `A::B::Foo`
|
978
|
+
# inside of the ["A", "B"] nesting, then we should not concatenate the nesting with the name or else we'll end up
|
979
|
+
# with `A::B::A::B::Foo`. This method will remove any redundant parts from the final name based on the reference and
|
980
|
+
# the nesting
|
981
981
|
sig { params(name: String, nesting: T::Array[String]).returns(String) }
|
982
982
|
def build_non_redundant_full_name(name, nesting)
|
983
983
|
# If there's no nesting, then we can just return the name as is
|
@@ -72,7 +72,7 @@ module RubyIndexer
|
|
72
72
|
assert_entry("self::Bar", Entry::Class, "/fake/path/foo.rb:0-0:1-3")
|
73
73
|
end
|
74
74
|
|
75
|
-
def
|
75
|
+
def test_dynamically_namespaced_class_does_not_affect_other_classes
|
76
76
|
index(<<~RUBY)
|
77
77
|
class Foo
|
78
78
|
class self::Bar
|
@@ -143,7 +143,7 @@ module RubyIndexer
|
|
143
143
|
assert_entry("self::Bar", Entry::Module, "/fake/path/foo.rb:0-0:1-3")
|
144
144
|
end
|
145
145
|
|
146
|
-
def
|
146
|
+
def test_dynamically_namespaced_module_does_not_affect_other_modules
|
147
147
|
index(<<~RUBY)
|
148
148
|
module Foo
|
149
149
|
class self::Bar
|
@@ -302,10 +302,10 @@ module RubyIndexer
|
|
302
302
|
RUBY
|
303
303
|
|
304
304
|
b_const = @index["A::B"].first
|
305
|
-
|
305
|
+
assert_predicate(b_const, :private?)
|
306
306
|
|
307
307
|
c_const = @index["A::C"].first
|
308
|
-
|
308
|
+
assert_predicate(c_const, :private?)
|
309
309
|
|
310
310
|
d_const = @index["A::D"].first
|
311
311
|
assert_equal(Entry::Visibility::PUBLIC, d_const.visibility)
|
@@ -160,5 +160,15 @@ module RubyIndexer
|
|
160
160
|
)
|
161
161
|
end
|
162
162
|
end
|
163
|
+
|
164
|
+
def test_includes_top_level_files
|
165
|
+
Dir.mktmpdir do |dir|
|
166
|
+
FileUtils.touch(File.join(dir, "find_me.rb"))
|
167
|
+
@config.workspace_path = dir
|
168
|
+
|
169
|
+
indexables = @config.indexables
|
170
|
+
assert(indexables.find { |i| File.basename(i.full_path) == "find_me.rb" })
|
171
|
+
end
|
172
|
+
end
|
163
173
|
end
|
164
174
|
end
|
@@ -130,13 +130,13 @@ module RubyIndexer
|
|
130
130
|
RUBY
|
131
131
|
|
132
132
|
b_const = @index["A::B"].first
|
133
|
-
|
133
|
+
assert_predicate(b_const, :private?)
|
134
134
|
|
135
135
|
c_const = @index["A::C"].first
|
136
|
-
|
136
|
+
assert_predicate(c_const, :private?)
|
137
137
|
|
138
138
|
d_const = @index["A::D"].first
|
139
|
-
|
139
|
+
assert_predicate(d_const, :public?)
|
140
140
|
end
|
141
141
|
|
142
142
|
def test_marking_constants_as_private_reopening_namespaces
|
@@ -163,13 +163,13 @@ module RubyIndexer
|
|
163
163
|
RUBY
|
164
164
|
|
165
165
|
a_const = @index["A::B::CONST_A"].first
|
166
|
-
|
166
|
+
assert_predicate(a_const, :private?)
|
167
167
|
|
168
168
|
b_const = @index["A::B::CONST_B"].first
|
169
|
-
|
169
|
+
assert_predicate(b_const, :private?)
|
170
170
|
|
171
171
|
c_const = @index["A::B::CONST_C"].first
|
172
|
-
|
172
|
+
assert_predicate(c_const, :private?)
|
173
173
|
end
|
174
174
|
|
175
175
|
def test_marking_constants_as_private_with_receiver
|
@@ -187,10 +187,10 @@ module RubyIndexer
|
|
187
187
|
RUBY
|
188
188
|
|
189
189
|
a_const = @index["A::B::CONST_A"].first
|
190
|
-
|
190
|
+
assert_predicate(a_const, :private?)
|
191
191
|
|
192
192
|
b_const = @index["A::B::CONST_B"].first
|
193
|
-
|
193
|
+
assert_predicate(b_const, :private?)
|
194
194
|
end
|
195
195
|
|
196
196
|
def test_indexing_constant_aliases
|
@@ -5,26 +5,28 @@ require_relative "test_case"
|
|
5
5
|
|
6
6
|
module RubyIndexer
|
7
7
|
class EnhancementTest < TestCase
|
8
|
-
def
|
9
|
-
|
10
|
-
|
8
|
+
def teardown
|
9
|
+
super
|
10
|
+
Enhancement.clear
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
+
def test_enhancing_indexing_included_hook
|
14
|
+
Class.new(Enhancement) do
|
15
|
+
def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
16
|
+
owner = @listener.current_owner
|
13
17
|
return unless owner
|
14
|
-
return unless
|
18
|
+
return unless call_node.name == :extend
|
15
19
|
|
16
|
-
arguments =
|
20
|
+
arguments = call_node.arguments&.arguments
|
17
21
|
return unless arguments
|
18
22
|
|
19
|
-
location = Location.from_prism_location(node.location, code_units_cache)
|
20
|
-
|
21
23
|
arguments.each do |node|
|
22
24
|
next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
23
25
|
|
24
26
|
module_name = node.full_name
|
25
27
|
next unless module_name == "ActiveSupport::Concern"
|
26
28
|
|
27
|
-
|
29
|
+
@listener.register_included_hook do |index, base|
|
28
30
|
class_methods_name = "#{owner.name}::ClassMethods"
|
29
31
|
|
30
32
|
if index.indexed?(class_methods_name)
|
@@ -33,16 +35,11 @@ module RubyIndexer
|
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
36
|
-
|
38
|
+
@listener.add_method(
|
37
39
|
"new_method",
|
38
|
-
|
39
|
-
location,
|
40
|
-
location,
|
41
|
-
nil,
|
40
|
+
call_node.location,
|
42
41
|
[Entry::Signature.new([Entry::RequiredParameter.new(name: :a)])],
|
43
|
-
|
44
|
-
owner,
|
45
|
-
))
|
42
|
+
)
|
46
43
|
rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
|
47
44
|
Prism::ConstantPathNode::MissingNodesInConstantPathError
|
48
45
|
# Do nothing
|
@@ -50,7 +47,6 @@ module RubyIndexer
|
|
50
47
|
end
|
51
48
|
end
|
52
49
|
|
53
|
-
@index.register_enhancement(enhancement_class.new)
|
54
50
|
index(<<~RUBY)
|
55
51
|
module ActiveSupport
|
56
52
|
module Concern
|
@@ -98,11 +94,9 @@ module RubyIndexer
|
|
98
94
|
end
|
99
95
|
|
100
96
|
def test_enhancing_indexing_configuration_dsl
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
def on_call_node(index, owner, node, file_path, code_units_cache)
|
105
|
-
return unless owner
|
97
|
+
Class.new(Enhancement) do
|
98
|
+
def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
99
|
+
return unless @listener.current_owner
|
106
100
|
|
107
101
|
name = node.name
|
108
102
|
return unless name == :has_many
|
@@ -113,22 +107,14 @@ module RubyIndexer
|
|
113
107
|
association_name = arguments.first
|
114
108
|
return unless association_name.is_a?(Prism::SymbolNode)
|
115
109
|
|
116
|
-
|
117
|
-
|
118
|
-
index.add(Entry::Method.new(
|
110
|
+
@listener.add_method(
|
119
111
|
T.must(association_name.value),
|
120
|
-
|
121
|
-
location,
|
122
|
-
location,
|
123
|
-
nil,
|
112
|
+
association_name.location,
|
124
113
|
[],
|
125
|
-
|
126
|
-
owner,
|
127
|
-
))
|
114
|
+
)
|
128
115
|
end
|
129
116
|
end
|
130
117
|
|
131
|
-
@index.register_enhancement(enhancement_class.new)
|
132
118
|
index(<<~RUBY)
|
133
119
|
module ActiveSupport
|
134
120
|
module Concern
|
@@ -160,11 +146,9 @@ module RubyIndexer
|
|
160
146
|
assert_entry("posts", Entry::Method, "/fake/path/foo.rb:23-11:23-17")
|
161
147
|
end
|
162
148
|
|
163
|
-
def
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
def on_call_node(index, owner, node, file_path, code_units_cache)
|
149
|
+
def test_error_handling_in_on_call_node_enter_enhancement
|
150
|
+
Class.new(Enhancement) do
|
151
|
+
def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
168
152
|
raise "Error"
|
169
153
|
end
|
170
154
|
|
@@ -175,7 +159,38 @@ module RubyIndexer
|
|
175
159
|
end
|
176
160
|
end
|
177
161
|
|
178
|
-
|
162
|
+
_stdout, stderr = capture_io do
|
163
|
+
index(<<~RUBY)
|
164
|
+
module ActiveSupport
|
165
|
+
module Concern
|
166
|
+
def self.extended(base)
|
167
|
+
base.class_eval("def new_method(a); end")
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
RUBY
|
172
|
+
end
|
173
|
+
|
174
|
+
assert_match(
|
175
|
+
%r{Indexing error in /fake/path/foo\.rb with 'TestEnhancement' on call node enter enhancement},
|
176
|
+
stderr,
|
177
|
+
)
|
178
|
+
# The module should still be indexed
|
179
|
+
assert_entry("ActiveSupport::Concern", Entry::Module, "/fake/path/foo.rb:1-2:5-5")
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_error_handling_in_on_call_node_leave_enhancement
|
183
|
+
Class.new(Enhancement) do
|
184
|
+
def on_call_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
185
|
+
raise "Error"
|
186
|
+
end
|
187
|
+
|
188
|
+
class << self
|
189
|
+
def name
|
190
|
+
"TestEnhancement"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
179
194
|
|
180
195
|
_stdout, stderr = capture_io do
|
181
196
|
index(<<~RUBY)
|
@@ -189,9 +204,122 @@ module RubyIndexer
|
|
189
204
|
RUBY
|
190
205
|
end
|
191
206
|
|
192
|
-
assert_match(
|
207
|
+
assert_match(
|
208
|
+
%r{Indexing error in /fake/path/foo\.rb with 'TestEnhancement' on call node leave enhancement},
|
209
|
+
stderr,
|
210
|
+
)
|
193
211
|
# The module should still be indexed
|
194
212
|
assert_entry("ActiveSupport::Concern", Entry::Module, "/fake/path/foo.rb:1-2:5-5")
|
195
213
|
end
|
214
|
+
|
215
|
+
def test_advancing_namespace_stack_from_enhancement
|
216
|
+
Class.new(Enhancement) do
|
217
|
+
def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
218
|
+
owner = @listener.current_owner
|
219
|
+
return unless owner
|
220
|
+
|
221
|
+
case call_node.name
|
222
|
+
when :class_methods
|
223
|
+
@listener.add_module("ClassMethods", call_node.location, call_node.location)
|
224
|
+
when :extend
|
225
|
+
arguments = call_node.arguments&.arguments
|
226
|
+
return unless arguments
|
227
|
+
|
228
|
+
arguments.each do |node|
|
229
|
+
next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode)
|
230
|
+
|
231
|
+
module_name = node.full_name
|
232
|
+
next unless module_name == "ActiveSupport::Concern"
|
233
|
+
|
234
|
+
@listener.register_included_hook do |index, base|
|
235
|
+
class_methods_name = "#{owner.name}::ClassMethods"
|
236
|
+
|
237
|
+
if index.indexed?(class_methods_name)
|
238
|
+
singleton = index.existing_or_new_singleton_class(base.name)
|
239
|
+
singleton.mixin_operations << Entry::Include.new(class_methods_name)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def on_call_node_leave(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
247
|
+
return unless call_node.name == :class_methods
|
248
|
+
|
249
|
+
@listener.pop_namespace_stack
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
index(<<~RUBY)
|
254
|
+
module ActiveSupport
|
255
|
+
module Concern
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
module MyConcern
|
260
|
+
extend ActiveSupport::Concern
|
261
|
+
|
262
|
+
class_methods do
|
263
|
+
def foo; end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
class User
|
268
|
+
include MyConcern
|
269
|
+
end
|
270
|
+
RUBY
|
271
|
+
|
272
|
+
assert_equal(
|
273
|
+
[
|
274
|
+
"User::<Class:User>",
|
275
|
+
"MyConcern::ClassMethods",
|
276
|
+
"Object::<Class:Object>",
|
277
|
+
"BasicObject::<Class:BasicObject>",
|
278
|
+
"Class",
|
279
|
+
"Module",
|
280
|
+
"Object",
|
281
|
+
"Kernel",
|
282
|
+
"BasicObject",
|
283
|
+
],
|
284
|
+
@index.linearized_ancestors_of("User::<Class:User>"),
|
285
|
+
)
|
286
|
+
|
287
|
+
refute_nil(@index.resolve_method("foo", "User::<Class:User>"))
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_creating_anonymous_classes_from_enhancement
|
291
|
+
Class.new(Enhancement) do
|
292
|
+
def on_call_node_enter(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
293
|
+
case call_node.name
|
294
|
+
when :context
|
295
|
+
arguments = call_node.arguments&.arguments
|
296
|
+
first_argument = arguments&.first
|
297
|
+
return unless first_argument.is_a?(Prism::StringNode)
|
298
|
+
|
299
|
+
@listener.add_class(
|
300
|
+
"<RSpec:#{first_argument.content}>",
|
301
|
+
call_node.location,
|
302
|
+
first_argument.location,
|
303
|
+
)
|
304
|
+
when :subject
|
305
|
+
@listener.add_method("subject", call_node.location, [])
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def on_call_node_leave(call_node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
310
|
+
return unless call_node.name == :context
|
311
|
+
|
312
|
+
@listener.pop_namespace_stack
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
index(<<~RUBY)
|
317
|
+
context "does something" do
|
318
|
+
subject { call_whatever }
|
319
|
+
end
|
320
|
+
RUBY
|
321
|
+
|
322
|
+
refute_nil(@index.resolve_method("subject", "<RSpec:does something>"))
|
323
|
+
end
|
196
324
|
end
|
197
325
|
end
|
@@ -904,7 +904,7 @@ module RubyIndexer
|
|
904
904
|
assert_equal(14, entry.location.start_line)
|
905
905
|
end
|
906
906
|
|
907
|
-
def
|
907
|
+
def test_resolving_inherited_aliased_namespace
|
908
908
|
index(<<~RUBY)
|
909
909
|
module Bar
|
910
910
|
TARGET = 123
|
@@ -1490,7 +1490,7 @@ module RubyIndexer
|
|
1490
1490
|
assert_kind_of(Entry::UnresolvedMethodAlias, entry)
|
1491
1491
|
end
|
1492
1492
|
|
1493
|
-
def
|
1493
|
+
def test_unresolvable_method_aliases
|
1494
1494
|
index(<<~RUBY)
|
1495
1495
|
class Foo
|
1496
1496
|
alias bar baz
|
@@ -1672,6 +1672,38 @@ module RubyIndexer
|
|
1672
1672
|
)
|
1673
1673
|
end
|
1674
1674
|
|
1675
|
+
def test_extend_self
|
1676
|
+
@index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
|
1677
|
+
module Foo
|
1678
|
+
def bar
|
1679
|
+
end
|
1680
|
+
|
1681
|
+
extend self
|
1682
|
+
|
1683
|
+
def baz
|
1684
|
+
end
|
1685
|
+
end
|
1686
|
+
RUBY
|
1687
|
+
|
1688
|
+
["bar", "baz"].product(["Foo", "Foo::<Class:Foo>"]).each do |method, receiver|
|
1689
|
+
entry = @index.resolve_method(method, receiver)&.first
|
1690
|
+
refute_nil(entry)
|
1691
|
+
assert_equal(method, T.must(entry).name)
|
1692
|
+
end
|
1693
|
+
|
1694
|
+
assert_equal(
|
1695
|
+
[
|
1696
|
+
"Foo::<Class:Foo>",
|
1697
|
+
"Foo",
|
1698
|
+
"Module",
|
1699
|
+
"Object",
|
1700
|
+
"Kernel",
|
1701
|
+
"BasicObject",
|
1702
|
+
],
|
1703
|
+
@index.linearized_ancestors_of("Foo::<Class:Foo>"),
|
1704
|
+
)
|
1705
|
+
end
|
1706
|
+
|
1675
1707
|
def test_linearizing_singleton_ancestors
|
1676
1708
|
@index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY)
|
1677
1709
|
module First
|
@@ -2023,5 +2055,12 @@ module RubyIndexer
|
|
2023
2055
|
),
|
2024
2056
|
)
|
2025
2057
|
end
|
2058
|
+
|
2059
|
+
def test_prevents_multiple_calls_to_index_all
|
2060
|
+
# For this test class, `index_all` is already called once in `setup`.
|
2061
|
+
assert_raises(Index::IndexNotEmptyError) do
|
2062
|
+
@index.index_all
|
2063
|
+
end
|
2064
|
+
end
|
2026
2065
|
end
|
2027
2066
|
end
|
@@ -209,7 +209,7 @@ module RubyIndexer
|
|
209
209
|
end
|
210
210
|
RUBY
|
211
211
|
|
212
|
-
# If the surrounding method is
|
212
|
+
# If the surrounding method is being defined on any dynamic value that isn't `self`, then we attribute the
|
213
213
|
# instance variable to the wrong owner since there's no way to understand that statically
|
214
214
|
entry = T.must(@index["@a"]&.first)
|
215
215
|
owner = T.must(entry.owner)
|
@@ -123,6 +123,145 @@ module RubyIndexer
|
|
123
123
|
assert_entry("baz", Entry::Method, "/fake/path/foo.rb:9-2:9-14", visibility: Entry::Visibility::PRIVATE)
|
124
124
|
end
|
125
125
|
|
126
|
+
def test_visibility_tracking_with_module_function
|
127
|
+
index(<<~RUBY)
|
128
|
+
module Test
|
129
|
+
def foo; end
|
130
|
+
def bar; end
|
131
|
+
module_function :foo, "bar"
|
132
|
+
end
|
133
|
+
RUBY
|
134
|
+
|
135
|
+
["foo", "bar"].each do |keyword|
|
136
|
+
entries = T.must(@index[keyword])
|
137
|
+
# should receive two entries because module_function creates a singleton method
|
138
|
+
# for the Test module and a private method for classes include the Test module
|
139
|
+
assert_equal(entries.size, 2)
|
140
|
+
first_entry, second_entry = *entries
|
141
|
+
# The first entry points to the location of the module_function call
|
142
|
+
assert_equal("Test", first_entry.owner.name)
|
143
|
+
assert_instance_of(Entry::Module, first_entry.owner)
|
144
|
+
assert_predicate(first_entry, :private?)
|
145
|
+
# The second entry points to the public singleton method
|
146
|
+
assert_equal("Test::<Class:Test>", second_entry.owner.name)
|
147
|
+
assert_instance_of(Entry::SingletonClass, second_entry.owner)
|
148
|
+
assert_equal(Entry::Visibility::PUBLIC, second_entry.visibility)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_private_class_method_visibility_tracking_string_symbol_arguments
|
153
|
+
index(<<~RUBY)
|
154
|
+
class Test
|
155
|
+
def self.foo
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.bar
|
159
|
+
end
|
160
|
+
|
161
|
+
private_class_method("foo", :bar)
|
162
|
+
|
163
|
+
def self.baz
|
164
|
+
end
|
165
|
+
end
|
166
|
+
RUBY
|
167
|
+
|
168
|
+
["foo", "bar"].each do |keyword|
|
169
|
+
entries = T.must(@index[keyword])
|
170
|
+
assert_equal(1, entries.size)
|
171
|
+
entry = entries.first
|
172
|
+
assert_predicate(entry, :private?)
|
173
|
+
end
|
174
|
+
|
175
|
+
entries = T.must(@index["baz"])
|
176
|
+
assert_equal(1, entries.size)
|
177
|
+
entry = entries.first
|
178
|
+
assert_predicate(entry, :public?)
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_private_class_method_visibility_tracking_array_argument
|
182
|
+
index(<<~RUBY)
|
183
|
+
class Test
|
184
|
+
def self.foo
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.bar
|
188
|
+
end
|
189
|
+
|
190
|
+
private_class_method(["foo", :bar])
|
191
|
+
|
192
|
+
def self.baz
|
193
|
+
end
|
194
|
+
end
|
195
|
+
RUBY
|
196
|
+
|
197
|
+
["foo", "bar"].each do |keyword|
|
198
|
+
entries = T.must(@index[keyword])
|
199
|
+
assert_equal(1, entries.size)
|
200
|
+
entry = entries.first
|
201
|
+
assert_predicate(entry, :private?)
|
202
|
+
end
|
203
|
+
|
204
|
+
entries = T.must(@index["baz"])
|
205
|
+
assert_equal(1, entries.size)
|
206
|
+
entry = entries.first
|
207
|
+
assert_predicate(entry, :public?)
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_private_class_method_visibility_tracking_method_argument
|
211
|
+
index(<<~RUBY)
|
212
|
+
class Test
|
213
|
+
private_class_method def self.foo
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.bar
|
217
|
+
end
|
218
|
+
end
|
219
|
+
RUBY
|
220
|
+
|
221
|
+
entries = T.must(@index["foo"])
|
222
|
+
assert_equal(1, entries.size)
|
223
|
+
entry = entries.first
|
224
|
+
assert_predicate(entry, :private?)
|
225
|
+
|
226
|
+
entries = T.must(@index["bar"])
|
227
|
+
assert_equal(1, entries.size)
|
228
|
+
entry = entries.first
|
229
|
+
assert_predicate(entry, :public?)
|
230
|
+
end
|
231
|
+
|
232
|
+
def test_comments_documentation
|
233
|
+
index(<<~RUBY)
|
234
|
+
# Documentation for Foo
|
235
|
+
|
236
|
+
class Foo
|
237
|
+
# ####################
|
238
|
+
# Documentation for bar
|
239
|
+
# ####################
|
240
|
+
#
|
241
|
+
def bar
|
242
|
+
end
|
243
|
+
|
244
|
+
# test
|
245
|
+
|
246
|
+
# Documentation for baz
|
247
|
+
def baz; end
|
248
|
+
def ban; end
|
249
|
+
end
|
250
|
+
RUBY
|
251
|
+
|
252
|
+
foo_comment = @index["Foo"].first.comments
|
253
|
+
assert_equal("Documentation for Foo", foo_comment)
|
254
|
+
|
255
|
+
bar_comment = @index["bar"].first.comments
|
256
|
+
assert_equal("####################\nDocumentation for bar\n####################\n", bar_comment)
|
257
|
+
|
258
|
+
baz_comment = @index["baz"].first.comments
|
259
|
+
assert_equal("Documentation for baz", baz_comment)
|
260
|
+
|
261
|
+
ban_comment = @index["ban"].first.comments
|
262
|
+
assert_empty(ban_comment)
|
263
|
+
end
|
264
|
+
|
126
265
|
def test_method_with_parameters
|
127
266
|
index(<<~RUBY)
|
128
267
|
class Foo
|