ruby-lsp 0.19.1 → 0.20.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +7 -1
  4. data/lib/core_ext/uri.rb +2 -2
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +63 -12
  6. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +85 -36
  7. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +5 -1
  8. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +39 -98
  9. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +12 -19
  10. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +22 -0
  11. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +2 -7
  12. data/lib/ruby_indexer/test/enhancements_test.rb +5 -7
  13. data/lib/ruby_indexer/test/global_variable_test.rb +49 -0
  14. data/lib/ruby_indexer/test/index_test.rb +89 -0
  15. data/lib/ruby_lsp/erb_document.rb +15 -2
  16. data/lib/ruby_lsp/listeners/definition.rb +20 -0
  17. data/lib/ruby_lsp/listeners/folding_ranges.rb +3 -3
  18. data/lib/ruby_lsp/requests/code_action_resolve.rb +8 -2
  19. data/lib/ruby_lsp/requests/completion.rb +1 -1
  20. data/lib/ruby_lsp/requests/definition.rb +2 -1
  21. data/lib/ruby_lsp/requests/document_highlight.rb +5 -1
  22. data/lib/ruby_lsp/requests/hover.rb +1 -1
  23. data/lib/ruby_lsp/requests/range_formatting.rb +4 -2
  24. data/lib/ruby_lsp/requests/references.rb +3 -1
  25. data/lib/ruby_lsp/requests/rename.rb +3 -1
  26. data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
  27. data/lib/ruby_lsp/requests/signature_help.rb +1 -1
  28. data/lib/ruby_lsp/requests/support/common.rb +1 -1
  29. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +6 -6
  30. data/lib/ruby_lsp/response_builders/document_symbol.rb +2 -2
  31. data/lib/ruby_lsp/response_builders/hover.rb +2 -2
  32. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +14 -8
  33. data/lib/ruby_lsp/response_builders/signature_help.rb +2 -2
  34. data/lib/ruby_lsp/ruby_document.rb +33 -12
  35. data/lib/ruby_lsp/setup_bundler.rb +30 -8
  36. data/lib/ruby_lsp/type_inferrer.rb +1 -1
  37. metadata +6 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d900634d5328c8b4b5fcf799f807456bec3cdb94d6294c80fa9152bcf45aedde
4
- data.tar.gz: b7661867d38331ec158312b10c7f530cbd58994ee94e8ff592f478e7e05efcb7
3
+ metadata.gz: 58327a9f9a3d85375cbbf81e3e170a948d5c8dfbdcdcac0a1fdef5f685d20c95
4
+ data.tar.gz: 45572eb6645bce73d2079bed65f0d92c98921517926b968bba96788769a3aada
5
5
  SHA512:
6
- metadata.gz: 8f7955f676bc5f231f990f04d8aea1a767165d4f2cac1e9514928014cd8a09eb68c60b4cd226a94b554dac88d77c2117ae3a3ea76bb66e68980974f1685d8970
7
- data.tar.gz: 1b914d5607e5730139780cd1f172923db7878a7a0a4098daa1e17df8a1a7a2d8b6731b092baf7eeefc9f55605305e4d70d8cca8489f68a17bab532026c38e8b2
6
+ metadata.gz: c506eeff4d24060e3afdb4ad9da319c12b725050c8f25a9348ec7dea9082f8adced7671b7b1eb8f7f09412942139e47a2df5059b5672d2b6a75f0ffc5a59885d
7
+ data.tar.gz: d9a89ce24946e5f9cc47b5a48086f6fd977176d31332a0b634950dc00a41fc9a36e4753eae2896e2da0129e92bff4278265eaa970252a5292c783e5bc82258e9
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.19.1
1
+ 0.20.1
data/exe/ruby-lsp CHANGED
@@ -63,7 +63,13 @@ if ENV["BUNDLE_GEMFILE"].nil?
63
63
  exit(78)
64
64
  end
65
65
 
66
- exit exec(env, "bundle exec ruby-lsp #{original_args.join(" ")}")
66
+ base_bundle = if env["BUNDLER_VERSION"]
67
+ "bundle _#{env["BUNDLER_VERSION"]}_"
68
+ else
69
+ "bundle"
70
+ end
71
+
72
+ exit exec(env, "#{base_bundle} exec ruby-lsp #{original_args.join(" ")}")
67
73
  end
68
74
 
69
75
  require "ruby_lsp/load_sorbet"
data/lib/core_ext/uri.rb CHANGED
@@ -3,6 +3,8 @@
3
3
 
4
4
  module URI
5
5
  class Generic
6
+ extend T::Sig
7
+
6
8
  # Avoid a deprecation warning with Ruby 3.4 where the default parser was changed to RFC3986.
7
9
  # This condition must remain even after support for 3.4 has been dropped for users that have
8
10
  # `uri` in their lockfile, decoupling it from the ruby version.
@@ -27,8 +29,6 @@ module URI
27
29
  end
28
30
  end
29
31
 
30
- extend T::Sig
31
-
32
32
  sig { returns(T.nilable(String)) }
33
33
  def to_standardized_path
34
34
  parsed_path = path
@@ -28,7 +28,17 @@ module RubyIndexer
28
28
  @encoding = T.let(Encoding::UTF_8, Encoding)
29
29
  @excluded_gems = T.let(initial_excluded_gems, T::Array[String])
30
30
  @included_gems = T.let([], T::Array[String])
31
- @excluded_patterns = T.let([File.join("**", "*_test.rb"), File.join("tmp", "**", "*")], T::Array[String])
31
+
32
+ @excluded_patterns = T.let(
33
+ [
34
+ File.join("**", "*_test.rb"),
35
+ File.join("node_modules", "**", "*"),
36
+ File.join("spec", "**", "*"),
37
+ File.join("test", "**", "*"),
38
+ File.join("tmp", "**", "*"),
39
+ ],
40
+ T::Array[String],
41
+ )
32
42
 
33
43
  path = Bundler.settings["path"]
34
44
  if path
@@ -56,6 +66,21 @@ module RubyIndexer
56
66
  )
57
67
  end
58
68
 
69
+ sig { returns(String) }
70
+ def merged_excluded_file_pattern
71
+ # This regex looks for @excluded_patterns that follow the format of "something/**/*", where
72
+ # "something" is one or more non-"/"
73
+ #
74
+ # Returns "/path/to/workspace/{tmp,node_modules}/**/*"
75
+ @excluded_patterns
76
+ .filter_map do |pattern|
77
+ next if File.absolute_path?(pattern)
78
+
79
+ pattern.match(%r{\A([^/]+)/\*\*/\*\z})&.captures&.first
80
+ end
81
+ .then { |dirs| File.join(@workspace_path, "{#{dirs.join(",")}}/**/*") }
82
+ end
83
+
59
84
  sig { returns(T::Array[IndexablePath]) }
60
85
  def indexables
61
86
  excluded_gems = @excluded_gems - @included_gems
@@ -64,21 +89,47 @@ module RubyIndexer
64
89
  # NOTE: indexing the patterns (both included and excluded) needs to happen before indexing gems, otherwise we risk
65
90
  # having duplicates if BUNDLE_PATH is set to a folder inside the project structure
66
91
 
92
+ flags = File::FNM_PATHNAME | File::FNM_EXTGLOB
93
+
94
+ # In order to speed up indexing, only traverse into top-level directories that are not entirely excluded.
95
+ # For example, if "tmp/**/*" is excluded, we don't need to traverse into "tmp" at all. However, if
96
+ # "vendor/bundle/**/*" is excluded, we will traverse all of "vendor" and `reject!` out all "vendor/bundle" entries
97
+ # later.
98
+ excluded_pattern = merged_excluded_file_pattern
99
+ included_paths = Dir.glob(File.join(@workspace_path, "*/"), flags)
100
+ .filter_map do |included_path|
101
+ next if File.fnmatch?(excluded_pattern, included_path, flags)
102
+
103
+ relative_path = included_path
104
+ .delete_prefix(@workspace_path)
105
+ .tap { |path| path.delete_prefix!("/") }
106
+
107
+ [included_path, relative_path]
108
+ end
109
+
110
+ indexables = T.let([], T::Array[IndexablePath])
111
+
67
112
  # Add user specified patterns
68
- indexables = @included_patterns.flat_map do |pattern|
113
+ @included_patterns.each do |pattern|
69
114
  load_path_entry = T.let(nil, T.nilable(String))
70
115
 
71
- Dir.glob(File.join(@workspace_path, pattern), File::FNM_PATHNAME | File::FNM_EXTGLOB).map! do |path|
72
- path = File.expand_path(path)
73
- # All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
74
- # entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This happens
75
- # on repositories that define multiple gems, like Rails. All frameworks are defined inside the current
76
- # workspace directory, but each one of them belongs to a different $LOAD_PATH entry
77
- if load_path_entry.nil? || !path.start_with?(load_path_entry)
78
- load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
79
- end
116
+ included_paths.each do |included_path, relative_path|
117
+ relative_pattern = pattern.delete_prefix(File.join(relative_path, "/"))
118
+
119
+ next unless pattern.start_with?("**") || pattern.start_with?(relative_path)
80
120
 
81
- IndexablePath.new(load_path_entry, path)
121
+ Dir.glob(File.join(included_path, relative_pattern), flags).each do |path|
122
+ path = File.expand_path(path)
123
+ # All entries for the same pattern match the same $LOAD_PATH entry. Since searching the $LOAD_PATH for every
124
+ # entry is expensive, we memoize it until we find a path that doesn't belong to that $LOAD_PATH. This
125
+ # happens on repositories that define multiple gems, like Rails. All frameworks are defined inside the
126
+ # current workspace directory, but each one of them belongs to a different $LOAD_PATH entry
127
+ if load_path_entry.nil? || !path.start_with?(load_path_entry)
128
+ load_path_entry = $LOAD_PATH.find { |load_path| path.start_with?(load_path) }
129
+ end
130
+
131
+ indexables << IndexablePath.new(load_path_entry, path)
132
+ end
82
133
  end
83
134
  end
84
135
 
@@ -33,6 +33,10 @@ module RubyIndexer
33
33
  T::Hash[Integer, Prism::Comment],
34
34
  )
35
35
  @inside_def = T.let(false, T::Boolean)
36
+ @code_units_cache = T.let(
37
+ parse_result.code_units_cache(@index.configuration.encoding),
38
+ T.any(T.proc.params(arg0: Integer).returns(Integer), Prism::CodeUnitsCache),
39
+ )
36
40
 
37
41
  # The nesting stack we're currently inside. Used to determine the fully qualified name of constants, but only
38
42
  # stored by unresolved aliases which need the original nesting to be lazily resolved
@@ -65,6 +69,11 @@ module RubyIndexer
65
69
  :on_constant_or_write_node_enter,
66
70
  :on_constant_and_write_node_enter,
67
71
  :on_constant_operator_write_node_enter,
72
+ :on_global_variable_and_write_node_enter,
73
+ :on_global_variable_operator_write_node_enter,
74
+ :on_global_variable_or_write_node_enter,
75
+ :on_global_variable_target_node_enter,
76
+ :on_global_variable_write_node_enter,
68
77
  :on_instance_variable_write_node_enter,
69
78
  :on_instance_variable_and_write_node_enter,
70
79
  :on_instance_variable_operator_write_node_enter,
@@ -106,10 +115,9 @@ module RubyIndexer
106
115
  entry = Entry::Class.new(
107
116
  nesting,
108
117
  @file_path,
109
- node.location,
110
- constant_path.location,
118
+ Location.from_prism_location(node.location, @code_units_cache),
119
+ Location.from_prism_location(constant_path.location, @code_units_cache),
111
120
  comments,
112
- @index.configuration.encoding,
113
121
  parent_class,
114
122
  )
115
123
 
@@ -136,10 +144,9 @@ module RubyIndexer
136
144
  entry = Entry::Module.new(
137
145
  actual_nesting(name),
138
146
  @file_path,
139
- node.location,
140
- constant_path.location,
147
+ Location.from_prism_location(node.location, @code_units_cache),
148
+ Location.from_prism_location(constant_path.location, @code_units_cache),
141
149
  comments,
142
- @index.configuration.encoding,
143
150
  )
144
151
 
145
152
  @owner_stack << entry
@@ -170,19 +177,17 @@ module RubyIndexer
170
177
  if existing_entries
171
178
  entry = T.must(existing_entries.first)
172
179
  entry.update_singleton_information(
173
- node.location,
174
- expression.location,
180
+ Location.from_prism_location(node.location, @code_units_cache),
181
+ Location.from_prism_location(expression.location, @code_units_cache),
175
182
  collect_comments(node),
176
- @index.configuration.encoding,
177
183
  )
178
184
  else
179
185
  entry = Entry::SingletonClass.new(
180
186
  real_nesting,
181
187
  @file_path,
182
- node.location,
183
- expression.location,
188
+ Location.from_prism_location(node.location, @code_units_cache),
189
+ Location.from_prism_location(expression.location, @code_units_cache),
184
190
  collect_comments(node),
185
- @index.configuration.encoding,
186
191
  nil,
187
192
  )
188
193
  @index.add(entry, skip_prefix_tree: true)
@@ -310,7 +315,7 @@ module RubyIndexer
310
315
  end
311
316
 
312
317
  @enhancements.each do |enhancement|
313
- enhancement.on_call_node(@index, @owner_stack.last, node, @file_path)
318
+ enhancement.on_call_node(@index, @owner_stack.last, node, @file_path, @code_units_cache)
314
319
  rescue StandardError => e
315
320
  @indexing_errors << "Indexing error in #{@file_path} with '#{enhancement.class.name}' enhancement: #{e.message}"
316
321
  end
@@ -340,10 +345,9 @@ module RubyIndexer
340
345
  @index.add(Entry::Method.new(
341
346
  method_name,
342
347
  @file_path,
343
- node.location,
344
- node.name_loc,
348
+ Location.from_prism_location(node.location, @code_units_cache),
349
+ Location.from_prism_location(node.name_loc, @code_units_cache),
345
350
  comments,
346
- @index.configuration.encoding,
347
351
  [Entry::Signature.new(list_params(node.parameters))],
348
352
  current_visibility,
349
353
  @owner_stack.last,
@@ -357,10 +361,9 @@ module RubyIndexer
357
361
  @index.add(Entry::Method.new(
358
362
  method_name,
359
363
  @file_path,
360
- node.location,
361
- node.name_loc,
364
+ Location.from_prism_location(node.location, @code_units_cache),
365
+ Location.from_prism_location(node.name_loc, @code_units_cache),
362
366
  comments,
363
- @index.configuration.encoding,
364
367
  [Entry::Signature.new(list_params(node.parameters))],
365
368
  current_visibility,
366
369
  singleton,
@@ -382,6 +385,31 @@ module RubyIndexer
382
385
  end
383
386
  end
384
387
 
388
+ sig { params(node: Prism::GlobalVariableAndWriteNode).void }
389
+ def on_global_variable_and_write_node_enter(node)
390
+ handle_global_variable(node, node.name_loc)
391
+ end
392
+
393
+ sig { params(node: Prism::GlobalVariableOperatorWriteNode).void }
394
+ def on_global_variable_operator_write_node_enter(node)
395
+ handle_global_variable(node, node.name_loc)
396
+ end
397
+
398
+ sig { params(node: Prism::GlobalVariableOrWriteNode).void }
399
+ def on_global_variable_or_write_node_enter(node)
400
+ handle_global_variable(node, node.name_loc)
401
+ end
402
+
403
+ sig { params(node: Prism::GlobalVariableTargetNode).void }
404
+ def on_global_variable_target_node_enter(node)
405
+ handle_global_variable(node, node.location)
406
+ end
407
+
408
+ sig { params(node: Prism::GlobalVariableWriteNode).void }
409
+ def on_global_variable_write_node_enter(node)
410
+ handle_global_variable(node, node.name_loc)
411
+ end
412
+
385
413
  sig { params(node: Prism::InstanceVariableWriteNode).void }
386
414
  def on_instance_variable_write_node_enter(node)
387
415
  handle_instance_variable(node, node.name_loc)
@@ -417,15 +445,38 @@ module RubyIndexer
417
445
  node.old_name.slice,
418
446
  @owner_stack.last,
419
447
  @file_path,
420
- node.new_name.location,
448
+ Location.from_prism_location(node.new_name.location, @code_units_cache),
421
449
  comments,
422
- @index.configuration.encoding,
423
450
  ),
424
451
  )
425
452
  end
426
453
 
427
454
  private
428
455
 
456
+ sig do
457
+ params(
458
+ node: T.any(
459
+ Prism::GlobalVariableAndWriteNode,
460
+ Prism::GlobalVariableOperatorWriteNode,
461
+ Prism::GlobalVariableOrWriteNode,
462
+ Prism::GlobalVariableTargetNode,
463
+ Prism::GlobalVariableWriteNode,
464
+ ),
465
+ loc: Prism::Location,
466
+ ).void
467
+ end
468
+ def handle_global_variable(node, loc)
469
+ name = node.name.to_s
470
+ comments = collect_comments(node)
471
+
472
+ @index.add(Entry::GlobalVariable.new(
473
+ name,
474
+ @file_path,
475
+ Location.from_prism_location(loc, @code_units_cache),
476
+ comments,
477
+ ))
478
+ end
479
+
429
480
  sig do
430
481
  params(
431
482
  node: T.any(
@@ -453,9 +504,8 @@ module RubyIndexer
453
504
  @index.add(Entry::InstanceVariable.new(
454
505
  name,
455
506
  @file_path,
456
- loc,
507
+ Location.from_prism_location(loc, @code_units_cache),
457
508
  collect_comments(node),
458
- @index.configuration.encoding,
459
509
  owner,
460
510
  ))
461
511
  end
@@ -518,9 +568,8 @@ module RubyIndexer
518
568
  old_name_value,
519
569
  @owner_stack.last,
520
570
  @file_path,
521
- new_name.location,
571
+ Location.from_prism_location(new_name.location, @code_units_cache),
522
572
  comments,
523
- @index.configuration.encoding,
524
573
  ),
525
574
  )
526
575
  end
@@ -555,9 +604,8 @@ module RubyIndexer
555
604
  @stack.dup,
556
605
  name,
557
606
  @file_path,
558
- node.location,
607
+ Location.from_prism_location(node.location, @code_units_cache),
559
608
  comments,
560
- @index.configuration.encoding,
561
609
  )
562
610
  when Prism::ConstantWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOrWriteNode,
563
611
  Prism::ConstantOperatorWriteNode
@@ -569,9 +617,8 @@ module RubyIndexer
569
617
  @stack.dup,
570
618
  name,
571
619
  @file_path,
572
- node.location,
620
+ Location.from_prism_location(node.location, @code_units_cache),
573
621
  comments,
574
- @index.configuration.encoding,
575
622
  )
576
623
  when Prism::ConstantPathWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathOperatorWriteNode,
577
624
  Prism::ConstantPathAndWriteNode
@@ -581,12 +628,16 @@ module RubyIndexer
581
628
  @stack.dup,
582
629
  name,
583
630
  @file_path,
584
- node.location,
631
+ Location.from_prism_location(node.location, @code_units_cache),
585
632
  comments,
586
- @index.configuration.encoding,
587
633
  )
588
634
  else
589
- Entry::Constant.new(name, @file_path, node.location, comments, @index.configuration.encoding)
635
+ Entry::Constant.new(
636
+ name,
637
+ @file_path,
638
+ Location.from_prism_location(node.location, @code_units_cache),
639
+ comments,
640
+ )
590
641
  end,
591
642
  )
592
643
  end
@@ -652,9 +703,8 @@ module RubyIndexer
652
703
  @index.add(Entry::Accessor.new(
653
704
  name,
654
705
  @file_path,
655
- loc,
706
+ Location.from_prism_location(loc, @code_units_cache),
656
707
  comments,
657
- @index.configuration.encoding,
658
708
  current_visibility,
659
709
  @owner_stack.last,
660
710
  ))
@@ -665,9 +715,8 @@ module RubyIndexer
665
715
  @index.add(Entry::Accessor.new(
666
716
  "#{name}=",
667
717
  @file_path,
668
- loc,
718
+ Location.from_prism_location(loc, @code_units_cache),
669
719
  comments,
670
- @index.configuration.encoding,
671
720
  current_visibility,
672
721
  @owner_stack.last,
673
722
  ))
@@ -19,8 +19,12 @@ module RubyIndexer
19
19
  owner: T.nilable(Entry::Namespace),
20
20
  node: Prism::CallNode,
21
21
  file_path: String,
22
+ code_units_cache: T.any(
23
+ T.proc.params(arg0: Integer).returns(Integer),
24
+ Prism::CodeUnitsCache,
25
+ ),
22
26
  ).void
23
27
  end
24
- def on_call_node(index, owner, node, file_path); end
28
+ def on_call_node(index, owner, node, file_path, code_units_cache); end
25
29
  end
26
30
  end