ruby-lsp 0.23.11 → 0.26.2
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 +183 -284
- 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 +225 -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 +149 -151
- 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 +231 -0
- data/lib/ruby_lsp/listeners/test_discovery.rb +107 -0
- data/lib/ruby_lsp/listeners/test_style.rb +207 -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 +139 -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 +23 -61
- 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 +38 -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 +305 -198
- data/lib/ruby_lsp/setup_bundler.rb +122 -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,14 +416,18 @@ 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 =
|
|
485
|
-
|
|
425
|
+
current_name = parts[0..i] #: as !nil
|
|
426
|
+
.join("::")
|
|
427
|
+
|
|
428
|
+
entry = unless seen_names.include?(current_name)
|
|
429
|
+
@entries[current_name]&.first
|
|
430
|
+
end
|
|
486
431
|
|
|
487
432
|
case entry
|
|
488
433
|
when Entry::ConstantAlias
|
|
@@ -498,7 +443,9 @@ module RubyIndexer
|
|
|
498
443
|
target = resolved.target
|
|
499
444
|
return follow_aliased_namespace("#{target}::#{real_parts.join("::")}", seen_names)
|
|
500
445
|
else
|
|
501
|
-
real_parts.unshift(
|
|
446
|
+
real_parts.unshift(
|
|
447
|
+
parts[i], #: as !nil
|
|
448
|
+
)
|
|
502
449
|
end
|
|
503
450
|
end
|
|
504
451
|
|
|
@@ -508,14 +455,7 @@ module RubyIndexer
|
|
|
508
455
|
# Attempts to find methods for a resolved fully qualified receiver name. Do not provide the `seen_names` parameter
|
|
509
456
|
# as it is used only internally to prevent infinite loops when resolving circular aliases
|
|
510
457
|
# 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
|
|
458
|
+
#: (String method_name, String receiver_name, ?Array[String] seen_names, ?inherited_only: bool) -> Array[(Entry::Member | Entry::MethodAlias)]?
|
|
519
459
|
def resolve_method(method_name, receiver_name, seen_names = [], inherited_only: false)
|
|
520
460
|
method_entries = self[method_name]
|
|
521
461
|
return unless method_entries
|
|
@@ -553,7 +493,7 @@ module RubyIndexer
|
|
|
553
493
|
# module that prepends another module, then the prepend module appears before the included module.
|
|
554
494
|
#
|
|
555
495
|
# The order of ancestors is [linearized_prepends, self, linearized_includes, linearized_superclass]
|
|
556
|
-
|
|
496
|
+
#: (String fully_qualified_name) -> Array[String]
|
|
557
497
|
def linearized_ancestors_of(fully_qualified_name)
|
|
558
498
|
# If we already computed the ancestors for this namespace, return it straight away
|
|
559
499
|
cached_ancestors = @ancestors[fully_qualified_name]
|
|
@@ -602,11 +542,12 @@ module RubyIndexer
|
|
|
602
542
|
|
|
603
543
|
# The original nesting where we discovered this namespace, so that we resolve the correct names of the
|
|
604
544
|
# included/prepended/extended modules and parent classes
|
|
605
|
-
nesting =
|
|
545
|
+
nesting = namespaces.first #: as !nil
|
|
546
|
+
.nesting.flat_map { |n| n.split("::") }
|
|
606
547
|
|
|
607
548
|
if nesting.any?
|
|
608
549
|
singleton_levels.times do
|
|
609
|
-
nesting << "<Class:#{
|
|
550
|
+
nesting << "<Class:#{nesting.last}>"
|
|
610
551
|
end
|
|
611
552
|
end
|
|
612
553
|
|
|
@@ -631,9 +572,9 @@ module RubyIndexer
|
|
|
631
572
|
|
|
632
573
|
# Resolves an instance variable name for a given owner name. This method will linearize the ancestors of the owner
|
|
633
574
|
# and find inherited instance variables as well
|
|
634
|
-
|
|
575
|
+
#: (String variable_name, String owner_name) -> Array[Entry::InstanceVariable]?
|
|
635
576
|
def resolve_instance_variable(variable_name, owner_name)
|
|
636
|
-
entries =
|
|
577
|
+
entries = self[variable_name] #: as Array[Entry::InstanceVariable]?
|
|
637
578
|
return unless entries
|
|
638
579
|
|
|
639
580
|
ancestors = linearized_ancestors_of(owner_name)
|
|
@@ -642,7 +583,7 @@ module RubyIndexer
|
|
|
642
583
|
entries.select { |e| ancestors.include?(e.owner&.name) }
|
|
643
584
|
end
|
|
644
585
|
|
|
645
|
-
|
|
586
|
+
#: (String variable_name, String owner_name) -> Array[Entry::ClassVariable]?
|
|
646
587
|
def resolve_class_variable(variable_name, owner_name)
|
|
647
588
|
entries = self[variable_name]&.grep(Entry::ClassVariable)
|
|
648
589
|
return unless entries&.any?
|
|
@@ -655,11 +596,9 @@ module RubyIndexer
|
|
|
655
596
|
|
|
656
597
|
# Returns a list of possible candidates for completion of instance variables for a given owner name. The name must
|
|
657
598
|
# include the `@` prefix
|
|
658
|
-
|
|
659
|
-
params(name: String, owner_name: String).returns(T::Array[T.any(Entry::InstanceVariable, Entry::ClassVariable)])
|
|
660
|
-
end
|
|
599
|
+
#: (String name, String owner_name) -> Array[(Entry::InstanceVariable | Entry::ClassVariable)]
|
|
661
600
|
def instance_variable_completion_candidates(name, owner_name)
|
|
662
|
-
entries =
|
|
601
|
+
entries = prefix_search(name).flatten #: as Array[Entry::InstanceVariable | Entry::ClassVariable]
|
|
663
602
|
# Avoid wasting time linearizing ancestors if we didn't find anything
|
|
664
603
|
return entries if entries.empty?
|
|
665
604
|
|
|
@@ -674,7 +613,8 @@ module RubyIndexer
|
|
|
674
613
|
name_parts = owner_name.split("::")
|
|
675
614
|
|
|
676
615
|
if name_parts.last&.start_with?("<Class:")
|
|
677
|
-
attached_name =
|
|
616
|
+
attached_name = name_parts[0..-2] #: as !nil
|
|
617
|
+
.join("::")
|
|
678
618
|
attached_ancestors = linearized_ancestors_of(attached_name)
|
|
679
619
|
variables.concat(class_variables.select { |e| attached_ancestors.any?(e.owner&.name) })
|
|
680
620
|
else
|
|
@@ -686,9 +626,9 @@ module RubyIndexer
|
|
|
686
626
|
variables
|
|
687
627
|
end
|
|
688
628
|
|
|
689
|
-
|
|
629
|
+
#: (String name, String owner_name) -> Array[Entry::ClassVariable]
|
|
690
630
|
def class_variable_completion_candidates(name, owner_name)
|
|
691
|
-
entries =
|
|
631
|
+
entries = prefix_search(name).flatten #: as Array[Entry::ClassVariable]
|
|
692
632
|
# Avoid wasting time linearizing ancestors if we didn't find anything
|
|
693
633
|
return entries if entries.empty?
|
|
694
634
|
|
|
@@ -702,9 +642,7 @@ module RubyIndexer
|
|
|
702
642
|
# declarations removed and that the ancestor linearization cache is cleared if necessary. If a block is passed, the
|
|
703
643
|
# consumer of this API has to handle deleting and inserting/updating entries in the index instead of passing the
|
|
704
644
|
# 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
|
|
645
|
+
#: (URI::Generic uri, ?String? source) ?{ (Index index) -> void } -> void
|
|
708
646
|
def handle_change(uri, source = nil, &block)
|
|
709
647
|
key = uri.to_s
|
|
710
648
|
original_entries = @uris_to_entries[key]
|
|
@@ -713,7 +651,10 @@ module RubyIndexer
|
|
|
713
651
|
block.call(self)
|
|
714
652
|
else
|
|
715
653
|
delete(uri)
|
|
716
|
-
index_single(
|
|
654
|
+
index_single(
|
|
655
|
+
uri,
|
|
656
|
+
source, #: as !nil
|
|
657
|
+
)
|
|
717
658
|
end
|
|
718
659
|
|
|
719
660
|
updated_entries = @uris_to_entries[key]
|
|
@@ -723,47 +664,50 @@ module RubyIndexer
|
|
|
723
664
|
# indirect means like including a module that than includes the ancestor. Trying to figure out exactly which
|
|
724
665
|
# ancestors need to be deleted is too expensive. Therefore, if any of the namespace entries has a change to their
|
|
725
666
|
# 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] }
|
|
667
|
+
original_map = original_entries
|
|
668
|
+
.select { |e| e.is_a?(Entry::Namespace) } #: as Array[Entry::Namespace]
|
|
669
|
+
.to_h { |e| [e.name, e.ancestor_hash] }
|
|
730
670
|
|
|
731
|
-
updated_map =
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
).to_h { |e| [e.name, e.ancestor_hash] }
|
|
671
|
+
updated_map = updated_entries
|
|
672
|
+
.select { |e| e.is_a?(Entry::Namespace) } #: as Array[Entry::Namespace]
|
|
673
|
+
.to_h { |e| [e.name, e.ancestor_hash] }
|
|
735
674
|
|
|
736
675
|
@ancestors.clear if original_map.any? { |name, hash| updated_map[name] != hash }
|
|
737
676
|
end
|
|
738
677
|
|
|
739
|
-
|
|
678
|
+
#: -> void
|
|
679
|
+
def clear_ancestors
|
|
680
|
+
@ancestors.clear
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
#: -> bool
|
|
740
684
|
def empty?
|
|
741
685
|
@entries.empty?
|
|
742
686
|
end
|
|
743
687
|
|
|
744
|
-
|
|
688
|
+
#: -> Array[String]
|
|
745
689
|
def names
|
|
746
690
|
@entries.keys
|
|
747
691
|
end
|
|
748
692
|
|
|
749
|
-
|
|
693
|
+
#: (String name) -> bool
|
|
750
694
|
def indexed?(name)
|
|
751
695
|
@entries.key?(name)
|
|
752
696
|
end
|
|
753
697
|
|
|
754
|
-
|
|
698
|
+
#: -> Integer
|
|
755
699
|
def length
|
|
756
700
|
@entries.count
|
|
757
701
|
end
|
|
758
702
|
|
|
759
|
-
|
|
703
|
+
#: (String name) -> Entry::SingletonClass
|
|
760
704
|
def existing_or_new_singleton_class(name)
|
|
761
705
|
*_namespace, unqualified_name = name.split("::")
|
|
762
706
|
full_singleton_name = "#{name}::<Class:#{unqualified_name}>"
|
|
763
|
-
singleton =
|
|
707
|
+
singleton = self[full_singleton_name]&.first #: as Entry::SingletonClass?
|
|
764
708
|
|
|
765
709
|
unless singleton
|
|
766
|
-
attached_ancestor =
|
|
710
|
+
attached_ancestor = self[name]&.first #: as !nil
|
|
767
711
|
|
|
768
712
|
singleton = Entry::SingletonClass.new(
|
|
769
713
|
[full_singleton_name],
|
|
@@ -779,12 +723,7 @@ module RubyIndexer
|
|
|
779
723
|
singleton
|
|
780
724
|
end
|
|
781
725
|
|
|
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
|
|
726
|
+
#: [T] (String uri, ?Class[(T & Entry)]? type) -> (Array[Entry] | Array[T])?
|
|
788
727
|
def entries_for(uri, type = nil)
|
|
789
728
|
entries = @uris_to_entries[uri.to_s]
|
|
790
729
|
return entries unless type
|
|
@@ -796,12 +735,13 @@ module RubyIndexer
|
|
|
796
735
|
|
|
797
736
|
# Always returns the linearized ancestors for the attached class, regardless of whether `name` refers to a singleton
|
|
798
737
|
# or attached namespace
|
|
799
|
-
|
|
738
|
+
#: (String name) -> Array[String]
|
|
800
739
|
def linearized_attached_ancestors(name)
|
|
801
740
|
name_parts = name.split("::")
|
|
802
741
|
|
|
803
742
|
if name_parts.last&.start_with?("<Class:")
|
|
804
|
-
attached_name =
|
|
743
|
+
attached_name = name_parts[0..-2] #: as !nil
|
|
744
|
+
.join("::")
|
|
805
745
|
linearized_ancestors_of(attached_name)
|
|
806
746
|
else
|
|
807
747
|
linearized_ancestors_of(name)
|
|
@@ -809,7 +749,7 @@ module RubyIndexer
|
|
|
809
749
|
end
|
|
810
750
|
|
|
811
751
|
# Runs the registered included hooks
|
|
812
|
-
|
|
752
|
+
#: (String fully_qualified_name, Array[String] nesting) -> void
|
|
813
753
|
def run_included_hooks(fully_qualified_name, nesting)
|
|
814
754
|
return if @included_hooks.empty?
|
|
815
755
|
|
|
@@ -824,7 +764,8 @@ module RubyIndexer
|
|
|
824
764
|
resolved_modules = resolve(operation.module_name, nesting)
|
|
825
765
|
next unless resolved_modules
|
|
826
766
|
|
|
827
|
-
module_name =
|
|
767
|
+
module_name = resolved_modules.first #: as !nil
|
|
768
|
+
.name
|
|
828
769
|
|
|
829
770
|
# Then we grab any hooks registered for that module
|
|
830
771
|
hooks = @included_hooks[module_name]
|
|
@@ -838,13 +779,7 @@ module RubyIndexer
|
|
|
838
779
|
|
|
839
780
|
# Linearize mixins for an array of namespace entries. This method will mutate the `ancestors` array with the
|
|
840
781
|
# 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
|
|
782
|
+
#: (Array[String] ancestors, Array[Entry::Namespace] namespace_entries, Array[String] nesting) -> void
|
|
848
783
|
def linearize_mixins(ancestors, namespace_entries, nesting)
|
|
849
784
|
mixin_operations = namespace_entries.flat_map(&:mixin_operations)
|
|
850
785
|
main_namespace_index = 0
|
|
@@ -853,7 +788,8 @@ module RubyIndexer
|
|
|
853
788
|
resolved_module = resolve(operation.module_name, nesting)
|
|
854
789
|
next unless resolved_module
|
|
855
790
|
|
|
856
|
-
module_fully_qualified_name =
|
|
791
|
+
module_fully_qualified_name = resolved_module.first #: as !nil
|
|
792
|
+
.name
|
|
857
793
|
|
|
858
794
|
case operation
|
|
859
795
|
when Entry::Prepend
|
|
@@ -865,36 +801,27 @@ module RubyIndexer
|
|
|
865
801
|
# When there are duplicate prepended modules, we have to insert the new prepends after the existing ones. For
|
|
866
802
|
# example, if the current ancestors are `["A", "Foo"]` and we try to prepend `["A", "B"]`, then `"B"` has to
|
|
867
803
|
# be inserted after `"A`
|
|
868
|
-
|
|
804
|
+
prepended_ancestors = ancestors[0...main_namespace_index] #: as !nil
|
|
805
|
+
uniq_prepends = linearized_prepends - prepended_ancestors
|
|
869
806
|
insert_position = linearized_prepends.length - uniq_prepends.length
|
|
870
807
|
|
|
871
|
-
|
|
872
|
-
insert_position,
|
|
873
|
-
*(linearized_prepends - T.must(ancestors[0...main_namespace_index])),
|
|
874
|
-
)
|
|
808
|
+
ancestors #: as untyped
|
|
809
|
+
.insert(insert_position, *uniq_prepends)
|
|
875
810
|
|
|
876
811
|
main_namespace_index += linearized_prepends.length
|
|
877
812
|
when Entry::Include
|
|
878
813
|
# When including a module, Ruby will always prevent duplicate entries in case the module has already been
|
|
879
814
|
# prepended or included
|
|
880
815
|
linearized_includes = linearized_ancestors_of(module_fully_qualified_name)
|
|
881
|
-
|
|
816
|
+
ancestors #: as untyped
|
|
817
|
+
.insert(main_namespace_index + 1, *(linearized_includes - ancestors))
|
|
882
818
|
end
|
|
883
819
|
end
|
|
884
820
|
end
|
|
885
821
|
|
|
886
822
|
# Linearize the superclass of a given namespace (including modules with the implicit `Module` superclass). This
|
|
887
823
|
# 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
|
|
824
|
+
#: (Array[String] ancestors, String attached_class_name, String fully_qualified_name, Array[Entry::Namespace] namespace_entries, Array[String] nesting, Integer singleton_levels) -> void
|
|
898
825
|
def linearize_superclass( # rubocop:disable Metrics/ParameterLists
|
|
899
826
|
ancestors,
|
|
900
827
|
attached_class_name,
|
|
@@ -905,19 +832,27 @@ module RubyIndexer
|
|
|
905
832
|
)
|
|
906
833
|
# Find the first class entry that has a parent class. Notice that if the developer makes a mistake and inherits
|
|
907
834
|
# from two different classes in different files, we simply ignore it
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
835
|
+
possible_parents = singleton_levels > 0 ? self[attached_class_name] : namespace_entries
|
|
836
|
+
superclass = nil #: Entry::Class?
|
|
837
|
+
|
|
838
|
+
possible_parents&.each do |n|
|
|
839
|
+
# Ignore non class entries
|
|
840
|
+
next unless n.is_a?(Entry::Class)
|
|
841
|
+
|
|
842
|
+
parent_class = n.parent_class
|
|
843
|
+
next unless parent_class
|
|
844
|
+
|
|
845
|
+
# Always set the superclass, but break early if we found one that isn't `::Object` (meaning we found an explicit
|
|
846
|
+
# parent class and not the implicit default). Note that when setting different parents to the same class, which
|
|
847
|
+
# is invalid, we pick whatever is the first one we find
|
|
848
|
+
superclass = n
|
|
849
|
+
break if parent_class != "::Object"
|
|
850
|
+
end
|
|
916
851
|
|
|
917
852
|
if superclass
|
|
918
853
|
# If the user makes a mistake and creates a class that inherits from itself, this method would throw a stack
|
|
919
854
|
# error. We need to ensure that this isn't the case
|
|
920
|
-
parent_class =
|
|
855
|
+
parent_class = superclass.parent_class #: as !nil
|
|
921
856
|
|
|
922
857
|
resolved_parent_class = resolve(parent_class, nesting)
|
|
923
858
|
parent_class_name = resolved_parent_class&.first&.name
|
|
@@ -946,7 +881,7 @@ module RubyIndexer
|
|
|
946
881
|
elsif singleton_levels > 0
|
|
947
882
|
# When computing the linearization for a module's singleton class, it inherits from the linearized ancestors of
|
|
948
883
|
# the `Module` class
|
|
949
|
-
mod =
|
|
884
|
+
mod = self[attached_class_name]&.find { |n| n.is_a?(Entry::Module) } #: as Entry::Module?
|
|
950
885
|
|
|
951
886
|
if mod
|
|
952
887
|
module_class_name_parts = ["Module"]
|
|
@@ -962,12 +897,7 @@ module RubyIndexer
|
|
|
962
897
|
|
|
963
898
|
# Attempts to resolve an UnresolvedAlias into a resolved Alias. If the unresolved alias is pointing to a constant
|
|
964
899
|
# 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
|
|
900
|
+
#: (Entry::UnresolvedConstantAlias entry, Array[String] seen_names) -> (Entry::ConstantAlias | Entry::UnresolvedConstantAlias)
|
|
971
901
|
def resolve_alias(entry, seen_names)
|
|
972
902
|
alias_name = entry.name
|
|
973
903
|
return entry if seen_names.include?(alias_name)
|
|
@@ -977,11 +907,15 @@ module RubyIndexer
|
|
|
977
907
|
target = resolve(entry.target, entry.nesting, seen_names)
|
|
978
908
|
return entry unless target
|
|
979
909
|
|
|
980
|
-
|
|
910
|
+
# Self referential alias can be unresolved we should bail out from resolving
|
|
911
|
+
return entry if target.first == entry
|
|
912
|
+
|
|
913
|
+
target_name = target.first #: as !nil
|
|
914
|
+
.name
|
|
981
915
|
resolved_alias = Entry::ConstantAlias.new(target_name, entry)
|
|
982
916
|
|
|
983
917
|
# Replace the UnresolvedAlias by a resolved one so that we don't have to do this again later
|
|
984
|
-
original_entries =
|
|
918
|
+
original_entries = @entries[alias_name] #: as !nil
|
|
985
919
|
original_entries.delete(entry)
|
|
986
920
|
original_entries << resolved_alias
|
|
987
921
|
|
|
@@ -990,20 +924,11 @@ module RubyIndexer
|
|
|
990
924
|
resolved_alias
|
|
991
925
|
end
|
|
992
926
|
|
|
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
|
|
927
|
+
#: (String name, Array[String] nesting, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
|
1004
928
|
def lookup_enclosing_scopes(name, nesting, seen_names)
|
|
1005
929
|
nesting.length.downto(1) do |i|
|
|
1006
|
-
namespace =
|
|
930
|
+
namespace = nesting[0...i] #: as !nil
|
|
931
|
+
.join("::")
|
|
1007
932
|
|
|
1008
933
|
# If we find an entry with `full_name` directly, then we can already return it, even if it contains aliases -
|
|
1009
934
|
# because the user might be trying to jump to the alias definition.
|
|
@@ -1019,17 +944,7 @@ module RubyIndexer
|
|
|
1019
944
|
nil
|
|
1020
945
|
end
|
|
1021
946
|
|
|
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
|
|
947
|
+
#: (String name, Array[String] nesting, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
|
1033
948
|
def lookup_ancestor_chain(name, nesting, seen_names)
|
|
1034
949
|
*nesting_parts, constant_name = build_non_redundant_full_name(name, nesting).split("::")
|
|
1035
950
|
return if nesting_parts.empty?
|
|
@@ -1037,7 +952,9 @@ module RubyIndexer
|
|
|
1037
952
|
namespace_entries = resolve(nesting_parts.join("::"), [], seen_names)
|
|
1038
953
|
return unless namespace_entries
|
|
1039
954
|
|
|
1040
|
-
|
|
955
|
+
namespace_name = namespace_entries.first #: as !nil
|
|
956
|
+
.name
|
|
957
|
+
ancestors = nesting_parts.empty? ? [] : linearized_ancestors_of(namespace_name)
|
|
1041
958
|
|
|
1042
959
|
ancestors.each do |ancestor_name|
|
|
1043
960
|
entries = direct_or_aliased_constant("#{ancestor_name}::#{constant_name}", seen_names)
|
|
@@ -1049,17 +966,7 @@ module RubyIndexer
|
|
|
1049
966
|
nil
|
|
1050
967
|
end
|
|
1051
968
|
|
|
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
|
|
969
|
+
#: (String? name, Array[String] nesting) -> Array[Array[(Entry::Namespace | Entry::ConstantAlias | Entry::UnresolvedConstantAlias | Entry::Constant)]]
|
|
1063
970
|
def inherited_constant_completion_candidates(name, nesting)
|
|
1064
971
|
namespace_entries = if name
|
|
1065
972
|
*nesting_parts, constant_name = build_non_redundant_full_name(name, nesting).split("::")
|
|
@@ -1071,7 +978,9 @@ module RubyIndexer
|
|
|
1071
978
|
end
|
|
1072
979
|
return [] unless namespace_entries
|
|
1073
980
|
|
|
1074
|
-
|
|
981
|
+
namespace_name = namespace_entries.first #: as !nil
|
|
982
|
+
.name
|
|
983
|
+
ancestors = linearized_ancestors_of(namespace_name)
|
|
1075
984
|
candidates = ancestors.flat_map do |ancestor_name|
|
|
1076
985
|
@entries_tree.search("#{ancestor_name}::#{constant_name}")
|
|
1077
986
|
end
|
|
@@ -1079,7 +988,8 @@ module RubyIndexer
|
|
|
1079
988
|
# For candidates with the same name, we must only show the first entry in the inheritance chain, since that's the
|
|
1080
989
|
# one the user will be referring to in completion
|
|
1081
990
|
completion_items = candidates.each_with_object({}) do |entries, hash|
|
|
1082
|
-
*parts, short_name =
|
|
991
|
+
*parts, short_name = entries.first #: as !nil
|
|
992
|
+
.name.split("::")
|
|
1083
993
|
namespace_name = parts.join("::")
|
|
1084
994
|
ancestor_index = ancestors.index(namespace_name)
|
|
1085
995
|
existing_entry, existing_entry_index = hash[short_name]
|
|
@@ -1098,7 +1008,7 @@ module RubyIndexer
|
|
|
1098
1008
|
# inside of the ["A", "B"] nesting, then we should not concatenate the nesting with the name or else we'll end up
|
|
1099
1009
|
# with `A::B::A::B::Foo`. This method will remove any redundant parts from the final name based on the reference and
|
|
1100
1010
|
# the nesting
|
|
1101
|
-
|
|
1011
|
+
#: (String name, Array[String] nesting) -> String
|
|
1102
1012
|
def build_non_redundant_full_name(name, nesting)
|
|
1103
1013
|
# If there's no nesting, then we can just return the name as is
|
|
1104
1014
|
return name if nesting.empty?
|
|
@@ -1115,44 +1025,30 @@ module RubyIndexer
|
|
|
1115
1025
|
# Otherwise, push all of the leading parts of the nesting that aren't redundant into the name. For example, if we
|
|
1116
1026
|
# have a reference to `Foo::Bar` inside the `[Namespace, Foo]` nesting, then only the `Foo` part is redundant, but
|
|
1117
1027
|
# we still need to include the `Namespace` part
|
|
1118
|
-
|
|
1028
|
+
name_parts.unshift(*nesting[0...first_redundant_part])
|
|
1119
1029
|
name_parts.join("::")
|
|
1120
1030
|
end
|
|
1121
1031
|
|
|
1122
|
-
|
|
1123
|
-
|
|
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
|
|
1032
|
+
# Tries to return direct entry from index then non seen canonicalized alias or nil
|
|
1033
|
+
#: (String full_name, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
|
1134
1034
|
def direct_or_aliased_constant(full_name, seen_names)
|
|
1135
|
-
entries = @entries[full_name]
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1035
|
+
if (entries = @entries[full_name])
|
|
1036
|
+
return entries.map do |e|
|
|
1037
|
+
e.is_a?(Entry::UnresolvedConstantAlias) ? resolve_alias(e, seen_names) : e
|
|
1038
|
+
end #: as Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias])?
|
|
1039
|
+
end
|
|
1040
|
+
|
|
1041
|
+
aliased = follow_aliased_namespace(full_name, seen_names)
|
|
1042
|
+
return if full_name == aliased || seen_names.include?(aliased)
|
|
1043
|
+
|
|
1044
|
+
@entries[aliased]&.map do |e|
|
|
1045
|
+
e.is_a?(Entry::UnresolvedConstantAlias) ? resolve_alias(e, seen_names) : e
|
|
1046
|
+
end #: as Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias])?
|
|
1145
1047
|
end
|
|
1146
1048
|
|
|
1147
1049
|
# Attempt to resolve a given unresolved method alias. This method returns the resolved alias if we managed to
|
|
1148
1050
|
# 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
|
|
1051
|
+
#: (Entry::UnresolvedMethodAlias entry, String receiver_name, Array[String] seen_names) -> (Entry::MethodAlias | Entry::UnresolvedMethodAlias)
|
|
1156
1052
|
def resolve_method_alias(entry, receiver_name, seen_names)
|
|
1157
1053
|
new_name = entry.new_name
|
|
1158
1054
|
return entry if new_name == entry.old_name
|
|
@@ -1163,8 +1059,11 @@ module RubyIndexer
|
|
|
1163
1059
|
target_method_entries = resolve_method(entry.old_name, receiver_name, seen_names)
|
|
1164
1060
|
return entry unless target_method_entries
|
|
1165
1061
|
|
|
1166
|
-
resolved_alias = Entry::MethodAlias.new(
|
|
1167
|
-
|
|
1062
|
+
resolved_alias = Entry::MethodAlias.new(
|
|
1063
|
+
target_method_entries.first, #: as !nil
|
|
1064
|
+
entry,
|
|
1065
|
+
)
|
|
1066
|
+
original_entries = @entries[new_name] #: as !nil
|
|
1168
1067
|
original_entries.delete(entry)
|
|
1169
1068
|
original_entries << resolved_alias
|
|
1170
1069
|
resolved_alias
|