ruby-lsp 0.17.3 → 0.17.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/VERSION +1 -1
  4. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +251 -100
  5. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +173 -114
  6. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +337 -77
  7. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +43 -14
  8. data/lib/ruby_indexer/test/classes_and_modules_test.rb +79 -3
  9. data/lib/ruby_indexer/test/index_test.rb +563 -29
  10. data/lib/ruby_indexer/test/instance_variables_test.rb +84 -7
  11. data/lib/ruby_indexer/test/method_test.rb +75 -25
  12. data/lib/ruby_indexer/test/rbs_indexer_test.rb +38 -2
  13. data/lib/ruby_indexer/test/test_case.rb +1 -5
  14. data/lib/ruby_lsp/addon.rb +13 -1
  15. data/lib/ruby_lsp/document.rb +50 -23
  16. data/lib/ruby_lsp/erb_document.rb +125 -0
  17. data/lib/ruby_lsp/global_state.rb +11 -4
  18. data/lib/ruby_lsp/internal.rb +3 -0
  19. data/lib/ruby_lsp/listeners/completion.rb +69 -34
  20. data/lib/ruby_lsp/listeners/definition.rb +34 -23
  21. data/lib/ruby_lsp/listeners/hover.rb +14 -7
  22. data/lib/ruby_lsp/listeners/signature_help.rb +5 -2
  23. data/lib/ruby_lsp/node_context.rb +6 -1
  24. data/lib/ruby_lsp/requests/code_action_resolve.rb +2 -2
  25. data/lib/ruby_lsp/requests/completion.rb +6 -5
  26. data/lib/ruby_lsp/requests/completion_resolve.rb +7 -4
  27. data/lib/ruby_lsp/requests/definition.rb +4 -3
  28. data/lib/ruby_lsp/requests/formatting.rb +2 -0
  29. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +88 -0
  30. data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
  31. data/lib/ruby_lsp/requests/show_syntax_tree.rb +3 -2
  32. data/lib/ruby_lsp/requests/support/common.rb +19 -1
  33. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +12 -4
  34. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +87 -0
  35. data/lib/ruby_lsp/requests/workspace_symbol.rb +1 -21
  36. data/lib/ruby_lsp/requests.rb +2 -0
  37. data/lib/ruby_lsp/ruby_document.rb +10 -0
  38. data/lib/ruby_lsp/server.rb +95 -26
  39. data/lib/ruby_lsp/store.rb +23 -8
  40. data/lib/ruby_lsp/test_helper.rb +3 -1
  41. data/lib/ruby_lsp/type_inferrer.rb +86 -0
  42. metadata +10 -6
@@ -22,6 +22,8 @@ module RubyIndexer
22
22
  sig { returns(RubyIndexer::Location) }
23
23
  attr_reader :location
24
24
 
25
+ alias_method :name_location, :location
26
+
25
27
  sig { returns(T::Array[String]) }
26
28
  attr_reader :comments
27
29
 
@@ -57,6 +59,16 @@ module RubyIndexer
57
59
  )
58
60
  end
59
61
 
62
+ sig { returns(T::Boolean) }
63
+ def public?
64
+ visibility == Visibility::PUBLIC
65
+ end
66
+
67
+ sig { returns(T::Boolean) }
68
+ def protected?
69
+ visibility == Visibility::PROTECTED
70
+ end
71
+
60
72
  sig { returns(T::Boolean) }
61
73
  def private?
62
74
  visibility == Visibility::PRIVATE
@@ -84,7 +96,6 @@ module RubyIndexer
84
96
 
85
97
  class Include < ModuleOperation; end
86
98
  class Prepend < ModuleOperation; end
87
- class Extend < ModuleOperation; end
88
99
 
89
100
  class Namespace < Entry
90
101
  extend T::Sig
@@ -95,20 +106,39 @@ module RubyIndexer
95
106
  sig { returns(T::Array[String]) }
96
107
  attr_reader :nesting
97
108
 
109
+ # Returns the location of the constant name, excluding the parent class or the body
110
+ sig { returns(Location) }
111
+ attr_reader :name_location
112
+
98
113
  sig do
99
114
  params(
100
115
  nesting: T::Array[String],
101
116
  file_path: String,
102
117
  location: T.any(Prism::Location, RubyIndexer::Location),
118
+ name_location: T.any(Prism::Location, Location),
103
119
  comments: T::Array[String],
104
120
  ).void
105
121
  end
106
- def initialize(nesting, file_path, location, comments)
122
+ def initialize(nesting, file_path, location, name_location, comments)
107
123
  @name = T.let(nesting.join("::"), String)
108
124
  # The original nesting where this namespace was discovered
109
125
  @nesting = nesting
110
126
 
111
127
  super(@name, file_path, location, comments)
128
+
129
+ @name_location = T.let(
130
+ if name_location.is_a?(Prism::Location)
131
+ Location.new(
132
+ name_location.start_line,
133
+ name_location.end_line,
134
+ name_location.start_column,
135
+ name_location.end_column,
136
+ )
137
+ else
138
+ name_location
139
+ end,
140
+ RubyIndexer::Location,
141
+ )
112
142
  end
113
143
 
114
144
  sig { returns(T::Array[String]) }
@@ -146,14 +176,14 @@ module RubyIndexer
146
176
  nesting: T::Array[String],
147
177
  file_path: String,
148
178
  location: T.any(Prism::Location, RubyIndexer::Location),
179
+ name_location: T.any(Prism::Location, Location),
149
180
  comments: T::Array[String],
150
181
  parent_class: T.nilable(String),
151
182
  ).void
152
183
  end
153
- def initialize(nesting, file_path, location, comments, parent_class)
154
- super(nesting, file_path, location, comments)
155
-
156
- @parent_class = T.let(parent_class, T.nilable(String))
184
+ def initialize(nesting, file_path, location, name_location, comments, parent_class) # rubocop:disable Metrics/ParameterLists
185
+ super(nesting, file_path, location, name_location, comments)
186
+ @parent_class = parent_class
157
187
  end
158
188
 
159
189
  sig { override.returns(Integer) }
@@ -162,6 +192,28 @@ module RubyIndexer
162
192
  end
163
193
  end
164
194
 
195
+ class SingletonClass < Class
196
+ extend T::Sig
197
+
198
+ sig { params(location: Prism::Location, name_location: Prism::Location, comments: T::Array[String]).void }
199
+ def update_singleton_information(location, name_location, comments)
200
+ # Create a new RubyIndexer::Location object from the Prism location
201
+ @location = Location.new(
202
+ location.start_line,
203
+ location.end_line,
204
+ location.start_column,
205
+ location.end_column,
206
+ )
207
+ @name_location = Location.new(
208
+ name_location.start_line,
209
+ name_location.end_line,
210
+ name_location.start_column,
211
+ name_location.end_column,
212
+ )
213
+ @comments.concat(comments)
214
+ end
215
+ end
216
+
165
217
  class Constant < Entry
166
218
  end
167
219
 
@@ -190,6 +242,10 @@ module RubyIndexer
190
242
 
191
243
  # An optional method parameter, e.g. `def foo(a = 123)`
192
244
  class OptionalParameter < Parameter
245
+ sig { override.returns(Symbol) }
246
+ def decorated_name
247
+ :"#{@name} = <default>"
248
+ end
193
249
  end
194
250
 
195
251
  # An required keyword method parameter, e.g. `def foo(a:)`
@@ -204,7 +260,7 @@ module RubyIndexer
204
260
  class OptionalKeywordParameter < Parameter
205
261
  sig { override.returns(Symbol) }
206
262
  def decorated_name
207
- :"#{@name}:"
263
+ :"#{@name}: <default>"
208
264
  end
209
265
  end
210
266
 
@@ -247,6 +303,11 @@ module RubyIndexer
247
303
  sig { returns(T.nilable(Entry::Namespace)) }
248
304
  attr_reader :owner
249
305
 
306
+ sig { returns(T::Array[RubyIndexer::Entry::Parameter]) }
307
+ def parameters
308
+ T.must(signatures.first).parameters
309
+ end
310
+
250
311
  sig do
251
312
  params(
252
313
  name: String,
@@ -263,140 +324,75 @@ module RubyIndexer
263
324
  @owner = owner
264
325
  end
265
326
 
266
- sig { abstract.returns(T::Array[Parameter]) }
267
- def parameters; end
327
+ sig { abstract.returns(T::Array[Entry::Signature]) }
328
+ def signatures; end
329
+
330
+ sig { returns(String) }
331
+ def decorated_parameters
332
+ first_signature = signatures.first
333
+ return "()" unless first_signature
334
+
335
+ "(#{first_signature.format})"
336
+ end
268
337
  end
269
338
 
270
339
  class Accessor < Member
271
340
  extend T::Sig
272
341
 
273
- sig { override.returns(T::Array[Parameter]) }
274
- def parameters
275
- params = []
276
- params << RequiredParameter.new(name: name.delete_suffix("=").to_sym) if name.end_with?("=")
277
- params
342
+ sig { override.returns(T::Array[Signature]) }
343
+ def signatures
344
+ @signatures ||= T.let(
345
+ begin
346
+ params = []
347
+ params << RequiredParameter.new(name: name.delete_suffix("=").to_sym) if name.end_with?("=")
348
+ [Entry::Signature.new(params)]
349
+ end,
350
+ T.nilable(T::Array[Signature]),
351
+ )
278
352
  end
279
353
  end
280
354
 
281
355
  class Method < Member
282
356
  extend T::Sig
283
- extend T::Helpers
284
357
 
285
- abstract!
358
+ sig { override.returns(T::Array[Signature]) }
359
+ attr_reader :signatures
286
360
 
287
- sig { override.returns(T::Array[Parameter]) }
288
- attr_reader :parameters
361
+ # Returns the location of the method name, excluding parameters or the body
362
+ sig { returns(Location) }
363
+ attr_reader :name_location
289
364
 
290
365
  sig do
291
366
  params(
292
367
  name: String,
293
368
  file_path: String,
294
369
  location: T.any(Prism::Location, RubyIndexer::Location),
370
+ name_location: T.any(Prism::Location, Location),
295
371
  comments: T::Array[String],
296
- parameters_node: T.nilable(Prism::ParametersNode),
372
+ signatures: T::Array[Signature],
297
373
  visibility: Visibility,
298
374
  owner: T.nilable(Entry::Namespace),
299
375
  ).void
300
376
  end
301
- def initialize(name, file_path, location, comments, parameters_node, visibility, owner) # rubocop:disable Metrics/ParameterLists
377
+ def initialize(name, file_path, location, name_location, comments, signatures, visibility, owner) # rubocop:disable Metrics/ParameterLists
302
378
  super(name, file_path, location, comments, visibility, owner)
303
-
304
- @parameters = T.let(list_params(parameters_node), T::Array[Parameter])
305
- end
306
-
307
- private
308
-
309
- sig { params(parameters_node: T.nilable(Prism::ParametersNode)).returns(T::Array[Parameter]) }
310
- def list_params(parameters_node)
311
- return [] unless parameters_node
312
-
313
- parameters = []
314
-
315
- parameters_node.requireds.each do |required|
316
- name = parameter_name(required)
317
- next unless name
318
-
319
- parameters << RequiredParameter.new(name: name)
320
- end
321
-
322
- parameters_node.optionals.each do |optional|
323
- name = parameter_name(optional)
324
- next unless name
325
-
326
- parameters << OptionalParameter.new(name: name)
327
- end
328
-
329
- parameters_node.keywords.each do |keyword|
330
- name = parameter_name(keyword)
331
- next unless name
332
-
333
- case keyword
334
- when Prism::RequiredKeywordParameterNode
335
- parameters << KeywordParameter.new(name: name)
336
- when Prism::OptionalKeywordParameterNode
337
- parameters << OptionalKeywordParameter.new(name: name)
338
- end
339
- end
340
-
341
- rest = parameters_node.rest
342
-
343
- if rest.is_a?(Prism::RestParameterNode)
344
- rest_name = rest.name || RestParameter::DEFAULT_NAME
345
- parameters << RestParameter.new(name: rest_name)
346
- end
347
-
348
- keyword_rest = parameters_node.keyword_rest
349
-
350
- if keyword_rest.is_a?(Prism::KeywordRestParameterNode)
351
- keyword_rest_name = parameter_name(keyword_rest) || KeywordRestParameter::DEFAULT_NAME
352
- parameters << KeywordRestParameter.new(name: keyword_rest_name)
353
- end
354
-
355
- parameters_node.posts.each do |post|
356
- name = parameter_name(post)
357
- next unless name
358
-
359
- parameters << RequiredParameter.new(name: name)
360
- end
361
-
362
- block = parameters_node.block
363
- parameters << BlockParameter.new(name: block.name || BlockParameter::DEFAULT_NAME) if block
364
-
365
- parameters
366
- end
367
-
368
- sig { params(node: T.nilable(Prism::Node)).returns(T.nilable(Symbol)) }
369
- def parameter_name(node)
370
- case node
371
- when Prism::RequiredParameterNode, Prism::OptionalParameterNode,
372
- Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode,
373
- Prism::RestParameterNode, Prism::KeywordRestParameterNode
374
- node.name
375
- when Prism::MultiTargetNode
376
- names = node.lefts.map { |parameter_node| parameter_name(parameter_node) }
377
-
378
- rest = node.rest
379
- if rest.is_a?(Prism::SplatNode)
380
- name = rest.expression&.slice
381
- names << (rest.operator == "*" ? "*#{name}".to_sym : name&.to_sym)
382
- end
383
-
384
- names << nil if rest.is_a?(Prism::ImplicitRestNode)
385
-
386
- names.concat(node.rights.map { |parameter_node| parameter_name(parameter_node) })
387
-
388
- names_with_commas = names.join(", ")
389
- :"(#{names_with_commas})"
390
- end
379
+ @signatures = signatures
380
+ @name_location = T.let(
381
+ if name_location.is_a?(Prism::Location)
382
+ Location.new(
383
+ name_location.start_line,
384
+ name_location.end_line,
385
+ name_location.start_column,
386
+ name_location.end_column,
387
+ )
388
+ else
389
+ name_location
390
+ end,
391
+ RubyIndexer::Location,
392
+ )
391
393
  end
392
394
  end
393
395
 
394
- class SingletonMethod < Method
395
- end
396
-
397
- class InstanceMethod < Method
398
- end
399
-
400
396
  # An UnresolvedAlias points to a constant alias with a right hand side that has not yet been resolved. For
401
397
  # example, if we find
402
398
  #
@@ -470,6 +466,9 @@ module RubyIndexer
470
466
  end
471
467
  end
472
468
 
469
+ # An unresolved method alias is an alias entry for which we aren't sure what the right hand side points to yet. For
470
+ # example, if we have `alias a b`, we create an unresolved alias for `a` because we aren't sure immediate what `b`
471
+ # is referring to
473
472
  class UnresolvedMethodAlias < Entry
474
473
  extend T::Sig
475
474
 
@@ -497,5 +496,65 @@ module RubyIndexer
497
496
  @owner = owner
498
497
  end
499
498
  end
499
+
500
+ # A method alias is a resolved alias entry that points to the exact method target it refers to
501
+ class MethodAlias < Entry
502
+ extend T::Sig
503
+
504
+ sig { returns(T.any(Member, MethodAlias)) }
505
+ attr_reader :target
506
+
507
+ sig { returns(T.nilable(Entry::Namespace)) }
508
+ attr_reader :owner
509
+
510
+ sig { params(target: T.any(Member, MethodAlias), unresolved_alias: UnresolvedMethodAlias).void }
511
+ def initialize(target, unresolved_alias)
512
+ full_comments = ["Alias for #{target.name}\n"]
513
+ full_comments.concat(unresolved_alias.comments)
514
+ full_comments << "\n"
515
+ full_comments.concat(target.comments)
516
+
517
+ super(
518
+ unresolved_alias.new_name,
519
+ unresolved_alias.file_path,
520
+ unresolved_alias.location,
521
+ full_comments,
522
+ )
523
+
524
+ @target = target
525
+ @owner = T.let(unresolved_alias.owner, T.nilable(Entry::Namespace))
526
+ end
527
+
528
+ sig { returns(T::Array[Parameter]) }
529
+ def parameters
530
+ @target.parameters
531
+ end
532
+
533
+ sig { returns(String) }
534
+ def decorated_parameters
535
+ @target.decorated_parameters
536
+ end
537
+ end
538
+
539
+ # Ruby doesn't support method overloading, so a method will have only one signature.
540
+ # However RBS can represent the concept of method overloading, with different return types based on the arguments
541
+ # passed, so we need to store all the signatures.
542
+ class Signature
543
+ extend T::Sig
544
+
545
+ sig { returns(T::Array[Parameter]) }
546
+ attr_reader :parameters
547
+
548
+ sig { params(parameters: T::Array[Parameter]).void }
549
+ def initialize(parameters)
550
+ @parameters = parameters
551
+ end
552
+
553
+ # Returns a string with the decorated names of the parameters of this member. E.g.: `(a, b = 1, c: 2)`
554
+ sig { returns(String) }
555
+ def format
556
+ @parameters.map(&:decorated_name).join(", ")
557
+ end
558
+ end
500
559
  end
501
560
  end