ruby-lsp 0.23.11 → 0.26.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 +10 -4
- data/exe/ruby-lsp-check +0 -4
- data/exe/ruby-lsp-launcher +45 -22
- data/exe/ruby-lsp-test-exec +6 -0
- data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -2
- data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -6
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +82 -116
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +140 -183
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +10 -14
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +107 -236
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +166 -281
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +23 -27
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +25 -57
- data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +58 -68
- 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 +49 -9
- 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 +185 -135
- data/lib/ruby_indexer/test/instance_variables_test.rb +61 -37
- data/lib/ruby_indexer/test/method_test.rb +166 -123
- data/lib/ruby_indexer/test/prefix_tree_test.rb +21 -21
- data/lib/ruby_indexer/test/rbs_indexer_test.rb +70 -75
- data/lib/ruby_indexer/test/reference_finder_test.rb +79 -14
- 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 +88 -86
- data/lib/ruby_lsp/base_server.rb +59 -54
- data/lib/ruby_lsp/client_capabilities.rb +16 -13
- data/lib/ruby_lsp/document.rb +205 -104
- data/lib/ruby_lsp/erb_document.rb +45 -47
- data/lib/ruby_lsp/global_state.rb +73 -57
- data/lib/ruby_lsp/internal.rb +8 -3
- data/lib/ruby_lsp/listeners/code_lens.rb +82 -89
- data/lib/ruby_lsp/listeners/completion.rb +81 -76
- 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 +107 -115
- data/lib/ruby_lsp/listeners/inlay_hints.rb +8 -13
- 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 +214 -0
- data/lib/ruby_lsp/listeners/test_discovery.rb +92 -0
- data/lib/ruby_lsp/listeners/test_style.rb +205 -95
- data/lib/ruby_lsp/node_context.rb +12 -39
- data/lib/ruby_lsp/rbs_document.rb +10 -11
- data/lib/ruby_lsp/requests/code_action_resolve.rb +65 -61
- data/lib/ruby_lsp/requests/code_actions.rb +14 -26
- data/lib/ruby_lsp/requests/code_lens.rb +31 -21
- data/lib/ruby_lsp/requests/completion.rb +8 -21
- data/lib/ruby_lsp/requests/completion_resolve.rb +6 -6
- data/lib/ruby_lsp/requests/definition.rb +8 -20
- data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
- data/lib/ruby_lsp/requests/discover_tests.rb +20 -7
- data/lib/ruby_lsp/requests/document_highlight.rb +6 -16
- 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 +12 -25
- data/lib/ruby_lsp/requests/inlay_hints.rb +8 -19
- data/lib/ruby_lsp/requests/on_type_formatting.rb +32 -40
- data/lib/ruby_lsp/requests/prepare_rename.rb +5 -10
- 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 +17 -57
- data/lib/ruby_lsp/requests/rename.rb +27 -51
- data/lib/ruby_lsp/requests/request.rb +13 -25
- data/lib/ruby_lsp/requests/selection_ranges.rb +7 -7
- 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 +9 -27
- data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
- data/lib/ruby_lsp/requests/support/common.rb +16 -58
- data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +13 -16
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +34 -36
- 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 +6 -9
- data/lib/ruby_lsp/response_builders/document_symbol.rb +15 -21
- data/lib/ruby_lsp/response_builders/hover.rb +12 -18
- data/lib/ruby_lsp/response_builders/response_builder.rb +6 -7
- data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +62 -91
- data/lib/ruby_lsp/response_builders/signature_help.rb +6 -8
- data/lib/ruby_lsp/response_builders/test_collection.rb +35 -13
- data/lib/ruby_lsp/ruby_document.rb +32 -98
- data/lib/ruby_lsp/scope.rb +7 -11
- data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
- data/lib/ruby_lsp/server.rb +303 -196
- data/lib/ruby_lsp/setup_bundler.rb +121 -82
- data/lib/ruby_lsp/static_docs.rb +12 -7
- data/lib/ruby_lsp/store.rb +21 -49
- data/lib/ruby_lsp/test_helper.rb +3 -16
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +233 -0
- data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +145 -0
- data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +92 -0
- data/lib/ruby_lsp/type_inferrer.rb +13 -14
- data/lib/ruby_lsp/utils.rb +138 -93
- data/static_docs/break.md +103 -0
- metadata +14 -20
- data/lib/ruby_lsp/load_sorbet.rb +0 -62
@@ -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]]
|
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
|
|
@@ -339,16 +291,19 @@ module RubyIndexer
|
|
339
291
|
|
340
292
|
# Top level constants
|
341
293
|
entries.concat(@entries_tree.search(name))
|
294
|
+
|
295
|
+
# Filter only constants since methods may have names that look like constants
|
296
|
+
entries.select! do |definitions|
|
297
|
+
definitions.select! do |entry|
|
298
|
+
entry.is_a?(Entry::Constant) || entry.is_a?(Entry::ConstantAlias) ||
|
299
|
+
entry.is_a?(Entry::Namespace) || entry.is_a?(Entry::UnresolvedConstantAlias)
|
300
|
+
end
|
301
|
+
|
302
|
+
definitions.any?
|
303
|
+
end
|
304
|
+
|
342
305
|
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
|
-
)
|
306
|
+
entries #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
|
352
307
|
end
|
353
308
|
|
354
309
|
# Resolve a constant to its declaration based on its name and the nesting where the reference was found. Parameter
|
@@ -358,17 +313,7 @@ module RubyIndexer
|
|
358
313
|
# nesting: the nesting structure where the reference was found (e.g.: ["Foo", "Bar"])
|
359
314
|
# seen_names: this parameter should not be used by consumers of the api. It is used to avoid infinite recursion when
|
360
315
|
# 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
|
316
|
+
#: (String name, Array[String] nesting, ?Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
372
317
|
def resolve(name, nesting, seen_names = [])
|
373
318
|
# If we have a top level reference, then we just search for it straight away ignoring the nesting
|
374
319
|
if name.start_with?("::")
|
@@ -404,12 +349,7 @@ module RubyIndexer
|
|
404
349
|
# Index all files for the given URIs, which defaults to what is configured. A block can be used to track and control
|
405
350
|
# indexing progress. That block is invoked with the current progress percentage and should return `true` to continue
|
406
351
|
# 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
|
352
|
+
#: (?uris: Array[URI::Generic]) ?{ (Integer progress) -> bool } -> void
|
413
353
|
def index_all(uris: @configuration.indexable_uris, &block)
|
414
354
|
# When troubleshooting an indexing issue, e.g. through irb, it's not obvious that `index_all` will augment the
|
415
355
|
# existing index values, meaning it may contain 'stale' entries. This check ensures that the user is aware of this
|
@@ -419,8 +359,6 @@ module RubyIndexer
|
|
419
359
|
"The index is not empty. To prevent invalid entries, `index_all` can only be called once."
|
420
360
|
end
|
421
361
|
|
422
|
-
@initial_indexing_completed = true
|
423
|
-
|
424
362
|
RBSIndexer.new(self).index_ruby_core
|
425
363
|
# Calculate how many paths are worth 1% of progress
|
426
364
|
progress_step = (uris.length / 100.0).ceil
|
@@ -433,9 +371,11 @@ module RubyIndexer
|
|
433
371
|
|
434
372
|
index_file(uri, collect_comments: false)
|
435
373
|
end
|
374
|
+
|
375
|
+
@initial_indexing_completed = true
|
436
376
|
end
|
437
377
|
|
438
|
-
|
378
|
+
#: (URI::Generic uri, String source, ?collect_comments: bool) -> void
|
439
379
|
def index_single(uri, source, collect_comments: true)
|
440
380
|
dispatcher = Prism::Dispatcher.new
|
441
381
|
|
@@ -457,9 +397,10 @@ module RubyIndexer
|
|
457
397
|
end
|
458
398
|
|
459
399
|
# Indexes a File URI by reading the contents from disk
|
460
|
-
|
400
|
+
#: (URI::Generic uri, ?collect_comments: bool) -> void
|
461
401
|
def index_file(uri, collect_comments: true)
|
462
|
-
|
402
|
+
path = uri.full_path #: as !nil
|
403
|
+
index_single(uri, File.read(path), collect_comments: collect_comments)
|
463
404
|
rescue Errno::EISDIR, Errno::ENOENT
|
464
405
|
# If `path` is a directory, just ignore it and continue indexing. If the file doesn't exist, then we also ignore
|
465
406
|
# it
|
@@ -475,13 +416,14 @@ module RubyIndexer
|
|
475
416
|
# If we find an alias, then we want to follow its target. In the same example, if `Foo::Bar` is an alias to
|
476
417
|
# `Something::Else`, then we first discover `Something::Else::Baz`. But `Something::Else::Baz` might contain other
|
477
418
|
# aliases, so we have to invoke `follow_aliased_namespace` again to check until we only return a real name
|
478
|
-
|
419
|
+
#: (String name, ?Array[String] seen_names) -> String
|
479
420
|
def follow_aliased_namespace(name, seen_names = [])
|
480
421
|
parts = name.split("::")
|
481
422
|
real_parts = []
|
482
423
|
|
483
424
|
(parts.length - 1).downto(0) do |i|
|
484
|
-
current_name =
|
425
|
+
current_name = parts[0..i] #: as !nil
|
426
|
+
.join("::")
|
485
427
|
entry = @entries[current_name]&.first
|
486
428
|
|
487
429
|
case entry
|
@@ -498,7 +440,9 @@ module RubyIndexer
|
|
498
440
|
target = resolved.target
|
499
441
|
return follow_aliased_namespace("#{target}::#{real_parts.join("::")}", seen_names)
|
500
442
|
else
|
501
|
-
real_parts.unshift(
|
443
|
+
real_parts.unshift(
|
444
|
+
parts[i], #: as !nil
|
445
|
+
)
|
502
446
|
end
|
503
447
|
end
|
504
448
|
|
@@ -508,14 +452,7 @@ module RubyIndexer
|
|
508
452
|
# Attempts to find methods for a resolved fully qualified receiver name. Do not provide the `seen_names` parameter
|
509
453
|
# as it is used only internally to prevent infinite loops when resolving circular aliases
|
510
454
|
# 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
|
455
|
+
#: (String method_name, String receiver_name, ?Array[String] seen_names, ?inherited_only: bool) -> Array[(Entry::Member | Entry::MethodAlias)]?
|
519
456
|
def resolve_method(method_name, receiver_name, seen_names = [], inherited_only: false)
|
520
457
|
method_entries = self[method_name]
|
521
458
|
return unless method_entries
|
@@ -553,7 +490,7 @@ module RubyIndexer
|
|
553
490
|
# module that prepends another module, then the prepend module appears before the included module.
|
554
491
|
#
|
555
492
|
# The order of ancestors is [linearized_prepends, self, linearized_includes, linearized_superclass]
|
556
|
-
|
493
|
+
#: (String fully_qualified_name) -> Array[String]
|
557
494
|
def linearized_ancestors_of(fully_qualified_name)
|
558
495
|
# If we already computed the ancestors for this namespace, return it straight away
|
559
496
|
cached_ancestors = @ancestors[fully_qualified_name]
|
@@ -602,11 +539,12 @@ module RubyIndexer
|
|
602
539
|
|
603
540
|
# The original nesting where we discovered this namespace, so that we resolve the correct names of the
|
604
541
|
# included/prepended/extended modules and parent classes
|
605
|
-
nesting =
|
542
|
+
nesting = namespaces.first #: as !nil
|
543
|
+
.nesting.flat_map { |n| n.split("::") }
|
606
544
|
|
607
545
|
if nesting.any?
|
608
546
|
singleton_levels.times do
|
609
|
-
nesting << "<Class:#{
|
547
|
+
nesting << "<Class:#{nesting.last}>"
|
610
548
|
end
|
611
549
|
end
|
612
550
|
|
@@ -631,9 +569,9 @@ module RubyIndexer
|
|
631
569
|
|
632
570
|
# Resolves an instance variable name for a given owner name. This method will linearize the ancestors of the owner
|
633
571
|
# and find inherited instance variables as well
|
634
|
-
|
572
|
+
#: (String variable_name, String owner_name) -> Array[Entry::InstanceVariable]?
|
635
573
|
def resolve_instance_variable(variable_name, owner_name)
|
636
|
-
entries =
|
574
|
+
entries = self[variable_name] #: as Array[Entry::InstanceVariable]?
|
637
575
|
return unless entries
|
638
576
|
|
639
577
|
ancestors = linearized_ancestors_of(owner_name)
|
@@ -642,7 +580,7 @@ module RubyIndexer
|
|
642
580
|
entries.select { |e| ancestors.include?(e.owner&.name) }
|
643
581
|
end
|
644
582
|
|
645
|
-
|
583
|
+
#: (String variable_name, String owner_name) -> Array[Entry::ClassVariable]?
|
646
584
|
def resolve_class_variable(variable_name, owner_name)
|
647
585
|
entries = self[variable_name]&.grep(Entry::ClassVariable)
|
648
586
|
return unless entries&.any?
|
@@ -655,11 +593,9 @@ module RubyIndexer
|
|
655
593
|
|
656
594
|
# Returns a list of possible candidates for completion of instance variables for a given owner name. The name must
|
657
595
|
# include the `@` prefix
|
658
|
-
|
659
|
-
params(name: String, owner_name: String).returns(T::Array[T.any(Entry::InstanceVariable, Entry::ClassVariable)])
|
660
|
-
end
|
596
|
+
#: (String name, String owner_name) -> Array[(Entry::InstanceVariable | Entry::ClassVariable)]
|
661
597
|
def instance_variable_completion_candidates(name, owner_name)
|
662
|
-
entries =
|
598
|
+
entries = prefix_search(name).flatten #: as Array[Entry::InstanceVariable | Entry::ClassVariable]
|
663
599
|
# Avoid wasting time linearizing ancestors if we didn't find anything
|
664
600
|
return entries if entries.empty?
|
665
601
|
|
@@ -674,7 +610,8 @@ module RubyIndexer
|
|
674
610
|
name_parts = owner_name.split("::")
|
675
611
|
|
676
612
|
if name_parts.last&.start_with?("<Class:")
|
677
|
-
attached_name =
|
613
|
+
attached_name = name_parts[0..-2] #: as !nil
|
614
|
+
.join("::")
|
678
615
|
attached_ancestors = linearized_ancestors_of(attached_name)
|
679
616
|
variables.concat(class_variables.select { |e| attached_ancestors.any?(e.owner&.name) })
|
680
617
|
else
|
@@ -686,9 +623,9 @@ module RubyIndexer
|
|
686
623
|
variables
|
687
624
|
end
|
688
625
|
|
689
|
-
|
626
|
+
#: (String name, String owner_name) -> Array[Entry::ClassVariable]
|
690
627
|
def class_variable_completion_candidates(name, owner_name)
|
691
|
-
entries =
|
628
|
+
entries = prefix_search(name).flatten #: as Array[Entry::ClassVariable]
|
692
629
|
# Avoid wasting time linearizing ancestors if we didn't find anything
|
693
630
|
return entries if entries.empty?
|
694
631
|
|
@@ -702,9 +639,7 @@ module RubyIndexer
|
|
702
639
|
# declarations removed and that the ancestor linearization cache is cleared if necessary. If a block is passed, the
|
703
640
|
# consumer of this API has to handle deleting and inserting/updating entries in the index instead of passing the
|
704
641
|
# 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
|
642
|
+
#: (URI::Generic uri, ?String? source) ?{ (Index index) -> void } -> void
|
708
643
|
def handle_change(uri, source = nil, &block)
|
709
644
|
key = uri.to_s
|
710
645
|
original_entries = @uris_to_entries[key]
|
@@ -713,7 +648,10 @@ module RubyIndexer
|
|
713
648
|
block.call(self)
|
714
649
|
else
|
715
650
|
delete(uri)
|
716
|
-
index_single(
|
651
|
+
index_single(
|
652
|
+
uri,
|
653
|
+
source, #: as !nil
|
654
|
+
)
|
717
655
|
end
|
718
656
|
|
719
657
|
updated_entries = @uris_to_entries[key]
|
@@ -723,47 +661,50 @@ module RubyIndexer
|
|
723
661
|
# indirect means like including a module that than includes the ancestor. Trying to figure out exactly which
|
724
662
|
# ancestors need to be deleted is too expensive. Therefore, if any of the namespace entries has a change to their
|
725
663
|
# 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] }
|
664
|
+
original_map = original_entries
|
665
|
+
.select { |e| e.is_a?(Entry::Namespace) } #: as Array[Entry::Namespace]
|
666
|
+
.to_h { |e| [e.name, e.ancestor_hash] }
|
730
667
|
|
731
|
-
updated_map =
|
732
|
-
|
733
|
-
|
734
|
-
).to_h { |e| [e.name, e.ancestor_hash] }
|
668
|
+
updated_map = updated_entries
|
669
|
+
.select { |e| e.is_a?(Entry::Namespace) } #: as Array[Entry::Namespace]
|
670
|
+
.to_h { |e| [e.name, e.ancestor_hash] }
|
735
671
|
|
736
672
|
@ancestors.clear if original_map.any? { |name, hash| updated_map[name] != hash }
|
737
673
|
end
|
738
674
|
|
739
|
-
|
675
|
+
#: -> void
|
676
|
+
def clear_ancestors
|
677
|
+
@ancestors.clear
|
678
|
+
end
|
679
|
+
|
680
|
+
#: -> bool
|
740
681
|
def empty?
|
741
682
|
@entries.empty?
|
742
683
|
end
|
743
684
|
|
744
|
-
|
685
|
+
#: -> Array[String]
|
745
686
|
def names
|
746
687
|
@entries.keys
|
747
688
|
end
|
748
689
|
|
749
|
-
|
690
|
+
#: (String name) -> bool
|
750
691
|
def indexed?(name)
|
751
692
|
@entries.key?(name)
|
752
693
|
end
|
753
694
|
|
754
|
-
|
695
|
+
#: -> Integer
|
755
696
|
def length
|
756
697
|
@entries.count
|
757
698
|
end
|
758
699
|
|
759
|
-
|
700
|
+
#: (String name) -> Entry::SingletonClass
|
760
701
|
def existing_or_new_singleton_class(name)
|
761
702
|
*_namespace, unqualified_name = name.split("::")
|
762
703
|
full_singleton_name = "#{name}::<Class:#{unqualified_name}>"
|
763
|
-
singleton =
|
704
|
+
singleton = self[full_singleton_name]&.first #: as Entry::SingletonClass?
|
764
705
|
|
765
706
|
unless singleton
|
766
|
-
attached_ancestor =
|
707
|
+
attached_ancestor = self[name]&.first #: as !nil
|
767
708
|
|
768
709
|
singleton = Entry::SingletonClass.new(
|
769
710
|
[full_singleton_name],
|
@@ -779,12 +720,7 @@ module RubyIndexer
|
|
779
720
|
singleton
|
780
721
|
end
|
781
722
|
|
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
|
723
|
+
#: [T] (String uri, ?Class[(T & Entry)]? type) -> (Array[Entry] | Array[T])?
|
788
724
|
def entries_for(uri, type = nil)
|
789
725
|
entries = @uris_to_entries[uri.to_s]
|
790
726
|
return entries unless type
|
@@ -796,12 +732,13 @@ module RubyIndexer
|
|
796
732
|
|
797
733
|
# Always returns the linearized ancestors for the attached class, regardless of whether `name` refers to a singleton
|
798
734
|
# or attached namespace
|
799
|
-
|
735
|
+
#: (String name) -> Array[String]
|
800
736
|
def linearized_attached_ancestors(name)
|
801
737
|
name_parts = name.split("::")
|
802
738
|
|
803
739
|
if name_parts.last&.start_with?("<Class:")
|
804
|
-
attached_name =
|
740
|
+
attached_name = name_parts[0..-2] #: as !nil
|
741
|
+
.join("::")
|
805
742
|
linearized_ancestors_of(attached_name)
|
806
743
|
else
|
807
744
|
linearized_ancestors_of(name)
|
@@ -809,7 +746,7 @@ module RubyIndexer
|
|
809
746
|
end
|
810
747
|
|
811
748
|
# Runs the registered included hooks
|
812
|
-
|
749
|
+
#: (String fully_qualified_name, Array[String] nesting) -> void
|
813
750
|
def run_included_hooks(fully_qualified_name, nesting)
|
814
751
|
return if @included_hooks.empty?
|
815
752
|
|
@@ -824,7 +761,8 @@ module RubyIndexer
|
|
824
761
|
resolved_modules = resolve(operation.module_name, nesting)
|
825
762
|
next unless resolved_modules
|
826
763
|
|
827
|
-
module_name =
|
764
|
+
module_name = resolved_modules.first #: as !nil
|
765
|
+
.name
|
828
766
|
|
829
767
|
# Then we grab any hooks registered for that module
|
830
768
|
hooks = @included_hooks[module_name]
|
@@ -838,13 +776,7 @@ module RubyIndexer
|
|
838
776
|
|
839
777
|
# Linearize mixins for an array of namespace entries. This method will mutate the `ancestors` array with the
|
840
778
|
# 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
|
779
|
+
#: (Array[String] ancestors, Array[Entry::Namespace] namespace_entries, Array[String] nesting) -> void
|
848
780
|
def linearize_mixins(ancestors, namespace_entries, nesting)
|
849
781
|
mixin_operations = namespace_entries.flat_map(&:mixin_operations)
|
850
782
|
main_namespace_index = 0
|
@@ -853,7 +785,8 @@ module RubyIndexer
|
|
853
785
|
resolved_module = resolve(operation.module_name, nesting)
|
854
786
|
next unless resolved_module
|
855
787
|
|
856
|
-
module_fully_qualified_name =
|
788
|
+
module_fully_qualified_name = resolved_module.first #: as !nil
|
789
|
+
.name
|
857
790
|
|
858
791
|
case operation
|
859
792
|
when Entry::Prepend
|
@@ -865,36 +798,27 @@ module RubyIndexer
|
|
865
798
|
# When there are duplicate prepended modules, we have to insert the new prepends after the existing ones. For
|
866
799
|
# example, if the current ancestors are `["A", "Foo"]` and we try to prepend `["A", "B"]`, then `"B"` has to
|
867
800
|
# be inserted after `"A`
|
868
|
-
|
801
|
+
prepended_ancestors = ancestors[0...main_namespace_index] #: as !nil
|
802
|
+
uniq_prepends = linearized_prepends - prepended_ancestors
|
869
803
|
insert_position = linearized_prepends.length - uniq_prepends.length
|
870
804
|
|
871
|
-
|
872
|
-
insert_position,
|
873
|
-
*(linearized_prepends - T.must(ancestors[0...main_namespace_index])),
|
874
|
-
)
|
805
|
+
ancestors #: as untyped
|
806
|
+
.insert(insert_position, *uniq_prepends)
|
875
807
|
|
876
808
|
main_namespace_index += linearized_prepends.length
|
877
809
|
when Entry::Include
|
878
810
|
# When including a module, Ruby will always prevent duplicate entries in case the module has already been
|
879
811
|
# prepended or included
|
880
812
|
linearized_includes = linearized_ancestors_of(module_fully_qualified_name)
|
881
|
-
|
813
|
+
ancestors #: as untyped
|
814
|
+
.insert(main_namespace_index + 1, *(linearized_includes - ancestors))
|
882
815
|
end
|
883
816
|
end
|
884
817
|
end
|
885
818
|
|
886
819
|
# Linearize the superclass of a given namespace (including modules with the implicit `Module` superclass). This
|
887
820
|
# 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
|
821
|
+
#: (Array[String] ancestors, String attached_class_name, String fully_qualified_name, Array[Entry::Namespace] namespace_entries, Array[String] nesting, Integer singleton_levels) -> void
|
898
822
|
def linearize_superclass( # rubocop:disable Metrics/ParameterLists
|
899
823
|
ancestors,
|
900
824
|
attached_class_name,
|
@@ -905,19 +829,27 @@ module RubyIndexer
|
|
905
829
|
)
|
906
830
|
# Find the first class entry that has a parent class. Notice that if the developer makes a mistake and inherits
|
907
831
|
# from two different classes in different files, we simply ignore it
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
832
|
+
possible_parents = singleton_levels > 0 ? self[attached_class_name] : namespace_entries
|
833
|
+
superclass = nil #: Entry::Class?
|
834
|
+
|
835
|
+
possible_parents&.each do |n|
|
836
|
+
# Ignore non class entries
|
837
|
+
next unless n.is_a?(Entry::Class)
|
838
|
+
|
839
|
+
parent_class = n.parent_class
|
840
|
+
next unless parent_class
|
841
|
+
|
842
|
+
# Always set the superclass, but break early if we found one that isn't `::Object` (meaning we found an explicit
|
843
|
+
# parent class and not the implicit default). Note that when setting different parents to the same class, which
|
844
|
+
# is invalid, we pick whatever is the first one we find
|
845
|
+
superclass = n
|
846
|
+
break if parent_class != "::Object"
|
847
|
+
end
|
916
848
|
|
917
849
|
if superclass
|
918
850
|
# If the user makes a mistake and creates a class that inherits from itself, this method would throw a stack
|
919
851
|
# error. We need to ensure that this isn't the case
|
920
|
-
parent_class =
|
852
|
+
parent_class = superclass.parent_class #: as !nil
|
921
853
|
|
922
854
|
resolved_parent_class = resolve(parent_class, nesting)
|
923
855
|
parent_class_name = resolved_parent_class&.first&.name
|
@@ -946,7 +878,7 @@ module RubyIndexer
|
|
946
878
|
elsif singleton_levels > 0
|
947
879
|
# When computing the linearization for a module's singleton class, it inherits from the linearized ancestors of
|
948
880
|
# the `Module` class
|
949
|
-
mod =
|
881
|
+
mod = self[attached_class_name]&.find { |n| n.is_a?(Entry::Module) } #: as Entry::Module?
|
950
882
|
|
951
883
|
if mod
|
952
884
|
module_class_name_parts = ["Module"]
|
@@ -962,12 +894,7 @@ module RubyIndexer
|
|
962
894
|
|
963
895
|
# Attempts to resolve an UnresolvedAlias into a resolved Alias. If the unresolved alias is pointing to a constant
|
964
896
|
# 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
|
897
|
+
#: (Entry::UnresolvedConstantAlias entry, Array[String] seen_names) -> (Entry::ConstantAlias | Entry::UnresolvedConstantAlias)
|
971
898
|
def resolve_alias(entry, seen_names)
|
972
899
|
alias_name = entry.name
|
973
900
|
return entry if seen_names.include?(alias_name)
|
@@ -977,11 +904,12 @@ module RubyIndexer
|
|
977
904
|
target = resolve(entry.target, entry.nesting, seen_names)
|
978
905
|
return entry unless target
|
979
906
|
|
980
|
-
target_name =
|
907
|
+
target_name = target.first #: as !nil
|
908
|
+
.name
|
981
909
|
resolved_alias = Entry::ConstantAlias.new(target_name, entry)
|
982
910
|
|
983
911
|
# Replace the UnresolvedAlias by a resolved one so that we don't have to do this again later
|
984
|
-
original_entries =
|
912
|
+
original_entries = @entries[alias_name] #: as !nil
|
985
913
|
original_entries.delete(entry)
|
986
914
|
original_entries << resolved_alias
|
987
915
|
|
@@ -990,20 +918,11 @@ module RubyIndexer
|
|
990
918
|
resolved_alias
|
991
919
|
end
|
992
920
|
|
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
|
921
|
+
#: (String name, Array[String] nesting, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
1004
922
|
def lookup_enclosing_scopes(name, nesting, seen_names)
|
1005
923
|
nesting.length.downto(1) do |i|
|
1006
|
-
namespace =
|
924
|
+
namespace = nesting[0...i] #: as !nil
|
925
|
+
.join("::")
|
1007
926
|
|
1008
927
|
# If we find an entry with `full_name` directly, then we can already return it, even if it contains aliases -
|
1009
928
|
# because the user might be trying to jump to the alias definition.
|
@@ -1019,17 +938,7 @@ module RubyIndexer
|
|
1019
938
|
nil
|
1020
939
|
end
|
1021
940
|
|
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
|
941
|
+
#: (String name, Array[String] nesting, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
1033
942
|
def lookup_ancestor_chain(name, nesting, seen_names)
|
1034
943
|
*nesting_parts, constant_name = build_non_redundant_full_name(name, nesting).split("::")
|
1035
944
|
return if nesting_parts.empty?
|
@@ -1037,7 +946,9 @@ module RubyIndexer
|
|
1037
946
|
namespace_entries = resolve(nesting_parts.join("::"), [], seen_names)
|
1038
947
|
return unless namespace_entries
|
1039
948
|
|
1040
|
-
|
949
|
+
namespace_name = namespace_entries.first #: as !nil
|
950
|
+
.name
|
951
|
+
ancestors = nesting_parts.empty? ? [] : linearized_ancestors_of(namespace_name)
|
1041
952
|
|
1042
953
|
ancestors.each do |ancestor_name|
|
1043
954
|
entries = direct_or_aliased_constant("#{ancestor_name}::#{constant_name}", seen_names)
|
@@ -1049,17 +960,7 @@ module RubyIndexer
|
|
1049
960
|
nil
|
1050
961
|
end
|
1051
962
|
|
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
|
963
|
+
#: (String? name, Array[String] nesting) -> Array[Array[(Entry::Namespace | Entry::ConstantAlias | Entry::UnresolvedConstantAlias | Entry::Constant)]]
|
1063
964
|
def inherited_constant_completion_candidates(name, nesting)
|
1064
965
|
namespace_entries = if name
|
1065
966
|
*nesting_parts, constant_name = build_non_redundant_full_name(name, nesting).split("::")
|
@@ -1071,7 +972,9 @@ module RubyIndexer
|
|
1071
972
|
end
|
1072
973
|
return [] unless namespace_entries
|
1073
974
|
|
1074
|
-
|
975
|
+
namespace_name = namespace_entries.first #: as !nil
|
976
|
+
.name
|
977
|
+
ancestors = linearized_ancestors_of(namespace_name)
|
1075
978
|
candidates = ancestors.flat_map do |ancestor_name|
|
1076
979
|
@entries_tree.search("#{ancestor_name}::#{constant_name}")
|
1077
980
|
end
|
@@ -1079,7 +982,8 @@ module RubyIndexer
|
|
1079
982
|
# For candidates with the same name, we must only show the first entry in the inheritance chain, since that's the
|
1080
983
|
# one the user will be referring to in completion
|
1081
984
|
completion_items = candidates.each_with_object({}) do |entries, hash|
|
1082
|
-
*parts, short_name =
|
985
|
+
*parts, short_name = entries.first #: as !nil
|
986
|
+
.name.split("::")
|
1083
987
|
namespace_name = parts.join("::")
|
1084
988
|
ancestor_index = ancestors.index(namespace_name)
|
1085
989
|
existing_entry, existing_entry_index = hash[short_name]
|
@@ -1098,7 +1002,7 @@ module RubyIndexer
|
|
1098
1002
|
# inside of the ["A", "B"] nesting, then we should not concatenate the nesting with the name or else we'll end up
|
1099
1003
|
# with `A::B::A::B::Foo`. This method will remove any redundant parts from the final name based on the reference and
|
1100
1004
|
# the nesting
|
1101
|
-
|
1005
|
+
#: (String name, Array[String] nesting) -> String
|
1102
1006
|
def build_non_redundant_full_name(name, nesting)
|
1103
1007
|
# If there's no nesting, then we can just return the name as is
|
1104
1008
|
return name if nesting.empty?
|
@@ -1115,44 +1019,22 @@ module RubyIndexer
|
|
1115
1019
|
# Otherwise, push all of the leading parts of the nesting that aren't redundant into the name. For example, if we
|
1116
1020
|
# have a reference to `Foo::Bar` inside the `[Namespace, Foo]` nesting, then only the `Foo` part is redundant, but
|
1117
1021
|
# we still need to include the `Namespace` part
|
1118
|
-
|
1022
|
+
name_parts.unshift(*nesting[0...first_redundant_part])
|
1119
1023
|
name_parts.join("::")
|
1120
1024
|
end
|
1121
1025
|
|
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
|
1026
|
+
#: (String full_name, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
1134
1027
|
def direct_or_aliased_constant(full_name, seen_names)
|
1135
1028
|
entries = @entries[full_name] || @entries[follow_aliased_namespace(full_name)]
|
1136
1029
|
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
Entry::Namespace,
|
1141
|
-
Entry::ConstantAlias,
|
1142
|
-
Entry::UnresolvedConstantAlias,
|
1143
|
-
)]),
|
1144
|
-
)
|
1030
|
+
entries&.map do |e|
|
1031
|
+
e.is_a?(Entry::UnresolvedConstantAlias) ? resolve_alias(e, seen_names) : e
|
1032
|
+
end #: as Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias])?
|
1145
1033
|
end
|
1146
1034
|
|
1147
1035
|
# Attempt to resolve a given unresolved method alias. This method returns the resolved alias if we managed to
|
1148
1036
|
# 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
|
1037
|
+
#: (Entry::UnresolvedMethodAlias entry, String receiver_name, Array[String] seen_names) -> (Entry::MethodAlias | Entry::UnresolvedMethodAlias)
|
1156
1038
|
def resolve_method_alias(entry, receiver_name, seen_names)
|
1157
1039
|
new_name = entry.new_name
|
1158
1040
|
return entry if new_name == entry.old_name
|
@@ -1163,8 +1045,11 @@ module RubyIndexer
|
|
1163
1045
|
target_method_entries = resolve_method(entry.old_name, receiver_name, seen_names)
|
1164
1046
|
return entry unless target_method_entries
|
1165
1047
|
|
1166
|
-
resolved_alias = Entry::MethodAlias.new(
|
1167
|
-
|
1048
|
+
resolved_alias = Entry::MethodAlias.new(
|
1049
|
+
target_method_entries.first, #: as !nil
|
1050
|
+
entry,
|
1051
|
+
)
|
1052
|
+
original_entries = @entries[new_name] #: as !nil
|
1168
1053
|
original_entries.delete(entry)
|
1169
1054
|
original_entries << resolved_alias
|
1170
1055
|
resolved_alias
|