ruby-lsp 0.26.4 → 0.26.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9873a3783f44e51f4631155591716e140f79732f0c1e0bf268aedc09ec670eb7
4
- data.tar.gz: 2d84a75fc80b719cbf50df63f66c08d096077adbd2256551370664e7f33ad81c
3
+ metadata.gz: 1c0285974bdf15d052431c1401f320645f57d8bd186ce9899dafc53f11da269a
4
+ data.tar.gz: d17e63e0b0db476033d2dfcaec8e40b4131f97cde55c7ade2cd2977ead23dc15
5
5
  SHA512:
6
- metadata.gz: cba3944e32212c7f2376b26659388e5a2b59c04eb454b475e605b543b0e208ea518e99330a942acc3b1ca66dcba829f8b74dbdba5ed63bee263ca6556da06803
7
- data.tar.gz: 2d10390c082fc925ae90e2cd630c58ac44649086d775d0ca6d0dd046c886fc0dd6577d386b6011e54decacac93d115ccaaf5cad5b1db0f73f76ab0f31110ca0d
6
+ metadata.gz: 26f63dc15bacf617c683dbb7b55fbf1eee1d2f8051b207a27cead0479b475c16d9cb31fbee3fc180f3c054cfd6754c2e5427ec1fa7d4330a17ff3a0ccd9c5f92
7
+ data.tar.gz: 1717199121bee0e9e72dc7bac4ba1779cbed7d4480f7f58bf046b9a3ac8c752c34fe4584eadcf45f98437bbdfb5a3ebcad36cf6951318cf4c63ae6fb84e5b41e
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.26.4
1
+ 0.26.5
@@ -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}")
@@ -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,
@@ -766,5 +766,25 @@ module RubyIndexer
766
766
  FileUtils.rm(path)
767
767
  end
768
768
  end
769
+
770
+ def test_lazy_comments_ignores_magic_comments
771
+ path = File.join(Dir.pwd, "lib", "foo.rb")
772
+ source = <<~RUBY
773
+ # frozen_string_literal: true
774
+
775
+ class Foo
776
+ end
777
+ RUBY
778
+ File.write(path, source)
779
+ @index.index_single(URI::Generic.from_path(path: path), source, collect_comments: false)
780
+
781
+ entry = @index["Foo"]&.first #: as !nil
782
+
783
+ begin
784
+ assert_empty(entry.comments)
785
+ ensure
786
+ FileUtils.rm(path)
787
+ end
788
+ end
769
789
  end
770
790
  end
@@ -233,18 +233,16 @@ module RubyIndexer
233
233
  RUBY
234
234
 
235
235
  Bundler.with_unbundled_env do
236
- capture_subprocess_io { system("bundle install") }
236
+ capture_subprocess_io do
237
+ system("bundle install")
237
238
 
238
- _stdout, stderr = capture_subprocess_io do
239
239
  script = [
240
240
  "require \"ruby_lsp/internal\"",
241
241
  "RubyIndexer::Configuration.new.indexable_uris",
242
242
  ].join(";")
243
243
 
244
- system("bundle exec ruby -e '#{script}'")
244
+ assert(system("bundle exec ruby -e '#{script}'"))
245
245
  end
246
-
247
- assert_empty(stderr)
248
246
  end
249
247
  end
250
248
  end
@@ -107,15 +107,12 @@ module RubyIndexer
107
107
  RUBY
108
108
 
109
109
  result = @index.fuzzy_search("Zws")
110
- assert_equal(2, result.length)
111
110
  assert_equal(["Zws", "Qtl::Zwo::Something"], result.map(&:name))
112
111
 
113
112
  result = @index.fuzzy_search("qtlzwssomeking")
114
- assert_equal(5, result.length)
115
- assert_equal(["Qtl::Zwo::Something", "Qtl::Zws", "Qtl::Zwo", "Qtl", "Zws"], result.map(&:name))
113
+ assert_equal(["Qtl::Zwo::Something", "Qtl::Zws", "Qtl::Zwo", "Qtl", "Zws", "blocking"], result.map(&:name))
116
114
 
117
115
  result = @index.fuzzy_search("QltZwo")
118
- assert_equal(4, result.length)
119
116
  assert_equal(["Qtl::Zwo", "Qtl::Zws", "Qtl::Zwo::Something", "Qtl"], result.map(&:name))
120
117
  end
121
118
 
@@ -28,7 +28,7 @@ module RubyIndexer
28
28
  def test_index_core_modules
29
29
  entries = @index["Kernel"] #: as !nil
30
30
  refute_nil(entries)
31
- assert_equal(1, entries.length)
31
+ assert_equal(2, entries.length)
32
32
  entry = entries.first #: as Entry::Module
33
33
  assert_match(%r{/gems/rbs-.*/core/kernel.rbs}, entry.file_path)
34
34
  assert_equal("kernel.rbs", entry.file_name)
@@ -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"]
@@ -283,10 +283,10 @@ module RubyLsp
283
283
  content = KEYWORD_DOCS[keyword]
284
284
  return unless content
285
285
 
286
- doc_path = File.join(STATIC_DOCS_PATH, "#{keyword}.md")
286
+ doc_uri = URI::Generic.from_path(path: File.join(STATIC_DOCS_PATH, "#{keyword}.md"))
287
287
 
288
288
  @response_builder.push("```ruby\n#{keyword}\n```", category: :title)
289
- @response_builder.push("[Read more](#{doc_path})", category: :links)
289
+ @response_builder.push("[Read more](#{doc_uri})", category: :links)
290
290
  @response_builder.push(content, category: :documentation)
291
291
  end
292
292
 
@@ -51,20 +51,42 @@ module RubyLsp
51
51
  #: -> (Interface::CodeAction)
52
52
  def switch_block_style
53
53
  source_range = @code_action.dig(:data, :range)
54
- raise EmptySelectionError, "Invalid selection for refactor" if source_range[:start] == source_range[:end]
54
+ if source_range[:start] == source_range[:end]
55
+ block_context = @document.locate_node(
56
+ source_range[:start],
57
+ node_types: [Prism::BlockNode],
58
+ )
59
+ node = block_context.node
60
+ unless node.is_a?(Prism::BlockNode)
61
+ raise InvalidTargetRangeError, "Cursor is not inside a block"
62
+ end
55
63
 
56
- target = @document.locate_first_within_range(
57
- @code_action.dig(:data, :range),
58
- node_types: [Prism::CallNode],
59
- )
64
+ # Find the call node at the block node's start position.
65
+ # This should be the call node whose block the cursor is inside of.
66
+ call_context = RubyDocument.locate(
67
+ @document.ast,
68
+ node.location.cached_start_code_units_offset(@document.code_units_cache),
69
+ node_types: [Prism::CallNode],
70
+ code_units_cache: @document.code_units_cache,
71
+ )
72
+ target = call_context.node
73
+ unless target.is_a?(Prism::CallNode) && target.block == node
74
+ raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
75
+ end
76
+ else
77
+ target = @document.locate_first_within_range(
78
+ @code_action.dig(:data, :range),
79
+ node_types: [Prism::CallNode],
80
+ )
60
81
 
61
- unless target.is_a?(Prism::CallNode)
62
- raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
63
- end
82
+ unless target.is_a?(Prism::CallNode)
83
+ raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
84
+ end
64
85
 
65
- node = target.block
66
- unless node.is_a?(Prism::BlockNode)
67
- raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
86
+ node = target.block
87
+ unless node.is_a?(Prism::BlockNode)
88
+ raise InvalidTargetRangeError, "Couldn't find an appropriate location to place extracted refactor"
89
+ end
68
90
  end
69
91
 
70
92
  indentation = " " * target.location.start_column unless node.opening_loc.slice == "do"
@@ -63,12 +63,8 @@ module RubyLsp
63
63
  kind: Constant::CodeActionKind::REFACTOR_EXTRACT,
64
64
  data: { range: @range, uri: @uri.to_s },
65
65
  )
66
- code_actions << Interface::CodeAction.new(
67
- title: TOGGLE_BLOCK_STYLE_TITLE,
68
- kind: Constant::CodeActionKind::REFACTOR_REWRITE,
69
- data: { range: @range, uri: @uri.to_s },
70
- )
71
66
  end
67
+ code_actions.concat(toggle_block_style_action)
72
68
  code_actions.concat(attribute_actions)
73
69
 
74
70
  code_actions
@@ -113,6 +109,25 @@ module RubyLsp
113
109
  ),
114
110
  ]
115
111
  end
112
+
113
+ #: -> Array[Interface::CodeAction]
114
+ def toggle_block_style_action
115
+ if @range[:start] == @range[:end]
116
+ block_context = @document.locate_node(
117
+ @range[:start],
118
+ node_types: [Prism::BlockNode],
119
+ )
120
+ return [] unless block_context.node.is_a?(Prism::BlockNode)
121
+ end
122
+
123
+ [
124
+ Interface::CodeAction.new(
125
+ title: TOGGLE_BLOCK_STYLE_TITLE,
126
+ kind: Constant::CodeActionKind::REFACTOR_REWRITE,
127
+ data: { range: @range, uri: @uri.to_s },
128
+ ),
129
+ ]
130
+ end
116
131
  end
117
132
  end
118
133
  end
@@ -68,10 +68,12 @@ module RubyLsp
68
68
  "[Learn more about guessed types](#{GUESSED_TYPES_URL})"
69
69
  end
70
70
 
71
- @item[:documentation] = Interface::MarkupContent.new(
72
- kind: "markdown",
73
- value: markdown_from_index_entries(label, entries, MAX_DOCUMENTATION_ENTRIES, extra_links: extra_links),
74
- )
71
+ unless @item[:kind] == Constant::CompletionItemKind::FILE
72
+ @item[:documentation] = Interface::MarkupContent.new(
73
+ kind: "markdown",
74
+ value: markdown_from_index_entries(label, entries, MAX_DOCUMENTATION_ENTRIES, extra_links: extra_links),
75
+ )
76
+ end
75
77
 
76
78
  @item
77
79
  end
@@ -84,7 +86,7 @@ module RubyLsp
84
86
  content = KEYWORD_DOCS[keyword]
85
87
 
86
88
  if content
87
- doc_path = File.join(STATIC_DOCS_PATH, "#{keyword}.md")
89
+ doc_uri = URI::Generic.from_path(path: File.join(STATIC_DOCS_PATH, "#{keyword}.md"))
88
90
 
89
91
  @item[:documentation] = Interface::MarkupContent.new(
90
92
  kind: "markdown",
@@ -93,7 +95,7 @@ module RubyLsp
93
95
  #{keyword}
94
96
  ```
95
97
 
96
- [Read more](#{doc_path})
98
+ [Read more](#{doc_uri})
97
99
 
98
100
  #{content}
99
101
  MARKDOWN
@@ -5,6 +5,7 @@ require "uri/file"
5
5
 
6
6
  module URI
7
7
  # Must be kept in sync with the one in Tapioca
8
+ # https://github.com/Shopify/tapioca/blob/main/lib/tapioca/helpers/source_uri.rb
8
9
  class Source < URI::File
9
10
  COMPONENT = [
10
11
  :scheme,
@@ -21,16 +22,14 @@ module URI
21
22
  # have the uri gem in their own bundle and thus not use a compatible version.
22
23
  PARSER = const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER #: RFC2396_Parser
23
24
 
24
- self #: as untyped # rubocop:disable Style/RedundantSelf
25
- .alias_method(:gem_name, :host)
26
- self #: as untyped # rubocop:disable Style/RedundantSelf
27
- .alias_method(:line_number, :fragment)
25
+ alias_method(:gem_name, :host)
26
+ alias_method(:line_number, :fragment)
28
27
 
29
28
  #: String?
30
29
  attr_reader :gem_version
31
30
 
32
31
  class << self
33
- #: (gem_name: String, gem_version: String?, path: String, line_number: String?) -> URI::Source
32
+ #: (gem_name: String, gem_version: String?, path: String, line_number: String?) -> instance
34
33
  def build(gem_name:, gem_version:, path:, line_number:)
35
34
  super(
36
35
  {
@@ -67,12 +66,14 @@ module URI
67
66
 
68
67
  #: -> String
69
68
  def to_s
70
- "source://#{gem_name}/#{gem_version}#{path}##{line_number}"
69
+ "source://#{gem_name}/#{gem_version}/#{path}##{line_number}"
71
70
  end
72
71
 
73
72
  if URI.respond_to?(:register_scheme)
73
+ # Handle URI 0.11.0 and newer https://github.com/ruby/uri/pull/26
74
74
  URI.register_scheme("SOURCE", self)
75
75
  else
76
+ # Fallback for URI <0.11.0
76
77
  @@schemes = @@schemes #: Hash[String, untyped] # rubocop:disable Style/ClassVars
77
78
  @@schemes["SOURCE"] = self
78
79
  end
@@ -5,7 +5,7 @@ def compose(raw_initialize)
5
5
  require_relative "../setup_bundler"
6
6
  require "json"
7
7
  require "uri"
8
- require "ruby_indexer/lib/ruby_indexer/uri"
8
+ require_relative "../../ruby_indexer/lib/ruby_indexer/uri"
9
9
 
10
10
  initialize_request = JSON.parse(raw_initialize, symbolize_names: true)
11
11
  workspace_uri = initialize_request.dig(:params, :workspaceFolders, 0, :uri)
@@ -36,7 +36,7 @@ module RubyLsp
36
36
  @project_path = project_path
37
37
  @branch = options[:branch] #: String?
38
38
  @launcher = options[:launcher] #: bool?
39
- patch_thor_to_print_progress_to_stderr! if @launcher
39
+ force_output_to_stderr! if @launcher
40
40
 
41
41
  # Regular bundle paths
42
42
  @gemfile = begin
@@ -282,14 +282,25 @@ module RubyLsp
282
282
 
283
283
  return update(env) if @needs_update_path.exist?
284
284
 
285
- # The ENV can only be merged after checking if an update is required because we depend on the original value of
286
- # ENV["BUNDLE_GEMFILE"], which gets overridden after the merge
287
285
  FileUtils.touch(@needs_update_path) if needs_update
288
286
 
289
287
  $stderr.puts("Ruby LSP> Checking if the composed bundle is satisfied...")
290
- missing_gems = bundle_check
291
288
 
292
- unless missing_gems.empty?
289
+ begin
290
+ missing_gems = bundle_check
291
+ rescue Errno::EPIPE, Bundler::HTTPError
292
+ # These are errors cases where we cannot recover
293
+ raise
294
+ rescue => e
295
+ # If anything fails with bundle check, try to bundle install
296
+ $stderr.puts("Ruby LSP> Running bundle install because #{e.message}")
297
+ bundle_install
298
+ return env
299
+ end
300
+
301
+ if missing_gems.empty?
302
+ $stderr.puts("Ruby LSP> Bundle already satisfied")
303
+ else
293
304
  $stderr.puts(<<~MESSAGE)
294
305
  Ruby LSP> Running bundle install because the following gems are not installed:
295
306
  #{missing_gems.map { |g| "#{g.name}: #{g.version}" }.join("\n")}
@@ -298,11 +309,6 @@ module RubyLsp
298
309
  bundle_install
299
310
  end
300
311
 
301
- $stderr.puts("Ruby LSP> Bundle already satisfied")
302
- env
303
- rescue => e
304
- $stderr.puts("Ruby LSP> Running bundle install because #{e.message}")
305
- bundle_install
306
312
  env
307
313
  end
308
314
 
@@ -489,11 +495,16 @@ module RubyLsp
489
495
  end
490
496
 
491
497
  #: -> void
492
- def patch_thor_to_print_progress_to_stderr!
493
- return unless defined?(Bundler::Thor::Shell::Basic)
498
+ def force_output_to_stderr!
499
+ # Bundler and RubyGems have different UI objects used for printing. We need to ensure that both are configured to
500
+ # print only to stderr or else they'll break the connection with the editor
501
+ Gem::DefaultUserInteraction.ui = Gem::StreamUI.new($stdin, $stderr, $stderr, false)
502
+
503
+ ui = Bundler.ui
504
+ ui.output_stream = :stderr if ui.respond_to?(:output_stream=)
505
+ ui.level = :info
494
506
 
495
- Bundler::Thor::Shell::Basic.prepend(ThorPatch)
496
- Bundler.ui.level = :info
507
+ Bundler::Thor::Shell::Basic.prepend(ThorPatch) if defined?(Bundler::Thor::Shell::Basic)
497
508
  end
498
509
  end
499
510
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.26.4
4
+ version: 0.26.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
@@ -217,7 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
217
217
  - !ruby/object:Gem::Version
218
218
  version: '0'
219
219
  requirements: []
220
- rubygems_version: 3.7.2
220
+ rubygems_version: 4.0.3
221
221
  specification_version: 4
222
222
  summary: An opinionated language server for Ruby
223
223
  test_files: []