ruby-lsp 0.27.0.beta2 → 0.27.0.beta3
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/VERSION +1 -1
- data/exe/ruby-lsp +0 -46
- data/exe/ruby-lsp-check +0 -15
- data/lib/ruby_lsp/global_state.rb +0 -5
- data/lib/ruby_lsp/internal.rb +2 -1
- data/lib/ruby_lsp/listeners/code_lens.rb +1 -1
- data/lib/ruby_lsp/listeners/completion.rb +246 -382
- data/lib/ruby_lsp/listeners/definition.rb +6 -9
- data/lib/ruby_lsp/listeners/hover.rb +11 -9
- data/lib/ruby_lsp/listeners/signature_help.rb +11 -12
- data/lib/ruby_lsp/listeners/test_discovery.rb +17 -1
- data/lib/ruby_lsp/listeners/test_style.rb +1 -1
- data/lib/ruby_lsp/requests/completion_resolve.rb +49 -29
- data/lib/ruby_lsp/requests/references.rb +21 -7
- data/lib/ruby_lsp/requests/rename.rb +1 -1
- data/lib/ruby_lsp/requests/support/common.rb +69 -68
- data/lib/ruby_lsp/ruby_document.rb +0 -73
- data/lib/ruby_lsp/rubydex/declaration.rb +128 -2
- data/lib/ruby_lsp/rubydex/definition.rb +16 -0
- data/lib/ruby_lsp/rubydex/signature.rb +107 -0
- data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
- data/lib/ruby_lsp/server.rb +7 -162
- data/lib/ruby_lsp/test_helper.rb +0 -1
- data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +1 -1
- data/lib/ruby_lsp/type_inferrer.rb +2 -2
- metadata +11 -14
- data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +0 -276
- data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +0 -1101
- data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +0 -44
- data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +0 -605
- data/lib/ruby_indexer/lib/ruby_indexer/index.rb +0 -1077
- data/lib/ruby_indexer/lib/ruby_indexer/location.rb +0 -37
- data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +0 -149
- data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +0 -294
- data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +0 -32
- data/lib/ruby_indexer/ruby_indexer.rb +0 -19
- /data/lib/{ruby_indexer/lib/ruby_indexer → ruby_lsp}/uri.rb +0 -0
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
module RubyIndexer
|
|
5
|
-
# @abstract
|
|
6
|
-
class Enhancement
|
|
7
|
-
@enhancements = [] #: Array[Class[Enhancement]]
|
|
8
|
-
|
|
9
|
-
class << self
|
|
10
|
-
#: (Class[Enhancement] child) -> void
|
|
11
|
-
def inherited(child)
|
|
12
|
-
@enhancements << child
|
|
13
|
-
super
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
#: (DeclarationListener listener) -> Array[Enhancement]
|
|
17
|
-
def all(listener)
|
|
18
|
-
@enhancements.map { |enhancement| enhancement.new(listener) }
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Only available for testing purposes
|
|
22
|
-
#: -> void
|
|
23
|
-
def clear
|
|
24
|
-
@enhancements.clear
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
#: (DeclarationListener listener) -> void
|
|
29
|
-
def initialize(listener)
|
|
30
|
-
@listener = listener
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# The `on_extend` indexing enhancement is invoked whenever an extend is encountered in the code. It can be used to
|
|
34
|
-
# register for an included callback, similar to what `ActiveSupport::Concern` does in order to auto-extend the
|
|
35
|
-
# `ClassMethods` modules
|
|
36
|
-
# @overridable
|
|
37
|
-
#: (Prism::CallNode node) -> void
|
|
38
|
-
def on_call_node_enter(node); end # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
39
|
-
|
|
40
|
-
# @overridable
|
|
41
|
-
#: (Prism::CallNode node) -> void
|
|
42
|
-
def on_call_node_leave(node); end # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod
|
|
43
|
-
end
|
|
44
|
-
end
|
|
@@ -1,605 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
module RubyIndexer
|
|
5
|
-
class Entry
|
|
6
|
-
#: Configuration
|
|
7
|
-
attr_reader :configuration
|
|
8
|
-
|
|
9
|
-
#: String
|
|
10
|
-
attr_reader :name
|
|
11
|
-
|
|
12
|
-
#: URI::Generic
|
|
13
|
-
attr_reader :uri
|
|
14
|
-
|
|
15
|
-
#: RubyIndexer::Location
|
|
16
|
-
attr_reader :location
|
|
17
|
-
|
|
18
|
-
alias_method :name_location, :location
|
|
19
|
-
|
|
20
|
-
#: Symbol
|
|
21
|
-
attr_accessor :visibility
|
|
22
|
-
|
|
23
|
-
#: (Configuration configuration, String name, URI::Generic uri, Location location, String? comments) -> void
|
|
24
|
-
def initialize(configuration, name, uri, location, comments)
|
|
25
|
-
@configuration = configuration
|
|
26
|
-
@name = name
|
|
27
|
-
@uri = uri
|
|
28
|
-
@comments = comments
|
|
29
|
-
@visibility = :public #: Symbol
|
|
30
|
-
@location = location
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
#: -> bool
|
|
34
|
-
def public?
|
|
35
|
-
@visibility == :public
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
#: -> bool
|
|
39
|
-
def protected?
|
|
40
|
-
@visibility == :protected
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
#: -> bool
|
|
44
|
-
def private?
|
|
45
|
-
@visibility == :private
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
#: -> String
|
|
49
|
-
def file_name
|
|
50
|
-
if @uri.scheme == "untitled"
|
|
51
|
-
@uri.opaque #: as !nil
|
|
52
|
-
else
|
|
53
|
-
File.basename(
|
|
54
|
-
file_path, #: as !nil
|
|
55
|
-
)
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
#: -> String?
|
|
60
|
-
def file_path
|
|
61
|
-
@uri.full_path
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
#: -> String
|
|
65
|
-
def comments
|
|
66
|
-
@comments ||= begin
|
|
67
|
-
# Parse only the comments based on the file path, which is much faster than parsing the entire file
|
|
68
|
-
path = file_path
|
|
69
|
-
parsed_comments = path ? Prism.parse_file_comments(path) : []
|
|
70
|
-
|
|
71
|
-
# Group comments based on whether they belong to a single block of comments
|
|
72
|
-
grouped = parsed_comments.slice_when do |left, right|
|
|
73
|
-
left.location.start_line + 1 != right.location.start_line
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Find the group that is either immediately or two lines above the current entry
|
|
77
|
-
correct_group = grouped.find do |group|
|
|
78
|
-
comment_end_line = group.last.location.start_line
|
|
79
|
-
(comment_end_line..comment_end_line + 1).cover?(@location.start_line - 1)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# If we found something, we join the comments together. Otherwise, the entry has no documentation and we don't
|
|
83
|
-
# want to accidentally re-parse it, so we set it to an empty string. If an entry is updated, the entire entry
|
|
84
|
-
# object is dropped, so this will not prevent updates
|
|
85
|
-
if correct_group
|
|
86
|
-
correct_group.filter_map do |comment|
|
|
87
|
-
content = comment.slice.chomp
|
|
88
|
-
|
|
89
|
-
if content.valid_encoding? && !content.match?(@configuration.magic_comment_regex)
|
|
90
|
-
content.delete_prefix!("#")
|
|
91
|
-
content.delete_prefix!(" ")
|
|
92
|
-
content
|
|
93
|
-
end
|
|
94
|
-
end.join("\n")
|
|
95
|
-
else
|
|
96
|
-
""
|
|
97
|
-
end
|
|
98
|
-
rescue Errno::ENOENT
|
|
99
|
-
# If the file was deleted, but the entry hasn't been removed yet (could happen due to concurrency), then we do
|
|
100
|
-
# not want to fail. Just set the comments to an empty string
|
|
101
|
-
""
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# @abstract
|
|
106
|
-
class ModuleOperation
|
|
107
|
-
#: String
|
|
108
|
-
attr_reader :module_name
|
|
109
|
-
|
|
110
|
-
#: (String module_name) -> void
|
|
111
|
-
def initialize(module_name)
|
|
112
|
-
@module_name = module_name
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
class Include < ModuleOperation; end
|
|
117
|
-
class Prepend < ModuleOperation; end
|
|
118
|
-
|
|
119
|
-
# @abstract
|
|
120
|
-
class Namespace < Entry
|
|
121
|
-
#: Array[String]
|
|
122
|
-
attr_reader :nesting
|
|
123
|
-
|
|
124
|
-
# Returns the location of the constant name, excluding the parent class or the body
|
|
125
|
-
#: Location
|
|
126
|
-
attr_reader :name_location
|
|
127
|
-
|
|
128
|
-
#: (Configuration configuration, Array[String] nesting, URI::Generic uri, Location location, Location name_location, String? comments) -> void
|
|
129
|
-
def initialize(configuration, nesting, uri, location, name_location, comments) # rubocop:disable Metrics/ParameterLists
|
|
130
|
-
@name = nesting.join("::") #: String
|
|
131
|
-
# The original nesting where this namespace was discovered
|
|
132
|
-
@nesting = nesting
|
|
133
|
-
|
|
134
|
-
super(configuration, @name, uri, location, comments)
|
|
135
|
-
|
|
136
|
-
@name_location = name_location
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
#: -> Array[String]
|
|
140
|
-
def mixin_operation_module_names
|
|
141
|
-
mixin_operations.map(&:module_name)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# Stores all explicit prepend, include and extend operations in the exact order they were discovered in the source
|
|
145
|
-
# code. Maintaining the order is essential to linearize ancestors the right way when a module is both included
|
|
146
|
-
# and prepended
|
|
147
|
-
#: -> Array[ModuleOperation]
|
|
148
|
-
def mixin_operations
|
|
149
|
-
@mixin_operations ||= [] #: Array[ModuleOperation]?
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
#: -> Integer
|
|
153
|
-
def ancestor_hash
|
|
154
|
-
mixin_operation_module_names.hash
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
class Module < Namespace
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
class Class < Namespace
|
|
162
|
-
# The unresolved name of the parent class. This may return `nil`, which indicates the lack of an explicit parent
|
|
163
|
-
# and therefore ::Object is the correct parent class
|
|
164
|
-
#: String?
|
|
165
|
-
attr_reader :parent_class
|
|
166
|
-
|
|
167
|
-
#: (Configuration configuration, Array[String] nesting, URI::Generic uri, Location location, Location name_location, String? comments, String? parent_class) -> void
|
|
168
|
-
def initialize(configuration, nesting, uri, location, name_location, comments, parent_class) # rubocop:disable Metrics/ParameterLists
|
|
169
|
-
super(configuration, nesting, uri, location, name_location, comments)
|
|
170
|
-
@parent_class = parent_class
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
# @override
|
|
174
|
-
#: -> Integer
|
|
175
|
-
def ancestor_hash
|
|
176
|
-
[mixin_operation_module_names, @parent_class].hash
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
class SingletonClass < Class
|
|
181
|
-
#: (Location location, Location name_location, String? comments) -> void
|
|
182
|
-
def update_singleton_information(location, name_location, comments)
|
|
183
|
-
@location = location
|
|
184
|
-
@name_location = name_location
|
|
185
|
-
(@comments ||= +"") << comments if comments
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
class Constant < Entry
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
# @abstract
|
|
193
|
-
class Parameter
|
|
194
|
-
# Name includes just the name of the parameter, excluding symbols like splats
|
|
195
|
-
#: Symbol
|
|
196
|
-
attr_reader :name
|
|
197
|
-
|
|
198
|
-
# Decorated name is the parameter name including the splat or block prefix, e.g.: `*foo`, `**foo` or `&block`
|
|
199
|
-
alias_method :decorated_name, :name
|
|
200
|
-
|
|
201
|
-
#: (name: Symbol) -> void
|
|
202
|
-
def initialize(name:)
|
|
203
|
-
@name = name
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# A required method parameter, e.g. `def foo(a)`
|
|
208
|
-
class RequiredParameter < Parameter
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# An optional method parameter, e.g. `def foo(a = 123)`
|
|
212
|
-
class OptionalParameter < Parameter
|
|
213
|
-
# @override
|
|
214
|
-
#: -> Symbol
|
|
215
|
-
def decorated_name
|
|
216
|
-
:"#{@name} = <default>"
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
# An required keyword method parameter, e.g. `def foo(a:)`
|
|
221
|
-
class KeywordParameter < Parameter
|
|
222
|
-
# @override
|
|
223
|
-
#: -> Symbol
|
|
224
|
-
def decorated_name
|
|
225
|
-
:"#{@name}:"
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
# An optional keyword method parameter, e.g. `def foo(a: 123)`
|
|
230
|
-
class OptionalKeywordParameter < Parameter
|
|
231
|
-
# @override
|
|
232
|
-
#: -> Symbol
|
|
233
|
-
def decorated_name
|
|
234
|
-
:"#{@name}: <default>"
|
|
235
|
-
end
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
# A rest method parameter, e.g. `def foo(*a)`
|
|
239
|
-
class RestParameter < Parameter
|
|
240
|
-
DEFAULT_NAME = :"<anonymous splat>" #: Symbol
|
|
241
|
-
|
|
242
|
-
# @override
|
|
243
|
-
#: -> Symbol
|
|
244
|
-
def decorated_name
|
|
245
|
-
:"*#{@name}"
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
# A keyword rest method parameter, e.g. `def foo(**a)`
|
|
250
|
-
class KeywordRestParameter < Parameter
|
|
251
|
-
DEFAULT_NAME = :"<anonymous keyword splat>" #: Symbol
|
|
252
|
-
|
|
253
|
-
# @override
|
|
254
|
-
#: -> Symbol
|
|
255
|
-
def decorated_name
|
|
256
|
-
:"**#{@name}"
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
# A block method parameter, e.g. `def foo(&block)`
|
|
261
|
-
class BlockParameter < Parameter
|
|
262
|
-
DEFAULT_NAME = :"<anonymous block>" #: Symbol
|
|
263
|
-
|
|
264
|
-
class << self
|
|
265
|
-
#: -> BlockParameter
|
|
266
|
-
def anonymous
|
|
267
|
-
new(name: DEFAULT_NAME)
|
|
268
|
-
end
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
# @override
|
|
272
|
-
#: -> Symbol
|
|
273
|
-
def decorated_name
|
|
274
|
-
:"&#{@name}"
|
|
275
|
-
end
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
# A forwarding method parameter, e.g. `def foo(...)`
|
|
279
|
-
class ForwardingParameter < Parameter
|
|
280
|
-
#: -> void
|
|
281
|
-
def initialize
|
|
282
|
-
# You can't name a forwarding parameter, it's always called `...`
|
|
283
|
-
super(name: :"...")
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
# @abstract
|
|
288
|
-
class Member < Entry
|
|
289
|
-
#: Entry::Namespace?
|
|
290
|
-
attr_reader :owner
|
|
291
|
-
|
|
292
|
-
#: (Configuration configuration, String name, URI::Generic uri, Location location, String? comments, Symbol visibility, Entry::Namespace? owner) -> void
|
|
293
|
-
def initialize(configuration, name, uri, location, comments, visibility, owner) # rubocop:disable Metrics/ParameterLists
|
|
294
|
-
super(configuration, name, uri, location, comments)
|
|
295
|
-
@visibility = visibility
|
|
296
|
-
@owner = owner
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
# @abstract
|
|
300
|
-
#: -> Array[Signature]
|
|
301
|
-
def signatures
|
|
302
|
-
raise AbstractMethodInvokedError
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
#: -> String
|
|
306
|
-
def decorated_parameters
|
|
307
|
-
first_signature = signatures.first
|
|
308
|
-
return "()" unless first_signature
|
|
309
|
-
|
|
310
|
-
"(#{first_signature.format})"
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
#: -> String
|
|
314
|
-
def formatted_signatures
|
|
315
|
-
overloads_count = signatures.size
|
|
316
|
-
case overloads_count
|
|
317
|
-
when 1
|
|
318
|
-
""
|
|
319
|
-
when 2
|
|
320
|
-
"\n(+1 overload)"
|
|
321
|
-
else
|
|
322
|
-
"\n(+#{overloads_count - 1} overloads)"
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
end
|
|
326
|
-
|
|
327
|
-
class Accessor < Member
|
|
328
|
-
# @override
|
|
329
|
-
#: -> Array[Signature]
|
|
330
|
-
def signatures
|
|
331
|
-
@signatures ||= begin
|
|
332
|
-
params = []
|
|
333
|
-
params << RequiredParameter.new(name: name.delete_suffix("=").to_sym) if name.end_with?("=")
|
|
334
|
-
[Entry::Signature.new(params)]
|
|
335
|
-
end #: Array[Signature]?
|
|
336
|
-
end
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
class Method < Member
|
|
340
|
-
# @override
|
|
341
|
-
#: Array[Signature]
|
|
342
|
-
attr_reader :signatures
|
|
343
|
-
|
|
344
|
-
# Returns the location of the method name, excluding parameters or the body
|
|
345
|
-
#: Location
|
|
346
|
-
attr_reader :name_location
|
|
347
|
-
|
|
348
|
-
#: (Configuration configuration, String name, URI::Generic uri, Location location, Location name_location, String? comments, Array[Signature] signatures, Symbol visibility, Entry::Namespace? owner) -> void
|
|
349
|
-
def initialize(configuration, name, uri, location, name_location, comments, signatures, visibility, owner) # rubocop:disable Metrics/ParameterLists
|
|
350
|
-
super(configuration, name, uri, location, comments, visibility, owner)
|
|
351
|
-
@signatures = signatures
|
|
352
|
-
@name_location = name_location
|
|
353
|
-
end
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
# An UnresolvedAlias points to a constant alias with a right hand side that has not yet been resolved. For
|
|
357
|
-
# example, if we find
|
|
358
|
-
#
|
|
359
|
-
# ```ruby
|
|
360
|
-
# CONST = Foo
|
|
361
|
-
# ```
|
|
362
|
-
# Before we have discovered `Foo`, there's no way to eagerly resolve this alias to the correct target constant.
|
|
363
|
-
# All aliases are inserted as UnresolvedAlias in the index first and then we lazily resolve them to the correct
|
|
364
|
-
# target in [rdoc-ref:Index#resolve]. If the right hand side contains a constant that doesn't exist, then it's not
|
|
365
|
-
# possible to resolve the alias and it will remain an UnresolvedAlias until the right hand side constant exists
|
|
366
|
-
class UnresolvedConstantAlias < Entry
|
|
367
|
-
#: String
|
|
368
|
-
attr_reader :target
|
|
369
|
-
|
|
370
|
-
#: Array[String]
|
|
371
|
-
attr_reader :nesting
|
|
372
|
-
|
|
373
|
-
#: (Configuration configuration, String target, Array[String] nesting, String name, URI::Generic uri, Location location, String? comments) -> void
|
|
374
|
-
def initialize(configuration, target, nesting, name, uri, location, comments) # rubocop:disable Metrics/ParameterLists
|
|
375
|
-
super(configuration, name, uri, location, comments)
|
|
376
|
-
|
|
377
|
-
@target = target
|
|
378
|
-
@nesting = nesting
|
|
379
|
-
end
|
|
380
|
-
end
|
|
381
|
-
|
|
382
|
-
# Alias represents a resolved alias, which points to an existing constant target
|
|
383
|
-
class ConstantAlias < Entry
|
|
384
|
-
#: String
|
|
385
|
-
attr_reader :target
|
|
386
|
-
|
|
387
|
-
#: (String target, UnresolvedConstantAlias unresolved_alias) -> void
|
|
388
|
-
def initialize(target, unresolved_alias)
|
|
389
|
-
super(
|
|
390
|
-
unresolved_alias.configuration,
|
|
391
|
-
unresolved_alias.name,
|
|
392
|
-
unresolved_alias.uri,
|
|
393
|
-
unresolved_alias.location,
|
|
394
|
-
unresolved_alias.comments,
|
|
395
|
-
)
|
|
396
|
-
|
|
397
|
-
@visibility = unresolved_alias.visibility
|
|
398
|
-
@target = target
|
|
399
|
-
end
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
# Represents a global variable e.g.: $DEBUG
|
|
403
|
-
class GlobalVariable < Entry; end
|
|
404
|
-
|
|
405
|
-
# Represents a class variable e.g.: @@a = 1
|
|
406
|
-
class ClassVariable < Entry
|
|
407
|
-
#: Entry::Namespace?
|
|
408
|
-
attr_reader :owner
|
|
409
|
-
|
|
410
|
-
#: (Configuration configuration, String name, URI::Generic uri, Location location, String? comments, Entry::Namespace? owner) -> void
|
|
411
|
-
def initialize(configuration, name, uri, location, comments, owner) # rubocop:disable Metrics/ParameterLists
|
|
412
|
-
super(configuration, name, uri, location, comments)
|
|
413
|
-
@owner = owner
|
|
414
|
-
end
|
|
415
|
-
end
|
|
416
|
-
|
|
417
|
-
# Represents an instance variable e.g.: @a = 1
|
|
418
|
-
class InstanceVariable < Entry
|
|
419
|
-
#: Entry::Namespace?
|
|
420
|
-
attr_reader :owner
|
|
421
|
-
|
|
422
|
-
#: (Configuration configuration, String name, URI::Generic uri, Location location, String? comments, Entry::Namespace? owner) -> void
|
|
423
|
-
def initialize(configuration, name, uri, location, comments, owner) # rubocop:disable Metrics/ParameterLists
|
|
424
|
-
super(configuration, name, uri, location, comments)
|
|
425
|
-
@owner = owner
|
|
426
|
-
end
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
# An unresolved method alias is an alias entry for which we aren't sure what the right hand side points to yet. For
|
|
430
|
-
# example, if we have `alias a b`, we create an unresolved alias for `a` because we aren't sure immediate what `b`
|
|
431
|
-
# is referring to
|
|
432
|
-
class UnresolvedMethodAlias < Entry
|
|
433
|
-
#: String
|
|
434
|
-
attr_reader :new_name, :old_name
|
|
435
|
-
|
|
436
|
-
#: Entry::Namespace?
|
|
437
|
-
attr_reader :owner
|
|
438
|
-
|
|
439
|
-
#: (Configuration configuration, String new_name, String old_name, Entry::Namespace? owner, URI::Generic uri, Location location, String? comments) -> void
|
|
440
|
-
def initialize(configuration, new_name, old_name, owner, uri, location, comments) # rubocop:disable Metrics/ParameterLists
|
|
441
|
-
super(configuration, new_name, uri, location, comments)
|
|
442
|
-
|
|
443
|
-
@new_name = new_name
|
|
444
|
-
@old_name = old_name
|
|
445
|
-
@owner = owner
|
|
446
|
-
end
|
|
447
|
-
end
|
|
448
|
-
|
|
449
|
-
# A method alias is a resolved alias entry that points to the exact method target it refers to
|
|
450
|
-
class MethodAlias < Entry
|
|
451
|
-
#: (Member | MethodAlias)
|
|
452
|
-
attr_reader :target
|
|
453
|
-
|
|
454
|
-
#: Entry::Namespace?
|
|
455
|
-
attr_reader :owner
|
|
456
|
-
|
|
457
|
-
#: ((Member | MethodAlias) target, UnresolvedMethodAlias unresolved_alias) -> void
|
|
458
|
-
def initialize(target, unresolved_alias)
|
|
459
|
-
full_comments = +"Alias for #{target.name}\n"
|
|
460
|
-
full_comments << "#{unresolved_alias.comments}\n"
|
|
461
|
-
full_comments << target.comments
|
|
462
|
-
|
|
463
|
-
super(
|
|
464
|
-
unresolved_alias.configuration,
|
|
465
|
-
unresolved_alias.new_name,
|
|
466
|
-
unresolved_alias.uri,
|
|
467
|
-
unresolved_alias.location,
|
|
468
|
-
full_comments,
|
|
469
|
-
)
|
|
470
|
-
|
|
471
|
-
@target = target
|
|
472
|
-
@owner = unresolved_alias.owner #: Entry::Namespace?
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
#: -> String
|
|
476
|
-
def decorated_parameters
|
|
477
|
-
@target.decorated_parameters
|
|
478
|
-
end
|
|
479
|
-
|
|
480
|
-
#: -> String
|
|
481
|
-
def formatted_signatures
|
|
482
|
-
@target.formatted_signatures
|
|
483
|
-
end
|
|
484
|
-
|
|
485
|
-
#: -> Array[Signature]
|
|
486
|
-
def signatures
|
|
487
|
-
@target.signatures
|
|
488
|
-
end
|
|
489
|
-
end
|
|
490
|
-
|
|
491
|
-
# Ruby doesn't support method overloading, so a method will have only one signature.
|
|
492
|
-
# However RBS can represent the concept of method overloading, with different return types based on the arguments
|
|
493
|
-
# passed, so we need to store all the signatures.
|
|
494
|
-
class Signature
|
|
495
|
-
#: Array[Parameter]
|
|
496
|
-
attr_reader :parameters
|
|
497
|
-
|
|
498
|
-
#: (Array[Parameter] parameters) -> void
|
|
499
|
-
def initialize(parameters)
|
|
500
|
-
@parameters = parameters
|
|
501
|
-
end
|
|
502
|
-
|
|
503
|
-
# Returns a string with the decorated names of the parameters of this member. E.g.: `(a, b = 1, c: 2)`
|
|
504
|
-
#: -> String
|
|
505
|
-
def format
|
|
506
|
-
@parameters.map(&:decorated_name).join(", ")
|
|
507
|
-
end
|
|
508
|
-
|
|
509
|
-
# Returns `true` if the given call node arguments array matches this method signature. This method will prefer
|
|
510
|
-
# returning `true` for situations that cannot be analyzed statically, like the presence of splats, keyword splats
|
|
511
|
-
# or forwarding arguments.
|
|
512
|
-
#
|
|
513
|
-
# Since this method is used to detect which overload should be displayed in signature help, it will also return
|
|
514
|
-
# `true` if there are missing arguments since the user may not be done typing yet. For example:
|
|
515
|
-
#
|
|
516
|
-
# ```ruby
|
|
517
|
-
# def foo(a, b); end
|
|
518
|
-
# # All of the following are considered matches because the user might be in the middle of typing and we have to
|
|
519
|
-
# # show them the signature
|
|
520
|
-
# foo
|
|
521
|
-
# foo(1)
|
|
522
|
-
# foo(1, 2)
|
|
523
|
-
# ```
|
|
524
|
-
#: (Array[Prism::Node] arguments) -> bool
|
|
525
|
-
def matches?(arguments)
|
|
526
|
-
min_pos = 0
|
|
527
|
-
max_pos = 0 #: (Integer | Float)
|
|
528
|
-
names = []
|
|
529
|
-
has_forward = false #: bool
|
|
530
|
-
has_keyword_rest = false #: bool
|
|
531
|
-
|
|
532
|
-
@parameters.each do |param|
|
|
533
|
-
case param
|
|
534
|
-
when RequiredParameter
|
|
535
|
-
min_pos += 1
|
|
536
|
-
max_pos += 1
|
|
537
|
-
when OptionalParameter
|
|
538
|
-
max_pos += 1
|
|
539
|
-
when RestParameter
|
|
540
|
-
max_pos = Float::INFINITY
|
|
541
|
-
when ForwardingParameter
|
|
542
|
-
max_pos = Float::INFINITY
|
|
543
|
-
has_forward = true
|
|
544
|
-
when KeywordParameter, OptionalKeywordParameter
|
|
545
|
-
names << param.name
|
|
546
|
-
when KeywordRestParameter
|
|
547
|
-
has_keyword_rest = true
|
|
548
|
-
end
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
keyword_hash_nodes, positional_args = arguments.partition { |arg| arg.is_a?(Prism::KeywordHashNode) }
|
|
552
|
-
keyword_args = keyword_hash_nodes.first #: as Prism::KeywordHashNode?
|
|
553
|
-
&.elements
|
|
554
|
-
forwarding_arguments, positionals = positional_args.partition do |arg|
|
|
555
|
-
arg.is_a?(Prism::ForwardingArgumentsNode)
|
|
556
|
-
end
|
|
557
|
-
|
|
558
|
-
return true if has_forward && min_pos == 0
|
|
559
|
-
|
|
560
|
-
# If the only argument passed is a forwarding argument, then anything will match
|
|
561
|
-
(positionals.empty? && forwarding_arguments.any?) ||
|
|
562
|
-
(
|
|
563
|
-
# Check if positional arguments match. This includes required, optional, rest arguments. We also need to
|
|
564
|
-
# verify if there's a trailing forwarding argument, like `def foo(a, ...); end`
|
|
565
|
-
positional_arguments_match?(positionals, forwarding_arguments, keyword_args, min_pos, max_pos) &&
|
|
566
|
-
# If the positional arguments match, we move on to checking keyword, optional keyword and keyword rest
|
|
567
|
-
# arguments. If there's a forward argument, then it will always match. If the method accepts a keyword rest
|
|
568
|
-
# (**kwargs), then we can't analyze statically because the user could be passing a hash and we don't know
|
|
569
|
-
# what the runtime values inside the hash are.
|
|
570
|
-
#
|
|
571
|
-
# If none of those match, then we verify if the user is passing the expect names for the keyword arguments
|
|
572
|
-
(has_forward || has_keyword_rest || keyword_arguments_match?(keyword_args, names))
|
|
573
|
-
)
|
|
574
|
-
end
|
|
575
|
-
|
|
576
|
-
#: (Array[Prism::Node] positional_args, Array[Prism::Node] forwarding_arguments, Array[Prism::Node]? keyword_args, Integer min_pos, (Integer | Float) max_pos) -> bool
|
|
577
|
-
def positional_arguments_match?(positional_args, forwarding_arguments, keyword_args, min_pos, max_pos)
|
|
578
|
-
# If the method accepts at least one positional argument and a splat has been passed
|
|
579
|
-
(min_pos > 0 && positional_args.any? { |arg| arg.is_a?(Prism::SplatNode) }) ||
|
|
580
|
-
# If there's at least one positional argument unaccounted for and a keyword splat has been passed
|
|
581
|
-
(min_pos - positional_args.length > 0 && keyword_args&.any? { |arg| arg.is_a?(Prism::AssocSplatNode) }) ||
|
|
582
|
-
# If there's at least one positional argument unaccounted for and a forwarding argument has been passed
|
|
583
|
-
(min_pos - positional_args.length > 0 && forwarding_arguments.any?) ||
|
|
584
|
-
# If the number of positional arguments is within the expected range
|
|
585
|
-
(min_pos > 0 && positional_args.length <= max_pos) ||
|
|
586
|
-
(min_pos == 0 && positional_args.empty?)
|
|
587
|
-
end
|
|
588
|
-
|
|
589
|
-
#: (Array[Prism::Node]? args, Array[Symbol] names) -> bool
|
|
590
|
-
def keyword_arguments_match?(args, names)
|
|
591
|
-
return true unless args
|
|
592
|
-
return true if args.any? { |arg| arg.is_a?(Prism::AssocSplatNode) }
|
|
593
|
-
|
|
594
|
-
arg_names = args.filter_map do |arg|
|
|
595
|
-
next unless arg.is_a?(Prism::AssocNode)
|
|
596
|
-
|
|
597
|
-
key = arg.key
|
|
598
|
-
key.value&.to_sym if key.is_a?(Prism::SymbolNode)
|
|
599
|
-
end
|
|
600
|
-
|
|
601
|
-
(arg_names - names).empty?
|
|
602
|
-
end
|
|
603
|
-
end
|
|
604
|
-
end
|
|
605
|
-
end
|