ruby-lsp 0.23.11 → 0.23.17
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-launcher +20 -11
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -1
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -5
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +82 -116
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +134 -183
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +9 -10
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +97 -217
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +139 -281
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +18 -19
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +23 -55
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +47 -61
- data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +17 -19
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +7 -11
- data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
- data/lib/ruby_indexer/test/classes_and_modules_test.rb +65 -40
- data/lib/ruby_indexer/test/configuration_test.rb +48 -7
- data/lib/ruby_indexer/test/constant_test.rb +34 -34
- data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
- data/lib/ruby_indexer/test/index_test.rb +146 -135
- data/lib/ruby_indexer/test/instance_variables_test.rb +37 -37
- data/lib/ruby_indexer/test/method_test.rb +149 -123
- data/lib/ruby_indexer/test/prefix_tree_test.rb +13 -13
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +68 -73
- data/lib/ruby_indexer/test/test_case.rb +9 -3
- data/lib/ruby_indexer/test/uri_test.rb +15 -2
- data/lib/ruby_lsp/addon.rb +44 -71
- data/lib/ruby_lsp/base_server.rb +29 -32
- data/lib/ruby_lsp/client_capabilities.rb +10 -12
- data/lib/ruby_lsp/document.rb +40 -54
- data/lib/ruby_lsp/erb_document.rb +37 -41
- data/lib/ruby_lsp/global_state.rb +52 -57
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listeners/code_lens.rb +82 -89
- data/lib/ruby_lsp/listeners/completion.rb +67 -73
- data/lib/ruby_lsp/listeners/definition.rb +44 -58
- data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
- data/lib/ruby_lsp/listeners/document_link.rb +50 -70
- data/lib/ruby_lsp/listeners/document_symbol.rb +38 -52
- data/lib/ruby_lsp/listeners/folding_ranges.rb +40 -43
- data/lib/ruby_lsp/listeners/hover.rb +92 -110
- data/lib/ruby_lsp/listeners/inlay_hints.rb +4 -11
- data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -56
- data/lib/ruby_lsp/listeners/signature_help.rb +12 -27
- data/lib/ruby_lsp/listeners/spec_style.rb +155 -0
- data/lib/ruby_lsp/listeners/test_discovery.rb +89 -0
- data/lib/ruby_lsp/listeners/test_style.rb +167 -90
- data/lib/ruby_lsp/node_context.rb +12 -39
- data/lib/ruby_lsp/rbs_document.rb +9 -7
- data/lib/ruby_lsp/requests/code_action_resolve.rb +63 -59
- data/lib/ruby_lsp/requests/code_actions.rb +14 -26
- data/lib/ruby_lsp/requests/code_lens.rb +20 -19
- data/lib/ruby_lsp/requests/completion.rb +7 -20
- data/lib/ruby_lsp/requests/completion_resolve.rb +6 -6
- data/lib/ruby_lsp/requests/definition.rb +7 -17
- data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
- data/lib/ruby_lsp/requests/discover_tests.rb +18 -5
- data/lib/ruby_lsp/requests/document_highlight.rb +5 -15
- data/lib/ruby_lsp/requests/document_link.rb +6 -17
- data/lib/ruby_lsp/requests/document_symbol.rb +5 -8
- data/lib/ruby_lsp/requests/folding_ranges.rb +7 -15
- data/lib/ruby_lsp/requests/formatting.rb +6 -9
- data/lib/ruby_lsp/requests/go_to_relevant_file.rb +85 -0
- data/lib/ruby_lsp/requests/hover.rb +10 -20
- data/lib/ruby_lsp/requests/inlay_hints.rb +6 -17
- data/lib/ruby_lsp/requests/on_type_formatting.rb +32 -40
- data/lib/ruby_lsp/requests/prepare_rename.rb +4 -9
- data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +5 -15
- data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
- data/lib/ruby_lsp/requests/references.rb +9 -53
- data/lib/ruby_lsp/requests/rename.rb +20 -46
- data/lib/ruby_lsp/requests/request.rb +8 -19
- data/lib/ruby_lsp/requests/selection_ranges.rb +6 -6
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +16 -35
- data/lib/ruby_lsp/requests/show_syntax_tree.rb +7 -8
- data/lib/ruby_lsp/requests/signature_help.rb +8 -26
- data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
- data/lib/ruby_lsp/requests/support/common.rb +15 -55
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +11 -14
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +22 -34
- data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
- data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
- data/lib/ruby_lsp/requests/support/source_uri.rb +20 -32
- data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
- data/lib/ruby_lsp/requests/support/test_item.rb +16 -14
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
- data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
- data/lib/ruby_lsp/response_builders/collection_response_builder.rb +5 -5
- data/lib/ruby_lsp/response_builders/document_symbol.rb +14 -19
- data/lib/ruby_lsp/response_builders/hover.rb +11 -14
- data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +60 -88
- data/lib/ruby_lsp/response_builders/signature_help.rb +5 -6
- data/lib/ruby_lsp/response_builders/test_collection.rb +43 -10
- data/lib/ruby_lsp/ruby_document.rb +24 -92
- data/lib/ruby_lsp/scope.rb +7 -11
- data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
- data/lib/ruby_lsp/server.rb +182 -99
- data/lib/ruby_lsp/setup_bundler.rb +65 -60
- data/lib/ruby_lsp/static_docs.rb +11 -7
- data/lib/ruby_lsp/store.rb +29 -47
- data/lib/ruby_lsp/test_helper.rb +2 -12
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +191 -0
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +105 -0
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +94 -0
- data/lib/ruby_lsp/type_inferrer.rb +13 -14
- data/lib/ruby_lsp/utils.rb +92 -83
- metadata +9 -3
@@ -3,8 +3,6 @@
|
|
3
3
|
|
4
4
|
module RubyIndexer
|
5
5
|
class Index
|
6
|
-
extend T::Sig
|
7
|
-
|
8
6
|
class UnresolvableAliasError < StandardError; end
|
9
7
|
class NonExistingNamespaceError < StandardError; end
|
10
8
|
class IndexNotEmptyError < StandardError; end
|
@@ -12,16 +10,17 @@ module RubyIndexer
|
|
12
10
|
# The minimum Jaro-Winkler similarity score for an entry to be considered a match for a given fuzzy search query
|
13
11
|
ENTRY_SIMILARITY_THRESHOLD = 0.7
|
14
12
|
|
15
|
-
|
13
|
+
#: Configuration
|
16
14
|
attr_reader :configuration
|
17
15
|
|
18
|
-
|
19
|
-
|
16
|
+
#: bool
|
17
|
+
attr_reader :initial_indexing_completed
|
20
18
|
|
19
|
+
class << self
|
21
20
|
# Returns the real nesting of a constant name taking into account top level
|
22
21
|
# references that may be included anywhere in the name or nesting where that
|
23
22
|
# constant was found
|
24
|
-
|
23
|
+
#: (Array[String] stack, String? name) -> Array[String]
|
25
24
|
def actual_nesting(stack, name)
|
26
25
|
nesting = name ? stack + [name] : stack
|
27
26
|
corrected_nesting = []
|
@@ -37,22 +36,10 @@ module RubyIndexer
|
|
37
36
|
|
38
37
|
# Returns the unresolved name for a constant reference including all parts of a constant path, or `nil` if the
|
39
38
|
# constant contains dynamic or incomplete parts
|
40
|
-
|
41
|
-
params(
|
42
|
-
node: T.any(
|
43
|
-
Prism::ConstantPathNode,
|
44
|
-
Prism::ConstantReadNode,
|
45
|
-
Prism::ConstantPathTargetNode,
|
46
|
-
Prism::CallNode,
|
47
|
-
Prism::MissingNode,
|
48
|
-
),
|
49
|
-
).returns(T.nilable(String))
|
50
|
-
end
|
39
|
+
#: (Prism::Node) -> String?
|
51
40
|
def constant_name(node)
|
52
41
|
case node
|
53
|
-
when Prism::
|
54
|
-
nil
|
55
|
-
else
|
42
|
+
when Prism::ConstantPathNode, Prism::ConstantReadNode, Prism::ConstantPathTargetNode
|
56
43
|
node.full_name
|
57
44
|
end
|
58
45
|
rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError,
|
@@ -61,17 +48,17 @@ module RubyIndexer
|
|
61
48
|
end
|
62
49
|
end
|
63
50
|
|
64
|
-
|
51
|
+
#: -> void
|
65
52
|
def initialize
|
66
53
|
# Holds all entries in the index using the following format:
|
67
54
|
# {
|
68
55
|
# "Foo" => [#<Entry::Class>, #<Entry::Class>],
|
69
56
|
# "Foo::Bar" => [#<Entry::Class>],
|
70
57
|
# }
|
71
|
-
@entries =
|
58
|
+
@entries = {} #: Hash[String, Array[Entry]]
|
72
59
|
|
73
60
|
# Holds all entries in the index using a prefix tree for searching based on prefixes to provide autocompletion
|
74
|
-
@entries_tree =
|
61
|
+
@entries_tree = PrefixTree.new #: PrefixTree[Array[Entry]]
|
75
62
|
|
76
63
|
# Holds references to where entries where discovered so that we can easily delete them
|
77
64
|
# {
|
@@ -79,32 +66,29 @@ module RubyIndexer
|
|
79
66
|
# "file:///my/project/bar.rb" => [#<Entry::Class>],
|
80
67
|
# "untitled:Untitled-1" => [#<Entry::Class>],
|
81
68
|
# }
|
82
|
-
@uris_to_entries =
|
69
|
+
@uris_to_entries = {} #: Hash[String, Array[Entry]]
|
83
70
|
|
84
71
|
# Holds all require paths for every indexed item so that we can provide autocomplete for requires
|
85
|
-
@require_paths_tree =
|
72
|
+
@require_paths_tree = PrefixTree.new #: PrefixTree[URI::Generic]
|
86
73
|
|
87
74
|
# Holds the linearized ancestors list for every namespace
|
88
|
-
@ancestors =
|
75
|
+
@ancestors = {} #: Hash[String, Array[String]]
|
89
76
|
|
90
77
|
# Map of module name to included hooks that have to be executed when we include the given module
|
91
|
-
@included_hooks =
|
92
|
-
{},
|
93
|
-
T::Hash[String, T::Array[T.proc.params(index: Index, base: Entry::Namespace).void]],
|
94
|
-
)
|
78
|
+
@included_hooks = {} #: Hash[String, Array[^(Index index, Entry::Namespace base) -> void]]
|
95
79
|
|
96
|
-
@configuration =
|
80
|
+
@configuration = RubyIndexer::Configuration.new #: Configuration
|
97
81
|
|
98
|
-
@initial_indexing_completed =
|
82
|
+
@initial_indexing_completed = false #: bool
|
99
83
|
end
|
100
84
|
|
101
85
|
# Register an included `hook` that will be executed when `module_name` is included into any namespace
|
102
|
-
|
86
|
+
#: (String module_name) { (Index index, Entry::Namespace base) -> void } -> void
|
103
87
|
def register_included_hook(module_name, &hook)
|
104
88
|
(@included_hooks[module_name] ||= []) << hook
|
105
89
|
end
|
106
90
|
|
107
|
-
|
91
|
+
#: (URI::Generic uri, ?skip_require_paths_tree: bool) -> void
|
108
92
|
def delete(uri, skip_require_paths_tree: false)
|
109
93
|
key = uri.to_s
|
110
94
|
# For each constant discovered in `path`, delete the associated entry from the index. If there are no entries
|
@@ -134,37 +118,34 @@ module RubyIndexer
|
|
134
118
|
@require_paths_tree.delete(require_path) if require_path
|
135
119
|
end
|
136
120
|
|
137
|
-
|
121
|
+
#: (Entry entry, ?skip_prefix_tree: bool) -> void
|
138
122
|
def add(entry, skip_prefix_tree: false)
|
139
123
|
name = entry.name
|
140
124
|
|
141
125
|
(@entries[name] ||= []) << entry
|
142
126
|
(@uris_to_entries[entry.uri.to_s] ||= []) << entry
|
143
|
-
|
127
|
+
|
128
|
+
unless skip_prefix_tree
|
129
|
+
@entries_tree.insert(
|
130
|
+
name,
|
131
|
+
@entries[name], #: as !nil
|
132
|
+
)
|
133
|
+
end
|
144
134
|
end
|
145
135
|
|
146
|
-
|
136
|
+
#: (String fully_qualified_name) -> Array[Entry]?
|
147
137
|
def [](fully_qualified_name)
|
148
138
|
@entries[fully_qualified_name.delete_prefix("::")]
|
149
139
|
end
|
150
140
|
|
151
|
-
|
141
|
+
#: (String query) -> Array[URI::Generic]
|
152
142
|
def search_require_paths(query)
|
153
143
|
@require_paths_tree.search(query)
|
154
144
|
end
|
155
145
|
|
156
146
|
# Searches for a constant based on an unqualified name and returns the first possible match regardless of whether
|
157
147
|
# there are more possible matching entries
|
158
|
-
|
159
|
-
params(
|
160
|
-
name: String,
|
161
|
-
).returns(T.nilable(T::Array[T.any(
|
162
|
-
Entry::Namespace,
|
163
|
-
Entry::ConstantAlias,
|
164
|
-
Entry::UnresolvedConstantAlias,
|
165
|
-
Entry::Constant,
|
166
|
-
)]))
|
167
|
-
end
|
148
|
+
#: (String name) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
168
149
|
def first_unqualified_const(name)
|
169
150
|
# Look for an exact match first
|
170
151
|
_name, entries = @entries.find do |const_name, _entries|
|
@@ -178,15 +159,7 @@ module RubyIndexer
|
|
178
159
|
end
|
179
160
|
end
|
180
161
|
|
181
|
-
|
182
|
-
entries,
|
183
|
-
T.nilable(T::Array[T.any(
|
184
|
-
Entry::Namespace,
|
185
|
-
Entry::ConstantAlias,
|
186
|
-
Entry::UnresolvedConstantAlias,
|
187
|
-
Entry::Constant,
|
188
|
-
)]),
|
189
|
-
)
|
162
|
+
entries #: as Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
190
163
|
end
|
191
164
|
|
192
165
|
# Searches entries in the index based on an exact prefix, intended for providing autocomplete. All possible matches
|
@@ -202,7 +175,7 @@ module RubyIndexer
|
|
202
175
|
# [#<Entry::Class name="Foo::Baz">],
|
203
176
|
# ]
|
204
177
|
# ```
|
205
|
-
|
178
|
+
#: (String query, ?Array[String]? nesting) -> Array[Array[Entry]]
|
206
179
|
def prefix_search(query, nesting = nil)
|
207
180
|
unless nesting
|
208
181
|
results = @entries_tree.search(query)
|
@@ -211,7 +184,8 @@ module RubyIndexer
|
|
211
184
|
end
|
212
185
|
|
213
186
|
results = nesting.length.downto(0).flat_map do |i|
|
214
|
-
prefix =
|
187
|
+
prefix = nesting[0...i] #: as !nil
|
188
|
+
.join("::")
|
215
189
|
namespaced_query = prefix.empty? ? query : "#{prefix}::#{query}"
|
216
190
|
@entries_tree.search(namespaced_query)
|
217
191
|
end
|
@@ -221,7 +195,7 @@ module RubyIndexer
|
|
221
195
|
end
|
222
196
|
|
223
197
|
# Fuzzy searches index entries based on Jaro-Winkler similarity. If no query is provided, all entries are returned
|
224
|
-
|
198
|
+
#: (String? query) -> Array[Entry]
|
225
199
|
def fuzzy_search(query)
|
226
200
|
unless query
|
227
201
|
entries = @entries.filter_map do |_name, entries|
|
@@ -245,12 +219,7 @@ module RubyIndexer
|
|
245
219
|
results.flat_map(&:first)
|
246
220
|
end
|
247
221
|
|
248
|
-
|
249
|
-
params(
|
250
|
-
name: T.nilable(String),
|
251
|
-
receiver_name: String,
|
252
|
-
).returns(T::Array[T.any(Entry::Member, Entry::MethodAlias)])
|
253
|
-
end
|
222
|
+
#: (String? name, String receiver_name) -> Array[(Entry::Member | Entry::MethodAlias)]
|
254
223
|
def method_completion_candidates(name, receiver_name)
|
255
224
|
ancestors = linearized_ancestors_of(receiver_name)
|
256
225
|
|
@@ -293,29 +262,11 @@ module RubyIndexer
|
|
293
262
|
completion_items.values.map!(&:first)
|
294
263
|
end
|
295
264
|
|
296
|
-
|
297
|
-
params(
|
298
|
-
name: String,
|
299
|
-
nesting: T::Array[String],
|
300
|
-
).returns(T::Array[T::Array[T.any(
|
301
|
-
Entry::Constant,
|
302
|
-
Entry::ConstantAlias,
|
303
|
-
Entry::Namespace,
|
304
|
-
Entry::UnresolvedConstantAlias,
|
305
|
-
)]])
|
306
|
-
end
|
265
|
+
#: (String name, Array[String] nesting) -> Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
|
307
266
|
def constant_completion_candidates(name, nesting)
|
308
267
|
# If we have a top level reference, then we don't need to include completions inside the current nesting
|
309
268
|
if name.start_with?("::")
|
310
|
-
return
|
311
|
-
@entries_tree.search(name.delete_prefix("::")),
|
312
|
-
T::Array[T::Array[T.any(
|
313
|
-
Entry::Constant,
|
314
|
-
Entry::ConstantAlias,
|
315
|
-
Entry::Namespace,
|
316
|
-
Entry::UnresolvedConstantAlias,
|
317
|
-
)]],
|
318
|
-
)
|
269
|
+
return @entries_tree.search(name.delete_prefix("::")) #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]] # rubocop:disable Layout/LineLength
|
319
270
|
end
|
320
271
|
|
321
272
|
# Otherwise, we have to include every possible constant the user might be referring to. This is essentially the
|
@@ -326,7 +277,8 @@ module RubyIndexer
|
|
326
277
|
|
327
278
|
# Constants defined in enclosing scopes
|
328
279
|
nesting.length.downto(1) do |i|
|
329
|
-
namespace =
|
280
|
+
namespace = nesting[0...i] #: as !nil
|
281
|
+
.join("::")
|
330
282
|
entries.concat(@entries_tree.search("#{namespace}::#{name}"))
|
331
283
|
end
|
332
284
|
|
@@ -340,15 +292,7 @@ module RubyIndexer
|
|
340
292
|
# Top level constants
|
341
293
|
entries.concat(@entries_tree.search(name))
|
342
294
|
entries.uniq!
|
343
|
-
|
344
|
-
entries,
|
345
|
-
T::Array[T::Array[T.any(
|
346
|
-
Entry::Constant,
|
347
|
-
Entry::ConstantAlias,
|
348
|
-
Entry::Namespace,
|
349
|
-
Entry::UnresolvedConstantAlias,
|
350
|
-
)]],
|
351
|
-
)
|
295
|
+
entries #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]] # rubocop:disable Layout/LineLength
|
352
296
|
end
|
353
297
|
|
354
298
|
# Resolve a constant to its declaration based on its name and the nesting where the reference was found. Parameter
|
@@ -358,17 +302,7 @@ module RubyIndexer
|
|
358
302
|
# nesting: the nesting structure where the reference was found (e.g.: ["Foo", "Bar"])
|
359
303
|
# seen_names: this parameter should not be used by consumers of the api. It is used to avoid infinite recursion when
|
360
304
|
# resolving circular references
|
361
|
-
|
362
|
-
params(
|
363
|
-
name: String,
|
364
|
-
nesting: T::Array[String],
|
365
|
-
seen_names: T::Array[String],
|
366
|
-
).returns(T.nilable(T::Array[T.any(
|
367
|
-
Entry::Namespace,
|
368
|
-
Entry::ConstantAlias,
|
369
|
-
Entry::UnresolvedConstantAlias,
|
370
|
-
)]))
|
371
|
-
end
|
305
|
+
#: (String name, Array[String] nesting, ?Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
372
306
|
def resolve(name, nesting, seen_names = [])
|
373
307
|
# If we have a top level reference, then we just search for it straight away ignoring the nesting
|
374
308
|
if name.start_with?("::")
|
@@ -404,12 +338,7 @@ module RubyIndexer
|
|
404
338
|
# Index all files for the given URIs, which defaults to what is configured. A block can be used to track and control
|
405
339
|
# indexing progress. That block is invoked with the current progress percentage and should return `true` to continue
|
406
340
|
# indexing or `false` to stop indexing.
|
407
|
-
|
408
|
-
params(
|
409
|
-
uris: T::Array[URI::Generic],
|
410
|
-
block: T.nilable(T.proc.params(progress: Integer).returns(T::Boolean)),
|
411
|
-
).void
|
412
|
-
end
|
341
|
+
#: (?uris: Array[URI::Generic]) ?{ (Integer progress) -> bool } -> void
|
413
342
|
def index_all(uris: @configuration.indexable_uris, &block)
|
414
343
|
# When troubleshooting an indexing issue, e.g. through irb, it's not obvious that `index_all` will augment the
|
415
344
|
# existing index values, meaning it may contain 'stale' entries. This check ensures that the user is aware of this
|
@@ -419,8 +348,6 @@ module RubyIndexer
|
|
419
348
|
"The index is not empty. To prevent invalid entries, `index_all` can only be called once."
|
420
349
|
end
|
421
350
|
|
422
|
-
@initial_indexing_completed = true
|
423
|
-
|
424
351
|
RBSIndexer.new(self).index_ruby_core
|
425
352
|
# Calculate how many paths are worth 1% of progress
|
426
353
|
progress_step = (uris.length / 100.0).ceil
|
@@ -433,9 +360,11 @@ module RubyIndexer
|
|
433
360
|
|
434
361
|
index_file(uri, collect_comments: false)
|
435
362
|
end
|
363
|
+
|
364
|
+
@initial_indexing_completed = true
|
436
365
|
end
|
437
366
|
|
438
|
-
|
367
|
+
#: (URI::Generic uri, String source, ?collect_comments: bool) -> void
|
439
368
|
def index_single(uri, source, collect_comments: true)
|
440
369
|
dispatcher = Prism::Dispatcher.new
|
441
370
|
|
@@ -457,9 +386,10 @@ module RubyIndexer
|
|
457
386
|
end
|
458
387
|
|
459
388
|
# Indexes a File URI by reading the contents from disk
|
460
|
-
|
389
|
+
#: (URI::Generic uri, ?collect_comments: bool) -> void
|
461
390
|
def index_file(uri, collect_comments: true)
|
462
|
-
|
391
|
+
path = uri.full_path #: as !nil
|
392
|
+
index_single(uri, File.read(path), collect_comments: collect_comments)
|
463
393
|
rescue Errno::EISDIR, Errno::ENOENT
|
464
394
|
# If `path` is a directory, just ignore it and continue indexing. If the file doesn't exist, then we also ignore
|
465
395
|
# it
|
@@ -475,13 +405,14 @@ module RubyIndexer
|
|
475
405
|
# If we find an alias, then we want to follow its target. In the same example, if `Foo::Bar` is an alias to
|
476
406
|
# `Something::Else`, then we first discover `Something::Else::Baz`. But `Something::Else::Baz` might contain other
|
477
407
|
# aliases, so we have to invoke `follow_aliased_namespace` again to check until we only return a real name
|
478
|
-
|
408
|
+
#: (String name, ?Array[String] seen_names) -> String
|
479
409
|
def follow_aliased_namespace(name, seen_names = [])
|
480
410
|
parts = name.split("::")
|
481
411
|
real_parts = []
|
482
412
|
|
483
413
|
(parts.length - 1).downto(0) do |i|
|
484
|
-
current_name =
|
414
|
+
current_name = parts[0..i] #: as !nil
|
415
|
+
.join("::")
|
485
416
|
entry = @entries[current_name]&.first
|
486
417
|
|
487
418
|
case entry
|
@@ -498,7 +429,9 @@ module RubyIndexer
|
|
498
429
|
target = resolved.target
|
499
430
|
return follow_aliased_namespace("#{target}::#{real_parts.join("::")}", seen_names)
|
500
431
|
else
|
501
|
-
real_parts.unshift(
|
432
|
+
real_parts.unshift(
|
433
|
+
parts[i], #: as !nil
|
434
|
+
)
|
502
435
|
end
|
503
436
|
end
|
504
437
|
|
@@ -508,14 +441,7 @@ module RubyIndexer
|
|
508
441
|
# Attempts to find methods for a resolved fully qualified receiver name. Do not provide the `seen_names` parameter
|
509
442
|
# as it is used only internally to prevent infinite loops when resolving circular aliases
|
510
443
|
# Returns `nil` if the method does not exist on that receiver
|
511
|
-
|
512
|
-
params(
|
513
|
-
method_name: String,
|
514
|
-
receiver_name: String,
|
515
|
-
seen_names: T::Array[String],
|
516
|
-
inherited_only: T::Boolean,
|
517
|
-
).returns(T.nilable(T::Array[T.any(Entry::Member, Entry::MethodAlias)]))
|
518
|
-
end
|
444
|
+
#: (String method_name, String receiver_name, ?Array[String] seen_names, ?inherited_only: bool) -> Array[(Entry::Member | Entry::MethodAlias)]?
|
519
445
|
def resolve_method(method_name, receiver_name, seen_names = [], inherited_only: false)
|
520
446
|
method_entries = self[method_name]
|
521
447
|
return unless method_entries
|
@@ -553,7 +479,7 @@ module RubyIndexer
|
|
553
479
|
# module that prepends another module, then the prepend module appears before the included module.
|
554
480
|
#
|
555
481
|
# The order of ancestors is [linearized_prepends, self, linearized_includes, linearized_superclass]
|
556
|
-
|
482
|
+
#: (String fully_qualified_name) -> Array[String]
|
557
483
|
def linearized_ancestors_of(fully_qualified_name)
|
558
484
|
# If we already computed the ancestors for this namespace, return it straight away
|
559
485
|
cached_ancestors = @ancestors[fully_qualified_name]
|
@@ -602,11 +528,12 @@ module RubyIndexer
|
|
602
528
|
|
603
529
|
# The original nesting where we discovered this namespace, so that we resolve the correct names of the
|
604
530
|
# included/prepended/extended modules and parent classes
|
605
|
-
nesting =
|
531
|
+
nesting = namespaces.first #: as !nil
|
532
|
+
.nesting.flat_map { |n| n.split("::") }
|
606
533
|
|
607
534
|
if nesting.any?
|
608
535
|
singleton_levels.times do
|
609
|
-
nesting << "<Class:#{
|
536
|
+
nesting << "<Class:#{nesting.last}>"
|
610
537
|
end
|
611
538
|
end
|
612
539
|
|
@@ -631,9 +558,9 @@ module RubyIndexer
|
|
631
558
|
|
632
559
|
# Resolves an instance variable name for a given owner name. This method will linearize the ancestors of the owner
|
633
560
|
# and find inherited instance variables as well
|
634
|
-
|
561
|
+
#: (String variable_name, String owner_name) -> Array[Entry::InstanceVariable]?
|
635
562
|
def resolve_instance_variable(variable_name, owner_name)
|
636
|
-
entries =
|
563
|
+
entries = self[variable_name] #: as Array[Entry::InstanceVariable]?
|
637
564
|
return unless entries
|
638
565
|
|
639
566
|
ancestors = linearized_ancestors_of(owner_name)
|
@@ -642,7 +569,7 @@ module RubyIndexer
|
|
642
569
|
entries.select { |e| ancestors.include?(e.owner&.name) }
|
643
570
|
end
|
644
571
|
|
645
|
-
|
572
|
+
#: (String variable_name, String owner_name) -> Array[Entry::ClassVariable]?
|
646
573
|
def resolve_class_variable(variable_name, owner_name)
|
647
574
|
entries = self[variable_name]&.grep(Entry::ClassVariable)
|
648
575
|
return unless entries&.any?
|
@@ -655,11 +582,9 @@ module RubyIndexer
|
|
655
582
|
|
656
583
|
# Returns a list of possible candidates for completion of instance variables for a given owner name. The name must
|
657
584
|
# include the `@` prefix
|
658
|
-
|
659
|
-
params(name: String, owner_name: String).returns(T::Array[T.any(Entry::InstanceVariable, Entry::ClassVariable)])
|
660
|
-
end
|
585
|
+
#: (String name, String owner_name) -> Array[(Entry::InstanceVariable | Entry::ClassVariable)]
|
661
586
|
def instance_variable_completion_candidates(name, owner_name)
|
662
|
-
entries =
|
587
|
+
entries = prefix_search(name).flatten #: as Array[Entry::InstanceVariable | Entry::ClassVariable]
|
663
588
|
# Avoid wasting time linearizing ancestors if we didn't find anything
|
664
589
|
return entries if entries.empty?
|
665
590
|
|
@@ -674,7 +599,8 @@ module RubyIndexer
|
|
674
599
|
name_parts = owner_name.split("::")
|
675
600
|
|
676
601
|
if name_parts.last&.start_with?("<Class:")
|
677
|
-
attached_name =
|
602
|
+
attached_name = name_parts[0..-2] #: as !nil
|
603
|
+
.join("::")
|
678
604
|
attached_ancestors = linearized_ancestors_of(attached_name)
|
679
605
|
variables.concat(class_variables.select { |e| attached_ancestors.any?(e.owner&.name) })
|
680
606
|
else
|
@@ -686,9 +612,9 @@ module RubyIndexer
|
|
686
612
|
variables
|
687
613
|
end
|
688
614
|
|
689
|
-
|
615
|
+
#: (String name, String owner_name) -> Array[Entry::ClassVariable]
|
690
616
|
def class_variable_completion_candidates(name, owner_name)
|
691
|
-
entries =
|
617
|
+
entries = prefix_search(name).flatten #: as Array[Entry::ClassVariable]
|
692
618
|
# Avoid wasting time linearizing ancestors if we didn't find anything
|
693
619
|
return entries if entries.empty?
|
694
620
|
|
@@ -702,9 +628,7 @@ module RubyIndexer
|
|
702
628
|
# declarations removed and that the ancestor linearization cache is cleared if necessary. If a block is passed, the
|
703
629
|
# consumer of this API has to handle deleting and inserting/updating entries in the index instead of passing the
|
704
630
|
# document's source (used to handle unsaved changes to files)
|
705
|
-
|
706
|
-
params(uri: URI::Generic, source: T.nilable(String), block: T.nilable(T.proc.params(index: Index).void)).void
|
707
|
-
end
|
631
|
+
#: (URI::Generic uri, ?String? source) ?{ (Index index) -> void } -> void
|
708
632
|
def handle_change(uri, source = nil, &block)
|
709
633
|
key = uri.to_s
|
710
634
|
original_entries = @uris_to_entries[key]
|
@@ -713,7 +637,10 @@ module RubyIndexer
|
|
713
637
|
block.call(self)
|
714
638
|
else
|
715
639
|
delete(uri)
|
716
|
-
index_single(
|
640
|
+
index_single(
|
641
|
+
uri,
|
642
|
+
source, #: as !nil
|
643
|
+
)
|
717
644
|
end
|
718
645
|
|
719
646
|
updated_entries = @uris_to_entries[key]
|
@@ -723,47 +650,45 @@ module RubyIndexer
|
|
723
650
|
# indirect means like including a module that than includes the ancestor. Trying to figure out exactly which
|
724
651
|
# ancestors need to be deleted is too expensive. Therefore, if any of the namespace entries has a change to their
|
725
652
|
# ancestor hash, we clear all ancestors and start linearizing lazily again from scratch
|
726
|
-
original_map =
|
727
|
-
|
728
|
-
|
729
|
-
).to_h { |e| [e.name, e.ancestor_hash] }
|
653
|
+
original_map = original_entries
|
654
|
+
.select { |e| e.is_a?(Entry::Namespace) } #: as Array[Entry::Namespace]
|
655
|
+
.to_h { |e| [e.name, e.ancestor_hash] }
|
730
656
|
|
731
|
-
updated_map =
|
732
|
-
|
733
|
-
|
734
|
-
).to_h { |e| [e.name, e.ancestor_hash] }
|
657
|
+
updated_map = updated_entries
|
658
|
+
.select { |e| e.is_a?(Entry::Namespace) } #: as Array[Entry::Namespace]
|
659
|
+
.to_h { |e| [e.name, e.ancestor_hash] }
|
735
660
|
|
736
661
|
@ancestors.clear if original_map.any? { |name, hash| updated_map[name] != hash }
|
737
662
|
end
|
738
663
|
|
739
|
-
|
664
|
+
#: -> bool
|
740
665
|
def empty?
|
741
666
|
@entries.empty?
|
742
667
|
end
|
743
668
|
|
744
|
-
|
669
|
+
#: -> Array[String]
|
745
670
|
def names
|
746
671
|
@entries.keys
|
747
672
|
end
|
748
673
|
|
749
|
-
|
674
|
+
#: (String name) -> bool
|
750
675
|
def indexed?(name)
|
751
676
|
@entries.key?(name)
|
752
677
|
end
|
753
678
|
|
754
|
-
|
679
|
+
#: -> Integer
|
755
680
|
def length
|
756
681
|
@entries.count
|
757
682
|
end
|
758
683
|
|
759
|
-
|
684
|
+
#: (String name) -> Entry::SingletonClass
|
760
685
|
def existing_or_new_singleton_class(name)
|
761
686
|
*_namespace, unqualified_name = name.split("::")
|
762
687
|
full_singleton_name = "#{name}::<Class:#{unqualified_name}>"
|
763
|
-
singleton =
|
688
|
+
singleton = self[full_singleton_name]&.first #: as Entry::SingletonClass?
|
764
689
|
|
765
690
|
unless singleton
|
766
|
-
attached_ancestor =
|
691
|
+
attached_ancestor = self[name]&.first #: as !nil
|
767
692
|
|
768
693
|
singleton = Entry::SingletonClass.new(
|
769
694
|
[full_singleton_name],
|
@@ -779,12 +704,7 @@ module RubyIndexer
|
|
779
704
|
singleton
|
780
705
|
end
|
781
706
|
|
782
|
-
|
783
|
-
type_parameters(:T).params(
|
784
|
-
uri: String,
|
785
|
-
type: T.nilable(T::Class[T.all(T.type_parameter(:T), Entry)]),
|
786
|
-
).returns(T.nilable(T.any(T::Array[Entry], T::Array[T.type_parameter(:T)])))
|
787
|
-
end
|
707
|
+
#: [T] (String uri, ?Class[(T & Entry)]? type) -> (Array[Entry] | Array[T])?
|
788
708
|
def entries_for(uri, type = nil)
|
789
709
|
entries = @uris_to_entries[uri.to_s]
|
790
710
|
return entries unless type
|
@@ -796,12 +716,13 @@ module RubyIndexer
|
|
796
716
|
|
797
717
|
# Always returns the linearized ancestors for the attached class, regardless of whether `name` refers to a singleton
|
798
718
|
# or attached namespace
|
799
|
-
|
719
|
+
#: (String name) -> Array[String]
|
800
720
|
def linearized_attached_ancestors(name)
|
801
721
|
name_parts = name.split("::")
|
802
722
|
|
803
723
|
if name_parts.last&.start_with?("<Class:")
|
804
|
-
attached_name =
|
724
|
+
attached_name = name_parts[0..-2] #: as !nil
|
725
|
+
.join("::")
|
805
726
|
linearized_ancestors_of(attached_name)
|
806
727
|
else
|
807
728
|
linearized_ancestors_of(name)
|
@@ -809,7 +730,7 @@ module RubyIndexer
|
|
809
730
|
end
|
810
731
|
|
811
732
|
# Runs the registered included hooks
|
812
|
-
|
733
|
+
#: (String fully_qualified_name, Array[String] nesting) -> void
|
813
734
|
def run_included_hooks(fully_qualified_name, nesting)
|
814
735
|
return if @included_hooks.empty?
|
815
736
|
|
@@ -824,7 +745,8 @@ module RubyIndexer
|
|
824
745
|
resolved_modules = resolve(operation.module_name, nesting)
|
825
746
|
next unless resolved_modules
|
826
747
|
|
827
|
-
module_name =
|
748
|
+
module_name = resolved_modules.first #: as !nil
|
749
|
+
.name
|
828
750
|
|
829
751
|
# Then we grab any hooks registered for that module
|
830
752
|
hooks = @included_hooks[module_name]
|
@@ -838,13 +760,7 @@ module RubyIndexer
|
|
838
760
|
|
839
761
|
# Linearize mixins for an array of namespace entries. This method will mutate the `ancestors` array with the
|
840
762
|
# linearized ancestors of the mixins
|
841
|
-
|
842
|
-
params(
|
843
|
-
ancestors: T::Array[String],
|
844
|
-
namespace_entries: T::Array[Entry::Namespace],
|
845
|
-
nesting: T::Array[String],
|
846
|
-
).void
|
847
|
-
end
|
763
|
+
#: (Array[String] ancestors, Array[Entry::Namespace] namespace_entries, Array[String] nesting) -> void
|
848
764
|
def linearize_mixins(ancestors, namespace_entries, nesting)
|
849
765
|
mixin_operations = namespace_entries.flat_map(&:mixin_operations)
|
850
766
|
main_namespace_index = 0
|
@@ -853,7 +769,8 @@ module RubyIndexer
|
|
853
769
|
resolved_module = resolve(operation.module_name, nesting)
|
854
770
|
next unless resolved_module
|
855
771
|
|
856
|
-
module_fully_qualified_name =
|
772
|
+
module_fully_qualified_name = resolved_module.first #: as !nil
|
773
|
+
.name
|
857
774
|
|
858
775
|
case operation
|
859
776
|
when Entry::Prepend
|
@@ -865,36 +782,27 @@ module RubyIndexer
|
|
865
782
|
# When there are duplicate prepended modules, we have to insert the new prepends after the existing ones. For
|
866
783
|
# example, if the current ancestors are `["A", "Foo"]` and we try to prepend `["A", "B"]`, then `"B"` has to
|
867
784
|
# be inserted after `"A`
|
868
|
-
|
785
|
+
prepended_ancestors = ancestors[0...main_namespace_index] #: as !nil
|
786
|
+
uniq_prepends = linearized_prepends - prepended_ancestors
|
869
787
|
insert_position = linearized_prepends.length - uniq_prepends.length
|
870
788
|
|
871
|
-
|
872
|
-
insert_position,
|
873
|
-
*(linearized_prepends - T.must(ancestors[0...main_namespace_index])),
|
874
|
-
)
|
789
|
+
ancestors #: as untyped
|
790
|
+
.insert(insert_position, *uniq_prepends)
|
875
791
|
|
876
792
|
main_namespace_index += linearized_prepends.length
|
877
793
|
when Entry::Include
|
878
794
|
# When including a module, Ruby will always prevent duplicate entries in case the module has already been
|
879
795
|
# prepended or included
|
880
796
|
linearized_includes = linearized_ancestors_of(module_fully_qualified_name)
|
881
|
-
|
797
|
+
ancestors #: as untyped
|
798
|
+
.insert(main_namespace_index + 1, *(linearized_includes - ancestors))
|
882
799
|
end
|
883
800
|
end
|
884
801
|
end
|
885
802
|
|
886
803
|
# Linearize the superclass of a given namespace (including modules with the implicit `Module` superclass). This
|
887
804
|
# method will mutate the `ancestors` array with the linearized ancestors of the superclass
|
888
|
-
|
889
|
-
params(
|
890
|
-
ancestors: T::Array[String],
|
891
|
-
attached_class_name: String,
|
892
|
-
fully_qualified_name: String,
|
893
|
-
namespace_entries: T::Array[Entry::Namespace],
|
894
|
-
nesting: T::Array[String],
|
895
|
-
singleton_levels: Integer,
|
896
|
-
).void
|
897
|
-
end
|
805
|
+
#: (Array[String] ancestors, String attached_class_name, String fully_qualified_name, Array[Entry::Namespace] namespace_entries, Array[String] nesting, Integer singleton_levels) -> void
|
898
806
|
def linearize_superclass( # rubocop:disable Metrics/ParameterLists
|
899
807
|
ancestors,
|
900
808
|
attached_class_name,
|
@@ -905,19 +813,16 @@ module RubyIndexer
|
|
905
813
|
)
|
906
814
|
# Find the first class entry that has a parent class. Notice that if the developer makes a mistake and inherits
|
907
815
|
# from two different classes in different files, we simply ignore it
|
908
|
-
superclass =
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
end,
|
914
|
-
T.nilable(Entry::Class),
|
915
|
-
)
|
816
|
+
superclass = if singleton_levels > 0
|
817
|
+
self[attached_class_name]&.find { |n| n.is_a?(Entry::Class) && n.parent_class }
|
818
|
+
else
|
819
|
+
namespace_entries.find { |n| n.is_a?(Entry::Class) && n.parent_class }
|
820
|
+
end #: as Entry::Class?
|
916
821
|
|
917
822
|
if superclass
|
918
823
|
# If the user makes a mistake and creates a class that inherits from itself, this method would throw a stack
|
919
824
|
# error. We need to ensure that this isn't the case
|
920
|
-
parent_class =
|
825
|
+
parent_class = superclass.parent_class #: as !nil
|
921
826
|
|
922
827
|
resolved_parent_class = resolve(parent_class, nesting)
|
923
828
|
parent_class_name = resolved_parent_class&.first&.name
|
@@ -946,7 +851,7 @@ module RubyIndexer
|
|
946
851
|
elsif singleton_levels > 0
|
947
852
|
# When computing the linearization for a module's singleton class, it inherits from the linearized ancestors of
|
948
853
|
# the `Module` class
|
949
|
-
mod =
|
854
|
+
mod = self[attached_class_name]&.find { |n| n.is_a?(Entry::Module) } #: as Entry::Module?
|
950
855
|
|
951
856
|
if mod
|
952
857
|
module_class_name_parts = ["Module"]
|
@@ -962,12 +867,7 @@ module RubyIndexer
|
|
962
867
|
|
963
868
|
# Attempts to resolve an UnresolvedAlias into a resolved Alias. If the unresolved alias is pointing to a constant
|
964
869
|
# that doesn't exist, then we return the same UnresolvedAlias
|
965
|
-
|
966
|
-
params(
|
967
|
-
entry: Entry::UnresolvedConstantAlias,
|
968
|
-
seen_names: T::Array[String],
|
969
|
-
).returns(T.any(Entry::ConstantAlias, Entry::UnresolvedConstantAlias))
|
970
|
-
end
|
870
|
+
#: (Entry::UnresolvedConstantAlias entry, Array[String] seen_names) -> (Entry::ConstantAlias | Entry::UnresolvedConstantAlias)
|
971
871
|
def resolve_alias(entry, seen_names)
|
972
872
|
alias_name = entry.name
|
973
873
|
return entry if seen_names.include?(alias_name)
|
@@ -977,11 +877,12 @@ module RubyIndexer
|
|
977
877
|
target = resolve(entry.target, entry.nesting, seen_names)
|
978
878
|
return entry unless target
|
979
879
|
|
980
|
-
target_name =
|
880
|
+
target_name = target.first #: as !nil
|
881
|
+
.name
|
981
882
|
resolved_alias = Entry::ConstantAlias.new(target_name, entry)
|
982
883
|
|
983
884
|
# Replace the UnresolvedAlias by a resolved one so that we don't have to do this again later
|
984
|
-
original_entries =
|
885
|
+
original_entries = @entries[alias_name] #: as !nil
|
985
886
|
original_entries.delete(entry)
|
986
887
|
original_entries << resolved_alias
|
987
888
|
|
@@ -990,20 +891,11 @@ module RubyIndexer
|
|
990
891
|
resolved_alias
|
991
892
|
end
|
992
893
|
|
993
|
-
|
994
|
-
params(
|
995
|
-
name: String,
|
996
|
-
nesting: T::Array[String],
|
997
|
-
seen_names: T::Array[String],
|
998
|
-
).returns(T.nilable(T::Array[T.any(
|
999
|
-
Entry::Namespace,
|
1000
|
-
Entry::ConstantAlias,
|
1001
|
-
Entry::UnresolvedConstantAlias,
|
1002
|
-
)]))
|
1003
|
-
end
|
894
|
+
#: (String name, Array[String] nesting, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
1004
895
|
def lookup_enclosing_scopes(name, nesting, seen_names)
|
1005
896
|
nesting.length.downto(1) do |i|
|
1006
|
-
namespace =
|
897
|
+
namespace = nesting[0...i] #: as !nil
|
898
|
+
.join("::")
|
1007
899
|
|
1008
900
|
# If we find an entry with `full_name` directly, then we can already return it, even if it contains aliases -
|
1009
901
|
# because the user might be trying to jump to the alias definition.
|
@@ -1019,17 +911,7 @@ module RubyIndexer
|
|
1019
911
|
nil
|
1020
912
|
end
|
1021
913
|
|
1022
|
-
|
1023
|
-
params(
|
1024
|
-
name: String,
|
1025
|
-
nesting: T::Array[String],
|
1026
|
-
seen_names: T::Array[String],
|
1027
|
-
).returns(T.nilable(T::Array[T.any(
|
1028
|
-
Entry::Namespace,
|
1029
|
-
Entry::ConstantAlias,
|
1030
|
-
Entry::UnresolvedConstantAlias,
|
1031
|
-
)]))
|
1032
|
-
end
|
914
|
+
#: (String name, Array[String] nesting, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
1033
915
|
def lookup_ancestor_chain(name, nesting, seen_names)
|
1034
916
|
*nesting_parts, constant_name = build_non_redundant_full_name(name, nesting).split("::")
|
1035
917
|
return if nesting_parts.empty?
|
@@ -1037,7 +919,9 @@ module RubyIndexer
|
|
1037
919
|
namespace_entries = resolve(nesting_parts.join("::"), [], seen_names)
|
1038
920
|
return unless namespace_entries
|
1039
921
|
|
1040
|
-
|
922
|
+
namespace_name = namespace_entries.first #: as !nil
|
923
|
+
.name
|
924
|
+
ancestors = nesting_parts.empty? ? [] : linearized_ancestors_of(namespace_name)
|
1041
925
|
|
1042
926
|
ancestors.each do |ancestor_name|
|
1043
927
|
entries = direct_or_aliased_constant("#{ancestor_name}::#{constant_name}", seen_names)
|
@@ -1049,17 +933,7 @@ module RubyIndexer
|
|
1049
933
|
nil
|
1050
934
|
end
|
1051
935
|
|
1052
|
-
|
1053
|
-
params(
|
1054
|
-
name: T.nilable(String),
|
1055
|
-
nesting: T::Array[String],
|
1056
|
-
).returns(T::Array[T::Array[T.any(
|
1057
|
-
Entry::Namespace,
|
1058
|
-
Entry::ConstantAlias,
|
1059
|
-
Entry::UnresolvedConstantAlias,
|
1060
|
-
Entry::Constant,
|
1061
|
-
)]])
|
1062
|
-
end
|
936
|
+
#: (String? name, Array[String] nesting) -> Array[Array[(Entry::Namespace | Entry::ConstantAlias | Entry::UnresolvedConstantAlias | Entry::Constant)]]
|
1063
937
|
def inherited_constant_completion_candidates(name, nesting)
|
1064
938
|
namespace_entries = if name
|
1065
939
|
*nesting_parts, constant_name = build_non_redundant_full_name(name, nesting).split("::")
|
@@ -1071,7 +945,9 @@ module RubyIndexer
|
|
1071
945
|
end
|
1072
946
|
return [] unless namespace_entries
|
1073
947
|
|
1074
|
-
|
948
|
+
namespace_name = namespace_entries.first #: as !nil
|
949
|
+
.name
|
950
|
+
ancestors = linearized_ancestors_of(namespace_name)
|
1075
951
|
candidates = ancestors.flat_map do |ancestor_name|
|
1076
952
|
@entries_tree.search("#{ancestor_name}::#{constant_name}")
|
1077
953
|
end
|
@@ -1079,7 +955,8 @@ module RubyIndexer
|
|
1079
955
|
# For candidates with the same name, we must only show the first entry in the inheritance chain, since that's the
|
1080
956
|
# one the user will be referring to in completion
|
1081
957
|
completion_items = candidates.each_with_object({}) do |entries, hash|
|
1082
|
-
*parts, short_name =
|
958
|
+
*parts, short_name = entries.first #: as !nil
|
959
|
+
.name.split("::")
|
1083
960
|
namespace_name = parts.join("::")
|
1084
961
|
ancestor_index = ancestors.index(namespace_name)
|
1085
962
|
existing_entry, existing_entry_index = hash[short_name]
|
@@ -1098,7 +975,7 @@ module RubyIndexer
|
|
1098
975
|
# inside of the ["A", "B"] nesting, then we should not concatenate the nesting with the name or else we'll end up
|
1099
976
|
# with `A::B::A::B::Foo`. This method will remove any redundant parts from the final name based on the reference and
|
1100
977
|
# the nesting
|
1101
|
-
|
978
|
+
#: (String name, Array[String] nesting) -> String
|
1102
979
|
def build_non_redundant_full_name(name, nesting)
|
1103
980
|
# If there's no nesting, then we can just return the name as is
|
1104
981
|
return name if nesting.empty?
|
@@ -1115,44 +992,22 @@ module RubyIndexer
|
|
1115
992
|
# Otherwise, push all of the leading parts of the nesting that aren't redundant into the name. For example, if we
|
1116
993
|
# have a reference to `Foo::Bar` inside the `[Namespace, Foo]` nesting, then only the `Foo` part is redundant, but
|
1117
994
|
# we still need to include the `Namespace` part
|
1118
|
-
|
995
|
+
name_parts.unshift(*nesting[0...first_redundant_part])
|
1119
996
|
name_parts.join("::")
|
1120
997
|
end
|
1121
998
|
|
1122
|
-
|
1123
|
-
params(
|
1124
|
-
full_name: String,
|
1125
|
-
seen_names: T::Array[String],
|
1126
|
-
).returns(
|
1127
|
-
T.nilable(T::Array[T.any(
|
1128
|
-
Entry::Namespace,
|
1129
|
-
Entry::ConstantAlias,
|
1130
|
-
Entry::UnresolvedConstantAlias,
|
1131
|
-
)]),
|
1132
|
-
)
|
1133
|
-
end
|
999
|
+
#: (String full_name, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
1134
1000
|
def direct_or_aliased_constant(full_name, seen_names)
|
1135
1001
|
entries = @entries[full_name] || @entries[follow_aliased_namespace(full_name)]
|
1136
1002
|
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
Entry::Namespace,
|
1141
|
-
Entry::ConstantAlias,
|
1142
|
-
Entry::UnresolvedConstantAlias,
|
1143
|
-
)]),
|
1144
|
-
)
|
1003
|
+
entries&.map do |e|
|
1004
|
+
e.is_a?(Entry::UnresolvedConstantAlias) ? resolve_alias(e, seen_names) : e
|
1005
|
+
end #: as Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias])?
|
1145
1006
|
end
|
1146
1007
|
|
1147
1008
|
# Attempt to resolve a given unresolved method alias. This method returns the resolved alias if we managed to
|
1148
1009
|
# identify the target or the same unresolved alias entry if we couldn't
|
1149
|
-
|
1150
|
-
params(
|
1151
|
-
entry: Entry::UnresolvedMethodAlias,
|
1152
|
-
receiver_name: String,
|
1153
|
-
seen_names: T::Array[String],
|
1154
|
-
).returns(T.any(Entry::MethodAlias, Entry::UnresolvedMethodAlias))
|
1155
|
-
end
|
1010
|
+
#: (Entry::UnresolvedMethodAlias entry, String receiver_name, Array[String] seen_names) -> (Entry::MethodAlias | Entry::UnresolvedMethodAlias)
|
1156
1011
|
def resolve_method_alias(entry, receiver_name, seen_names)
|
1157
1012
|
new_name = entry.new_name
|
1158
1013
|
return entry if new_name == entry.old_name
|
@@ -1163,8 +1018,11 @@ module RubyIndexer
|
|
1163
1018
|
target_method_entries = resolve_method(entry.old_name, receiver_name, seen_names)
|
1164
1019
|
return entry unless target_method_entries
|
1165
1020
|
|
1166
|
-
resolved_alias = Entry::MethodAlias.new(
|
1167
|
-
|
1021
|
+
resolved_alias = Entry::MethodAlias.new(
|
1022
|
+
target_method_entries.first, #: as !nil
|
1023
|
+
entry,
|
1024
|
+
)
|
1025
|
+
original_entries = @entries[new_name] #: as !nil
|
1168
1026
|
original_entries.delete(entry)
|
1169
1027
|
original_entries << resolved_alias
|
1170
1028
|
resolved_alias
|