ruby-lsp 0.23.11 → 0.26.4
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 +188 -285
- 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 +79 -65
- 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 +94 -82
- 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/package_url.rb +414 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
- data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +13 -16
- data/lib/ruby_lsp/requests/support/rubocop_runner.rb +34 -36
- data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
- data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
- data/lib/ruby_lsp/requests/support/source_uri.rb +20 -32
- data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
- data/lib/ruby_lsp/requests/support/test_item.rb +16 -14
- data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
- data/lib/ruby_lsp/requests/workspace_symbol.rb +24 -16
- 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 +131 -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 +241 -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 +15 -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,12 +195,13 @@ 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
|
-
|
|
225
|
-
def fuzzy_search(query)
|
|
198
|
+
#: (String? query) ?{ (Entry) -> bool? } -> Array[Entry]
|
|
199
|
+
def fuzzy_search(query, &condition)
|
|
226
200
|
unless query
|
|
227
201
|
entries = @entries.filter_map do |_name, entries|
|
|
228
202
|
next if entries.first.is_a?(Entry::SingletonClass)
|
|
229
203
|
|
|
204
|
+
entries = entries.select(&condition) if condition
|
|
230
205
|
entries
|
|
231
206
|
end
|
|
232
207
|
|
|
@@ -238,6 +213,9 @@ module RubyIndexer
|
|
|
238
213
|
results = @entries.filter_map do |name, entries|
|
|
239
214
|
next if entries.first.is_a?(Entry::SingletonClass)
|
|
240
215
|
|
|
216
|
+
entries = entries.select(&condition) if condition
|
|
217
|
+
next if entries.empty?
|
|
218
|
+
|
|
241
219
|
similarity = DidYouMean::JaroWinkler.distance(name.gsub("::", "").downcase, normalized_query)
|
|
242
220
|
[entries, -similarity] if similarity > ENTRY_SIMILARITY_THRESHOLD
|
|
243
221
|
end
|
|
@@ -245,12 +223,7 @@ module RubyIndexer
|
|
|
245
223
|
results.flat_map(&:first)
|
|
246
224
|
end
|
|
247
225
|
|
|
248
|
-
|
|
249
|
-
params(
|
|
250
|
-
name: T.nilable(String),
|
|
251
|
-
receiver_name: String,
|
|
252
|
-
).returns(T::Array[T.any(Entry::Member, Entry::MethodAlias)])
|
|
253
|
-
end
|
|
226
|
+
#: (String? name, String receiver_name) -> Array[(Entry::Member | Entry::MethodAlias)]
|
|
254
227
|
def method_completion_candidates(name, receiver_name)
|
|
255
228
|
ancestors = linearized_ancestors_of(receiver_name)
|
|
256
229
|
|
|
@@ -293,29 +266,11 @@ module RubyIndexer
|
|
|
293
266
|
completion_items.values.map!(&:first)
|
|
294
267
|
end
|
|
295
268
|
|
|
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
|
|
269
|
+
#: (String name, Array[String] nesting) -> Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
|
|
307
270
|
def constant_completion_candidates(name, nesting)
|
|
308
271
|
# If we have a top level reference, then we don't need to include completions inside the current nesting
|
|
309
272
|
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
|
-
)
|
|
273
|
+
return @entries_tree.search(name.delete_prefix("::")) #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
|
|
319
274
|
end
|
|
320
275
|
|
|
321
276
|
# Otherwise, we have to include every possible constant the user might be referring to. This is essentially the
|
|
@@ -326,7 +281,8 @@ module RubyIndexer
|
|
|
326
281
|
|
|
327
282
|
# Constants defined in enclosing scopes
|
|
328
283
|
nesting.length.downto(1) do |i|
|
|
329
|
-
namespace =
|
|
284
|
+
namespace = nesting[0...i] #: as !nil
|
|
285
|
+
.join("::")
|
|
330
286
|
entries.concat(@entries_tree.search("#{namespace}::#{name}"))
|
|
331
287
|
end
|
|
332
288
|
|
|
@@ -339,16 +295,19 @@ module RubyIndexer
|
|
|
339
295
|
|
|
340
296
|
# Top level constants
|
|
341
297
|
entries.concat(@entries_tree.search(name))
|
|
298
|
+
|
|
299
|
+
# Filter only constants since methods may have names that look like constants
|
|
300
|
+
entries.select! do |definitions|
|
|
301
|
+
definitions.select! do |entry|
|
|
302
|
+
entry.is_a?(Entry::Constant) || entry.is_a?(Entry::ConstantAlias) ||
|
|
303
|
+
entry.is_a?(Entry::Namespace) || entry.is_a?(Entry::UnresolvedConstantAlias)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
definitions.any?
|
|
307
|
+
end
|
|
308
|
+
|
|
342
309
|
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
|
-
)
|
|
310
|
+
entries #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
|
|
352
311
|
end
|
|
353
312
|
|
|
354
313
|
# Resolve a constant to its declaration based on its name and the nesting where the reference was found. Parameter
|
|
@@ -358,17 +317,7 @@ module RubyIndexer
|
|
|
358
317
|
# nesting: the nesting structure where the reference was found (e.g.: ["Foo", "Bar"])
|
|
359
318
|
# seen_names: this parameter should not be used by consumers of the api. It is used to avoid infinite recursion when
|
|
360
319
|
# 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
|
|
320
|
+
#: (String name, Array[String] nesting, ?Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
|
372
321
|
def resolve(name, nesting, seen_names = [])
|
|
373
322
|
# If we have a top level reference, then we just search for it straight away ignoring the nesting
|
|
374
323
|
if name.start_with?("::")
|
|
@@ -404,12 +353,7 @@ module RubyIndexer
|
|
|
404
353
|
# Index all files for the given URIs, which defaults to what is configured. A block can be used to track and control
|
|
405
354
|
# indexing progress. That block is invoked with the current progress percentage and should return `true` to continue
|
|
406
355
|
# 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
|
|
356
|
+
#: (?uris: Array[URI::Generic]) ?{ (Integer progress) -> bool } -> void
|
|
413
357
|
def index_all(uris: @configuration.indexable_uris, &block)
|
|
414
358
|
# When troubleshooting an indexing issue, e.g. through irb, it's not obvious that `index_all` will augment the
|
|
415
359
|
# existing index values, meaning it may contain 'stale' entries. This check ensures that the user is aware of this
|
|
@@ -419,8 +363,6 @@ module RubyIndexer
|
|
|
419
363
|
"The index is not empty. To prevent invalid entries, `index_all` can only be called once."
|
|
420
364
|
end
|
|
421
365
|
|
|
422
|
-
@initial_indexing_completed = true
|
|
423
|
-
|
|
424
366
|
RBSIndexer.new(self).index_ruby_core
|
|
425
367
|
# Calculate how many paths are worth 1% of progress
|
|
426
368
|
progress_step = (uris.length / 100.0).ceil
|
|
@@ -433,9 +375,11 @@ module RubyIndexer
|
|
|
433
375
|
|
|
434
376
|
index_file(uri, collect_comments: false)
|
|
435
377
|
end
|
|
378
|
+
|
|
379
|
+
@initial_indexing_completed = true
|
|
436
380
|
end
|
|
437
381
|
|
|
438
|
-
|
|
382
|
+
#: (URI::Generic uri, String source, ?collect_comments: bool) -> void
|
|
439
383
|
def index_single(uri, source, collect_comments: true)
|
|
440
384
|
dispatcher = Prism::Dispatcher.new
|
|
441
385
|
|
|
@@ -457,9 +401,10 @@ module RubyIndexer
|
|
|
457
401
|
end
|
|
458
402
|
|
|
459
403
|
# Indexes a File URI by reading the contents from disk
|
|
460
|
-
|
|
404
|
+
#: (URI::Generic uri, ?collect_comments: bool) -> void
|
|
461
405
|
def index_file(uri, collect_comments: true)
|
|
462
|
-
|
|
406
|
+
path = uri.full_path #: as !nil
|
|
407
|
+
index_single(uri, File.read(path), collect_comments: collect_comments)
|
|
463
408
|
rescue Errno::EISDIR, Errno::ENOENT
|
|
464
409
|
# If `path` is a directory, just ignore it and continue indexing. If the file doesn't exist, then we also ignore
|
|
465
410
|
# it
|
|
@@ -475,14 +420,18 @@ module RubyIndexer
|
|
|
475
420
|
# If we find an alias, then we want to follow its target. In the same example, if `Foo::Bar` is an alias to
|
|
476
421
|
# `Something::Else`, then we first discover `Something::Else::Baz`. But `Something::Else::Baz` might contain other
|
|
477
422
|
# aliases, so we have to invoke `follow_aliased_namespace` again to check until we only return a real name
|
|
478
|
-
|
|
423
|
+
#: (String name, ?Array[String] seen_names) -> String
|
|
479
424
|
def follow_aliased_namespace(name, seen_names = [])
|
|
480
425
|
parts = name.split("::")
|
|
481
426
|
real_parts = []
|
|
482
427
|
|
|
483
428
|
(parts.length - 1).downto(0) do |i|
|
|
484
|
-
current_name =
|
|
485
|
-
|
|
429
|
+
current_name = parts[0..i] #: as !nil
|
|
430
|
+
.join("::")
|
|
431
|
+
|
|
432
|
+
entry = unless seen_names.include?(current_name)
|
|
433
|
+
@entries[current_name]&.first
|
|
434
|
+
end
|
|
486
435
|
|
|
487
436
|
case entry
|
|
488
437
|
when Entry::ConstantAlias
|
|
@@ -498,7 +447,9 @@ module RubyIndexer
|
|
|
498
447
|
target = resolved.target
|
|
499
448
|
return follow_aliased_namespace("#{target}::#{real_parts.join("::")}", seen_names)
|
|
500
449
|
else
|
|
501
|
-
real_parts.unshift(
|
|
450
|
+
real_parts.unshift(
|
|
451
|
+
parts[i], #: as !nil
|
|
452
|
+
)
|
|
502
453
|
end
|
|
503
454
|
end
|
|
504
455
|
|
|
@@ -508,14 +459,7 @@ module RubyIndexer
|
|
|
508
459
|
# Attempts to find methods for a resolved fully qualified receiver name. Do not provide the `seen_names` parameter
|
|
509
460
|
# as it is used only internally to prevent infinite loops when resolving circular aliases
|
|
510
461
|
# 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
|
|
462
|
+
#: (String method_name, String receiver_name, ?Array[String] seen_names, ?inherited_only: bool) -> Array[(Entry::Member | Entry::MethodAlias)]?
|
|
519
463
|
def resolve_method(method_name, receiver_name, seen_names = [], inherited_only: false)
|
|
520
464
|
method_entries = self[method_name]
|
|
521
465
|
return unless method_entries
|
|
@@ -553,7 +497,7 @@ module RubyIndexer
|
|
|
553
497
|
# module that prepends another module, then the prepend module appears before the included module.
|
|
554
498
|
#
|
|
555
499
|
# The order of ancestors is [linearized_prepends, self, linearized_includes, linearized_superclass]
|
|
556
|
-
|
|
500
|
+
#: (String fully_qualified_name) -> Array[String]
|
|
557
501
|
def linearized_ancestors_of(fully_qualified_name)
|
|
558
502
|
# If we already computed the ancestors for this namespace, return it straight away
|
|
559
503
|
cached_ancestors = @ancestors[fully_qualified_name]
|
|
@@ -602,11 +546,12 @@ module RubyIndexer
|
|
|
602
546
|
|
|
603
547
|
# The original nesting where we discovered this namespace, so that we resolve the correct names of the
|
|
604
548
|
# included/prepended/extended modules and parent classes
|
|
605
|
-
nesting =
|
|
549
|
+
nesting = namespaces.first #: as !nil
|
|
550
|
+
.nesting.flat_map { |n| n.split("::") }
|
|
606
551
|
|
|
607
552
|
if nesting.any?
|
|
608
553
|
singleton_levels.times do
|
|
609
|
-
nesting << "<Class:#{
|
|
554
|
+
nesting << "<Class:#{nesting.last}>"
|
|
610
555
|
end
|
|
611
556
|
end
|
|
612
557
|
|
|
@@ -631,9 +576,9 @@ module RubyIndexer
|
|
|
631
576
|
|
|
632
577
|
# Resolves an instance variable name for a given owner name. This method will linearize the ancestors of the owner
|
|
633
578
|
# and find inherited instance variables as well
|
|
634
|
-
|
|
579
|
+
#: (String variable_name, String owner_name) -> Array[Entry::InstanceVariable]?
|
|
635
580
|
def resolve_instance_variable(variable_name, owner_name)
|
|
636
|
-
entries =
|
|
581
|
+
entries = self[variable_name] #: as Array[Entry::InstanceVariable]?
|
|
637
582
|
return unless entries
|
|
638
583
|
|
|
639
584
|
ancestors = linearized_ancestors_of(owner_name)
|
|
@@ -642,7 +587,7 @@ module RubyIndexer
|
|
|
642
587
|
entries.select { |e| ancestors.include?(e.owner&.name) }
|
|
643
588
|
end
|
|
644
589
|
|
|
645
|
-
|
|
590
|
+
#: (String variable_name, String owner_name) -> Array[Entry::ClassVariable]?
|
|
646
591
|
def resolve_class_variable(variable_name, owner_name)
|
|
647
592
|
entries = self[variable_name]&.grep(Entry::ClassVariable)
|
|
648
593
|
return unless entries&.any?
|
|
@@ -655,11 +600,9 @@ module RubyIndexer
|
|
|
655
600
|
|
|
656
601
|
# Returns a list of possible candidates for completion of instance variables for a given owner name. The name must
|
|
657
602
|
# include the `@` prefix
|
|
658
|
-
|
|
659
|
-
params(name: String, owner_name: String).returns(T::Array[T.any(Entry::InstanceVariable, Entry::ClassVariable)])
|
|
660
|
-
end
|
|
603
|
+
#: (String name, String owner_name) -> Array[(Entry::InstanceVariable | Entry::ClassVariable)]
|
|
661
604
|
def instance_variable_completion_candidates(name, owner_name)
|
|
662
|
-
entries =
|
|
605
|
+
entries = prefix_search(name).flatten #: as Array[Entry::InstanceVariable | Entry::ClassVariable]
|
|
663
606
|
# Avoid wasting time linearizing ancestors if we didn't find anything
|
|
664
607
|
return entries if entries.empty?
|
|
665
608
|
|
|
@@ -674,7 +617,8 @@ module RubyIndexer
|
|
|
674
617
|
name_parts = owner_name.split("::")
|
|
675
618
|
|
|
676
619
|
if name_parts.last&.start_with?("<Class:")
|
|
677
|
-
attached_name =
|
|
620
|
+
attached_name = name_parts[0..-2] #: as !nil
|
|
621
|
+
.join("::")
|
|
678
622
|
attached_ancestors = linearized_ancestors_of(attached_name)
|
|
679
623
|
variables.concat(class_variables.select { |e| attached_ancestors.any?(e.owner&.name) })
|
|
680
624
|
else
|
|
@@ -686,9 +630,9 @@ module RubyIndexer
|
|
|
686
630
|
variables
|
|
687
631
|
end
|
|
688
632
|
|
|
689
|
-
|
|
633
|
+
#: (String name, String owner_name) -> Array[Entry::ClassVariable]
|
|
690
634
|
def class_variable_completion_candidates(name, owner_name)
|
|
691
|
-
entries =
|
|
635
|
+
entries = prefix_search(name).flatten #: as Array[Entry::ClassVariable]
|
|
692
636
|
# Avoid wasting time linearizing ancestors if we didn't find anything
|
|
693
637
|
return entries if entries.empty?
|
|
694
638
|
|
|
@@ -702,9 +646,7 @@ module RubyIndexer
|
|
|
702
646
|
# declarations removed and that the ancestor linearization cache is cleared if necessary. If a block is passed, the
|
|
703
647
|
# consumer of this API has to handle deleting and inserting/updating entries in the index instead of passing the
|
|
704
648
|
# 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
|
|
649
|
+
#: (URI::Generic uri, ?String? source) ?{ (Index index) -> void } -> void
|
|
708
650
|
def handle_change(uri, source = nil, &block)
|
|
709
651
|
key = uri.to_s
|
|
710
652
|
original_entries = @uris_to_entries[key]
|
|
@@ -713,7 +655,10 @@ module RubyIndexer
|
|
|
713
655
|
block.call(self)
|
|
714
656
|
else
|
|
715
657
|
delete(uri)
|
|
716
|
-
index_single(
|
|
658
|
+
index_single(
|
|
659
|
+
uri,
|
|
660
|
+
source, #: as !nil
|
|
661
|
+
)
|
|
717
662
|
end
|
|
718
663
|
|
|
719
664
|
updated_entries = @uris_to_entries[key]
|
|
@@ -723,47 +668,50 @@ module RubyIndexer
|
|
|
723
668
|
# indirect means like including a module that than includes the ancestor. Trying to figure out exactly which
|
|
724
669
|
# ancestors need to be deleted is too expensive. Therefore, if any of the namespace entries has a change to their
|
|
725
670
|
# 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] }
|
|
671
|
+
original_map = original_entries
|
|
672
|
+
.select { |e| e.is_a?(Entry::Namespace) } #: as Array[Entry::Namespace]
|
|
673
|
+
.to_h { |e| [e.name, e.ancestor_hash] }
|
|
730
674
|
|
|
731
|
-
updated_map =
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
).to_h { |e| [e.name, e.ancestor_hash] }
|
|
675
|
+
updated_map = updated_entries
|
|
676
|
+
.select { |e| e.is_a?(Entry::Namespace) } #: as Array[Entry::Namespace]
|
|
677
|
+
.to_h { |e| [e.name, e.ancestor_hash] }
|
|
735
678
|
|
|
736
679
|
@ancestors.clear if original_map.any? { |name, hash| updated_map[name] != hash }
|
|
737
680
|
end
|
|
738
681
|
|
|
739
|
-
|
|
682
|
+
#: -> void
|
|
683
|
+
def clear_ancestors
|
|
684
|
+
@ancestors.clear
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
#: -> bool
|
|
740
688
|
def empty?
|
|
741
689
|
@entries.empty?
|
|
742
690
|
end
|
|
743
691
|
|
|
744
|
-
|
|
692
|
+
#: -> Array[String]
|
|
745
693
|
def names
|
|
746
694
|
@entries.keys
|
|
747
695
|
end
|
|
748
696
|
|
|
749
|
-
|
|
697
|
+
#: (String name) -> bool
|
|
750
698
|
def indexed?(name)
|
|
751
699
|
@entries.key?(name)
|
|
752
700
|
end
|
|
753
701
|
|
|
754
|
-
|
|
702
|
+
#: -> Integer
|
|
755
703
|
def length
|
|
756
704
|
@entries.count
|
|
757
705
|
end
|
|
758
706
|
|
|
759
|
-
|
|
707
|
+
#: (String name) -> Entry::SingletonClass
|
|
760
708
|
def existing_or_new_singleton_class(name)
|
|
761
709
|
*_namespace, unqualified_name = name.split("::")
|
|
762
710
|
full_singleton_name = "#{name}::<Class:#{unqualified_name}>"
|
|
763
|
-
singleton =
|
|
711
|
+
singleton = self[full_singleton_name]&.first #: as Entry::SingletonClass?
|
|
764
712
|
|
|
765
713
|
unless singleton
|
|
766
|
-
attached_ancestor =
|
|
714
|
+
attached_ancestor = self[name]&.first #: as !nil
|
|
767
715
|
|
|
768
716
|
singleton = Entry::SingletonClass.new(
|
|
769
717
|
[full_singleton_name],
|
|
@@ -779,12 +727,7 @@ module RubyIndexer
|
|
|
779
727
|
singleton
|
|
780
728
|
end
|
|
781
729
|
|
|
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
|
|
730
|
+
#: [T] (String uri, ?Class[(T & Entry)]? type) -> (Array[Entry] | Array[T])?
|
|
788
731
|
def entries_for(uri, type = nil)
|
|
789
732
|
entries = @uris_to_entries[uri.to_s]
|
|
790
733
|
return entries unless type
|
|
@@ -796,12 +739,13 @@ module RubyIndexer
|
|
|
796
739
|
|
|
797
740
|
# Always returns the linearized ancestors for the attached class, regardless of whether `name` refers to a singleton
|
|
798
741
|
# or attached namespace
|
|
799
|
-
|
|
742
|
+
#: (String name) -> Array[String]
|
|
800
743
|
def linearized_attached_ancestors(name)
|
|
801
744
|
name_parts = name.split("::")
|
|
802
745
|
|
|
803
746
|
if name_parts.last&.start_with?("<Class:")
|
|
804
|
-
attached_name =
|
|
747
|
+
attached_name = name_parts[0..-2] #: as !nil
|
|
748
|
+
.join("::")
|
|
805
749
|
linearized_ancestors_of(attached_name)
|
|
806
750
|
else
|
|
807
751
|
linearized_ancestors_of(name)
|
|
@@ -809,7 +753,7 @@ module RubyIndexer
|
|
|
809
753
|
end
|
|
810
754
|
|
|
811
755
|
# Runs the registered included hooks
|
|
812
|
-
|
|
756
|
+
#: (String fully_qualified_name, Array[String] nesting) -> void
|
|
813
757
|
def run_included_hooks(fully_qualified_name, nesting)
|
|
814
758
|
return if @included_hooks.empty?
|
|
815
759
|
|
|
@@ -824,7 +768,8 @@ module RubyIndexer
|
|
|
824
768
|
resolved_modules = resolve(operation.module_name, nesting)
|
|
825
769
|
next unless resolved_modules
|
|
826
770
|
|
|
827
|
-
module_name =
|
|
771
|
+
module_name = resolved_modules.first #: as !nil
|
|
772
|
+
.name
|
|
828
773
|
|
|
829
774
|
# Then we grab any hooks registered for that module
|
|
830
775
|
hooks = @included_hooks[module_name]
|
|
@@ -838,13 +783,7 @@ module RubyIndexer
|
|
|
838
783
|
|
|
839
784
|
# Linearize mixins for an array of namespace entries. This method will mutate the `ancestors` array with the
|
|
840
785
|
# 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
|
|
786
|
+
#: (Array[String] ancestors, Array[Entry::Namespace] namespace_entries, Array[String] nesting) -> void
|
|
848
787
|
def linearize_mixins(ancestors, namespace_entries, nesting)
|
|
849
788
|
mixin_operations = namespace_entries.flat_map(&:mixin_operations)
|
|
850
789
|
main_namespace_index = 0
|
|
@@ -853,7 +792,8 @@ module RubyIndexer
|
|
|
853
792
|
resolved_module = resolve(operation.module_name, nesting)
|
|
854
793
|
next unless resolved_module
|
|
855
794
|
|
|
856
|
-
module_fully_qualified_name =
|
|
795
|
+
module_fully_qualified_name = resolved_module.first #: as !nil
|
|
796
|
+
.name
|
|
857
797
|
|
|
858
798
|
case operation
|
|
859
799
|
when Entry::Prepend
|
|
@@ -865,36 +805,27 @@ module RubyIndexer
|
|
|
865
805
|
# When there are duplicate prepended modules, we have to insert the new prepends after the existing ones. For
|
|
866
806
|
# example, if the current ancestors are `["A", "Foo"]` and we try to prepend `["A", "B"]`, then `"B"` has to
|
|
867
807
|
# be inserted after `"A`
|
|
868
|
-
|
|
808
|
+
prepended_ancestors = ancestors[0...main_namespace_index] #: as !nil
|
|
809
|
+
uniq_prepends = linearized_prepends - prepended_ancestors
|
|
869
810
|
insert_position = linearized_prepends.length - uniq_prepends.length
|
|
870
811
|
|
|
871
|
-
|
|
872
|
-
insert_position,
|
|
873
|
-
*(linearized_prepends - T.must(ancestors[0...main_namespace_index])),
|
|
874
|
-
)
|
|
812
|
+
ancestors #: as untyped
|
|
813
|
+
.insert(insert_position, *uniq_prepends)
|
|
875
814
|
|
|
876
815
|
main_namespace_index += linearized_prepends.length
|
|
877
816
|
when Entry::Include
|
|
878
817
|
# When including a module, Ruby will always prevent duplicate entries in case the module has already been
|
|
879
818
|
# prepended or included
|
|
880
819
|
linearized_includes = linearized_ancestors_of(module_fully_qualified_name)
|
|
881
|
-
|
|
820
|
+
ancestors #: as untyped
|
|
821
|
+
.insert(main_namespace_index + 1, *(linearized_includes - ancestors))
|
|
882
822
|
end
|
|
883
823
|
end
|
|
884
824
|
end
|
|
885
825
|
|
|
886
826
|
# Linearize the superclass of a given namespace (including modules with the implicit `Module` superclass). This
|
|
887
827
|
# 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
|
|
828
|
+
#: (Array[String] ancestors, String attached_class_name, String fully_qualified_name, Array[Entry::Namespace] namespace_entries, Array[String] nesting, Integer singleton_levels) -> void
|
|
898
829
|
def linearize_superclass( # rubocop:disable Metrics/ParameterLists
|
|
899
830
|
ancestors,
|
|
900
831
|
attached_class_name,
|
|
@@ -905,19 +836,27 @@ module RubyIndexer
|
|
|
905
836
|
)
|
|
906
837
|
# Find the first class entry that has a parent class. Notice that if the developer makes a mistake and inherits
|
|
907
838
|
# from two different classes in different files, we simply ignore it
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
839
|
+
possible_parents = singleton_levels > 0 ? self[attached_class_name] : namespace_entries
|
|
840
|
+
superclass = nil #: Entry::Class?
|
|
841
|
+
|
|
842
|
+
possible_parents&.each do |n|
|
|
843
|
+
# Ignore non class entries
|
|
844
|
+
next unless n.is_a?(Entry::Class)
|
|
845
|
+
|
|
846
|
+
parent_class = n.parent_class
|
|
847
|
+
next unless parent_class
|
|
848
|
+
|
|
849
|
+
# Always set the superclass, but break early if we found one that isn't `::Object` (meaning we found an explicit
|
|
850
|
+
# parent class and not the implicit default). Note that when setting different parents to the same class, which
|
|
851
|
+
# is invalid, we pick whatever is the first one we find
|
|
852
|
+
superclass = n
|
|
853
|
+
break if parent_class != "::Object"
|
|
854
|
+
end
|
|
916
855
|
|
|
917
856
|
if superclass
|
|
918
857
|
# If the user makes a mistake and creates a class that inherits from itself, this method would throw a stack
|
|
919
858
|
# error. We need to ensure that this isn't the case
|
|
920
|
-
parent_class =
|
|
859
|
+
parent_class = superclass.parent_class #: as !nil
|
|
921
860
|
|
|
922
861
|
resolved_parent_class = resolve(parent_class, nesting)
|
|
923
862
|
parent_class_name = resolved_parent_class&.first&.name
|
|
@@ -946,7 +885,7 @@ module RubyIndexer
|
|
|
946
885
|
elsif singleton_levels > 0
|
|
947
886
|
# When computing the linearization for a module's singleton class, it inherits from the linearized ancestors of
|
|
948
887
|
# the `Module` class
|
|
949
|
-
mod =
|
|
888
|
+
mod = self[attached_class_name]&.find { |n| n.is_a?(Entry::Module) } #: as Entry::Module?
|
|
950
889
|
|
|
951
890
|
if mod
|
|
952
891
|
module_class_name_parts = ["Module"]
|
|
@@ -962,12 +901,7 @@ module RubyIndexer
|
|
|
962
901
|
|
|
963
902
|
# Attempts to resolve an UnresolvedAlias into a resolved Alias. If the unresolved alias is pointing to a constant
|
|
964
903
|
# 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
|
|
904
|
+
#: (Entry::UnresolvedConstantAlias entry, Array[String] seen_names) -> (Entry::ConstantAlias | Entry::UnresolvedConstantAlias)
|
|
971
905
|
def resolve_alias(entry, seen_names)
|
|
972
906
|
alias_name = entry.name
|
|
973
907
|
return entry if seen_names.include?(alias_name)
|
|
@@ -977,11 +911,15 @@ module RubyIndexer
|
|
|
977
911
|
target = resolve(entry.target, entry.nesting, seen_names)
|
|
978
912
|
return entry unless target
|
|
979
913
|
|
|
980
|
-
|
|
914
|
+
# Self referential alias can be unresolved we should bail out from resolving
|
|
915
|
+
return entry if target.first == entry
|
|
916
|
+
|
|
917
|
+
target_name = target.first #: as !nil
|
|
918
|
+
.name
|
|
981
919
|
resolved_alias = Entry::ConstantAlias.new(target_name, entry)
|
|
982
920
|
|
|
983
921
|
# Replace the UnresolvedAlias by a resolved one so that we don't have to do this again later
|
|
984
|
-
original_entries =
|
|
922
|
+
original_entries = @entries[alias_name] #: as !nil
|
|
985
923
|
original_entries.delete(entry)
|
|
986
924
|
original_entries << resolved_alias
|
|
987
925
|
|
|
@@ -990,20 +928,11 @@ module RubyIndexer
|
|
|
990
928
|
resolved_alias
|
|
991
929
|
end
|
|
992
930
|
|
|
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
|
|
931
|
+
#: (String name, Array[String] nesting, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
|
1004
932
|
def lookup_enclosing_scopes(name, nesting, seen_names)
|
|
1005
933
|
nesting.length.downto(1) do |i|
|
|
1006
|
-
namespace =
|
|
934
|
+
namespace = nesting[0...i] #: as !nil
|
|
935
|
+
.join("::")
|
|
1007
936
|
|
|
1008
937
|
# If we find an entry with `full_name` directly, then we can already return it, even if it contains aliases -
|
|
1009
938
|
# because the user might be trying to jump to the alias definition.
|
|
@@ -1019,17 +948,7 @@ module RubyIndexer
|
|
|
1019
948
|
nil
|
|
1020
949
|
end
|
|
1021
950
|
|
|
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
|
|
951
|
+
#: (String name, Array[String] nesting, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
|
1033
952
|
def lookup_ancestor_chain(name, nesting, seen_names)
|
|
1034
953
|
*nesting_parts, constant_name = build_non_redundant_full_name(name, nesting).split("::")
|
|
1035
954
|
return if nesting_parts.empty?
|
|
@@ -1037,7 +956,9 @@ module RubyIndexer
|
|
|
1037
956
|
namespace_entries = resolve(nesting_parts.join("::"), [], seen_names)
|
|
1038
957
|
return unless namespace_entries
|
|
1039
958
|
|
|
1040
|
-
|
|
959
|
+
namespace_name = namespace_entries.first #: as !nil
|
|
960
|
+
.name
|
|
961
|
+
ancestors = nesting_parts.empty? ? [] : linearized_ancestors_of(namespace_name)
|
|
1041
962
|
|
|
1042
963
|
ancestors.each do |ancestor_name|
|
|
1043
964
|
entries = direct_or_aliased_constant("#{ancestor_name}::#{constant_name}", seen_names)
|
|
@@ -1049,17 +970,7 @@ module RubyIndexer
|
|
|
1049
970
|
nil
|
|
1050
971
|
end
|
|
1051
972
|
|
|
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
|
|
973
|
+
#: (String? name, Array[String] nesting) -> Array[Array[(Entry::Namespace | Entry::ConstantAlias | Entry::UnresolvedConstantAlias | Entry::Constant)]]
|
|
1063
974
|
def inherited_constant_completion_candidates(name, nesting)
|
|
1064
975
|
namespace_entries = if name
|
|
1065
976
|
*nesting_parts, constant_name = build_non_redundant_full_name(name, nesting).split("::")
|
|
@@ -1071,7 +982,9 @@ module RubyIndexer
|
|
|
1071
982
|
end
|
|
1072
983
|
return [] unless namespace_entries
|
|
1073
984
|
|
|
1074
|
-
|
|
985
|
+
namespace_name = namespace_entries.first #: as !nil
|
|
986
|
+
.name
|
|
987
|
+
ancestors = linearized_ancestors_of(namespace_name)
|
|
1075
988
|
candidates = ancestors.flat_map do |ancestor_name|
|
|
1076
989
|
@entries_tree.search("#{ancestor_name}::#{constant_name}")
|
|
1077
990
|
end
|
|
@@ -1079,7 +992,8 @@ module RubyIndexer
|
|
|
1079
992
|
# For candidates with the same name, we must only show the first entry in the inheritance chain, since that's the
|
|
1080
993
|
# one the user will be referring to in completion
|
|
1081
994
|
completion_items = candidates.each_with_object({}) do |entries, hash|
|
|
1082
|
-
*parts, short_name =
|
|
995
|
+
*parts, short_name = entries.first #: as !nil
|
|
996
|
+
.name.split("::")
|
|
1083
997
|
namespace_name = parts.join("::")
|
|
1084
998
|
ancestor_index = ancestors.index(namespace_name)
|
|
1085
999
|
existing_entry, existing_entry_index = hash[short_name]
|
|
@@ -1098,7 +1012,7 @@ module RubyIndexer
|
|
|
1098
1012
|
# inside of the ["A", "B"] nesting, then we should not concatenate the nesting with the name or else we'll end up
|
|
1099
1013
|
# with `A::B::A::B::Foo`. This method will remove any redundant parts from the final name based on the reference and
|
|
1100
1014
|
# the nesting
|
|
1101
|
-
|
|
1015
|
+
#: (String name, Array[String] nesting) -> String
|
|
1102
1016
|
def build_non_redundant_full_name(name, nesting)
|
|
1103
1017
|
# If there's no nesting, then we can just return the name as is
|
|
1104
1018
|
return name if nesting.empty?
|
|
@@ -1115,44 +1029,30 @@ module RubyIndexer
|
|
|
1115
1029
|
# Otherwise, push all of the leading parts of the nesting that aren't redundant into the name. For example, if we
|
|
1116
1030
|
# have a reference to `Foo::Bar` inside the `[Namespace, Foo]` nesting, then only the `Foo` part is redundant, but
|
|
1117
1031
|
# we still need to include the `Namespace` part
|
|
1118
|
-
|
|
1032
|
+
name_parts.unshift(*nesting[0...first_redundant_part])
|
|
1119
1033
|
name_parts.join("::")
|
|
1120
1034
|
end
|
|
1121
1035
|
|
|
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
|
|
1036
|
+
# Tries to return direct entry from index then non seen canonicalized alias or nil
|
|
1037
|
+
#: (String full_name, Array[String] seen_names) -> Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]?
|
|
1134
1038
|
def direct_or_aliased_constant(full_name, seen_names)
|
|
1135
|
-
entries = @entries[full_name]
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1039
|
+
if (entries = @entries[full_name])
|
|
1040
|
+
return entries.map do |e|
|
|
1041
|
+
e.is_a?(Entry::UnresolvedConstantAlias) ? resolve_alias(e, seen_names) : e
|
|
1042
|
+
end #: as Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias])?
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
aliased = follow_aliased_namespace(full_name, seen_names)
|
|
1046
|
+
return if full_name == aliased || seen_names.include?(aliased)
|
|
1047
|
+
|
|
1048
|
+
@entries[aliased]&.map do |e|
|
|
1049
|
+
e.is_a?(Entry::UnresolvedConstantAlias) ? resolve_alias(e, seen_names) : e
|
|
1050
|
+
end #: as Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias])?
|
|
1145
1051
|
end
|
|
1146
1052
|
|
|
1147
1053
|
# Attempt to resolve a given unresolved method alias. This method returns the resolved alias if we managed to
|
|
1148
1054
|
# 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
|
|
1055
|
+
#: (Entry::UnresolvedMethodAlias entry, String receiver_name, Array[String] seen_names) -> (Entry::MethodAlias | Entry::UnresolvedMethodAlias)
|
|
1156
1056
|
def resolve_method_alias(entry, receiver_name, seen_names)
|
|
1157
1057
|
new_name = entry.new_name
|
|
1158
1058
|
return entry if new_name == entry.old_name
|
|
@@ -1163,8 +1063,11 @@ module RubyIndexer
|
|
|
1163
1063
|
target_method_entries = resolve_method(entry.old_name, receiver_name, seen_names)
|
|
1164
1064
|
return entry unless target_method_entries
|
|
1165
1065
|
|
|
1166
|
-
resolved_alias = Entry::MethodAlias.new(
|
|
1167
|
-
|
|
1066
|
+
resolved_alias = Entry::MethodAlias.new(
|
|
1067
|
+
target_method_entries.first, #: as !nil
|
|
1068
|
+
entry,
|
|
1069
|
+
)
|
|
1070
|
+
original_entries = @entries[new_name] #: as !nil
|
|
1168
1071
|
original_entries.delete(entry)
|
|
1169
1072
|
original_entries << resolved_alias
|
|
1170
1073
|
resolved_alias
|