ruby-lsp 0.26.2 → 0.26.9

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/exe/ruby-lsp +7 -10
  4. data/exe/ruby-lsp-launcher +16 -3
  5. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +3 -2
  6. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +19 -0
  7. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +33 -27
  8. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +7 -2
  9. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +6 -2
  10. data/lib/ruby_lsp/base_server.rb +23 -12
  11. data/lib/ruby_lsp/global_state.rb +65 -33
  12. data/lib/ruby_lsp/listeners/definition.rb +34 -14
  13. data/lib/ruby_lsp/listeners/document_link.rb +62 -23
  14. data/lib/ruby_lsp/listeners/hover.rb +2 -2
  15. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +2 -2
  16. data/lib/ruby_lsp/requests/code_action_resolve.rb +33 -11
  17. data/lib/ruby_lsp/requests/code_actions.rb +20 -5
  18. data/lib/ruby_lsp/requests/completion_resolve.rb +8 -6
  19. data/lib/ruby_lsp/requests/support/package_url.rb +414 -0
  20. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +7 -1
  21. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +6 -10
  22. data/lib/ruby_lsp/requests/support/source_uri.rb +7 -6
  23. data/lib/ruby_lsp/requests/workspace_symbol.rb +20 -12
  24. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +3 -3
  25. data/lib/ruby_lsp/scripts/compose_bundle.rb +3 -3
  26. data/lib/ruby_lsp/scripts/compose_bundle_windows.rb +3 -1
  27. data/lib/ruby_lsp/server.rb +5 -2
  28. data/lib/ruby_lsp/setup_bundler.rb +68 -37
  29. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +50 -27
  30. data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +1 -1
  31. data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +1 -1
  32. metadata +3 -16
  33. data/lib/ruby_indexer/test/class_variables_test.rb +0 -140
  34. data/lib/ruby_indexer/test/classes_and_modules_test.rb +0 -770
  35. data/lib/ruby_indexer/test/configuration_test.rb +0 -279
  36. data/lib/ruby_indexer/test/constant_test.rb +0 -402
  37. data/lib/ruby_indexer/test/enhancements_test.rb +0 -325
  38. data/lib/ruby_indexer/test/global_variable_test.rb +0 -49
  39. data/lib/ruby_indexer/test/index_test.rb +0 -2276
  40. data/lib/ruby_indexer/test/instance_variables_test.rb +0 -264
  41. data/lib/ruby_indexer/test/method_test.rb +0 -990
  42. data/lib/ruby_indexer/test/prefix_tree_test.rb +0 -150
  43. data/lib/ruby_indexer/test/rbs_indexer_test.rb +0 -381
  44. data/lib/ruby_indexer/test/reference_finder_test.rb +0 -395
  45. data/lib/ruby_indexer/test/test_case.rb +0 -57
  46. data/lib/ruby_indexer/test/uri_test.rb +0 -85
@@ -31,12 +31,17 @@ module RubyLsp
31
31
 
32
32
  FOUR_HOURS = 4 * 60 * 60 #: Integer
33
33
 
34
+ # Gems that should be kept up to date in the composed bundle. When updating, any of these gems that are not
35
+ # already in the user's Gemfile will be updated together.
36
+ GEMS_TO_UPDATE = ["ruby-lsp", "debug", "prism", "rbs"].freeze #: Array[String]
37
+ RUBY_LSP_MIN_VERSION = "0.18.0" #: String
38
+
34
39
  #: (String project_path, **untyped options) -> void
35
40
  def initialize(project_path, **options)
36
41
  @project_path = project_path
37
- @branch = options[:branch] #: String?
38
42
  @launcher = options[:launcher] #: bool?
39
- patch_thor_to_print_progress_to_stderr! if @launcher
43
+ @beta = options[:beta] #: bool?
44
+ force_output_to_stderr! if @launcher
40
45
 
41
46
  # Regular bundle paths
42
47
  @gemfile = begin
@@ -55,7 +60,7 @@ module RubyLsp
55
60
  @custom_dir = Pathname.new(".ruby-lsp").expand_path(@project_path) #: Pathname
56
61
  @custom_gemfile = @custom_dir + @gemfile_name #: Pathname
57
62
  @custom_lockfile = @custom_dir + (@lockfile&.basename || "Gemfile.lock") #: Pathname
58
- @lockfile_hash_path = @custom_dir + "main_lockfile_hash" #: Pathname
63
+ @freshness_hash_path = @custom_dir + "freshness_hash" #: Pathname
59
64
  @last_updated_path = @custom_dir + "last_updated" #: Pathname
60
65
  @error_path = @custom_dir + "install_error" #: Pathname
61
66
  @already_composed_path = @custom_dir + "bundle_is_composed" #: Pathname
@@ -114,17 +119,24 @@ module RubyLsp
114
119
  return run_bundle_install(@custom_gemfile)
115
120
  end
116
121
 
117
- if @lockfile_hash && @custom_lockfile.exist? && @lockfile_hash_path.exist? &&
118
- @lockfile_hash_path.read == @lockfile_hash
122
+ # Our freshness hash determines if we need to copy the lockfile from the main app again and run bundle install
123
+ # from scratch. We use a combination of the main app's lockfile and the composed Gemfile. The goal is to
124
+ # automatically account for CLI arguments which can change the Gemfile we compose. If the CLI arguments or the
125
+ # main lockfile change, we need to make sure we're re-composing.
126
+ freshness_digest = Digest::SHA256.hexdigest("#{@lockfile_hash}#{@custom_gemfile.read}")
127
+
128
+ if @lockfile_hash && @custom_lockfile.exist? && @freshness_hash_path.exist? &&
129
+ @freshness_hash_path.read == freshness_digest
119
130
  $stderr.puts(
120
131
  "Ruby LSP> Skipping composed bundle setup since #{@custom_lockfile} already exists and is up to date",
121
132
  )
122
133
  return run_bundle_install(@custom_gemfile)
123
134
  end
124
135
 
136
+ @needs_update_path.delete if @needs_update_path.exist?
125
137
  FileUtils.cp(@lockfile.to_s, @custom_lockfile.to_s)
126
138
  correct_relative_remote_paths
127
- @lockfile_hash_path.write(@lockfile_hash)
139
+ @freshness_hash_path.write(freshness_digest)
128
140
  run_bundle_install(@custom_gemfile)
129
141
  end
130
142
 
@@ -164,9 +176,8 @@ module RubyLsp
164
176
  end
165
177
 
166
178
  unless @dependencies["ruby-lsp"]
167
- ruby_lsp_entry = +'gem "ruby-lsp", require: false, group: :development'
168
- ruby_lsp_entry << ", github: \"Shopify/ruby-lsp\", branch: \"#{@branch}\"" if @branch
169
- parts << ruby_lsp_entry
179
+ version = @beta ? "0.a" : RUBY_LSP_MIN_VERSION
180
+ parts << "gem \"ruby-lsp\", \">= #{version}\", require: false, group: :development"
170
181
  end
171
182
 
172
183
  unless @dependencies["debug"]
@@ -234,7 +245,7 @@ module RubyLsp
234
245
  # If no error occurred, then clear previous errors
235
246
  @error_path.delete if @error_path.exist?
236
247
  $stderr.puts("Ruby LSP> Composed bundle installation complete")
237
- rescue Errno::EPIPE, Bundler::HTTPError
248
+ rescue Errno::EPIPE, Bundler::HTTPError, Bundler::InstallError
238
249
  # There are cases where we expect certain errors to happen occasionally, and we don't want to write them to
239
250
  # a file, which would report to telemetry on the next launch.
240
251
  #
@@ -242,6 +253,7 @@ module RubyLsp
242
253
  # install. This situation may happen because, while running bundle install, the server is not yet ready to
243
254
  # receive shutdown requests and we may continue doing work until the process is killed.
244
255
  # - Bundler might also encounter a network error.
256
+ # - Native extension build failures (InstallError) are user environment issues that Ruby LSP cannot resolve.
245
257
  @error_path.delete if @error_path.exist?
246
258
  rescue => e
247
259
  # Write the error object to a file so that we can read it from the parent process
@@ -270,17 +282,37 @@ module RubyLsp
270
282
  #: (Hash[String, String] env, ?force_install: bool) -> Hash[String, String]
271
283
  def run_bundle_install_directly(env, force_install: false)
272
284
  RubyVM::YJIT.enable if defined?(RubyVM::YJIT.enable)
273
- return update(env) if @needs_update_path.exist?
274
285
 
275
- # The ENV can only be merged after checking if an update is required because we depend on the original value of
276
- # ENV["BUNDLE_GEMFILE"], which gets overridden after the merge
277
- FileUtils.touch(@needs_update_path) if should_bundle_update?
286
+ # The should_bundle_update? check needs to run on the original Bundler environment, but everything else (like
287
+ # updating or running install) requires the modified environment. Here we compute the check ahead of time and
288
+ # merge the environment to ensure correct results.
289
+ #
290
+ # The symptoms of having these operations in the wrong order is seeing unwanted modifications in the application's
291
+ # main lockfile because we accidentally run update on the main bundle instead of the composed one.
292
+ needs_update = should_bundle_update?
278
293
  ENV.merge!(env)
279
294
 
295
+ return update(env) if @needs_update_path.exist?
296
+
297
+ FileUtils.touch(@needs_update_path) if needs_update
298
+
280
299
  $stderr.puts("Ruby LSP> Checking if the composed bundle is satisfied...")
281
- missing_gems = bundle_check
282
300
 
283
- unless missing_gems.empty?
301
+ begin
302
+ missing_gems = bundle_check
303
+ rescue Errno::EPIPE, Bundler::HTTPError
304
+ # These are errors cases where we cannot recover
305
+ raise
306
+ rescue => e
307
+ # If anything fails with bundle check, try to bundle install
308
+ $stderr.puts("Ruby LSP> Running bundle install because #{e.message}")
309
+ bundle_install
310
+ return env
311
+ end
312
+
313
+ if missing_gems.empty?
314
+ $stderr.puts("Ruby LSP> Bundle already satisfied")
315
+ else
284
316
  $stderr.puts(<<~MESSAGE)
285
317
  Ruby LSP> Running bundle install because the following gems are not installed:
286
318
  #{missing_gems.map { |g| "#{g.name}: #{g.version}" }.join("\n")}
@@ -289,11 +321,6 @@ module RubyLsp
289
321
  bundle_install
290
322
  end
291
323
 
292
- $stderr.puts("Ruby LSP> Bundle already satisfied")
293
- env
294
- rescue => e
295
- $stderr.puts("Ruby LSP> Running bundle install because #{e.message}")
296
- bundle_install
297
324
  env
298
325
  end
299
326
 
@@ -316,26 +343,26 @@ module RubyLsp
316
343
  def update(env)
317
344
  # Try to auto upgrade the gems we depend on, unless they are in the Gemfile as that would result in undesired
318
345
  # source control changes
319
- gems = ["ruby-lsp", "debug", "prism"].reject { |dep| @dependencies[dep] }
346
+ gems = GEMS_TO_UPDATE.reject { |dep| @dependencies[dep] }
320
347
  gems << "ruby-lsp-rails" if @rails_app && !@dependencies["ruby-lsp-rails"]
321
348
 
322
349
  Bundler::CLI::Update.new({ conservative: true }, gems).run
323
350
  correct_relative_remote_paths if @custom_lockfile.exist?
324
- @needs_update_path.delete
351
+ @needs_update_path.delete if @needs_update_path.exist?
325
352
  @last_updated_path.write(Time.now.iso8601)
326
353
  env
327
354
  end
328
355
 
329
356
  #: (Hash[String, String] env) -> Hash[String, String]
330
357
  def run_bundle_install_through_command(env)
331
- # If `ruby-lsp` and `debug` (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't try
332
- # to upgrade them or else we'll produce undesired source control changes. If the composed bundle was just created
333
- # and any of `ruby-lsp`, `ruby-lsp-rails` or `debug` weren't a part of the Gemfile, then we need to run `bundle
334
- # install` for the first time to generate the Gemfile.lock with them included or else Bundler will complain that
335
- # they're missing. We can only update if the custom `.ruby-lsp/Gemfile.lock` already exists and includes all gems
358
+ # If the gems in GEMS_TO_UPDATE (and potentially `ruby-lsp-rails`) are already in the Gemfile, then we shouldn't
359
+ # try to upgrade them or else we'll produce undesired source control changes. If the composed bundle was just
360
+ # created and any of those gems weren't a part of the Gemfile, then we need to run `bundle install` for the first
361
+ # time to generate the Gemfile.lock with them included or else Bundler will complain that they're missing. We can
362
+ # only update if the custom `.ruby-lsp/Gemfile.lock` already exists and includes all gems
336
363
 
337
364
  # When not updating, we run `(bundle check || bundle install)`
338
- # When updating, we run `((bundle check && bundle update ruby-lsp debug) || bundle install)`
365
+ # When updating, we run `((bundle check && bundle update <GEMS_TO_UPDATE>) || bundle install)`
339
366
  bundler_path = File.join(Gem.default_bindir, "bundle")
340
367
  base_command = (!Gem.win_platform? && File.exist?(bundler_path) ? "#{Gem.ruby} #{bundler_path}" : "bundle").dup
341
368
 
@@ -346,12 +373,11 @@ module RubyLsp
346
373
  command = +"(#{base_command} check"
347
374
 
348
375
  if should_bundle_update?
349
- # If any of `ruby-lsp`, `ruby-lsp-rails` or `debug` are not in the Gemfile, try to update them to the latest
350
- # version
376
+ # If any of the gems in GEMS_TO_UPDATE (or `ruby-lsp-rails` for Rails apps) are not in the Gemfile, try to
377
+ # update them to the latest version
351
378
  command.prepend("(")
352
379
  command << " && #{base_command} update "
353
- command << "ruby-lsp " unless @dependencies["ruby-lsp"]
354
- command << "debug " unless @dependencies["debug"]
380
+ GEMS_TO_UPDATE.each { |gem| command << "#{gem} " unless @dependencies[gem] }
355
381
  command << "ruby-lsp-rails " if @rails_app && !@dependencies["ruby-lsp-rails"]
356
382
  command.delete_suffix!(" ")
357
383
  command << ")"
@@ -480,11 +506,16 @@ module RubyLsp
480
506
  end
481
507
 
482
508
  #: -> void
483
- def patch_thor_to_print_progress_to_stderr!
484
- return unless defined?(Bundler::Thor::Shell::Basic)
509
+ def force_output_to_stderr!
510
+ # Bundler and RubyGems have different UI objects used for printing. We need to ensure that both are configured to
511
+ # print only to stderr or else they'll break the connection with the editor
512
+ Gem::DefaultUserInteraction.ui = Gem::StreamUI.new($stdin, $stderr, $stderr, false)
513
+
514
+ ui = Bundler.ui
515
+ ui.output_stream = :stderr if ui.respond_to?(:output_stream=)
516
+ ui.level = :info
485
517
 
486
- Bundler::Thor::Shell::Basic.prepend(ThorPatch)
487
- Bundler.ui.level = :info
518
+ Bundler::Thor::Shell::Basic.prepend(ThorPatch) if defined?(Bundler::Thor::Shell::Basic)
488
519
  end
489
520
  end
490
521
  end
@@ -4,13 +4,40 @@
4
4
  require "English"
5
5
  require "json"
6
6
  require "socket"
7
- require "singleton"
8
7
  require "tmpdir"
9
8
  require_relative "../../ruby_indexer/lib/ruby_indexer/uri"
10
9
 
11
10
  module RubyLsp
12
11
  class LspReporter
13
- include Singleton
12
+ @instance = nil #: LspReporter?
13
+
14
+ class << self
15
+ #: -> LspReporter
16
+ def instance
17
+ @instance ||= new
18
+ end
19
+
20
+ #: -> bool
21
+ def start_coverage?
22
+ ENV["RUBY_LSP_TEST_RUNNER"] == "coverage"
23
+ end
24
+
25
+ #: -> bool
26
+ def executed_under_test_runner?
27
+ !!(ENV["RUBY_LSP_TEST_RUNNER"] && ENV["RUBY_LSP_ENV"] != "test")
28
+ end
29
+
30
+ #: (Method | UnboundMethod) -> [URI::Generic, Integer?]?
31
+ def uri_and_line_for(method_object)
32
+ file_path, line = method_object.source_location
33
+ return unless file_path
34
+ return if file_path.start_with?("(eval at ")
35
+
36
+ uri = URI::Generic.from_path(path: File.expand_path(file_path))
37
+ zero_based_line = line ? line - 1 : nil
38
+ [uri, zero_based_line]
39
+ end
40
+ end
14
41
 
15
42
  # https://code.visualstudio.com/api/references/vscode-api#Position
16
43
  #: type position = { line: Integer, character: Integer }
@@ -35,10 +62,10 @@ module RubyLsp
35
62
  @io = begin
36
63
  # The environment variable is only used for tests. The extension always writes to the temporary file
37
64
  if port
38
- TCPSocket.new("localhost", port)
65
+ socket(port)
39
66
  elsif File.exist?(port_db_path)
40
67
  db = JSON.load_file(port_db_path)
41
- TCPSocket.new("localhost", db[Dir.pwd])
68
+ socket(db[Dir.pwd])
42
69
  else
43
70
  # For tests that don't spawn the TCP server
44
71
  require "stringio"
@@ -50,6 +77,8 @@ module RubyLsp
50
77
  end #: IO | StringIO
51
78
 
52
79
  @invoked_shutdown = false #: bool
80
+ @message_queue = Thread::Queue.new #: Thread::Queue
81
+ @writer = Thread.new { write_loop } #: Thread
53
82
  end
54
83
 
55
84
  #: -> void
@@ -68,6 +97,8 @@ module RubyLsp
68
97
  @invoked_shutdown = true
69
98
 
70
99
  send_message("finish")
100
+ @message_queue.close
101
+ @writer.join
71
102
  @io.close
72
103
  end
73
104
 
@@ -96,17 +127,6 @@ module RubyLsp
96
127
  send_message("error", id: id, message: message, uri: uri.to_s)
97
128
  end
98
129
 
99
- #: (Method | UnboundMethod) -> [URI::Generic, Integer?]?
100
- def uri_and_line_for(method_object)
101
- file_path, line = method_object.source_location
102
- return unless file_path
103
- return if file_path.start_with?("(eval at ")
104
-
105
- uri = URI::Generic.from_path(path: File.expand_path(file_path))
106
- zero_based_line = line ? line - 1 : nil
107
- [uri, zero_based_line]
108
- end
109
-
110
130
  # Gather the results returned by Coverage.result and format like the VS Code test explorer expects
111
131
  #
112
132
  # Coverage result format:
@@ -195,24 +215,27 @@ module RubyLsp
195
215
  internal_shutdown unless @invoked_shutdown
196
216
  end
197
217
 
198
- class << self
199
- #: -> bool
200
- def start_coverage?
201
- ENV["RUBY_LSP_TEST_RUNNER"] == "coverage"
202
- end
218
+ private
203
219
 
204
- #: -> bool
205
- def executed_under_test_runner?
206
- !!(ENV["RUBY_LSP_TEST_RUNNER"] && ENV["RUBY_LSP_ENV"] != "test")
207
- end
220
+ #: (String) -> Socket
221
+ def socket(port)
222
+ socket = Socket.tcp("localhost", port)
223
+ socket.binmode
224
+ socket.sync = true
225
+ socket
208
226
  end
209
227
 
210
- private
211
-
212
228
  #: (String?, **untyped) -> void
213
229
  def send_message(method_name, **params)
214
230
  json_message = { method: method_name, params: params }.to_json
215
- @io.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")
231
+ @message_queue << "Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}"
232
+ end
233
+
234
+ #: -> void
235
+ def write_loop
236
+ while (message = @message_queue.pop)
237
+ @io.write(message)
238
+ end
216
239
  end
217
240
  end
218
241
  end
@@ -89,7 +89,7 @@ module RubyLsp
89
89
  test_class_or_wrapper
90
90
  end
91
91
 
92
- uri, line = LspReporter.instance.uri_and_line_for(klass.instance_method(method_name))
92
+ uri, line = LspReporter.uri_and_line_for(klass.instance_method(method_name))
93
93
  return unless uri
94
94
 
95
95
  id = "#{name}##{handle_spec_test_id(method_name, line)}"
@@ -25,7 +25,7 @@ module RubyLsp
25
25
  def test_started(test)
26
26
  super
27
27
 
28
- uri, line = LspReporter.instance.uri_and_line_for(test.method(test.method_name))
28
+ uri, line = LspReporter.uri_and_line_for(test.method(test.method_name))
29
29
  return unless uri
30
30
 
31
31
  @current_uri = uri
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.2
4
+ version: 0.26.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
@@ -96,20 +96,6 @@ files:
96
96
  - lib/ruby_indexer/lib/ruby_indexer/uri.rb
97
97
  - lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb
98
98
  - lib/ruby_indexer/ruby_indexer.rb
99
- - lib/ruby_indexer/test/class_variables_test.rb
100
- - lib/ruby_indexer/test/classes_and_modules_test.rb
101
- - lib/ruby_indexer/test/configuration_test.rb
102
- - lib/ruby_indexer/test/constant_test.rb
103
- - lib/ruby_indexer/test/enhancements_test.rb
104
- - lib/ruby_indexer/test/global_variable_test.rb
105
- - lib/ruby_indexer/test/index_test.rb
106
- - lib/ruby_indexer/test/instance_variables_test.rb
107
- - lib/ruby_indexer/test/method_test.rb
108
- - lib/ruby_indexer/test/prefix_tree_test.rb
109
- - lib/ruby_indexer/test/rbs_indexer_test.rb
110
- - lib/ruby_indexer/test/reference_finder_test.rb
111
- - lib/ruby_indexer/test/test_case.rb
112
- - lib/ruby_indexer/test/uri_test.rb
113
99
  - lib/ruby_lsp/addon.rb
114
100
  - lib/ruby_lsp/base_server.rb
115
101
  - lib/ruby_lsp/client_capabilities.rb
@@ -163,6 +149,7 @@ files:
163
149
  - lib/ruby_lsp/requests/support/annotation.rb
164
150
  - lib/ruby_lsp/requests/support/common.rb
165
151
  - lib/ruby_lsp/requests/support/formatter.rb
152
+ - lib/ruby_lsp/requests/support/package_url.rb
166
153
  - lib/ruby_lsp/requests/support/rubocop_diagnostic.rb
167
154
  - lib/ruby_lsp/requests/support/rubocop_formatter.rb
168
155
  - lib/ruby_lsp/requests/support/rubocop_runner.rb
@@ -216,7 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
216
203
  - !ruby/object:Gem::Version
217
204
  version: '0'
218
205
  requirements: []
219
- rubygems_version: 3.7.2
206
+ rubygems_version: 4.0.3
220
207
  specification_version: 4
221
208
  summary: An opinionated language server for Ruby
222
209
  test_files: []
@@ -1,140 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- require_relative "test_case"
5
-
6
- module RubyIndexer
7
- class ClassVariableTest < TestCase
8
- def test_class_variable_and_write
9
- index(<<~RUBY)
10
- class Foo
11
- @@bar &&= 1
12
- end
13
- RUBY
14
-
15
- assert_entry("@@bar", Entry::ClassVariable, "/fake/path/foo.rb:1-2:1-7")
16
-
17
- entry = @index["@@bar"]&.first #: as Entry::ClassVariable
18
- owner = entry.owner #: as !nil
19
- assert_instance_of(Entry::Class, owner)
20
- assert_equal("Foo", owner.name)
21
- end
22
-
23
- def test_class_variable_operator_write
24
- index(<<~RUBY)
25
- class Foo
26
- @@bar += 1
27
- end
28
- RUBY
29
-
30
- assert_entry("@@bar", Entry::ClassVariable, "/fake/path/foo.rb:1-2:1-7")
31
- end
32
-
33
- def test_class_variable_or_write
34
- index(<<~RUBY)
35
- class Foo
36
- @@bar ||= 1
37
- end
38
- RUBY
39
-
40
- assert_entry("@@bar", Entry::ClassVariable, "/fake/path/foo.rb:1-2:1-7")
41
- end
42
-
43
- def test_class_variable_target_node
44
- index(<<~RUBY)
45
- class Foo
46
- @@foo, @@bar = 1
47
- end
48
- RUBY
49
-
50
- assert_entry("@@foo", Entry::ClassVariable, "/fake/path/foo.rb:1-2:1-7")
51
- assert_entry("@@bar", Entry::ClassVariable, "/fake/path/foo.rb:1-9:1-14")
52
-
53
- entry = @index["@@foo"]&.first #: as Entry::ClassVariable
54
- owner = entry.owner #: as !nil
55
- assert_instance_of(Entry::Class, owner)
56
- assert_equal("Foo", owner.name)
57
-
58
- entry = @index["@@bar"]&.first #: as Entry::ClassVariable
59
- owner = entry.owner #: as !nil
60
- assert_instance_of(Entry::Class, owner)
61
- assert_equal("Foo", owner.name)
62
- end
63
-
64
- def test_class_variable_write
65
- index(<<~RUBY)
66
- class Foo
67
- @@bar = 1
68
- end
69
- RUBY
70
-
71
- assert_entry("@@bar", Entry::ClassVariable, "/fake/path/foo.rb:1-2:1-7")
72
- end
73
-
74
- def test_empty_name_class_variable
75
- index(<<~RUBY)
76
- module Foo
77
- @@ = 1
78
- end
79
- RUBY
80
-
81
- refute_entry("@@")
82
- end
83
-
84
- def test_top_level_class_variable
85
- index(<<~RUBY)
86
- @@foo = 123
87
- RUBY
88
-
89
- entry = @index["@@foo"]&.first #: as Entry::ClassVariable
90
- assert_nil(entry.owner)
91
- end
92
-
93
- def test_class_variable_inside_self_method
94
- index(<<~RUBY)
95
- class Foo
96
- def self.bar
97
- @@bar = 123
98
- end
99
- end
100
- RUBY
101
-
102
- entry = @index["@@bar"]&.first #: as Entry::ClassVariable
103
- owner = entry.owner #: as !nil
104
- assert_instance_of(Entry::Class, owner)
105
- assert_equal("Foo", owner.name)
106
- end
107
-
108
- def test_class_variable_inside_singleton_class
109
- index(<<~RUBY)
110
- class Foo
111
- class << self
112
- @@bar = 123
113
- end
114
- end
115
- RUBY
116
-
117
- entry = @index["@@bar"]&.first #: as Entry::ClassVariable
118
- owner = entry.owner #: as !nil
119
- assert_instance_of(Entry::Class, owner)
120
- assert_equal("Foo", owner.name)
121
- end
122
-
123
- def test_class_variable_in_singleton_class_method
124
- index(<<~RUBY)
125
- class Foo
126
- class << self
127
- def self.bar
128
- @@bar = 123
129
- end
130
- end
131
- end
132
- RUBY
133
-
134
- entry = @index["@@bar"]&.first #: as Entry::ClassVariable
135
- owner = entry.owner #: as !nil
136
- assert_instance_of(Entry::Class, owner)
137
- assert_equal("Foo", owner.name)
138
- end
139
- end
140
- end