ruby-lsp 0.23.21 → 0.25.0

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +10 -4
  4. data/exe/ruby-lsp-check +0 -4
  5. data/exe/ruby-lsp-launcher +25 -11
  6. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +0 -1
  7. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +0 -1
  8. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +7 -1
  9. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +1 -4
  10. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +9 -19
  11. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +18 -7
  12. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +2 -2
  13. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +12 -8
  14. data/lib/ruby_indexer/test/configuration_test.rb +1 -1
  15. data/lib/ruby_indexer/test/index_test.rb +24 -0
  16. data/lib/ruby_indexer/test/instance_variables_test.rb +24 -0
  17. data/lib/ruby_indexer/test/method_test.rb +17 -0
  18. data/lib/ruby_indexer/test/rbs_indexer_test.rb +2 -2
  19. data/lib/ruby_indexer/test/reference_finder_test.rb +79 -14
  20. data/lib/ruby_lsp/addon.rb +29 -15
  21. data/lib/ruby_lsp/base_server.rb +14 -12
  22. data/lib/ruby_lsp/document.rb +158 -46
  23. data/lib/ruby_lsp/erb_document.rb +8 -3
  24. data/lib/ruby_lsp/global_state.rb +21 -0
  25. data/lib/ruby_lsp/internal.rb +0 -2
  26. data/lib/ruby_lsp/listeners/completion.rb +9 -1
  27. data/lib/ruby_lsp/listeners/hover.rb +7 -0
  28. data/lib/ruby_lsp/listeners/inlay_hints.rb +5 -3
  29. data/lib/ruby_lsp/listeners/spec_style.rb +63 -20
  30. data/lib/ruby_lsp/listeners/test_discovery.rb +18 -15
  31. data/lib/ruby_lsp/listeners/test_style.rb +21 -10
  32. data/lib/ruby_lsp/requests/code_action_resolve.rb +3 -3
  33. data/lib/ruby_lsp/requests/code_lens.rb +14 -5
  34. data/lib/ruby_lsp/requests/completion.rb +1 -1
  35. data/lib/ruby_lsp/requests/definition.rb +1 -1
  36. data/lib/ruby_lsp/requests/discover_tests.rb +2 -2
  37. data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
  38. data/lib/ruby_lsp/requests/hover.rb +1 -1
  39. data/lib/ruby_lsp/requests/inlay_hints.rb +3 -3
  40. data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
  41. data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
  42. data/lib/ruby_lsp/requests/references.rb +10 -6
  43. data/lib/ruby_lsp/requests/rename.rb +8 -6
  44. data/lib/ruby_lsp/requests/request.rb +6 -7
  45. data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
  46. data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -1
  47. data/lib/ruby_lsp/requests/signature_help.rb +1 -1
  48. data/lib/ruby_lsp/requests/support/common.rb +1 -3
  49. data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
  50. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
  51. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +9 -3
  52. data/lib/ruby_lsp/response_builders/response_builder.rb +6 -8
  53. data/lib/ruby_lsp/ruby_document.rb +10 -5
  54. data/lib/ruby_lsp/server.rb +50 -49
  55. data/lib/ruby_lsp/setup_bundler.rb +51 -25
  56. data/lib/ruby_lsp/static_docs.rb +1 -0
  57. data/lib/ruby_lsp/store.rb +0 -10
  58. data/lib/ruby_lsp/test_helper.rb +1 -4
  59. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +13 -5
  60. data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +43 -4
  61. data/lib/ruby_lsp/utils.rb +47 -11
  62. data/static_docs/break.md +103 -0
  63. metadata +4 -18
  64. data/lib/ruby_lsp/load_sorbet.rb +0 -62
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 336c4740e2cc0c03bec77c7854065bc9115ed14ab88949d41ae8a8da7d58b1da
4
- data.tar.gz: 0b1ccf86525c8db95beb310c23977353520e0951f7c627199db567c50b8b29f8
3
+ metadata.gz: 7637d6f68616069c2f8c0d870f3daf31808a1cccd8ab32d2bdaf1f67274f9b59
4
+ data.tar.gz: 7ea1d75ad921adbf01f69a0f5d2fba905673311c13b0999d44c08bdf54737053
5
5
  SHA512:
6
- metadata.gz: ffb2a4605bf6c3b7e82dde8ca73cd624c22aa16a2cf91569b7b38d06a7d876a932023efa4a4dea1871ca0e5965a923be97b9e870f2fd9f0b36d70d354213e9ea
7
- data.tar.gz: e488ffda15f9d7a0549294d70f607c09620144ff3a1fea3441d834c3c2db06cf691997005c357cffcf596b4e41e83599111c48da07d92ea26bde293ae6edde0a
6
+ metadata.gz: a354535a3853b0826635408fc46dc85cb7cee97c4993012dd983af11b8917121962d97bddf0853406b54f619faa8c2383cccca9638b4a0624309fb1e4c838249
7
+ data.tar.gz: 9e49e2436c63d90fc373b52ac9f6b65e83964fe108abd0a18fd46b655cd99bd55c471dd198624bb8701890f62175af2c9fcb50fcfa2bbf69e706406b9fa1e472
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.23.21
1
+ 0.25.0
data/exe/ruby-lsp CHANGED
@@ -88,13 +88,17 @@ if ENV["BUNDLE_GEMFILE"].nil?
88
88
  exit exec(env, "#{base_command} exec ruby-lsp #{original_args.join(" ")}".strip)
89
89
  end
90
90
 
91
+ $stdin.sync = true
92
+ $stdout.sync = true
93
+ $stderr.sync = true
94
+ $stdin.binmode
95
+ $stdout.binmode
96
+ $stderr.binmode
97
+
91
98
  $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
92
99
 
93
- require "ruby_lsp/load_sorbet"
94
100
  require "ruby_lsp/internal"
95
101
 
96
- T::Utils.run_all_sig_blocks
97
-
98
102
  if options[:debug]
99
103
  if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
100
104
  $stderr.puts "Debugging is not supported on Windows"
@@ -147,8 +151,10 @@ if options[:doctor]
147
151
  return
148
152
  end
149
153
 
154
+ server = RubyLsp::Server.new
155
+
150
156
  # Ensure all output goes out stderr by default to allow puts/p/pp to work
151
157
  # without specifying output device.
152
158
  $> = $stderr
153
159
 
154
- RubyLsp::Server.new.start
160
+ server.start
data/exe/ruby-lsp-check CHANGED
@@ -3,13 +3,9 @@
3
3
 
4
4
  # This executable checks if all automatic LSP requests run successfully on every Ruby file under the current directory
5
5
 
6
- require "ruby_lsp/load_sorbet"
7
-
8
6
  $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
9
7
  require "ruby_lsp/internal"
10
8
 
11
- T::Utils.run_all_sig_blocks
12
-
13
9
  files = Dir.glob("#{Dir.pwd}/**/*.rb")
14
10
 
15
11
  puts "Verifying that all automatic LSP requests execute successfully. This may take a while..."
@@ -6,6 +6,13 @@
6
6
  # composed bundle
7
7
  # !!!!!!!
8
8
 
9
+ $stdin.sync = true
10
+ $stdout.sync = true
11
+ $stderr.sync = true
12
+ $stdin.binmode
13
+ $stdout.binmode
14
+ $stderr.binmode
15
+
9
16
  setup_error = nil
10
17
  install_error = nil
11
18
  reboot = false
@@ -28,7 +35,6 @@ else
28
35
  # Read the initialize request before even starting the server. We need to do this to figure out the workspace URI.
29
36
  # Editors are not required to spawn the language server process on the same directory as the workspace URI, so we need
30
37
  # to ensure that we're setting up the bundle in the right place
31
- $stdin.binmode
32
38
  headers = $stdin.gets("\r\n\r\n")
33
39
  content_length = headers[/Content-Length: (\d+)/i, 1].to_i
34
40
  $stdin.read(content_length)
@@ -83,8 +89,13 @@ begin
83
89
  # This Marshal load can only happen after requiring Bundler because it will load a custom error class from Bundler
84
90
  # itself. If we try to load before requiring, the class will not be defined and loading will fail
85
91
  error_path = File.join(".ruby-lsp", "install_error")
86
- install_error = if File.exist?(error_path)
87
- Marshal.load(File.read(error_path))
92
+ install_error = begin
93
+ Marshal.load(File.read(error_path)) if File.exist?(error_path)
94
+ rescue ArgumentError
95
+ # The class we tried to load is not defined. This might happen when the user upgrades Bundler and new error
96
+ # classes are introduced or removed
97
+ File.delete(error_path)
98
+ nil
88
99
  end
89
100
 
90
101
  Bundler.setup
@@ -120,11 +131,8 @@ end
120
131
  # Now that the bundle is set up, we can begin actually launching the server. Note that `Bundler.setup` will have already
121
132
  # configured the load path using the version of the Ruby LSP present in the composed bundle. Do not push any Ruby LSP
122
133
  # paths into the load path manually or we may end up requiring the wrong version of the gem
123
- require "ruby_lsp/load_sorbet"
124
134
  require "ruby_lsp/internal"
125
135
 
126
- T::Utils.run_all_sig_blocks
127
-
128
136
  if ARGV.include?("--debug")
129
137
  if ["x64-mingw-ucrt", "x64-mingw32"].include?(RUBY_PLATFORM)
130
138
  $stderr.puts "Debugging is not supported on Windows"
@@ -138,22 +146,28 @@ if ARGV.include?("--debug")
138
146
  end
139
147
  end
140
148
 
141
- # Ensure all output goes out stderr by default to allow puts/p/pp to work without specifying output device.
142
- $> = $stderr
143
-
144
149
  initialize_request = JSON.parse(raw_initialize, symbolize_names: true) if raw_initialize
145
150
 
146
151
  begin
147
- RubyLsp::Server.new(
152
+ server = RubyLsp::Server.new(
148
153
  install_error: install_error,
149
154
  setup_error: setup_error,
150
155
  initialize_request: initialize_request,
151
- ).start
156
+ )
157
+
158
+ # Ensure all output goes out stderr by default to allow puts/p/pp to work without specifying output device.
159
+ $> = $stderr
160
+
161
+ server.start
152
162
  rescue ArgumentError
153
163
  # If the launcher is booting an outdated version of the server, then the initializer doesn't accept a keyword splat
154
164
  # and we already read the initialize request from the stdin pipe. In this case, we need to process the initialize
155
165
  # request manually and then start the main loop
156
166
  server = RubyLsp::Server.new
167
+
168
+ # Ensure all output goes out stderr by default to allow puts/p/pp to work without specifying output device.
169
+ $> = $stderr
170
+
157
171
  server.process_message(initialize_request)
158
172
  server.start
159
173
  end
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "rubocop"
5
- require "sorbet-runtime"
6
5
 
7
6
  module RuboCop
8
7
  module Cop
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "rubocop"
5
- require "sorbet-runtime"
6
5
 
7
6
  module RuboCop
8
7
  module Cop
@@ -9,7 +9,7 @@ module RubyIndexer
9
9
  #: Array[String]
10
10
  attr_reader :indexing_errors
11
11
 
12
- #: (Index index, Prism::Dispatcher dispatcher, Prism::ParseResult parse_result, URI::Generic uri, ?collect_comments: bool) -> void
12
+ #: (Index index, Prism::Dispatcher dispatcher, Prism::ParseLexResult | Prism::ParseResult parse_result, URI::Generic uri, ?collect_comments: bool) -> void
13
13
  def initialize(index, dispatcher, parse_result, uri, collect_comments: false)
14
14
  @index = index
15
15
  @uri = uri
@@ -260,6 +260,9 @@ module RubyIndexer
260
260
  handle_attribute(node, reader: false, writer: true)
261
261
  when :attr_accessor
262
262
  handle_attribute(node, reader: true, writer: true)
263
+ when :attr
264
+ has_writer = node.arguments&.arguments&.last&.is_a?(Prism::TrueNode) || false
265
+ handle_attribute(node, reader: true, writer: has_writer)
263
266
  when :alias_method
264
267
  handle_alias_method(node)
265
268
  when :include, :prepend, :extend
@@ -726,6 +729,9 @@ module RubyIndexer
726
729
  comment = @comments_by_line[line]
727
730
  break unless comment
728
731
 
732
+ # a trailing comment from a previous line is not a comment for this node
733
+ break if comment.trailing?
734
+
729
735
  comment_content = comment.location.slice
730
736
 
731
737
  # invalid encodings would raise an "invalid byte sequence" exception
@@ -2,11 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module RubyIndexer
5
+ # @abstract
5
6
  class Enhancement
6
- extend T::Helpers
7
-
8
- abstract!
9
-
10
7
  @enhancements = [] #: Array[Class[Enhancement]]
11
8
 
12
9
  class << self
@@ -98,11 +98,8 @@ module RubyIndexer
98
98
  end
99
99
  end
100
100
 
101
+ # @abstract
101
102
  class ModuleOperation
102
- extend T::Helpers
103
-
104
- abstract!
105
-
106
103
  #: String
107
104
  attr_reader :module_name
108
105
 
@@ -115,11 +112,8 @@ module RubyIndexer
115
112
  class Include < ModuleOperation; end
116
113
  class Prepend < ModuleOperation; end
117
114
 
115
+ # @abstract
118
116
  class Namespace < Entry
119
- extend T::Helpers
120
-
121
- abstract!
122
-
123
117
  #: Array[String]
124
118
  attr_reader :nesting
125
119
 
@@ -191,11 +185,8 @@ module RubyIndexer
191
185
  class Constant < Entry
192
186
  end
193
187
 
188
+ # @abstract
194
189
  class Parameter
195
- extend T::Helpers
196
-
197
- abstract!
198
-
199
190
  # Name includes just the name of the parameter, excluding symbols like splats
200
191
  #: Symbol
201
192
  attr_reader :name
@@ -289,12 +280,8 @@ module RubyIndexer
289
280
  end
290
281
  end
291
282
 
283
+ # @abstract
292
284
  class Member < Entry
293
- extend T::Sig
294
- extend T::Helpers
295
-
296
- abstract!
297
-
298
285
  #: Entry::Namespace?
299
286
  attr_reader :owner
300
287
 
@@ -305,8 +292,11 @@ module RubyIndexer
305
292
  @owner = owner
306
293
  end
307
294
 
308
- sig { abstract.returns(T::Array[Entry::Signature]) }
309
- def signatures; end
295
+ # @abstract
296
+ #: -> Array[Signature]
297
+ def signatures
298
+ raise AbstractMethodInvokedError
299
+ end
310
300
 
311
301
  #: -> String
312
302
  def decorated_parameters
@@ -266,7 +266,7 @@ module RubyIndexer
266
266
  def constant_completion_candidates(name, nesting)
267
267
  # If we have a top level reference, then we don't need to include completions inside the current nesting
268
268
  if name.start_with?("::")
269
- return @entries_tree.search(name.delete_prefix("::")) #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]] # rubocop:disable Layout/LineLength
269
+ return @entries_tree.search(name.delete_prefix("::")) #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
270
270
  end
271
271
 
272
272
  # Otherwise, we have to include every possible constant the user might be referring to. This is essentially the
@@ -292,7 +292,7 @@ module RubyIndexer
292
292
  # Top level constants
293
293
  entries.concat(@entries_tree.search(name))
294
294
  entries.uniq!
295
- entries #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]] # rubocop:disable Layout/LineLength
295
+ entries #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
296
296
  end
297
297
 
298
298
  # Resolve a constant to its declaration based on its name and the nesting where the reference was found. Parameter
@@ -818,11 +818,22 @@ module RubyIndexer
818
818
  )
819
819
  # Find the first class entry that has a parent class. Notice that if the developer makes a mistake and inherits
820
820
  # from two different classes in different files, we simply ignore it
821
- superclass = if singleton_levels > 0
822
- self[attached_class_name]&.find { |n| n.is_a?(Entry::Class) && n.parent_class }
823
- else
824
- namespace_entries.find { |n| n.is_a?(Entry::Class) && n.parent_class }
825
- end #: as Entry::Class?
821
+ possible_parents = singleton_levels > 0 ? self[attached_class_name] : namespace_entries
822
+ superclass = nil #: Entry::Class?
823
+
824
+ possible_parents&.each do |n|
825
+ # Ignore non class entries
826
+ next unless n.is_a?(Entry::Class)
827
+
828
+ parent_class = n.parent_class
829
+ next unless parent_class
830
+
831
+ # Always set the superclass, but break early if we found one that isn't `::Object` (meaning we found an explicit
832
+ # parent class and not the implicit default). Note that when setting different parents to the same class, which
833
+ # is invalid, we pick whatever is the first one we find
834
+ superclass = n
835
+ break if parent_class != "::Object"
836
+ end
826
837
 
827
838
  if superclass
828
839
  # If the user makes a mistake and creates a class that inherits from itself, this method would throw a stack
@@ -103,7 +103,7 @@ module RubyIndexer
103
103
  #: (RBS::AST::Members::MethodDefinition member, Entry::Namespace owner) -> void
104
104
  def handle_method(member, owner)
105
105
  name = member.name.name
106
- uri = URI::Generic.from_path(path: member.location.buffer.name)
106
+ uri = URI::Generic.from_path(path: member.location.buffer.name.to_s)
107
107
  location = to_ruby_indexer_location(member.location)
108
108
  comments = comments_to_string(member)
109
109
 
@@ -267,7 +267,7 @@ module RubyIndexer
267
267
 
268
268
  #: (RBS::AST::Members::Alias member, Entry::Namespace owner_entry) -> void
269
269
  def handle_signature_alias(member, owner_entry)
270
- uri = URI::Generic.from_path(path: member.location.buffer.name)
270
+ uri = URI::Generic.from_path(path: member.location.buffer.name.to_s)
271
271
  comments = comments_to_string(member)
272
272
 
273
273
  entry = Entry::UnresolvedMethodAlias.new(
@@ -3,11 +3,8 @@
3
3
 
4
4
  module RubyIndexer
5
5
  class ReferenceFinder
6
- class Target
7
- extend T::Helpers
8
-
9
- abstract!
10
- end
6
+ # @abstract
7
+ class Target; end
11
8
 
12
9
  class ConstTarget < Target
13
10
  #: String
@@ -35,10 +32,14 @@ module RubyIndexer
35
32
  #: String
36
33
  attr_reader :name
37
34
 
38
- #: (String name) -> void
39
- def initialize(name)
35
+ #: Array[String]
36
+ attr_reader :owner_ancestors
37
+
38
+ #: (String name, Array[String] owner_ancestors) -> void
39
+ def initialize(name, owner_ancestors)
40
40
  super()
41
41
  @name = name
42
+ @owner_ancestors = owner_ancestors
42
43
  end
43
44
  end
44
45
 
@@ -325,7 +326,10 @@ module RubyIndexer
325
326
  def collect_instance_variable_references(name, location, declaration)
326
327
  return unless @target.is_a?(InstanceVariableTarget) && name == @target.name
327
328
 
328
- @references << Reference.new(name, location, declaration: declaration)
329
+ receiver_type = Index.actual_nesting(@stack, nil).join("::")
330
+ if @target.owner_ancestors.include?(receiver_type)
331
+ @references << Reference.new(name, location, declaration: declaration)
332
+ end
329
333
  end
330
334
  end
331
335
  end
@@ -20,7 +20,7 @@ module RubyIndexer
20
20
  assert(uris.none? { |uri| uri.full_path.include?("test/fixtures") })
21
21
  assert(uris.none? { |uri| uri.full_path.include?(bundle_path.join("minitest-reporters").to_s) })
22
22
  assert(uris.none? { |uri| uri.full_path.include?(bundle_path.join("ansi").to_s) })
23
- assert(uris.any? { |uri| uri.full_path.include?(bundle_path.join("sorbet-runtime").to_s) })
23
+ assert(uris.any? { |uri| uri.full_path.include?(bundle_path.join("prism").to_s) })
24
24
  assert(uris.none? { |uri| uri.full_path == __FILE__ })
25
25
  end
26
26
 
@@ -728,6 +728,30 @@ module RubyIndexer
728
728
  assert_equal(["A", "ALIAS"], @index.linearized_ancestors_of("A"))
729
729
  end
730
730
 
731
+ def test_linearizing_ancestors_for_classes_with_overridden_parents
732
+ index(<<~RUBY)
733
+ # Find the re-open of a class first, without specifying a parent
734
+ class Child
735
+ end
736
+
737
+ # Now, find the actual definition of the class, which includes a parent
738
+ class Parent; end
739
+ class Child < Parent
740
+ end
741
+ RUBY
742
+
743
+ assert_equal(
744
+ [
745
+ "Child",
746
+ "Parent",
747
+ "Object",
748
+ "Kernel",
749
+ "BasicObject",
750
+ ],
751
+ @index.linearized_ancestors_of("Child"),
752
+ )
753
+ end
754
+
731
755
  def test_resolving_an_inherited_method
732
756
  index(<<~RUBY)
733
757
  module Foo
@@ -236,5 +236,29 @@ module RubyIndexer
236
236
  assert_instance_of(Entry::SingletonClass, owner)
237
237
  assert_equal("Foo::<Class:Foo>", owner&.name)
238
238
  end
239
+
240
+ def test_class_instance_variable_comments
241
+ index(<<~RUBY)
242
+ class Foo
243
+ # Documentation for @a
244
+ @a = "Hello" #: String
245
+ @b = "World" # trailing comment
246
+ @c = "!"
247
+ end
248
+ end
249
+ RUBY
250
+
251
+ assert_entry("@a", Entry::InstanceVariable, "/fake/path/foo.rb:2-4:2-6")
252
+ entry = @index["@a"]&.first #: as Entry::InstanceVariable
253
+ assert_equal("Documentation for @a", entry.comments)
254
+
255
+ assert_entry("@b", Entry::InstanceVariable, "/fake/path/foo.rb:3-4:3-6")
256
+ entry = @index["@b"]&.first #: as Entry::InstanceVariable
257
+ assert_empty(entry.comments)
258
+
259
+ assert_entry("@c", Entry::InstanceVariable, "/fake/path/foo.rb:4-4:4-6")
260
+ entry = @index["@c"]&.first #: as Entry::InstanceVariable
261
+ assert_empty(entry.comments)
262
+ end
239
263
  end
240
264
  end
@@ -950,6 +950,23 @@ module RubyIndexer
950
950
  assert_predicate(entry, :public?)
951
951
  end
952
952
 
953
+ def test_handling_attr
954
+ index(<<~RUBY)
955
+ class Foo
956
+ attr :bar
957
+ attr :baz, true
958
+ attr :qux, false
959
+ end
960
+ RUBY
961
+
962
+ assert_entry("bar", Entry::Accessor, "/fake/path/foo.rb:1-8:1-11")
963
+ assert_no_entry("bar=")
964
+ assert_entry("baz", Entry::Accessor, "/fake/path/foo.rb:2-8:2-11")
965
+ assert_entry("baz=", Entry::Accessor, "/fake/path/foo.rb:2-8:2-11")
966
+ assert_entry("qux", Entry::Accessor, "/fake/path/foo.rb:3-8:3-11")
967
+ assert_no_entry("qux=")
968
+ end
969
+
953
970
  private
954
971
 
955
972
  #: (Entry::Method entry, String call_string) -> void
@@ -232,8 +232,8 @@ module RubyIndexer
232
232
  signatures = entry.signatures
233
233
  assert_equal(2, signatures.length)
234
234
 
235
- # def self.select: [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array) -> [ Array[X], Array[Y], Array[Z] ] # rubocop:disable Layout/LineLength
236
- # | [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array, Time::_Timeout? timeout) -> [ Array[X], Array[Y], Array[Z] ]? # rubocop:disable Layout/LineLength
235
+ # def self.select: [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array) -> [ Array[X], Array[Y], Array[Z] ]
236
+ # | [X, Y, Z] (::Array[X & io]? read_array, ?::Array[Y & io]? write_array, ?::Array[Z & io]? error_array, Time::_Timeout? timeout) -> [ Array[X], Array[Y], Array[Z] ]?
237
237
 
238
238
  parameters = signatures[0]&.parameters #: as !nil
239
239
  assert_equal([:read_array, :write_array, :error_array], parameters.map(&:name))
@@ -216,22 +216,43 @@ module RubyIndexer
216
216
  assert_equal(11, refs[2].location.start_line)
217
217
  end
218
218
 
219
- def test_finds_instance_variable_read_references
220
- refs = find_instance_variable_references("@foo", <<~RUBY)
219
+ def test_finds_instance_variable_references
220
+ refs = find_instance_variable_references("@name", ["Foo"], <<~RUBY)
221
221
  class Foo
222
- def foo
223
- @foo
222
+ def initialize
223
+ @name = "foo"
224
+ end
225
+ def name
226
+ @name
227
+ end
228
+ def name_capital
229
+ @name[0]
230
+ end
231
+ end
232
+
233
+ class Bar
234
+ def initialize
235
+ @name = "foo"
236
+ end
237
+ def name
238
+ @name
224
239
  end
225
240
  end
226
241
  RUBY
227
- assert_equal(1, refs.size)
242
+ assert_equal(3, refs.size)
228
243
 
229
- assert_equal("@foo", refs[0].name)
244
+ assert_equal("@name", refs[0].name)
230
245
  assert_equal(3, refs[0].location.start_line)
246
+
247
+ assert_equal("@name", refs[1].name)
248
+ assert_equal(6, refs[1].location.start_line)
249
+
250
+ assert_equal("@name", refs[2].name)
251
+ assert_equal(9, refs[2].location.start_line)
231
252
  end
232
253
 
233
254
  def test_finds_instance_variable_write_references
234
- refs = find_instance_variable_references("@foo", <<~RUBY)
255
+ refs = find_instance_variable_references("@foo", ["Foo"], <<~RUBY)
235
256
  class Foo
236
257
  def write
237
258
  @foo = 1
@@ -252,26 +273,70 @@ module RubyIndexer
252
273
  assert_equal(7, refs[4].location.start_line)
253
274
  end
254
275
 
255
- def test_finds_instance_variable_references_ignore_context
256
- refs = find_instance_variable_references("@name", <<~RUBY)
257
- class Foo
276
+ def test_finds_instance_variable_references_in_owner_ancestors
277
+ refs = find_instance_variable_references("@name", ["Foo", "Base", "Top", "Parent"], <<~RUBY)
278
+ module Base
279
+ def change_name(name)
280
+ @name = name
281
+ end
258
282
  def name
283
+ @name
284
+ end
285
+
286
+ module ::Top
287
+ def name
288
+ @name
289
+ end
290
+ end
291
+ end
292
+
293
+ class Parent
294
+ def initialize
295
+ @name = "parent"
296
+ end
297
+ def name_capital
298
+ @name[0]
299
+ end
300
+ end
301
+
302
+ class Foo < Parent
303
+ include Base
304
+ def initialize
259
305
  @name = "foo"
260
306
  end
307
+ def name
308
+ @name
309
+ end
261
310
  end
311
+
262
312
  class Bar
263
313
  def name
264
314
  @name = "bar"
265
315
  end
266
316
  end
267
317
  RUBY
268
- assert_equal(2, refs.size)
318
+ assert_equal(7, refs.size)
269
319
 
270
320
  assert_equal("@name", refs[0].name)
271
321
  assert_equal(3, refs[0].location.start_line)
272
322
 
273
323
  assert_equal("@name", refs[1].name)
274
- assert_equal(8, refs[1].location.start_line)
324
+ assert_equal(6, refs[1].location.start_line)
325
+
326
+ assert_equal("@name", refs[2].name)
327
+ assert_equal(11, refs[2].location.start_line)
328
+
329
+ assert_equal("@name", refs[3].name)
330
+ assert_equal(18, refs[3].location.start_line)
331
+
332
+ assert_equal("@name", refs[4].name)
333
+ assert_equal(21, refs[4].location.start_line)
334
+
335
+ assert_equal("@name", refs[5].name)
336
+ assert_equal(28, refs[5].location.start_line)
337
+
338
+ assert_equal("@name", refs[6].name)
339
+ assert_equal(31, refs[6].location.start_line)
275
340
  end
276
341
 
277
342
  def test_accounts_for_reopened_classes
@@ -310,8 +375,8 @@ module RubyIndexer
310
375
  find_references(target, source)
311
376
  end
312
377
 
313
- def find_instance_variable_references(instance_variable_name, source)
314
- target = ReferenceFinder::InstanceVariableTarget.new(instance_variable_name)
378
+ def find_instance_variable_references(instance_variable_name, owner_ancestors, source)
379
+ target = ReferenceFinder::InstanceVariableTarget.new(instance_variable_name, owner_ancestors)
315
380
  find_references(target, source)
316
381
  end
317
382