ruby-lsp 0.26.4 → 0.26.6

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp-launcher +4 -3
  4. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +3 -2
  5. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +19 -0
  6. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +33 -27
  7. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +1 -0
  8. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +6 -2
  9. data/lib/ruby_lsp/global_state.rb +65 -33
  10. data/lib/ruby_lsp/listeners/definition.rb +34 -14
  11. data/lib/ruby_lsp/listeners/document_link.rb +1 -6
  12. data/lib/ruby_lsp/listeners/hover.rb +2 -2
  13. data/lib/ruby_lsp/requests/code_action_resolve.rb +33 -11
  14. data/lib/ruby_lsp/requests/code_actions.rb +20 -5
  15. data/lib/ruby_lsp/requests/completion_resolve.rb +8 -6
  16. data/lib/ruby_lsp/requests/support/source_uri.rb +7 -6
  17. data/lib/ruby_lsp/scripts/compose_bundle.rb +1 -1
  18. data/lib/ruby_lsp/setup_bundler.rb +39 -25
  19. metadata +2 -16
  20. data/lib/ruby_indexer/test/class_variables_test.rb +0 -140
  21. data/lib/ruby_indexer/test/classes_and_modules_test.rb +0 -770
  22. data/lib/ruby_indexer/test/configuration_test.rb +0 -279
  23. data/lib/ruby_indexer/test/constant_test.rb +0 -402
  24. data/lib/ruby_indexer/test/enhancements_test.rb +0 -325
  25. data/lib/ruby_indexer/test/global_variable_test.rb +0 -49
  26. data/lib/ruby_indexer/test/index_test.rb +0 -2276
  27. data/lib/ruby_indexer/test/instance_variables_test.rb +0 -264
  28. data/lib/ruby_indexer/test/method_test.rb +0 -990
  29. data/lib/ruby_indexer/test/prefix_tree_test.rb +0 -150
  30. data/lib/ruby_indexer/test/rbs_indexer_test.rb +0 -381
  31. data/lib/ruby_indexer/test/reference_finder_test.rb +0 -395
  32. data/lib/ruby_indexer/test/test_case.rb +0 -57
  33. data/lib/ruby_indexer/test/uri_test.rb +0 -85
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9873a3783f44e51f4631155591716e140f79732f0c1e0bf268aedc09ec670eb7
4
- data.tar.gz: 2d84a75fc80b719cbf50df63f66c08d096077adbd2256551370664e7f33ad81c
3
+ metadata.gz: ce859aba34e036ede0a47740d7a1beb65ed4680d58bf95fa17ac85e7c74e8560
4
+ data.tar.gz: 49e26820d59a101fb64bbef651741225f4670b892ce7f6317eb7566389e62f8c
5
5
  SHA512:
6
- metadata.gz: cba3944e32212c7f2376b26659388e5a2b59c04eb454b475e605b543b0e208ea518e99330a942acc3b1ca66dcba829f8b74dbdba5ed63bee263ca6556da06803
7
- data.tar.gz: 2d10390c082fc925ae90e2cd630c58ac44649086d775d0ca6d0dd046c886fc0dd6577d386b6011e54decacac93d115ccaaf5cad5b1db0f73f76ab0f31110ca0d
6
+ metadata.gz: 7a5dc4ad02c3e8fc462a08fc059e84039b2d63b39e066a46aedfa724837161941a92a8fd198f645818de819a7140e8abe576bc3a770fe355cab4f689ef9d568e
7
+ data.tar.gz: 2e7fa7e3d5e409c91fc4e3a05d1c15ea4b3bbabf41e2edd4078acb80e9817e1436b50a0a6656f1c46e7e6eb6d4f19f8b499cb89dc8aa761e7984bbad94cb5263
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.26.4
1
+ 0.26.6
@@ -108,10 +108,11 @@ rescue Bundler::GemNotFound, Bundler::GitError
108
108
  unless install_error || ARGV.include?("--retry")
109
109
  $stderr.puts("Initial bundle compose succeeded, but Bundler.setup failed. Trying to restart from scratch...")
110
110
  File.write(raw_initialize_path, raw_initialize)
111
- exec(Gem.ruby, __FILE__, *ARGV, "--retry")
112
- end
113
111
 
114
- $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
112
+ Bundler.with_unbundled_env do
113
+ exec(Gem.ruby, __FILE__, *ARGV, "--retry")
114
+ end
115
+ end
115
116
  rescue StandardError => e
116
117
  setup_error = e
117
118
  $stderr.puts("Failed to set up composed Bundle\n#{e.full_message}")
@@ -227,7 +227,7 @@ module RubyIndexer
227
227
  others.uniq!
228
228
  others.map!(&:name)
229
229
 
230
- excluded.each do |dependency|
230
+ transitive_excluded = excluded.each_with_object([]) do |dependency, acc|
231
231
  next unless dependency.runtime?
232
232
 
233
233
  spec = dependency.to_spec
@@ -236,7 +236,7 @@ module RubyIndexer
236
236
  spec.dependencies.each do |transitive_dependency|
237
237
  next if others.include?(transitive_dependency.name)
238
238
 
239
- excluded << transitive_dependency
239
+ acc << transitive_dependency
240
240
  end
241
241
  rescue Gem::MissingSpecError
242
242
  # If a gem is scoped only to some specific platform, then its dependencies may not be installed either, but they
@@ -244,6 +244,7 @@ module RubyIndexer
244
244
  # just ignore if they're missing
245
245
  end
246
246
 
247
+ excluded.concat(transitive_excluded)
247
248
  excluded.uniq!
248
249
  excluded.map(&:name)
249
250
  rescue Bundler::GemfileNotFound
@@ -143,6 +143,7 @@ module RubyIndexer
143
143
  )
144
144
  else
145
145
  entry = Entry::SingletonClass.new(
146
+ @index.configuration,
146
147
  real_nesting,
147
148
  @uri,
148
149
  Location.from_prism_location(node.location, @code_units_cache),
@@ -326,6 +327,7 @@ module RubyIndexer
326
327
  signatures = [Entry::Signature.new(list_params(node.parameters))]
327
328
 
328
329
  @index.add(Entry::Method.new(
330
+ @index.configuration,
329
331
  method_name,
330
332
  @uri,
331
333
  location,
@@ -340,6 +342,7 @@ module RubyIndexer
340
342
  singleton = @index.existing_or_new_singleton_class(owner.name)
341
343
 
342
344
  @index.add(Entry::Method.new(
345
+ @index.configuration,
343
346
  method_name,
344
347
  @uri,
345
348
  location,
@@ -354,6 +357,7 @@ module RubyIndexer
354
357
  singleton = @index.existing_or_new_singleton_class(owner.name)
355
358
 
356
359
  @index.add(Entry::Method.new(
360
+ @index.configuration,
357
361
  method_name,
358
362
  @uri,
359
363
  Location.from_prism_location(node.location, @code_units_cache),
@@ -433,6 +437,7 @@ module RubyIndexer
433
437
  comments = collect_comments(node)
434
438
  @index.add(
435
439
  Entry::UnresolvedMethodAlias.new(
440
+ @index.configuration,
436
441
  method_name,
437
442
  node.old_name.slice,
438
443
  @owner_stack.last,
@@ -473,6 +478,7 @@ module RubyIndexer
473
478
  location = Location.from_prism_location(node_location, @code_units_cache)
474
479
 
475
480
  @index.add(Entry::Method.new(
481
+ @index.configuration,
476
482
  name,
477
483
  @uri,
478
484
  location,
@@ -490,6 +496,7 @@ module RubyIndexer
490
496
  name_loc = Location.from_prism_location(name_location, @code_units_cache)
491
497
 
492
498
  entry = Entry::Module.new(
499
+ @index.configuration,
493
500
  Index.actual_nesting(@stack, name),
494
501
  @uri,
495
502
  location,
@@ -504,6 +511,7 @@ module RubyIndexer
504
511
  def add_class(name_or_nesting, full_location, name_location, parent_class_name: nil, comments: nil)
505
512
  nesting = name_or_nesting.is_a?(Array) ? name_or_nesting : Index.actual_nesting(@stack, name_or_nesting)
506
513
  entry = Entry::Class.new(
514
+ @index.configuration,
507
515
  nesting,
508
516
  @uri,
509
517
  Location.from_prism_location(full_location, @code_units_cache),
@@ -548,6 +556,7 @@ module RubyIndexer
548
556
  comments = collect_comments(node)
549
557
 
550
558
  @index.add(Entry::GlobalVariable.new(
559
+ @index.configuration,
551
560
  name,
552
561
  @uri,
553
562
  Location.from_prism_location(loc, @code_units_cache),
@@ -572,6 +581,7 @@ module RubyIndexer
572
581
  end
573
582
 
574
583
  @index.add(Entry::ClassVariable.new(
584
+ @index.configuration,
575
585
  name,
576
586
  @uri,
577
587
  Location.from_prism_location(loc, @code_units_cache),
@@ -594,6 +604,7 @@ module RubyIndexer
594
604
  end
595
605
 
596
606
  @index.add(Entry::InstanceVariable.new(
607
+ @index.configuration,
597
608
  name,
598
609
  @uri,
599
610
  Location.from_prism_location(loc, @code_units_cache),
@@ -656,6 +667,7 @@ module RubyIndexer
656
667
  comments = collect_comments(node)
657
668
  @index.add(
658
669
  Entry::UnresolvedMethodAlias.new(
670
+ @index.configuration,
659
671
  new_name_value,
660
672
  old_name_value,
661
673
  @owner_stack.last,
@@ -675,6 +687,7 @@ module RubyIndexer
675
687
  case value
676
688
  when Prism::ConstantReadNode, Prism::ConstantPathNode
677
689
  Entry::UnresolvedConstantAlias.new(
690
+ @index.configuration,
678
691
  value.slice,
679
692
  @stack.dup,
680
693
  name,
@@ -688,6 +701,7 @@ module RubyIndexer
688
701
  # If the right hand side is another constant assignment, we need to visit it because that constant has to be
689
702
  # indexed too
690
703
  Entry::UnresolvedConstantAlias.new(
704
+ @index.configuration,
691
705
  value.name.to_s,
692
706
  @stack.dup,
693
707
  name,
@@ -699,6 +713,7 @@ module RubyIndexer
699
713
  Prism::ConstantPathAndWriteNode
700
714
 
701
715
  Entry::UnresolvedConstantAlias.new(
716
+ @index.configuration,
702
717
  value.target.slice,
703
718
  @stack.dup,
704
719
  name,
@@ -708,6 +723,7 @@ module RubyIndexer
708
723
  )
709
724
  else
710
725
  Entry::Constant.new(
726
+ @index.configuration,
711
727
  name,
712
728
  @uri,
713
729
  Location.from_prism_location(node.location, @code_units_cache),
@@ -785,6 +801,7 @@ module RubyIndexer
785
801
 
786
802
  if reader
787
803
  @index.add(Entry::Accessor.new(
804
+ @index.configuration,
788
805
  name,
789
806
  @uri,
790
807
  Location.from_prism_location(loc, @code_units_cache),
@@ -797,6 +814,7 @@ module RubyIndexer
797
814
  next unless writer
798
815
 
799
816
  @index.add(Entry::Accessor.new(
817
+ @index.configuration,
800
818
  "#{name}=",
801
819
  @uri,
802
820
  Location.from_prism_location(loc, @code_units_cache),
@@ -879,6 +897,7 @@ module RubyIndexer
879
897
  singleton = @index.existing_or_new_singleton_class(entry_owner_name)
880
898
  location = Location.from_prism_location(argument.location, @code_units_cache)
881
899
  @index.add(Entry::Method.new(
900
+ @index.configuration,
882
901
  method_name,
883
902
  @uri,
884
903
  location,
@@ -3,6 +3,9 @@
3
3
 
4
4
  module RubyIndexer
5
5
  class Entry
6
+ #: Configuration
7
+ attr_reader :configuration
8
+
6
9
  #: String
7
10
  attr_reader :name
8
11
 
@@ -17,8 +20,9 @@ module RubyIndexer
17
20
  #: Symbol
18
21
  attr_accessor :visibility
19
22
 
20
- #: (String name, URI::Generic uri, Location location, String? comments) -> void
21
- def initialize(name, uri, location, comments)
23
+ #: (Configuration configuration, String name, URI::Generic uri, Location location, String? comments) -> void
24
+ def initialize(configuration, name, uri, location, comments)
25
+ @configuration = configuration
22
26
  @name = name
23
27
  @uri = uri
24
28
  @comments = comments
@@ -82,7 +86,7 @@ module RubyIndexer
82
86
  correct_group.filter_map do |comment|
83
87
  content = comment.slice.chomp
84
88
 
85
- if content.valid_encoding?
89
+ if content.valid_encoding? && !content.match?(@configuration.magic_comment_regex)
86
90
  content.delete_prefix!("#")
87
91
  content.delete_prefix!(" ")
88
92
  content
@@ -121,13 +125,13 @@ module RubyIndexer
121
125
  #: Location
122
126
  attr_reader :name_location
123
127
 
124
- #: (Array[String] nesting, URI::Generic uri, Location location, Location name_location, String? comments) -> void
125
- def initialize(nesting, uri, location, name_location, comments)
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
126
130
  @name = nesting.join("::") #: String
127
131
  # The original nesting where this namespace was discovered
128
132
  @nesting = nesting
129
133
 
130
- super(@name, uri, location, comments)
134
+ super(configuration, @name, uri, location, comments)
131
135
 
132
136
  @name_location = name_location
133
137
  end
@@ -160,9 +164,9 @@ module RubyIndexer
160
164
  #: String?
161
165
  attr_reader :parent_class
162
166
 
163
- #: (Array[String] nesting, URI::Generic uri, Location location, Location name_location, String? comments, String? parent_class) -> void
164
- def initialize(nesting, uri, location, name_location, comments, parent_class) # rubocop:disable Metrics/ParameterLists
165
- super(nesting, uri, location, name_location, comments)
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)
166
170
  @parent_class = parent_class
167
171
  end
168
172
 
@@ -285,9 +289,9 @@ module RubyIndexer
285
289
  #: Entry::Namespace?
286
290
  attr_reader :owner
287
291
 
288
- #: (String name, URI::Generic uri, Location location, String? comments, Symbol visibility, Entry::Namespace? owner) -> void
289
- def initialize(name, uri, location, comments, visibility, owner) # rubocop:disable Metrics/ParameterLists
290
- super(name, uri, location, comments)
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)
291
295
  @visibility = visibility
292
296
  @owner = owner
293
297
  end
@@ -341,9 +345,9 @@ module RubyIndexer
341
345
  #: Location
342
346
  attr_reader :name_location
343
347
 
344
- #: (String name, URI::Generic uri, Location location, Location name_location, String? comments, Array[Signature] signatures, Symbol visibility, Entry::Namespace? owner) -> void
345
- def initialize(name, uri, location, name_location, comments, signatures, visibility, owner) # rubocop:disable Metrics/ParameterLists
346
- super(name, uri, location, comments, visibility, owner)
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)
347
351
  @signatures = signatures
348
352
  @name_location = name_location
349
353
  end
@@ -366,9 +370,9 @@ module RubyIndexer
366
370
  #: Array[String]
367
371
  attr_reader :nesting
368
372
 
369
- #: (String target, Array[String] nesting, String name, URI::Generic uri, Location location, String? comments) -> void
370
- def initialize(target, nesting, name, uri, location, comments) # rubocop:disable Metrics/ParameterLists
371
- super(name, uri, location, comments)
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)
372
376
 
373
377
  @target = target
374
378
  @nesting = nesting
@@ -383,6 +387,7 @@ module RubyIndexer
383
387
  #: (String target, UnresolvedConstantAlias unresolved_alias) -> void
384
388
  def initialize(target, unresolved_alias)
385
389
  super(
390
+ unresolved_alias.configuration,
386
391
  unresolved_alias.name,
387
392
  unresolved_alias.uri,
388
393
  unresolved_alias.location,
@@ -402,9 +407,9 @@ module RubyIndexer
402
407
  #: Entry::Namespace?
403
408
  attr_reader :owner
404
409
 
405
- #: (String name, URI::Generic uri, Location location, String? comments, Entry::Namespace? owner) -> void
406
- def initialize(name, uri, location, comments, owner)
407
- super(name, uri, location, comments)
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)
408
413
  @owner = owner
409
414
  end
410
415
  end
@@ -414,9 +419,9 @@ module RubyIndexer
414
419
  #: Entry::Namespace?
415
420
  attr_reader :owner
416
421
 
417
- #: (String name, URI::Generic uri, Location location, String? comments, Entry::Namespace? owner) -> void
418
- def initialize(name, uri, location, comments, owner)
419
- super(name, uri, location, comments)
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)
420
425
  @owner = owner
421
426
  end
422
427
  end
@@ -431,9 +436,9 @@ module RubyIndexer
431
436
  #: Entry::Namespace?
432
437
  attr_reader :owner
433
438
 
434
- #: (String new_name, String old_name, Entry::Namespace? owner, URI::Generic uri, Location location, String? comments) -> void
435
- def initialize(new_name, old_name, owner, uri, location, comments) # rubocop:disable Metrics/ParameterLists
436
- super(new_name, uri, location, comments)
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)
437
442
 
438
443
  @new_name = new_name
439
444
  @old_name = old_name
@@ -456,6 +461,7 @@ module RubyIndexer
456
461
  full_comments << target.comments
457
462
 
458
463
  super(
464
+ unresolved_alias.configuration,
459
465
  unresolved_alias.new_name,
460
466
  unresolved_alias.uri,
461
467
  unresolved_alias.location,
@@ -714,6 +714,7 @@ module RubyIndexer
714
714
  attached_ancestor = self[name]&.first #: as !nil
715
715
 
716
716
  singleton = Entry::SingletonClass.new(
717
+ @configuration,
717
718
  [full_singleton_name],
718
719
  attached_ancestor.uri,
719
720
  attached_ancestor.location,
@@ -52,9 +52,9 @@ module RubyIndexer
52
52
  comments = comments_to_string(declaration)
53
53
  entry = if declaration.is_a?(RBS::AST::Declarations::Class)
54
54
  parent_class = declaration.super_class&.name&.name&.to_s
55
- Entry::Class.new(nesting, uri, location, location, comments, parent_class)
55
+ Entry::Class.new(@index.configuration, nesting, uri, location, location, comments, parent_class)
56
56
  else
57
- Entry::Module.new(nesting, uri, location, location, comments)
57
+ Entry::Module.new(@index.configuration, nesting, uri, location, location, comments)
58
58
  end
59
59
 
60
60
  add_declaration_mixins_to_entry(declaration, entry)
@@ -110,6 +110,7 @@ module RubyIndexer
110
110
  real_owner = member.singleton? ? @index.existing_or_new_singleton_class(owner.name) : owner
111
111
  signatures = signatures(member)
112
112
  @index.add(Entry::Method.new(
113
+ @index.configuration,
113
114
  name,
114
115
  uri,
115
116
  location,
@@ -243,6 +244,7 @@ module RubyIndexer
243
244
  def handle_constant(declaration, nesting, uri)
244
245
  fully_qualified_name = [*nesting, declaration.name.name.to_s].join("::")
245
246
  @index.add(Entry::Constant.new(
247
+ @index.configuration,
246
248
  fully_qualified_name,
247
249
  uri,
248
250
  to_ruby_indexer_location(declaration.location),
@@ -258,6 +260,7 @@ module RubyIndexer
258
260
  comments = comments_to_string(declaration)
259
261
 
260
262
  @index.add(Entry::GlobalVariable.new(
263
+ @index.configuration,
261
264
  name,
262
265
  uri,
263
266
  location,
@@ -271,6 +274,7 @@ module RubyIndexer
271
274
  comments = comments_to_string(member)
272
275
 
273
276
  entry = Entry::UnresolvedMethodAlias.new(
277
+ @index.configuration,
274
278
  member.new_name.to_s,
275
279
  member.old_name.to_s,
276
280
  owner_entry,
@@ -2,6 +2,21 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyLsp
5
+ # Holds the detected value and the reason for detection
6
+ class DetectionResult
7
+ #: String
8
+ attr_reader :value
9
+
10
+ #: String
11
+ attr_reader :reason
12
+
13
+ #: (String value, String reason) -> void
14
+ def initialize(value, reason)
15
+ @value = value
16
+ @reason = reason
17
+ end
18
+ end
19
+
5
20
  class GlobalState
6
21
  #: String
7
22
  attr_reader :test_library
@@ -122,8 +137,11 @@ module RubyLsp
122
137
  end
123
138
 
124
139
  if @formatter == "auto"
125
- @formatter = detect_formatter(direct_dependencies, all_dependencies)
126
- notifications << Notification.window_log_message("Auto detected formatter: #{@formatter}")
140
+ formatter_result = detect_formatter(direct_dependencies, all_dependencies)
141
+ @formatter = formatter_result.value
142
+ notifications << Notification.window_log_message(
143
+ "Auto detected formatter: #{@formatter} (#{formatter_result.reason})",
144
+ )
127
145
  end
128
146
 
129
147
  specified_linters = options.dig(:initializationOptions, :linters)
@@ -144,21 +162,28 @@ module RubyLsp
144
162
  specified_linters << "rubocop_internal"
145
163
  end
146
164
 
147
- @linters = specified_linters || detect_linters(direct_dependencies, all_dependencies)
148
-
149
- notifications << if specified_linters
150
- Notification.window_log_message("Using linters specified by user: #{@linters.join(", ")}")
165
+ if specified_linters
166
+ @linters = specified_linters
167
+ notifications << Notification.window_log_message("Using linters specified by user: #{@linters.join(", ")}")
151
168
  else
152
- Notification.window_log_message("Auto detected linters: #{@linters.join(", ")}")
169
+ linter_results = detect_linters(direct_dependencies, all_dependencies)
170
+ @linters = linter_results.map(&:value)
171
+ linter_messages = linter_results.map { |r| "#{r.value} (#{r.reason})" }
172
+ notifications << Notification.window_log_message("Auto detected linters: #{linter_messages.join(", ")}")
153
173
  end
154
174
 
155
- @test_library = detect_test_library(direct_dependencies)
156
- notifications << Notification.window_log_message("Detected test library: #{@test_library}")
175
+ test_library_result = detect_test_library(direct_dependencies)
176
+ @test_library = test_library_result.value
177
+ notifications << Notification.window_log_message(
178
+ "Detected test library: #{@test_library} (#{test_library_result.reason})",
179
+ )
157
180
 
158
- @has_type_checker = detect_typechecker(all_dependencies)
159
- if @has_type_checker
181
+ typechecker_result = detect_typechecker(all_dependencies)
182
+ @has_type_checker = !typechecker_result.nil?
183
+ if typechecker_result
160
184
  notifications << Notification.window_log_message(
161
- "Ruby LSP detected this is a Sorbet project and will defer to the Sorbet LSP for some functionality",
185
+ "Ruby LSP detected this is a Sorbet project (#{typechecker_result.reason}) and will defer to the " \
186
+ "Sorbet LSP for some functionality",
162
187
  )
163
188
  end
164
189
 
@@ -228,60 +253,67 @@ module RubyLsp
228
253
 
229
254
  private
230
255
 
231
- #: (Array[String] direct_dependencies, Array[String] all_dependencies) -> String
256
+ #: (Array[String] direct_dependencies, Array[String] all_dependencies) -> DetectionResult
232
257
  def detect_formatter(direct_dependencies, all_dependencies)
233
258
  # NOTE: Intentionally no $ at end, since we want to match rubocop-shopify, etc.
234
- return "rubocop_internal" if direct_dependencies.any?(/^rubocop/)
259
+ if direct_dependencies.any?(/^rubocop/)
260
+ return DetectionResult.new("rubocop_internal", "direct dependency matching /^rubocop/")
261
+ end
235
262
 
236
- syntax_tree_is_direct_dependency = direct_dependencies.include?("syntax_tree")
237
- return "syntax_tree" if syntax_tree_is_direct_dependency
263
+ if direct_dependencies.include?("syntax_tree")
264
+ return DetectionResult.new("syntax_tree", "direct dependency")
265
+ end
238
266
 
239
- rubocop_is_transitive_dependency = all_dependencies.include?("rubocop")
240
- return "rubocop_internal" if dot_rubocop_yml_present && rubocop_is_transitive_dependency
267
+ if all_dependencies.include?("rubocop") && dot_rubocop_yml_present
268
+ return DetectionResult.new("rubocop_internal", "transitive dependency with .rubocop.yml present")
269
+ end
241
270
 
242
- "none"
271
+ DetectionResult.new("none", "no formatter detected")
243
272
  end
244
273
 
245
274
  # Try to detect if there are linters in the project's dependencies. For auto-detection, we always only consider a
246
275
  # single linter. To have multiple linters running, the user must configure them manually
247
- #: (Array[String] dependencies, Array[String] all_dependencies) -> Array[String]
276
+ #: (Array[String] dependencies, Array[String] all_dependencies) -> Array[DetectionResult]
248
277
  def detect_linters(dependencies, all_dependencies)
249
- linters = []
278
+ linters = [] #: Array[DetectionResult]
250
279
 
251
- if dependencies.any?(/^rubocop/) || (all_dependencies.include?("rubocop") && dot_rubocop_yml_present)
252
- linters << "rubocop_internal"
280
+ if dependencies.any?(/^rubocop/)
281
+ linters << DetectionResult.new("rubocop_internal", "direct dependency matching /^rubocop/")
282
+ elsif all_dependencies.include?("rubocop") && dot_rubocop_yml_present
283
+ linters << DetectionResult.new("rubocop_internal", "transitive dependency with .rubocop.yml present")
253
284
  end
254
285
 
255
286
  linters
256
287
  end
257
288
 
258
- #: (Array[String] dependencies) -> String
289
+ #: (Array[String] dependencies) -> DetectionResult
259
290
  def detect_test_library(dependencies)
260
291
  if dependencies.any?(/^rspec/)
261
- "rspec"
292
+ DetectionResult.new("rspec", "direct dependency matching /^rspec/")
262
293
  # A Rails app may have a dependency on minitest, but we would instead want to use the Rails test runner provided
263
294
  # by ruby-lsp-rails. A Rails app doesn't need to depend on the rails gem itself, individual components like
264
295
  # activestorage may be added to the gemfile so that other components aren't downloaded. Check for the presence
265
296
  # of bin/rails to support these cases.
266
297
  elsif bin_rails_present
267
- "rails"
298
+ DetectionResult.new("rails", "bin/rails present")
268
299
  # NOTE: Intentionally ends with $ to avoid mis-matching minitest-reporters, etc. in a Rails app.
269
300
  elsif dependencies.any?(/^minitest$/)
270
- "minitest"
301
+ DetectionResult.new("minitest", "direct dependency matching /^minitest$/")
271
302
  elsif dependencies.any?(/^test-unit/)
272
- "test-unit"
303
+ DetectionResult.new("test-unit", "direct dependency matching /^test-unit/")
273
304
  else
274
- "unknown"
305
+ DetectionResult.new("unknown", "no test library detected")
275
306
  end
276
307
  end
277
308
 
278
- #: (Array[String] dependencies) -> bool
309
+ #: (Array[String] dependencies) -> DetectionResult?
279
310
  def detect_typechecker(dependencies)
280
- return false if ENV["RUBY_LSP_BYPASS_TYPECHECKER"]
311
+ return if ENV["RUBY_LSP_BYPASS_TYPECHECKER"]
312
+ return if dependencies.none?(/^sorbet-static/)
281
313
 
282
- dependencies.any?(/^sorbet-static/)
314
+ DetectionResult.new("sorbet", "sorbet-static in dependencies")
283
315
  rescue Bundler::GemfileNotFound
284
- false
316
+ nil
285
317
  end
286
318
 
287
319
  #: -> bool
@@ -71,24 +71,26 @@ module RubyLsp
71
71
 
72
72
  #: (Prism::StringNode node) -> void
73
73
  def on_string_node_enter(node)
74
- enclosing_call = @node_context.call_node
75
- return unless enclosing_call
76
-
77
- name = enclosing_call.name
78
- return unless name == :require || name == :require_relative
79
-
80
- handle_require_definition(node, name)
74
+ with_enclosing_call(node) do |enclosing_call, name|
75
+ case name
76
+ when :require, :require_relative
77
+ handle_require_definition(node, name)
78
+ when :send, :public_send
79
+ handle_send_or_public_send_definition(enclosing_call, node) { node.content }
80
+ end
81
+ end
81
82
  end
82
83
 
83
84
  #: (Prism::SymbolNode node) -> void
84
85
  def on_symbol_node_enter(node)
85
- enclosing_call = @node_context.call_node
86
- return unless enclosing_call
87
-
88
- name = enclosing_call.name
89
- return unless name == :autoload
90
-
91
- handle_autoload_definition(enclosing_call)
86
+ with_enclosing_call(node) do |enclosing_call, name|
87
+ case name
88
+ when :autoload
89
+ handle_autoload_definition(enclosing_call)
90
+ when :send, :public_send
91
+ handle_send_or_public_send_definition(enclosing_call, node) { node.unescaped }
92
+ end
93
+ end
92
94
  end
93
95
 
94
96
  #: (Prism::BlockArgumentNode node) -> void
@@ -220,6 +222,24 @@ module RubyLsp
220
222
 
221
223
  private
222
224
 
225
+ #: (Prism::Node node) { (Prism::CallNode, Symbol) -> void } -> void
226
+ def with_enclosing_call(node, &block)
227
+ enclosing_call = @node_context.call_node
228
+ return unless enclosing_call
229
+
230
+ block.call(enclosing_call, enclosing_call.name)
231
+ end
232
+
233
+ #: (Prism::CallNode enclosing_call, Prism::Node node) { -> String } -> void
234
+ def handle_send_or_public_send_definition(enclosing_call, node, &block)
235
+ first_argument = enclosing_call.arguments&.arguments&.first
236
+ return unless first_argument.eql?(node)
237
+
238
+ method_name = block.call
239
+
240
+ handle_method_definition(method_name, nil)
241
+ end
242
+
223
243
  #: -> void
224
244
  def handle_super_node_definition
225
245
  # Sorbet can handle super hover on typed true or higher
@@ -135,8 +135,6 @@ module RubyLsp
135
135
  return unless path
136
136
 
137
137
  gem_name = purl.name
138
- return unless gem_name
139
-
140
138
  file_path = self.class.gem_paths.dig(gem_name, gem_version, CGI.unescape(path))
141
139
  return if file_path.nil?
142
140
 
@@ -160,10 +158,7 @@ module RubyLsp
160
158
  path = uri.path
161
159
  return unless path
162
160
 
163
- gem_name = uri.gem_name
164
- return unless gem_name
165
-
166
- file_path = self.class.gem_paths.dig(gem_name, gem_version, CGI.unescape(path))
161
+ file_path = self.class.gem_paths.dig(uri.gem_name, gem_version, CGI.unescape(path))
167
162
  return if file_path.nil?
168
163
 
169
164
  [file_path, uri.line_number || "0"]