ruby-lsp 0.23.20 → 0.26.1

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 (66) 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/exe/ruby-lsp-test-exec +6 -0
  7. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +0 -1
  8. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +0 -1
  9. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +7 -1
  10. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +1 -4
  11. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +10 -19
  12. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +29 -7
  13. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +2 -2
  14. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +12 -8
  15. data/lib/ruby_indexer/test/configuration_test.rb +1 -2
  16. data/lib/ruby_indexer/test/index_test.rb +39 -0
  17. data/lib/ruby_indexer/test/instance_variables_test.rb +24 -0
  18. data/lib/ruby_indexer/test/method_test.rb +17 -0
  19. data/lib/ruby_indexer/test/rbs_indexer_test.rb +2 -2
  20. data/lib/ruby_indexer/test/reference_finder_test.rb +79 -14
  21. data/lib/ruby_lsp/addon.rb +44 -15
  22. data/lib/ruby_lsp/base_server.rb +34 -26
  23. data/lib/ruby_lsp/document.rb +162 -52
  24. data/lib/ruby_lsp/erb_document.rb +8 -3
  25. data/lib/ruby_lsp/global_state.rb +21 -0
  26. data/lib/ruby_lsp/internal.rb +0 -2
  27. data/lib/ruby_lsp/listeners/completion.rb +14 -3
  28. data/lib/ruby_lsp/listeners/hover.rb +7 -0
  29. data/lib/ruby_lsp/listeners/inlay_hints.rb +5 -3
  30. data/lib/ruby_lsp/listeners/spec_style.rb +126 -67
  31. data/lib/ruby_lsp/listeners/test_discovery.rb +18 -15
  32. data/lib/ruby_lsp/listeners/test_style.rb +56 -23
  33. data/lib/ruby_lsp/requests/code_action_resolve.rb +3 -3
  34. data/lib/ruby_lsp/requests/code_lens.rb +14 -5
  35. data/lib/ruby_lsp/requests/completion.rb +1 -1
  36. data/lib/ruby_lsp/requests/definition.rb +1 -1
  37. data/lib/ruby_lsp/requests/discover_tests.rb +2 -2
  38. data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
  39. data/lib/ruby_lsp/requests/hover.rb +1 -1
  40. data/lib/ruby_lsp/requests/inlay_hints.rb +3 -3
  41. data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
  42. data/lib/ruby_lsp/requests/prepare_rename.rb +1 -1
  43. data/lib/ruby_lsp/requests/references.rb +10 -6
  44. data/lib/ruby_lsp/requests/rename.rb +8 -6
  45. data/lib/ruby_lsp/requests/request.rb +6 -7
  46. data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
  47. data/lib/ruby_lsp/requests/show_syntax_tree.rb +1 -1
  48. data/lib/ruby_lsp/requests/signature_help.rb +1 -1
  49. data/lib/ruby_lsp/requests/support/common.rb +1 -3
  50. data/lib/ruby_lsp/requests/support/formatter.rb +16 -15
  51. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +2 -2
  52. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +13 -3
  53. data/lib/ruby_lsp/response_builders/response_builder.rb +6 -8
  54. data/lib/ruby_lsp/ruby_document.rb +10 -5
  55. data/lib/ruby_lsp/server.rb +95 -110
  56. data/lib/ruby_lsp/setup_bundler.rb +59 -25
  57. data/lib/ruby_lsp/static_docs.rb +1 -0
  58. data/lib/ruby_lsp/store.rb +0 -10
  59. data/lib/ruby_lsp/test_helper.rb +1 -4
  60. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +18 -7
  61. data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +54 -7
  62. data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +0 -1
  63. data/lib/ruby_lsp/utils.rb +47 -11
  64. data/static_docs/break.md +103 -0
  65. metadata +7 -19
  66. data/lib/ruby_lsp/load_sorbet.rb +0 -62
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63ffbd4bd2ac09693c25c61a71ec89528578c3eaeaf35bf547e5ee748085f3d6
4
- data.tar.gz: f3390b3e9f7f86c3dcf98130c04d18a40720ab21a6b521035859bafdac67ae60
3
+ metadata.gz: c80f549675508ffbb28d649de04506adabec01eac9aa4c6eee057ec848adf858
4
+ data.tar.gz: 71ea1a4d628444b98bc1173748f5aecf0d71bdc8d3dc80f33b2779c9c78d9de0
5
5
  SHA512:
6
- metadata.gz: 0232de2d936eaa8baa579293884fdb47c69d70be87b834b655292111b50ab9609f0d52d2a627a679483919116a6215df25c0966fc725b9c5a11a5b0160f12550
7
- data.tar.gz: 3c744d0e29846cc5bf4406a06675fb789d1d6efe959cf982fba2505ca58b151ea45b4c800aceedfd2b242d75e792f557d99ffea5108afdc3894c76d0885ea5ae
6
+ metadata.gz: 7261bf15c095154ff36152492aa252cbd84b84f6e83eb458623c7bb9790a57957885a855c2ee10683b7035267177e323c013988befc02e96f3fdf0274d2312ca
7
+ data.tar.gz: 74bcea4844e876230713400776bf7def1db44492bcf913526195b5d01919efd24ac69b517594ce2499e332184bf7ef4881e17e7694574bf4d35afabeda25b8c4
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.23.20
1
+ 0.26.1
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
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # This executable will be removed thanks to the changes in https://github.com/Shopify/ruby-lsp/pull/3661.
5
+ # Remove this a few months after extension updates have rolled out
6
+ exec(*ARGV)
@@ -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
@@ -343,6 +333,7 @@ module RubyIndexer
343
333
  end
344
334
 
345
335
  class Method < Member
336
+ # @override
346
337
  #: Array[Signature]
347
338
  attr_reader :signatures
348
339
 
@@ -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
@@ -291,8 +291,19 @@ module RubyIndexer
291
291
 
292
292
  # Top level constants
293
293
  entries.concat(@entries_tree.search(name))
294
+
295
+ # Filter only constants since methods may have names that look like constants
296
+ entries.select! do |definitions|
297
+ definitions.select! do |entry|
298
+ entry.is_a?(Entry::Constant) || entry.is_a?(Entry::ConstantAlias) ||
299
+ entry.is_a?(Entry::Namespace) || entry.is_a?(Entry::UnresolvedConstantAlias)
300
+ end
301
+
302
+ definitions.any?
303
+ end
304
+
294
305
  entries.uniq!
295
- entries #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]] # rubocop:disable Layout/LineLength
306
+ entries #: as Array[Array[Entry::Constant | Entry::ConstantAlias | Entry::Namespace | Entry::UnresolvedConstantAlias]]
296
307
  end
297
308
 
298
309
  # Resolve a constant to its declaration based on its name and the nesting where the reference was found. Parameter
@@ -818,11 +829,22 @@ module RubyIndexer
818
829
  )
819
830
  # Find the first class entry that has a parent class. Notice that if the developer makes a mistake and inherits
820
831
  # 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?
832
+ possible_parents = singleton_levels > 0 ? self[attached_class_name] : namespace_entries
833
+ superclass = nil #: Entry::Class?
834
+
835
+ possible_parents&.each do |n|
836
+ # Ignore non class entries
837
+ next unless n.is_a?(Entry::Class)
838
+
839
+ parent_class = n.parent_class
840
+ next unless parent_class
841
+
842
+ # Always set the superclass, but break early if we found one that isn't `::Object` (meaning we found an explicit
843
+ # parent class and not the implicit default). Note that when setting different parents to the same class, which
844
+ # is invalid, we pick whatever is the first one we find
845
+ superclass = n
846
+ break if parent_class != "::Object"
847
+ end
826
848
 
827
849
  if superclass
828
850
  # 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
 
@@ -59,7 +59,6 @@ module RubyIndexer
59
59
 
60
60
  assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/pathname.rb")
61
61
  assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/ipaddr.rb")
62
- assert_includes(paths, "#{RbConfig::CONFIG["rubylibdir"]}/erb.rb")
63
62
  end
64
63
 
65
64
  def test_indexable_uris_includes_project_files
@@ -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
@@ -1959,6 +1983,21 @@ module RubyIndexer
1959
1983
  assert_equal(["XQRK"], result.map { |entries| entries.first&.name })
1960
1984
  end
1961
1985
 
1986
+ def test_constant_completion_does_not_confuse_uppercase_methods
1987
+ index(<<~RUBY)
1988
+ class Foo
1989
+ def Qux
1990
+ end
1991
+ end
1992
+ RUBY
1993
+
1994
+ candidates = @index.constant_completion_candidates("Q", [])
1995
+ refute_includes(candidates.flat_map { |entries| entries.map(&:name) }, "Qux")
1996
+
1997
+ candidates = @index.constant_completion_candidates("Qux", [])
1998
+ assert_equal(0, candidates.length)
1999
+ end
2000
+
1962
2001
  def test_constant_completion_candidates_for_empty_name
1963
2002
  index(<<~RUBY)
1964
2003
  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))