ruby-lsp 0.23.11 → 0.25.0
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 +18 -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 +106 -236
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +155 -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 -8
- 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 +170 -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 +73 -86
- data/lib/ruby_lsp/base_server.rb +41 -42
- data/lib/ruby_lsp/client_capabilities.rb +16 -13
- data/lib/ruby_lsp/document.rb +201 -98
- 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 +76 -74
- 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 +203 -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 +30 -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 +266 -143
- data/lib/ruby_lsp/setup_bundler.rb +113 -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 +236 -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
|
|
|
@@ -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]]
|
|
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,50 @@ 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
|
+
#: -> void
|
|
665
|
+
def clear_ancestors
|
|
666
|
+
@ancestors.clear
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
#: -> bool
|
|
740
670
|
def empty?
|
|
741
671
|
@entries.empty?
|
|
742
672
|
end
|
|
743
673
|
|
|
744
|
-
|
|
674
|
+
#: -> Array[String]
|
|
745
675
|
def names
|
|
746
676
|
@entries.keys
|
|
747
677
|
end
|
|
748
678
|
|
|
749
|
-
|
|
679
|
+
#: (String name) -> bool
|
|
750
680
|
def indexed?(name)
|
|
751
681
|
@entries.key?(name)
|
|
752
682
|
end
|
|
753
683
|
|
|
754
|
-
|
|
684
|
+
#: -> Integer
|
|
755
685
|
def length
|
|
756
686
|
@entries.count
|
|
757
687
|
end
|
|
758
688
|
|
|
759
|
-
|
|
689
|
+
#: (String name) -> Entry::SingletonClass
|
|
760
690
|
def existing_or_new_singleton_class(name)
|
|
761
691
|
*_namespace, unqualified_name = name.split("::")
|
|
762
692
|
full_singleton_name = "#{name}::<Class:#{unqualified_name}>"
|
|
763
|
-
singleton =
|
|
693
|
+
singleton = self[full_singleton_name]&.first #: as Entry::SingletonClass?
|
|
764
694
|
|
|
765
695
|
unless singleton
|
|
766
|
-
attached_ancestor =
|
|
696
|
+
attached_ancestor = self[name]&.first #: as !nil
|
|
767
697
|
|
|
768
698
|
singleton = Entry::SingletonClass.new(
|
|
769
699
|
[full_singleton_name],
|
|
@@ -779,12 +709,7 @@ module RubyIndexer
|
|
|
779
709
|
singleton
|
|
780
710
|
end
|
|
781
711
|
|
|
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
|
|
712
|
+
#: [T] (String uri, ?Class[(T & Entry)]? type) -> (Array[Entry] | Array[T])?
|
|
788
713
|
def entries_for(uri, type = nil)
|
|
789
714
|
entries = @uris_to_entries[uri.to_s]
|
|
790
715
|
return entries unless type
|
|
@@ -796,12 +721,13 @@ module RubyIndexer
|
|
|
796
721
|
|
|
797
722
|
# Always returns the linearized ancestors for the attached class, regardless of whether `name` refers to a singleton
|
|
798
723
|
# or attached namespace
|
|
799
|
-
|
|
724
|
+
#: (String name) -> Array[String]
|
|
800
725
|
def linearized_attached_ancestors(name)
|
|
801
726
|
name_parts = name.split("::")
|
|
802
727
|
|
|
803
728
|
if name_parts.last&.start_with?("<Class:")
|
|
804
|
-
attached_name =
|
|
729
|
+
attached_name = name_parts[0..-2] #: as !nil
|
|
730
|
+
.join("::")
|
|
805
731
|
linearized_ancestors_of(attached_name)
|
|
806
732
|
else
|
|
807
733
|
linearized_ancestors_of(name)
|
|
@@ -809,7 +735,7 @@ module RubyIndexer
|
|
|
809
735
|
end
|
|
810
736
|
|
|
811
737
|
# Runs the registered included hooks
|
|
812
|
-
|
|
738
|
+
#: (String fully_qualified_name, Array[String] nesting) -> void
|
|
813
739
|
def run_included_hooks(fully_qualified_name, nesting)
|
|
814
740
|
return if @included_hooks.empty?
|
|
815
741
|
|
|
@@ -824,7 +750,8 @@ module RubyIndexer
|
|
|
824
750
|
resolved_modules = resolve(operation.module_name, nesting)
|
|
825
751
|
next unless resolved_modules
|
|
826
752
|
|
|
827
|
-
module_name =
|
|
753
|
+
module_name = resolved_modules.first #: as !nil
|
|
754
|
+
.name
|
|
828
755
|
|
|
829
756
|
# Then we grab any hooks registered for that module
|
|
830
757
|
hooks = @included_hooks[module_name]
|
|
@@ -838,13 +765,7 @@ module RubyIndexer
|
|
|
838
765
|
|
|
839
766
|
# Linearize mixins for an array of namespace entries. This method will mutate the `ancestors` array with the
|
|
840
767
|
# 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
|
|
768
|
+
#: (Array[String] ancestors, Array[Entry::Namespace] namespace_entries, Array[String] nesting) -> void
|
|
848
769
|
def linearize_mixins(ancestors, namespace_entries, nesting)
|
|
849
770
|
mixin_operations = namespace_entries.flat_map(&:mixin_operations)
|
|
850
771
|
main_namespace_index = 0
|
|
@@ -853,7 +774,8 @@ module RubyIndexer
|
|
|
853
774
|
resolved_module = resolve(operation.module_name, nesting)
|
|
854
775
|
next unless resolved_module
|
|
855
776
|
|
|
856
|
-
module_fully_qualified_name =
|
|
777
|
+
module_fully_qualified_name = resolved_module.first #: as !nil
|
|
778
|
+
.name
|
|
857
779
|
|
|
858
780
|
case operation
|
|
859
781
|
when Entry::Prepend
|
|
@@ -865,36 +787,27 @@ module RubyIndexer
|
|
|
865
787
|
# When there are duplicate prepended modules, we have to insert the new prepends after the existing ones. For
|
|
866
788
|
# example, if the current ancestors are `["A", "Foo"]` and we try to prepend `["A", "B"]`, then `"B"` has to
|
|
867
789
|
# be inserted after `"A`
|
|
868
|
-
|
|
790
|
+
prepended_ancestors = ancestors[0...main_namespace_index] #: as !nil
|
|
791
|
+
uniq_prepends = linearized_prepends - prepended_ancestors
|
|
869
792
|
insert_position = linearized_prepends.length - uniq_prepends.length
|
|
870
793
|
|
|
871
|
-
|
|
872
|
-
insert_position,
|
|
873
|
-
*(linearized_prepends - T.must(ancestors[0...main_namespace_index])),
|
|
874
|
-
)
|
|
794
|
+
ancestors #: as untyped
|
|
795
|
+
.insert(insert_position, *uniq_prepends)
|
|
875
796
|
|
|
876
797
|
main_namespace_index += linearized_prepends.length
|
|
877
798
|
when Entry::Include
|
|
878
799
|
# When including a module, Ruby will always prevent duplicate entries in case the module has already been
|
|
879
800
|
# prepended or included
|
|
880
801
|
linearized_includes = linearized_ancestors_of(module_fully_qualified_name)
|
|
881
|
-
|
|
802
|
+
ancestors #: as untyped
|
|
803
|
+
.insert(main_namespace_index + 1, *(linearized_includes - ancestors))
|
|
882
804
|
end
|
|
883
805
|
end
|
|
884
806
|
end
|
|
885
807
|
|
|
886
808
|
# Linearize the superclass of a given namespace (including modules with the implicit `Module` superclass). This
|
|
887
809
|
# 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
|
|
810
|
+
#: (Array[String] ancestors, String attached_class_name, String fully_qualified_name, Array[Entry::Namespace] namespace_entries, Array[String] nesting, Integer singleton_levels) -> void
|
|
898
811
|
def linearize_superclass( # rubocop:disable Metrics/ParameterLists
|
|
899
812
|
ancestors,
|
|
900
813
|
attached_class_name,
|
|
@@ -905,19 +818,27 @@ module RubyIndexer
|
|
|
905
818
|
)
|
|
906
819
|
# Find the first class entry that has a parent class. Notice that if the developer makes a mistake and inherits
|
|
907
820
|
# from two different classes in different files, we simply ignore it
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
821
|
+
possible_parents = singleton_levels > 0 ? self[attached_class_name] : namespace_entries
|
|
822
|
+
superclass = nil #: Entry::Class?
|
|
823
|
+
|
|
824
|
+
possible_parents&.each do |n|
|
|
825
|
+
# Ignore non class entries
|
|
826
|
+
next unless n.is_a?(Entry::Class)
|
|
827
|
+
|
|
828
|
+
parent_class = n.parent_class
|
|
829
|
+
next unless parent_class
|
|
830
|
+
|
|
831
|
+
# Always set the superclass, but break early if we found one that isn't `::Object` (meaning we found an explicit
|
|
832
|
+
# parent class and not the implicit default). Note that when setting different parents to the same class, which
|
|
833
|
+
# is invalid, we pick whatever is the first one we find
|
|
834
|
+
superclass = n
|
|
835
|
+
break if parent_class != "::Object"
|
|
836
|
+
end
|
|
916
837
|
|
|
917
838
|
if superclass
|
|
918
839
|
# If the user makes a mistake and creates a class that inherits from itself, this method would throw a stack
|
|
919
840
|
# error. We need to ensure that this isn't the case
|
|
920
|
-
parent_class =
|
|
841
|
+
parent_class = superclass.parent_class #: as !nil
|
|
921
842
|
|
|
922
843
|
resolved_parent_class = resolve(parent_class, nesting)
|
|
923
844
|
parent_class_name = resolved_parent_class&.first&.name
|
|
@@ -946,7 +867,7 @@ module RubyIndexer
|
|
|
946
867
|
elsif singleton_levels > 0
|
|
947
868
|
# When computing the linearization for a module's singleton class, it inherits from the linearized ancestors of
|
|
948
869
|
# the `Module` class
|
|
949
|
-
mod =
|
|
870
|
+
mod = self[attached_class_name]&.find { |n| n.is_a?(Entry::Module) } #: as Entry::Module?
|
|
950
871
|
|
|
951
872
|
if mod
|
|
952
873
|
module_class_name_parts = ["Module"]
|
|
@@ -962,12 +883,7 @@ module RubyIndexer
|
|
|
962
883
|
|
|
963
884
|
# Attempts to resolve an UnresolvedAlias into a resolved Alias. If the unresolved alias is pointing to a constant
|
|
964
885
|
# 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
|
|
886
|
+
#: (Entry::UnresolvedConstantAlias entry, Array[String] seen_names) -> (Entry::ConstantAlias | Entry::UnresolvedConstantAlias)
|
|
971
887
|
def resolve_alias(entry, seen_names)
|
|
972
888
|
alias_name = entry.name
|
|
973
889
|
return entry if seen_names.include?(alias_name)
|
|
@@ -977,11 +893,12 @@ module RubyIndexer
|
|
|
977
893
|
target = resolve(entry.target, entry.nesting, seen_names)
|
|
978
894
|
return entry unless target
|
|
979
895
|
|
|
980
|
-
target_name =
|
|
896
|
+
target_name = target.first #: as !nil
|
|
897
|
+
.name
|
|
981
898
|
resolved_alias = Entry::ConstantAlias.new(target_name, entry)
|
|
982
899
|
|
|
983
900
|
# Replace the UnresolvedAlias by a resolved one so that we don't have to do this again later
|
|
984
|
-
original_entries =
|
|
901
|
+
original_entries = @entries[alias_name] #: as !nil
|
|
985
902
|
original_entries.delete(entry)
|
|
986
903
|
original_entries << resolved_alias
|
|
987
904
|
|
|
@@ -990,20 +907,11 @@ module RubyIndexer
|
|
|
990
907
|
resolved_alias
|
|
991
908
|
end
|
|
992
909
|
|
|
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
|
|
910
|
+
#: (String name, Array[String] nesting, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
|
1004
911
|
def lookup_enclosing_scopes(name, nesting, seen_names)
|
|
1005
912
|
nesting.length.downto(1) do |i|
|
|
1006
|
-
namespace =
|
|
913
|
+
namespace = nesting[0...i] #: as !nil
|
|
914
|
+
.join("::")
|
|
1007
915
|
|
|
1008
916
|
# If we find an entry with `full_name` directly, then we can already return it, even if it contains aliases -
|
|
1009
917
|
# because the user might be trying to jump to the alias definition.
|
|
@@ -1019,17 +927,7 @@ module RubyIndexer
|
|
|
1019
927
|
nil
|
|
1020
928
|
end
|
|
1021
929
|
|
|
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
|
|
930
|
+
#: (String name, Array[String] nesting, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
|
1033
931
|
def lookup_ancestor_chain(name, nesting, seen_names)
|
|
1034
932
|
*nesting_parts, constant_name = build_non_redundant_full_name(name, nesting).split("::")
|
|
1035
933
|
return if nesting_parts.empty?
|
|
@@ -1037,7 +935,9 @@ module RubyIndexer
|
|
|
1037
935
|
namespace_entries = resolve(nesting_parts.join("::"), [], seen_names)
|
|
1038
936
|
return unless namespace_entries
|
|
1039
937
|
|
|
1040
|
-
|
|
938
|
+
namespace_name = namespace_entries.first #: as !nil
|
|
939
|
+
.name
|
|
940
|
+
ancestors = nesting_parts.empty? ? [] : linearized_ancestors_of(namespace_name)
|
|
1041
941
|
|
|
1042
942
|
ancestors.each do |ancestor_name|
|
|
1043
943
|
entries = direct_or_aliased_constant("#{ancestor_name}::#{constant_name}", seen_names)
|
|
@@ -1049,17 +949,7 @@ module RubyIndexer
|
|
|
1049
949
|
nil
|
|
1050
950
|
end
|
|
1051
951
|
|
|
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
|
|
952
|
+
#: (String? name, Array[String] nesting) -> Array[Array[(Entry::Namespace | Entry::ConstantAlias | Entry::UnresolvedConstantAlias | Entry::Constant)]]
|
|
1063
953
|
def inherited_constant_completion_candidates(name, nesting)
|
|
1064
954
|
namespace_entries = if name
|
|
1065
955
|
*nesting_parts, constant_name = build_non_redundant_full_name(name, nesting).split("::")
|
|
@@ -1071,7 +961,9 @@ module RubyIndexer
|
|
|
1071
961
|
end
|
|
1072
962
|
return [] unless namespace_entries
|
|
1073
963
|
|
|
1074
|
-
|
|
964
|
+
namespace_name = namespace_entries.first #: as !nil
|
|
965
|
+
.name
|
|
966
|
+
ancestors = linearized_ancestors_of(namespace_name)
|
|
1075
967
|
candidates = ancestors.flat_map do |ancestor_name|
|
|
1076
968
|
@entries_tree.search("#{ancestor_name}::#{constant_name}")
|
|
1077
969
|
end
|
|
@@ -1079,7 +971,8 @@ module RubyIndexer
|
|
|
1079
971
|
# For candidates with the same name, we must only show the first entry in the inheritance chain, since that's the
|
|
1080
972
|
# one the user will be referring to in completion
|
|
1081
973
|
completion_items = candidates.each_with_object({}) do |entries, hash|
|
|
1082
|
-
*parts, short_name =
|
|
974
|
+
*parts, short_name = entries.first #: as !nil
|
|
975
|
+
.name.split("::")
|
|
1083
976
|
namespace_name = parts.join("::")
|
|
1084
977
|
ancestor_index = ancestors.index(namespace_name)
|
|
1085
978
|
existing_entry, existing_entry_index = hash[short_name]
|
|
@@ -1098,7 +991,7 @@ module RubyIndexer
|
|
|
1098
991
|
# inside of the ["A", "B"] nesting, then we should not concatenate the nesting with the name or else we'll end up
|
|
1099
992
|
# with `A::B::A::B::Foo`. This method will remove any redundant parts from the final name based on the reference and
|
|
1100
993
|
# the nesting
|
|
1101
|
-
|
|
994
|
+
#: (String name, Array[String] nesting) -> String
|
|
1102
995
|
def build_non_redundant_full_name(name, nesting)
|
|
1103
996
|
# If there's no nesting, then we can just return the name as is
|
|
1104
997
|
return name if nesting.empty?
|
|
@@ -1115,44 +1008,22 @@ module RubyIndexer
|
|
|
1115
1008
|
# Otherwise, push all of the leading parts of the nesting that aren't redundant into the name. For example, if we
|
|
1116
1009
|
# have a reference to `Foo::Bar` inside the `[Namespace, Foo]` nesting, then only the `Foo` part is redundant, but
|
|
1117
1010
|
# we still need to include the `Namespace` part
|
|
1118
|
-
|
|
1011
|
+
name_parts.unshift(*nesting[0...first_redundant_part])
|
|
1119
1012
|
name_parts.join("::")
|
|
1120
1013
|
end
|
|
1121
1014
|
|
|
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
|
|
1015
|
+
#: (String full_name, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
|
1134
1016
|
def direct_or_aliased_constant(full_name, seen_names)
|
|
1135
1017
|
entries = @entries[full_name] || @entries[follow_aliased_namespace(full_name)]
|
|
1136
1018
|
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
Entry::Namespace,
|
|
1141
|
-
Entry::ConstantAlias,
|
|
1142
|
-
Entry::UnresolvedConstantAlias,
|
|
1143
|
-
)]),
|
|
1144
|
-
)
|
|
1019
|
+
entries&.map do |e|
|
|
1020
|
+
e.is_a?(Entry::UnresolvedConstantAlias) ? resolve_alias(e, seen_names) : e
|
|
1021
|
+
end #: as Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias])?
|
|
1145
1022
|
end
|
|
1146
1023
|
|
|
1147
1024
|
# Attempt to resolve a given unresolved method alias. This method returns the resolved alias if we managed to
|
|
1148
1025
|
# 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
|
|
1026
|
+
#: (Entry::UnresolvedMethodAlias entry, String receiver_name, Array[String] seen_names) -> (Entry::MethodAlias | Entry::UnresolvedMethodAlias)
|
|
1156
1027
|
def resolve_method_alias(entry, receiver_name, seen_names)
|
|
1157
1028
|
new_name = entry.new_name
|
|
1158
1029
|
return entry if new_name == entry.old_name
|
|
@@ -1163,8 +1034,11 @@ module RubyIndexer
|
|
|
1163
1034
|
target_method_entries = resolve_method(entry.old_name, receiver_name, seen_names)
|
|
1164
1035
|
return entry unless target_method_entries
|
|
1165
1036
|
|
|
1166
|
-
resolved_alias = Entry::MethodAlias.new(
|
|
1167
|
-
|
|
1037
|
+
resolved_alias = Entry::MethodAlias.new(
|
|
1038
|
+
target_method_entries.first, #: as !nil
|
|
1039
|
+
entry,
|
|
1040
|
+
)
|
|
1041
|
+
original_entries = @entries[new_name] #: as !nil
|
|
1168
1042
|
original_entries.delete(entry)
|
|
1169
1043
|
original_entries << resolved_alias
|
|
1170
1044
|
resolved_alias
|