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
@@ -8,7 +8,6 @@ rescue LoadError
8
8
  end
9
9
 
10
10
  require_relative "lsp_reporter"
11
- require "ruby_indexer/lib/ruby_indexer/uri"
12
11
 
13
12
  module RubyLsp
14
13
  # An override of the default progress reporter in Minitest to add color to the output
@@ -31,6 +30,30 @@ module RubyLsp
31
30
  end
32
31
  end
33
32
 
33
+ # This patch is here to prevent other gems from overriding or adding more Minitest reporters. Otherwise, they may
34
+ # break the integration between the server and extension
35
+ module PreventReporterOverridePatch
36
+ @lsp_reporters = [] #: Array[Minitest::AbstractReporter]
37
+
38
+ class << self
39
+ #: Array[Minitest::AbstractReporter]
40
+ attr_accessor :lsp_reporters
41
+ end
42
+
43
+ # Patch the writer to prevent replacing the entire array
44
+ #: (untyped) -> void
45
+ def reporters=(reporters)
46
+ # Do nothing. We don't want other gems to override our reporter
47
+ end
48
+
49
+ # Patch the reader to prevent appending more reporters. This method always returns a temporary copy of the real
50
+ # reporters so that if any gem mutates it, it continues to return the original reporters
51
+ #: -> Array[untyped]
52
+ def reporters
53
+ PreventReporterOverridePatch.lsp_reporters.dup
54
+ end
55
+ end
56
+
34
57
  class MinitestReporter < Minitest::AbstractReporter
35
58
  class << self
36
59
  #: (Hash[untyped, untyped]) -> void
@@ -46,23 +69,42 @@ module RubyLsp
46
69
 
47
70
  # Add the JSON RPC reporter
48
71
  reporters << MinitestReporter.new
72
+ PreventReporterOverridePatch.lsp_reporters = reporters
73
+ Minitest.reporter.class.prepend(PreventReporterOverridePatch)
49
74
  end
50
75
  end
51
76
 
52
- #: (singleton(Minitest::Test) test_class, String method_name) -> void
53
- def prerecord(test_class, method_name)
54
- uri, line = LspReporter.instance.uri_and_line_for(test_class.instance_method(method_name))
77
+ #: (untyped, String) -> void
78
+ def prerecord(test_class_or_wrapper, method_name)
79
+ # In frameworks like Rails, they can control the Minitest execution by wrapping the test class
80
+ # But they conform to responding to `name`, so we can use that as a guarantee
81
+ # We are interested in the test class, not the wrapper
82
+ name = test_class_or_wrapper.name
83
+
84
+ klass = begin
85
+ Object.const_get(name) # rubocop:disable Sorbet/ConstantsFromStrings
86
+ rescue NameError
87
+ # Handle Minitest specs that create classes with invalid constant names like "MySpec::when something is true"
88
+ # If we can't resolve the constant, it means we were given the actual test class object, not the wrapper
89
+ test_class_or_wrapper
90
+ end
91
+
92
+ uri, line = LspReporter.instance.uri_and_line_for(klass.instance_method(method_name))
55
93
  return unless uri
56
94
 
57
- LspReporter.instance.start_test(id: "#{test_class.name}##{method_name}", uri: uri, line: line)
95
+ id = "#{name}##{handle_spec_test_id(method_name, line)}"
96
+ LspReporter.instance.start_test(id: id, uri: uri, line: line)
58
97
  end
59
98
 
60
99
  #: (Minitest::Result result) -> void
61
100
  def record(result)
62
- id = "#{result.klass}##{result.name}"
63
- file_path, _line = result.source_location
101
+ file_path, line = result.source_location
64
102
  return unless file_path
65
103
 
104
+ zero_based_line = line ? line - 1 : nil
105
+ name = handle_spec_test_id(result.name, zero_based_line)
106
+ id = "#{result.klass}##{name}"
107
+
66
108
  uri = URI::Generic.from_path(path: File.expand_path(file_path))
67
109
 
68
110
  if result.error?
@@ -82,6 +124,11 @@ module RubyLsp
82
124
  def report
83
125
  LspReporter.instance.shutdown
84
126
  end
127
+
128
+ #: (String, Integer?) -> String
129
+ def handle_spec_test_id(method_name, line)
130
+ method_name.gsub(/(?<=test_)\d{4}(?=_)/, format("%04d", line.to_s))
131
+ end
85
132
  end
86
133
  end
87
134
 
@@ -10,7 +10,6 @@ rescue LoadError
10
10
  end
11
11
 
12
12
  require_relative "lsp_reporter"
13
- require "ruby_indexer/lib/ruby_indexer/uri"
14
13
 
15
14
  module RubyLsp
16
15
  class TestUnitReporter < Test::Unit::UI::Console::TestRunner
@@ -5,7 +5,6 @@ module RubyLsp
5
5
  # rubocop:disable RubyLsp/UseLanguageServerAliases
6
6
  Interface = LanguageServer::Protocol::Interface
7
7
  Constant = LanguageServer::Protocol::Constant
8
- Transport = LanguageServer::Protocol::Transport
9
8
  # rubocop:enable RubyLsp/UseLanguageServerAliases
10
9
 
11
10
  # Used to indicate that a request shouldn't return a response
@@ -20,6 +19,7 @@ module RubyLsp
20
19
  "Gemfile"
21
20
  end #: String
22
21
  GUESSED_TYPES_URL = "https://shopify.github.io/ruby-lsp/#guessed-types"
22
+ TEST_PATH_PATTERN = "**/{test,spec,features}/**/{*_test.rb,test_*.rb,*_spec.rb,*.feature}"
23
23
 
24
24
  # Request delegation for embedded languages is not yet standardized into the language server specification. Here we
25
25
  # use this custom error class as a way to return a signal to the client that the request should be delegated to the
@@ -31,29 +31,30 @@ module RubyLsp
31
31
  CODE = -32900
32
32
  end
33
33
 
34
+ class AbstractMethodInvokedError < StandardError; end
35
+
34
36
  BUNDLE_COMPOSE_FAILED_CODE = -33000
35
37
 
36
38
  # A notification to be sent to the client
39
+ # @abstract
37
40
  class Message
38
- extend T::Sig
39
- extend T::Helpers
40
-
41
41
  #: String
42
42
  attr_reader :method
43
43
 
44
44
  #: Object
45
45
  attr_reader :params
46
46
 
47
- abstract!
48
-
49
47
  #: (method: String, params: Object) -> void
50
48
  def initialize(method:, params:)
51
49
  @method = method
52
50
  @params = params
53
51
  end
54
52
 
55
- sig { abstract.returns(T::Hash[Symbol, T.untyped]) }
56
- def to_hash; end
53
+ # @abstract
54
+ #: -> Hash[Symbol, untyped]
55
+ def to_hash
56
+ raise AbstractMethodInvokedError
57
+ end
57
58
  end
58
59
 
59
60
  class Notification < Message
@@ -246,9 +247,6 @@ module RubyLsp
246
247
 
247
248
  # A request configuration, to turn on/off features
248
249
  class RequestConfig
249
- #: Hash[Symbol, bool]
250
- attr_accessor :configuration
251
-
252
250
  #: (Hash[Symbol, bool] configuration) -> void
253
251
  def initialize(configuration)
254
252
  @configuration = configuration
@@ -258,6 +256,11 @@ module RubyLsp
258
256
  def enabled?(feature)
259
257
  @configuration[:enableAll] || @configuration[feature]
260
258
  end
259
+
260
+ #: (Hash[Symbol, bool]) -> void
261
+ def merge!(hash)
262
+ @configuration.merge!(hash)
263
+ end
261
264
  end
262
265
 
263
266
  class SorbetLevel
@@ -302,4 +305,37 @@ module RubyLsp
302
305
  #: -> bool
303
306
  def true_or_higher? = @level == :true || @level == :strict
304
307
  end
308
+
309
+ # Reads JSON RPC messages from the given IO in a loop
310
+ class MessageReader
311
+ #: (IO) -> void
312
+ def initialize(io)
313
+ @io = io
314
+ end
315
+
316
+ #: () { (Hash[Symbol, untyped]) -> void } -> void
317
+ def each_message(&block)
318
+ while (headers = @io.gets("\r\n\r\n"))
319
+ raw_message = @io.read(headers[/Content-Length: (\d+)/i, 1].to_i) #: as !nil
320
+ block.call(JSON.parse(raw_message, symbolize_names: true))
321
+ end
322
+ end
323
+ end
324
+
325
+ # Writes JSON RPC messages to the given IO
326
+ class MessageWriter
327
+ #: (IO) -> void
328
+ def initialize(io)
329
+ @io = io
330
+ end
331
+
332
+ #: (Hash[Symbol, untyped]) -> void
333
+ def write(message)
334
+ message[:jsonrpc] = "2.0"
335
+ json_message = message.to_json
336
+
337
+ @io.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")
338
+ @io.flush
339
+ end
340
+ end
305
341
  end
@@ -0,0 +1,103 @@
1
+ # Break
2
+
3
+ In Ruby, the `break` keyword is used to exit a loop or block prematurely. Unlike `next` which skips to the next iteration, `break` terminates the loop entirely and continues with the code after the loop.
4
+
5
+ ```ruby
6
+ # Basic break usage in a loop
7
+ 5.times do |i|
8
+ break if i == 3
9
+
10
+ puts i
11
+ end
12
+ # Output:
13
+ # 0
14
+ # 1
15
+ # 2
16
+ ```
17
+
18
+ The `break` statement can be used with any of Ruby's iteration methods or loops.
19
+
20
+ ```ruby
21
+ array = [1, 2, 3, 4, 5]
22
+
23
+ # Break in each iteration
24
+ array.each do |num|
25
+ break if num > 3
26
+
27
+ puts "Number: #{num}"
28
+ end
29
+ # Output:
30
+ # Number: 1
31
+ # Number: 2
32
+ # Number: 3
33
+
34
+ # Break in an infinite loop
35
+ count = 0
36
+ loop do
37
+ count += 1
38
+ break if count >= 3
39
+
40
+ puts "Count: #{count}"
41
+ end
42
+ # Output:
43
+ # Count: 1
44
+ # Count: 2
45
+ ```
46
+
47
+ ## Break with a Value
48
+
49
+ When used inside a block, `break` can return a value that becomes the result of the method call.
50
+
51
+ ```ruby
52
+ # Break with a return value in map
53
+ result = [1, 2, 3, 4, 5].map do |num|
54
+ break "Too large!" if num > 3
55
+
56
+ num * 2
57
+ end
58
+ puts result # Output: "Too large!"
59
+
60
+ # Break with a value in find
61
+ number = (1..10).find do |n|
62
+ break n if n > 5 && n.even?
63
+ end
64
+ puts number # Output: 6
65
+ ```
66
+
67
+ ## Break in Nested Loops
68
+
69
+ When using `break` in nested loops, it only exits the innermost loop. To break from nested loops, you typically need to use a flag or return.
70
+
71
+ ```ruby
72
+ # Break in nested iteration
73
+ (1..3).each do |i|
74
+ puts "Outer: #{i}"
75
+
76
+ (1..3).each do |j|
77
+ break if j == 2
78
+
79
+ puts " Inner: #{j}"
80
+ end
81
+ end
82
+ # Output:
83
+ # Outer: 1
84
+ # Inner: 1
85
+ # Outer: 2
86
+ # Inner: 1
87
+ # Outer: 3
88
+ # Inner: 1
89
+
90
+ # Breaking from nested loops with a flag
91
+ found = false
92
+ (1..3).each do |i|
93
+ (1..3).each do |j|
94
+ if i * j == 4
95
+ found = true
96
+ break
97
+ end
98
+ end
99
+ break if found
100
+ end
101
+ ```
102
+
103
+ The `break` keyword is essential for controlling loop execution and implementing early exit conditions. It's particularly useful when you've found what you're looking for and don't need to continue iterating.
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.23.20
4
+ version: 0.26.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
@@ -52,7 +52,7 @@ dependencies:
52
52
  version: '3'
53
53
  - - "<"
54
54
  - !ruby/object:Gem::Version
55
- version: '4'
55
+ version: '5'
56
56
  type: :runtime
57
57
  prerelease: false
58
58
  version_requirements: !ruby/object:Gem::Requirement
@@ -62,21 +62,7 @@ dependencies:
62
62
  version: '3'
63
63
  - - "<"
64
64
  - !ruby/object:Gem::Version
65
- version: '4'
66
- - !ruby/object:Gem::Dependency
67
- name: sorbet-runtime
68
- requirement: !ruby/object:Gem::Requirement
69
- requirements:
70
- - - ">="
71
- - !ruby/object:Gem::Version
72
- version: 0.5.10782
73
- type: :runtime
74
- prerelease: false
75
- version_requirements: !ruby/object:Gem::Requirement
76
- requirements:
77
- - - ">="
78
- - !ruby/object:Gem::Version
79
- version: 0.5.10782
65
+ version: '5'
80
66
  description: An opinionated language server for Ruby
81
67
  email:
82
68
  - ruby@shopify.com
@@ -84,6 +70,7 @@ executables:
84
70
  - ruby-lsp
85
71
  - ruby-lsp-check
86
72
  - ruby-lsp-launcher
73
+ - ruby-lsp-test-exec
87
74
  extensions: []
88
75
  extra_rdoc_files: []
89
76
  files:
@@ -93,6 +80,7 @@ files:
93
80
  - exe/ruby-lsp
94
81
  - exe/ruby-lsp-check
95
82
  - exe/ruby-lsp-launcher
83
+ - exe/ruby-lsp-test-exec
96
84
  - lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb
97
85
  - lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb
98
86
  - lib/ruby-lsp.rb
@@ -143,7 +131,6 @@ files:
143
131
  - lib/ruby_lsp/listeners/spec_style.rb
144
132
  - lib/ruby_lsp/listeners/test_discovery.rb
145
133
  - lib/ruby_lsp/listeners/test_style.rb
146
- - lib/ruby_lsp/load_sorbet.rb
147
134
  - lib/ruby_lsp/node_context.rb
148
135
  - lib/ruby_lsp/rbs_document.rb
149
136
  - lib/ruby_lsp/requests/code_action_resolve.rb
@@ -207,6 +194,7 @@ files:
207
194
  - lib/ruby_lsp/test_reporters/test_unit_reporter.rb
208
195
  - lib/ruby_lsp/type_inferrer.rb
209
196
  - lib/ruby_lsp/utils.rb
197
+ - static_docs/break.md
210
198
  - static_docs/yield.md
211
199
  homepage: https://github.com/Shopify/ruby-lsp
212
200
  licenses:
@@ -228,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
228
216
  - !ruby/object:Gem::Version
229
217
  version: '0'
230
218
  requirements: []
231
- rubygems_version: 3.6.8
219
+ rubygems_version: 3.6.9
232
220
  specification_version: 4
233
221
  summary: An opinionated language server for Ruby
234
222
  test_files: []
@@ -1,62 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- require "sorbet-runtime"
5
-
6
- begin
7
- T::Configuration.default_checked_level = :never
8
- # Suppresses call validation errors
9
- T::Configuration.call_validation_error_handler = ->(*arg) {}
10
- # Suppresses errors caused by T.cast, T.let, T.must, etc.
11
- T::Configuration.inline_type_error_handler = ->(*arg) {}
12
- # Suppresses errors caused by incorrect parameter ordering
13
- T::Configuration.sig_validation_error_handler = ->(*arg) {}
14
- rescue
15
- # Need this rescue so that if another gem has
16
- # already set the checked level by the time we
17
- # get to it, we don't fail outright.
18
- nil
19
- end
20
-
21
- module RubyLsp
22
- # No-op all inline type assertions defined in T
23
- module InlineTypeAssertions
24
- def absurd(value)
25
- value
26
- end
27
-
28
- def any(type_a, type_b, *types)
29
- T::Types::Union.new([type_a, type_b, *types])
30
- end
31
-
32
- def assert_type!(value, type, checked: true)
33
- value
34
- end
35
-
36
- def bind(value, type, checked: true)
37
- value
38
- end
39
-
40
- def cast(value, type, checked: true)
41
- value
42
- end
43
-
44
- def let(value, type, checked: true)
45
- value
46
- end
47
-
48
- def must(arg)
49
- arg
50
- end
51
-
52
- def nilable(type)
53
- T::Types::Union.new([type, T::Utils::Nilable::NIL_TYPE])
54
- end
55
-
56
- def unsafe(value)
57
- value
58
- end
59
-
60
- T.singleton_class.prepend(self)
61
- end
62
- end