ruby-lsp 0.23.11 → 0.23.16

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 (112) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/VERSION +1 -1
  4. data/exe/ruby-lsp-launcher +20 -11
  5. data/lib/rubocop/cop/ruby_lsp/use_language_server_aliases.rb +1 -1
  6. data/lib/rubocop/cop/ruby_lsp/use_register_with_handler_method.rb +3 -5
  7. data/lib/ruby_indexer/lib/ruby_indexer/configuration.rb +82 -116
  8. data/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +123 -169
  9. data/lib/ruby_indexer/lib/ruby_indexer/enhancement.rb +9 -7
  10. data/lib/ruby_indexer/lib/ruby_indexer/entry.rb +92 -202
  11. data/lib/ruby_indexer/lib/ruby_indexer/index.rb +116 -222
  12. data/lib/ruby_indexer/lib/ruby_indexer/location.rb +4 -27
  13. data/lib/ruby_indexer/lib/ruby_indexer/prefix_tree.rb +18 -19
  14. data/lib/ruby_indexer/lib/ruby_indexer/rbs_indexer.rb +22 -45
  15. data/lib/ruby_indexer/lib/ruby_indexer/reference_finder.rb +47 -61
  16. data/lib/ruby_indexer/lib/ruby_indexer/uri.rb +17 -19
  17. data/lib/ruby_indexer/lib/ruby_indexer/visibility_scope.rb +5 -9
  18. data/lib/ruby_indexer/test/class_variables_test.rb +14 -14
  19. data/lib/ruby_indexer/test/classes_and_modules_test.rb +65 -40
  20. data/lib/ruby_indexer/test/configuration_test.rb +48 -7
  21. data/lib/ruby_indexer/test/constant_test.rb +34 -34
  22. data/lib/ruby_indexer/test/enhancements_test.rb +1 -1
  23. data/lib/ruby_indexer/test/index_test.rb +139 -135
  24. data/lib/ruby_indexer/test/instance_variables_test.rb +37 -37
  25. data/lib/ruby_indexer/test/method_test.rb +143 -117
  26. data/lib/ruby_indexer/test/prefix_tree_test.rb +13 -13
  27. data/lib/ruby_indexer/test/rbs_indexer_test.rb +65 -71
  28. data/lib/ruby_indexer/test/test_case.rb +2 -2
  29. data/lib/ruby_indexer/test/uri_test.rb +15 -2
  30. data/lib/ruby_lsp/addon.rb +44 -71
  31. data/lib/ruby_lsp/base_server.rb +29 -32
  32. data/lib/ruby_lsp/client_capabilities.rb +10 -12
  33. data/lib/ruby_lsp/document.rb +39 -45
  34. data/lib/ruby_lsp/erb_document.rb +36 -40
  35. data/lib/ruby_lsp/global_state.rb +52 -57
  36. data/lib/ruby_lsp/internal.rb +2 -0
  37. data/lib/ruby_lsp/listeners/code_lens.rb +82 -89
  38. data/lib/ruby_lsp/listeners/completion.rb +60 -66
  39. data/lib/ruby_lsp/listeners/definition.rb +38 -52
  40. data/lib/ruby_lsp/listeners/document_highlight.rb +123 -150
  41. data/lib/ruby_lsp/listeners/document_link.rb +46 -63
  42. data/lib/ruby_lsp/listeners/document_symbol.rb +38 -52
  43. data/lib/ruby_lsp/listeners/folding_ranges.rb +40 -43
  44. data/lib/ruby_lsp/listeners/hover.rb +83 -102
  45. data/lib/ruby_lsp/listeners/inlay_hints.rb +4 -11
  46. data/lib/ruby_lsp/listeners/semantic_highlighting.rb +54 -56
  47. data/lib/ruby_lsp/listeners/signature_help.rb +11 -26
  48. data/lib/ruby_lsp/listeners/spec_style.rb +155 -0
  49. data/lib/ruby_lsp/listeners/test_discovery.rb +89 -0
  50. data/lib/ruby_lsp/listeners/test_style.rb +160 -88
  51. data/lib/ruby_lsp/node_context.rb +12 -39
  52. data/lib/ruby_lsp/rbs_document.rb +8 -6
  53. data/lib/ruby_lsp/requests/code_action_resolve.rb +24 -25
  54. data/lib/ruby_lsp/requests/code_actions.rb +14 -26
  55. data/lib/ruby_lsp/requests/code_lens.rb +6 -17
  56. data/lib/ruby_lsp/requests/completion.rb +7 -20
  57. data/lib/ruby_lsp/requests/completion_resolve.rb +6 -6
  58. data/lib/ruby_lsp/requests/definition.rb +8 -17
  59. data/lib/ruby_lsp/requests/diagnostics.rb +8 -11
  60. data/lib/ruby_lsp/requests/discover_tests.rb +18 -5
  61. data/lib/ruby_lsp/requests/document_highlight.rb +5 -15
  62. data/lib/ruby_lsp/requests/document_link.rb +6 -17
  63. data/lib/ruby_lsp/requests/document_symbol.rb +5 -8
  64. data/lib/ruby_lsp/requests/folding_ranges.rb +7 -15
  65. data/lib/ruby_lsp/requests/formatting.rb +6 -9
  66. data/lib/ruby_lsp/requests/go_to_relevant_file.rb +87 -0
  67. data/lib/ruby_lsp/requests/hover.rb +10 -20
  68. data/lib/ruby_lsp/requests/inlay_hints.rb +6 -17
  69. data/lib/ruby_lsp/requests/on_type_formatting.rb +32 -40
  70. data/lib/ruby_lsp/requests/prepare_rename.rb +4 -9
  71. data/lib/ruby_lsp/requests/prepare_type_hierarchy.rb +5 -15
  72. data/lib/ruby_lsp/requests/range_formatting.rb +5 -6
  73. data/lib/ruby_lsp/requests/references.rb +8 -37
  74. data/lib/ruby_lsp/requests/rename.rb +19 -42
  75. data/lib/ruby_lsp/requests/request.rb +7 -19
  76. data/lib/ruby_lsp/requests/selection_ranges.rb +6 -6
  77. data/lib/ruby_lsp/requests/semantic_highlighting.rb +16 -35
  78. data/lib/ruby_lsp/requests/show_syntax_tree.rb +7 -8
  79. data/lib/ruby_lsp/requests/signature_help.rb +8 -26
  80. data/lib/ruby_lsp/requests/support/annotation.rb +4 -10
  81. data/lib/ruby_lsp/requests/support/common.rb +16 -51
  82. data/lib/ruby_lsp/requests/support/rubocop_diagnostic.rb +27 -35
  83. data/lib/ruby_lsp/requests/support/rubocop_formatter.rb +11 -14
  84. data/lib/ruby_lsp/requests/support/rubocop_runner.rb +22 -34
  85. data/lib/ruby_lsp/requests/support/selection_range.rb +1 -3
  86. data/lib/ruby_lsp/requests/support/sorbet.rb +29 -38
  87. data/lib/ruby_lsp/requests/support/source_uri.rb +20 -32
  88. data/lib/ruby_lsp/requests/support/syntax_tree_formatter.rb +12 -19
  89. data/lib/ruby_lsp/requests/support/test_item.rb +10 -14
  90. data/lib/ruby_lsp/requests/type_hierarchy_supertypes.rb +5 -6
  91. data/lib/ruby_lsp/requests/workspace_symbol.rb +4 -4
  92. data/lib/ruby_lsp/response_builders/collection_response_builder.rb +5 -5
  93. data/lib/ruby_lsp/response_builders/document_symbol.rb +13 -18
  94. data/lib/ruby_lsp/response_builders/hover.rb +11 -14
  95. data/lib/ruby_lsp/response_builders/response_builder.rb +1 -1
  96. data/lib/ruby_lsp/response_builders/semantic_highlighting.rb +60 -88
  97. data/lib/ruby_lsp/response_builders/signature_help.rb +5 -6
  98. data/lib/ruby_lsp/response_builders/test_collection.rb +6 -10
  99. data/lib/ruby_lsp/ruby_document.rb +24 -62
  100. data/lib/ruby_lsp/scope.rb +7 -11
  101. data/lib/ruby_lsp/scripts/compose_bundle.rb +6 -4
  102. data/lib/ruby_lsp/server.rb +147 -79
  103. data/lib/ruby_lsp/setup_bundler.rb +65 -60
  104. data/lib/ruby_lsp/static_docs.rb +11 -7
  105. data/lib/ruby_lsp/store.rb +24 -42
  106. data/lib/ruby_lsp/test_helper.rb +2 -12
  107. data/lib/ruby_lsp/test_reporters/lsp_reporter.rb +164 -0
  108. data/lib/ruby_lsp/test_reporters/minitest_reporter.rb +105 -0
  109. data/lib/ruby_lsp/test_reporters/test_unit_reporter.rb +94 -0
  110. data/lib/ruby_lsp/type_inferrer.rb +13 -14
  111. data/lib/ruby_lsp/utils.rb +49 -83
  112. metadata +9 -3
@@ -6,16 +6,16 @@ require_relative "test_case"
6
6
  module RubyIndexer
7
7
  class RBSIndexerTest < TestCase
8
8
  def test_index_core_classes
9
- entries = @index["Array"]
9
+ entries = @index["Array"] #: as !nil
10
10
  refute_nil(entries)
11
11
  # Array is a class but also an instance method on Kernel
12
12
  assert_equal(2, entries.length)
13
- entry = entries.find { |entry| entry.is_a?(RubyIndexer::Entry::Class) }
13
+ entry = entries.find { |entry| entry.is_a?(Entry::Class) } #: as Entry::Class
14
14
  assert_match(%r{/gems/rbs-.*/core/array.rbs}, entry.file_path)
15
15
  assert_equal("array.rbs", entry.file_name)
16
16
  assert_equal("Object", entry.parent_class)
17
17
  assert_equal(1, entry.mixin_operations.length)
18
- enumerable_include = entry.mixin_operations.first
18
+ enumerable_include = entry.mixin_operations.first #: as !nil
19
19
  assert_equal("Enumerable", enumerable_include.module_name)
20
20
 
21
21
  # Using fixed positions would be fragile, so let's just check some basics.
@@ -26,10 +26,10 @@ module RubyIndexer
26
26
  end
27
27
 
28
28
  def test_index_core_modules
29
- entries = @index["Kernel"]
29
+ entries = @index["Kernel"] #: as !nil
30
30
  refute_nil(entries)
31
31
  assert_equal(1, entries.length)
32
- entry = entries.first
32
+ entry = entries.first #: as Entry::Module
33
33
  assert_match(%r{/gems/rbs-.*/core/kernel.rbs}, entry.file_path)
34
34
  assert_equal("kernel.rbs", entry.file_name)
35
35
 
@@ -41,30 +41,23 @@ module RubyIndexer
41
41
  end
42
42
 
43
43
  def test_index_core_constants
44
- entries = @index["RUBY_VERSION"]
44
+ entries = @index["RUBY_VERSION"] #: as !nil
45
45
  refute_nil(entries)
46
46
  assert_equal(1, entries.length)
47
47
 
48
- # Complex::I is defined as `Complex::I = ...`
49
- entries = @index["Complex::I"]
48
+ entries = @index["Complex::I"] #: as !nil
50
49
  refute_nil(entries)
51
50
  assert_equal(1, entries.length)
52
51
 
53
- # Encoding::US_ASCII is defined as
54
- # ```
55
- # module Encoding
56
- # US_ASCII = ...
57
- # ...
58
- # ````
59
- entries = @index["Encoding::US_ASCII"]
52
+ entries = @index["Encoding::US_ASCII"] #: as !nil
60
53
  refute_nil(entries)
61
54
  assert_equal(1, entries.length)
62
55
  end
63
56
 
64
57
  def test_index_methods
65
- entries = @index["initialize"]
58
+ entries = @index["initialize"] #: as Array[Entry::Method]
66
59
  refute_nil(entries)
67
- entry = entries.find { |entry| entry.owner.name == "Array" }
60
+ entry = entries.find { |entry| entry.owner&.name == "Array" } #: as Entry::Method
68
61
  assert_match(%r{/gems/rbs-.*/core/array.rbs}, entry.file_path)
69
62
  assert_equal("array.rbs", entry.file_name)
70
63
  assert_equal(Entry::Visibility::PUBLIC, entry.visibility)
@@ -77,11 +70,11 @@ module RubyIndexer
77
70
  end
78
71
 
79
72
  def test_index_global_declaration
80
- entries = @index["$DEBUG"]
73
+ entries = @index["$DEBUG"] #: as Array[Entry::GlobalVariable]
81
74
  refute_nil(entries)
82
75
  assert_equal(1, entries.length)
83
76
 
84
- entry = entries.first
77
+ entry = entries.first #: as Entry::GlobalVariable
85
78
 
86
79
  assert_instance_of(Entry::GlobalVariable, entry)
87
80
  assert_equal("$DEBUG", entry.name)
@@ -91,10 +84,10 @@ module RubyIndexer
91
84
  end
92
85
 
93
86
  def test_attaches_correct_owner_to_singleton_methods
94
- entries = @index["basename"]
87
+ entries = @index["basename"] #: as Array[Entry::Method]
95
88
  refute_nil(entries)
96
89
 
97
- owner = entries.first.owner
90
+ owner = entries.first&.owner #: as Entry::SingletonClass
98
91
  assert_instance_of(Entry::SingletonClass, owner)
99
92
  assert_equal("File::<Class:File>", owner.name)
100
93
  end
@@ -103,47 +96,47 @@ module RubyIndexer
103
96
  # NOTE: RBS does not store the name location for classes, modules or methods. This behavior is not exactly what
104
97
  # we would like, but for now we assign the same location to both
105
98
 
106
- entries = @index["Array"]
99
+ entries = @index["Array"] #: as Array[Entry::Class]
107
100
  refute_nil(entries)
108
- entry = entries.find { |entry| entry.is_a?(Entry::Class) }
101
+ entry = entries.find { |entry| entry.is_a?(Entry::Class) } #: as Entry::Class
109
102
 
110
103
  assert_same(entry.location, entry.name_location)
111
104
  end
112
105
 
113
106
  def test_rbs_method_with_required_positionals
114
- entries = @index["crypt"]
107
+ entries = @index["crypt"] #: as Array[Entry::Method]
115
108
  assert_equal(1, entries.length)
116
109
 
117
- entry = entries.first
110
+ entry = entries.first #: as Entry::Method
118
111
  signatures = entry.signatures
119
112
  assert_equal(1, signatures.length)
120
113
 
121
- first_signature = signatures.first
114
+ first_signature = signatures.first #: as Entry::Signature
122
115
 
123
116
  # (::string salt_str) -> ::String
124
117
 
125
118
  assert_equal(1, first_signature.parameters.length)
126
119
  assert_kind_of(Entry::RequiredParameter, first_signature.parameters[0])
127
- assert_equal(:salt_str, first_signature.parameters[0].name)
120
+ assert_equal(:salt_str, first_signature.parameters[0]&.name)
128
121
  end
129
122
 
130
123
  def test_rbs_method_with_unnamed_required_positionals
131
- entries = @index["try_convert"]
132
- entry = entries.find { |entry| entry.owner.name == "Array::<Class:Array>" }
124
+ entries = @index["try_convert"] #: as Array[Entry::Method]
125
+ entry = entries.find { |entry| entry.owner&.name == "Array::<Class:Array>" } #: as Entry::Method
133
126
 
134
- parameters = entry.signatures[0].parameters
127
+ parameters = entry.signatures[0]&.parameters #: as Array[Entry::Parameter]
135
128
 
136
129
  assert_equal([:arg0], parameters.map(&:name))
137
130
  assert_kind_of(Entry::RequiredParameter, parameters[0])
138
131
  end
139
132
 
140
133
  def test_rbs_method_with_optional_positionals
141
- entries = @index["polar"]
142
- entry = entries.find { |entry| entry.owner.name == "Complex::<Class:Complex>" }
134
+ entries = @index["polar"] #: as Array[Entry::Method]
135
+ entry = entries.find { |entry| entry.owner&.name == "Complex::<Class:Complex>" } #: as Entry::Method
143
136
 
144
137
  # def self.polar: (Numeric, ?Numeric) -> Complex
145
138
 
146
- parameters = entry.signatures[0].parameters
139
+ parameters = entry.signatures[0]&.parameters #: as Array[Entry::Parameter]
147
140
 
148
141
  assert_equal([:arg0, :arg1], parameters.map(&:name))
149
142
  assert_kind_of(Entry::RequiredParameter, parameters[0])
@@ -151,27 +144,27 @@ module RubyIndexer
151
144
  end
152
145
 
153
146
  def test_rbs_method_with_optional_parameter
154
- entries = @index["chomp"]
147
+ entries = @index["chomp"] #: as Array[Entry::Method]
155
148
  assert_equal(1, entries.length)
156
149
 
157
- entry = entries.first
150
+ entry = entries.first #: as Entry::Method
158
151
  signatures = entry.signatures
159
152
  assert_equal(1, signatures.length)
160
153
 
161
- first_signature = signatures.first
154
+ first_signature = signatures.first #: as Entry::Signature
162
155
 
163
156
  # (?::string? separator) -> ::String
164
157
 
165
158
  assert_equal(1, first_signature.parameters.length)
166
159
  assert_kind_of(Entry::OptionalParameter, first_signature.parameters[0])
167
- assert_equal(:separator, first_signature.parameters[0].name)
160
+ assert_equal(:separator, first_signature.parameters[0]&.name)
168
161
  end
169
162
 
170
163
  def test_rbs_method_with_required_and_optional_parameters
171
- entries = @index["gsub"]
164
+ entries = @index["gsub"] #: as Array[Entry::Method]
172
165
  assert_equal(1, entries.length)
173
166
 
174
- entry = entries.first
167
+ entry = entries.first #: as Entry::Method
175
168
 
176
169
  signatures = entry.signatures
177
170
  assert_equal(3, signatures.length)
@@ -180,37 +173,37 @@ module RubyIndexer
180
173
  # | (::Regexp | ::string pattern) -> ::Enumerator[::String, ::String]
181
174
  # | (::Regexp | ::string pattern) { (::String match) -> ::_ToS } -> ::String
182
175
 
183
- parameters = signatures[0].parameters
176
+ parameters = signatures[0]&.parameters #: as !nil
184
177
  assert_equal([:pattern, :replacement], parameters.map(&:name))
185
178
  assert_kind_of(Entry::RequiredParameter, parameters[0])
186
179
  assert_kind_of(Entry::RequiredParameter, parameters[1])
187
180
 
188
- parameters = signatures[1].parameters
181
+ parameters = signatures[1]&.parameters #: as !nil
189
182
  assert_equal([:pattern], parameters.map(&:name))
190
183
  assert_kind_of(Entry::RequiredParameter, parameters[0])
191
184
 
192
- parameters = signatures[2].parameters
185
+ parameters = signatures[2]&.parameters #: as !nil
193
186
  assert_equal([:pattern, :"<anonymous block>"], parameters.map(&:name))
194
187
  assert_kind_of(Entry::RequiredParameter, parameters[0])
195
188
  assert_kind_of(Entry::BlockParameter, parameters[1])
196
189
  end
197
190
 
198
191
  def test_rbs_anonymous_block_parameter
199
- entries = @index["open"]
200
- entry = entries.find { |entry| entry.owner.name == "File::<Class:File>" }
192
+ entries = @index["open"] #: as Array[Entry::Method]
193
+ entry = entries.find { |entry| entry.owner&.name == "File::<Class:File>" } #: as Entry::Method
201
194
 
202
195
  assert_equal(2, entry.signatures.length)
203
196
 
204
197
  # (::String name, ?::String mode, ?::Integer perm) -> ::IO?
205
198
  # | [T] (::String name, ?::String mode, ?::Integer perm) { (::IO) -> T } -> T
206
199
 
207
- parameters = entry.signatures[0].parameters
200
+ parameters = entry.signatures[0]&.parameters #: as !nil
208
201
  assert_equal([:file_name, :mode, :perm], parameters.map(&:name))
209
202
  assert_kind_of(Entry::RequiredParameter, parameters[0])
210
203
  assert_kind_of(Entry::OptionalParameter, parameters[1])
211
204
  assert_kind_of(Entry::OptionalParameter, parameters[2])
212
205
 
213
- parameters = entry.signatures[1].parameters
206
+ parameters = entry.signatures[1]&.parameters #: as !nil
214
207
  assert_equal([:file_name, :mode, :perm, :"<anonymous block>"], parameters.map(&:name))
215
208
  assert_kind_of(Entry::RequiredParameter, parameters[0])
216
209
  assert_kind_of(Entry::OptionalParameter, parameters[1])
@@ -219,10 +212,10 @@ module RubyIndexer
219
212
  end
220
213
 
221
214
  def test_rbs_method_with_rest_positionals
222
- entries = @index["count"]
223
- entry = entries.find { |entry| entry.owner.name == "String" }
215
+ entries = @index["count"] #: as Array[Entry::Method]
216
+ entry = entries.find { |entry| entry.owner&.name == "String" } #: as Entry::Method
224
217
 
225
- parameters = entry.signatures.first.parameters
218
+ parameters = entry.signatures.first&.parameters #: as !nil
226
219
  assert_equal(1, entry.signatures.length)
227
220
 
228
221
  # (::String::selector selector_0, *::String::selector more_selectors) -> ::Integer
@@ -233,8 +226,8 @@ module RubyIndexer
233
226
  end
234
227
 
235
228
  def test_rbs_method_with_trailing_positionals
236
- entries = @index["select"] # https://ruby-doc.org/3.3.3/IO.html#method-c-select
237
- entry = entries.find { |entry| entry.owner.name == "IO::<Class:IO>" }
229
+ entries = @index["select"] #: as Array[Entry::Method]
230
+ entry = entries.find { |entry| entry.owner&.name == "IO::<Class:IO>" } #: as !nil
238
231
 
239
232
  signatures = entry.signatures
240
233
  assert_equal(2, signatures.length)
@@ -242,13 +235,13 @@ module RubyIndexer
242
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
243
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
244
237
 
245
- parameters = signatures[0].parameters
238
+ parameters = signatures[0]&.parameters #: as !nil
246
239
  assert_equal([:read_array, :write_array, :error_array], parameters.map(&:name))
247
240
  assert_kind_of(Entry::RequiredParameter, parameters[0])
248
241
  assert_kind_of(Entry::OptionalParameter, parameters[1])
249
242
  assert_kind_of(Entry::OptionalParameter, parameters[2])
250
243
 
251
- parameters = signatures[1].parameters
244
+ parameters = signatures[1]&.parameters #: as !nil
252
245
  assert_equal([:read_array, :write_array, :error_array, :timeout], parameters.map(&:name))
253
246
  assert_kind_of(Entry::RequiredParameter, parameters[0])
254
247
  assert_kind_of(Entry::OptionalParameter, parameters[1])
@@ -257,8 +250,8 @@ module RubyIndexer
257
250
  end
258
251
 
259
252
  def test_rbs_method_with_optional_keywords
260
- entries = @index["step"]
261
- entry = entries.find { |entry| entry.owner.name == "Numeric" }
253
+ entries = @index["step"] #: as Array[Entry::Method]
254
+ entry = entries.find { |entry| entry.owner&.name == "Numeric" } #: as !nil
262
255
 
263
256
  signatures = entry.signatures
264
257
  assert_equal(4, signatures.length)
@@ -268,24 +261,24 @@ module RubyIndexer
268
261
  # | (?by: ::Numeric, ?to: ::Numeric) { (::Numeric) -> void } -> self
269
262
  # | (?by: ::Numeric, ?to: ::Numeric) -> ::Enumerator[::Numeric, self]
270
263
 
271
- parameters = signatures[0].parameters
264
+ parameters = signatures[0]&.parameters #: as !nil
272
265
  assert_equal([:limit, :step, :"<anonymous block>"], parameters.map(&:name))
273
266
  assert_kind_of(Entry::OptionalParameter, parameters[0])
274
267
  assert_kind_of(Entry::OptionalParameter, parameters[1])
275
268
  assert_kind_of(Entry::BlockParameter, parameters[2])
276
269
 
277
- parameters = signatures[1].parameters
270
+ parameters = signatures[1]&.parameters #: as !nil
278
271
  assert_equal([:limit, :step], parameters.map(&:name))
279
272
  assert_kind_of(Entry::OptionalParameter, parameters[0])
280
273
  assert_kind_of(Entry::OptionalParameter, parameters[1])
281
274
 
282
- parameters = signatures[2].parameters
275
+ parameters = signatures[2]&.parameters #: as !nil
283
276
  assert_equal([:by, :to, :"<anonymous block>"], parameters.map(&:name))
284
277
  assert_kind_of(Entry::OptionalKeywordParameter, parameters[0])
285
278
  assert_kind_of(Entry::OptionalKeywordParameter, parameters[1])
286
279
  assert_kind_of(Entry::BlockParameter, parameters[2])
287
280
 
288
- parameters = signatures[3].parameters
281
+ parameters = signatures[3]&.parameters #: as !nil
289
282
  assert_equal([:by, :to], parameters.map(&:name))
290
283
  assert_kind_of(Entry::OptionalKeywordParameter, parameters[0])
291
284
  assert_kind_of(Entry::OptionalKeywordParameter, parameters[1])
@@ -308,14 +301,14 @@ module RubyIndexer
308
301
  end
309
302
 
310
303
  def test_rbs_method_with_rest_keywords
311
- entries = @index["method_missing"]
312
- entry = entries.find { |entry| entry.owner.name == "BasicObject" }
304
+ entries = @index["method_missing"] #: as Array[Entry::Method]
305
+ entry = entries.find { |entry| entry.owner&.name == "BasicObject" } #: as !nil
313
306
  signatures = entry.signatures
314
307
  assert_equal(1, signatures.length)
315
308
 
316
309
  # (Symbol, *untyped, **untyped) ?{ (*untyped, **untyped) -> untyped } -> untyped
317
310
 
318
- parameters = signatures[0].parameters
311
+ parameters = signatures[0]&.parameters #: as !nil
319
312
  assert_equal([:arg0, :"<anonymous splat>", :"<anonymous keyword splat>"], parameters.map(&:name))
320
313
  assert_kind_of(Entry::RequiredParameter, parameters[0])
321
314
  assert_kind_of(Entry::RestParameter, parameters[1])
@@ -348,24 +341,24 @@ module RubyIndexer
348
341
  def test_signature_alias
349
342
  # In RBS, an alias means that two methods have the same signature.
350
343
  # It does not mean the same thing as a Ruby alias.
351
- any_entries = @index["any?"]
344
+ any_entries = @index["any?"] #: as Array[Entry::UnresolvedMethodAlias]
352
345
 
353
- assert_equal(["Array", "Enumerable", "Hash"], any_entries.map { _1.owner.name })
346
+ assert_equal(["Array", "Enumerable", "Hash"], any_entries.map { _1.owner&.name })
354
347
 
355
- entry = any_entries.find { |entry| entry.owner.name == "Array" }
348
+ entry = any_entries.find { |entry| entry.owner&.name == "Array" } #: as !nil
356
349
 
357
350
  assert_kind_of(RubyIndexer::Entry::UnresolvedMethodAlias, entry)
358
351
  assert_equal("any?", entry.name)
359
352
  assert_equal("all?", entry.old_name)
360
- assert_equal("Array", entry.owner.name)
361
- assert(entry.file_path.end_with?("core/array.rbs"))
362
- assert_includes(entry.comments, "Returns `true` if any element of `self` meets a given criterion.")
353
+ assert_equal("Array", entry.owner&.name)
354
+ assert(entry.file_path&.end_with?("core/array.rbs"))
355
+ refute_empty(entry.comments)
363
356
  end
364
357
 
365
358
  def test_indexing_untyped_functions
366
- entries = @index.resolve_method("call", "Method")
359
+ entries = @index.resolve_method("call", "Method") #: as Array[Entry::Method]
367
360
 
368
- parameters = entries.first.signatures.first.parameters
361
+ parameters = entries.first&.signatures&.first&.parameters #: as !nil
369
362
  assert_equal(1, parameters.length)
370
363
  assert_instance_of(Entry::ForwardingParameter, parameters.first)
371
364
  end
@@ -379,7 +372,8 @@ module RubyIndexer
379
372
  indexer = RubyIndexer::RBSIndexer.new(index)
380
373
  pathname = Pathname.new("/file.rbs")
381
374
  indexer.process_signature(pathname, declarations)
382
- entry = T.must(index[method_name]).first
375
+ entry = index[method_name] #: as !nil
376
+ .first
383
377
  T.cast(entry, Entry::Method).signatures
384
378
  end
385
379
  end
@@ -18,11 +18,11 @@ module RubyIndexer
18
18
  end
19
19
 
20
20
  def assert_entry(expected_name, type, expected_location, visibility: nil)
21
- entries = @index[expected_name]
21
+ entries = @index[expected_name] #: as !nil
22
22
  refute_nil(entries, "Expected #{expected_name} to be indexed")
23
23
  refute_empty(entries, "Expected #{expected_name} to be indexed")
24
24
 
25
- entry = entries.first
25
+ entry = entries.first #: as !nil
26
26
  assert_instance_of(type, entry, "Expected #{expected_name} to be a #{type}")
27
27
 
28
28
  location = entry.location
@@ -12,12 +12,12 @@ module RubyIndexer
12
12
 
13
13
  def test_from_path_on_windows
14
14
  uri = URI::Generic.from_path(path: "C:/some/windows/path/to/file.rb")
15
- assert_equal("/C:/some/windows/path/to/file.rb", uri.path)
15
+ assert_equal("/C%3A/some/windows/path/to/file.rb", uri.path)
16
16
  end
17
17
 
18
18
  def test_from_path_on_windows_with_lowercase_drive
19
19
  uri = URI::Generic.from_path(path: "c:/some/windows/path/to/file.rb")
20
- assert_equal("/c:/some/windows/path/to/file.rb", uri.path)
20
+ assert_equal("/c%3A/some/windows/path/to/file.rb", uri.path)
21
21
  end
22
22
 
23
23
  def test_to_standardized_path_on_unix
@@ -68,5 +68,18 @@ module RubyIndexer
68
68
  uri.add_require_path_from_load_entry("/some/unix/path")
69
69
  assert_equal("to/file", uri.require_path)
70
70
  end
71
+
72
+ def test_from_path_escapes_colon_characters
73
+ uri = URI::Generic.from_path(path: "c:/some/windows/path with/spaces/file.rb")
74
+ assert_equal("c:/some/windows/path with/spaces/file.rb", uri.to_standardized_path)
75
+ assert_equal("file:///c%3A/some/windows/path%20with/spaces/file.rb", uri.to_s)
76
+ end
77
+
78
+ def test_from_path_with_unicode_characters
79
+ path = "/path/with/unicode/文件.rb"
80
+ uri = URI::Generic.from_path(path: path)
81
+ assert_equal(path, uri.to_standardized_path)
82
+ assert_equal("file:///path/with/unicode/%E6%96%87%E4%BB%B6.rb", uri.to_s)
83
+ end
71
84
  end
72
85
  end
@@ -25,42 +25,34 @@ module RubyLsp
25
25
 
26
26
  abstract!
27
27
 
28
- @addons = T.let([], T::Array[Addon])
29
- @addon_classes = T.let([], T::Array[T.class_of(Addon)])
28
+ @addons = [] #: Array[Addon]
29
+ @addon_classes = [] #: Array[singleton(Addon)]
30
30
  # Add-on instances that have declared a handler to accept file watcher events
31
- @file_watcher_addons = T.let([], T::Array[Addon])
31
+ @file_watcher_addons = [] #: Array[Addon]
32
32
 
33
33
  AddonNotFoundError = Class.new(StandardError)
34
34
 
35
35
  class IncompatibleApiError < StandardError; end
36
36
 
37
37
  class << self
38
- extend T::Sig
39
-
40
- sig { returns(T::Array[Addon]) }
38
+ #: Array[Addon]
41
39
  attr_accessor :addons
42
40
 
43
- sig { returns(T::Array[Addon]) }
41
+ #: Array[Addon]
44
42
  attr_accessor :file_watcher_addons
45
43
 
46
- sig { returns(T::Array[T.class_of(Addon)]) }
44
+ #: Array[singleton(Addon)]
47
45
  attr_reader :addon_classes
48
46
 
49
47
  # Automatically track and instantiate add-on classes
50
- sig { params(child_class: T.class_of(Addon)).void }
48
+ #: (singleton(Addon) child_class) -> void
51
49
  def inherited(child_class)
52
50
  addon_classes << child_class
53
51
  super
54
52
  end
55
53
 
56
54
  # Discovers and loads all add-ons. Returns a list of errors when trying to require add-ons
57
- sig do
58
- params(
59
- global_state: GlobalState,
60
- outgoing_queue: Thread::Queue,
61
- include_project_addons: T::Boolean,
62
- ).returns(T::Array[StandardError])
63
- end
55
+ #: (GlobalState global_state, Thread::Queue outgoing_queue, ?include_project_addons: bool) -> Array[StandardError]
64
56
  def load_addons(global_state, outgoing_queue, include_project_addons: true)
65
57
  # Require all add-ons entry points, which should be placed under
66
58
  # `some_gem/lib/ruby_lsp/your_gem_name/addon.rb` or in the workspace under
@@ -98,7 +90,7 @@ module RubyLsp
98
90
  end
99
91
 
100
92
  # Unloads all add-ons. Only intended to be invoked once when shutting down the Ruby LSP server
101
- sig { void }
93
+ #: -> void
102
94
  def unload_addons
103
95
  @addons.each(&:deactivate)
104
96
  @addons.clear
@@ -112,7 +104,7 @@ module RubyLsp
112
104
  # Important: if the add-on is not found, AddonNotFoundError will be raised. If the add-on is found, but its
113
105
  # current version does not satisfy the given version constraint, then IncompatibleApiError will be raised. It is
114
106
  # the responsibility of the add-ons using this API to handle these errors appropriately.
115
- sig { params(addon_name: String, version_constraints: String).returns(Addon) }
107
+ #: (String addon_name, *String version_constraints) -> Addon
116
108
  def get(addon_name, *version_constraints)
117
109
  if version_constraints.empty?
118
110
  raise IncompatibleApiError, "Must specify version constraints when accessing other add-ons"
@@ -144,7 +136,7 @@ module RubyLsp
144
136
  # end
145
137
  # end
146
138
  # ```
147
- sig { params(version_constraints: String).void }
139
+ #: (*String version_constraints) -> void
148
140
  def depend_on_ruby_lsp!(*version_constraints)
149
141
  version_object = Gem::Version.new(RubyLsp::VERSION)
150
142
 
@@ -155,23 +147,23 @@ module RubyLsp
155
147
  end
156
148
  end
157
149
 
158
- sig { void }
150
+ #: -> void
159
151
  def initialize
160
- @errors = T.let([], T::Array[StandardError])
152
+ @errors = [] #: Array[StandardError]
161
153
  end
162
154
 
163
- sig { params(error: StandardError).returns(T.self_type) }
155
+ #: (StandardError error) -> self
164
156
  def add_error(error)
165
157
  @errors << error
166
158
  self
167
159
  end
168
160
 
169
- sig { returns(T::Boolean) }
161
+ #: -> bool
170
162
  def error?
171
163
  @errors.any?
172
164
  end
173
165
 
174
- sig { returns(String) }
166
+ #: -> String
175
167
  def formatted_errors
176
168
  <<~ERRORS
177
169
  #{name}:
@@ -179,7 +171,7 @@ module RubyLsp
179
171
  ERRORS
180
172
  end
181
173
 
182
- sig { returns(String) }
174
+ #: -> String
183
175
  def errors_details
184
176
  @errors.map(&:full_message).join("\n\n")
185
177
  end
@@ -207,69 +199,50 @@ module RubyLsp
207
199
  # original request so that the response is delegated to the correct add-on and must override this method to handle
208
200
  # the response
209
201
  # https://microsoft.github.io/language-server-protocol/specification#window_showMessageRequest
210
- sig { overridable.params(title: String).void }
202
+ # @overridable
203
+ #: (String title) -> void
211
204
  def handle_window_show_message_response(title); end
212
205
 
213
206
  # Creates a new CodeLens listener. This method is invoked on every CodeLens request
214
- sig do
215
- overridable.params(
216
- response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
217
- uri: URI::Generic,
218
- dispatcher: Prism::Dispatcher,
219
- ).void
220
- end
207
+ # @overridable
208
+ #: (ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens] response_builder, URI::Generic uri, Prism::Dispatcher dispatcher) -> void
221
209
  def create_code_lens_listener(response_builder, uri, dispatcher); end
222
210
 
223
211
  # Creates a new Hover listener. This method is invoked on every Hover request
224
- sig do
225
- overridable.params(
226
- response_builder: ResponseBuilders::Hover,
227
- node_context: NodeContext,
228
- dispatcher: Prism::Dispatcher,
229
- ).void
230
- end
212
+ # @overridable
213
+ #: (ResponseBuilders::Hover response_builder, NodeContext node_context, Prism::Dispatcher dispatcher) -> void
231
214
  def create_hover_listener(response_builder, node_context, dispatcher); end
232
215
 
233
216
  # Creates a new DocumentSymbol listener. This method is invoked on every DocumentSymbol request
234
- sig do
235
- overridable.params(
236
- response_builder: ResponseBuilders::DocumentSymbol,
237
- dispatcher: Prism::Dispatcher,
238
- ).void
239
- end
217
+ # @overridable
218
+ #: (ResponseBuilders::DocumentSymbol response_builder, Prism::Dispatcher dispatcher) -> void
240
219
  def create_document_symbol_listener(response_builder, dispatcher); end
241
220
 
242
- sig do
243
- overridable.params(
244
- response_builder: ResponseBuilders::SemanticHighlighting,
245
- dispatcher: Prism::Dispatcher,
246
- ).void
247
- end
221
+ # @overridable
222
+ #: (ResponseBuilders::SemanticHighlighting response_builder, Prism::Dispatcher dispatcher) -> void
248
223
  def create_semantic_highlighting_listener(response_builder, dispatcher); end
249
224
 
250
225
  # Creates a new Definition listener. This method is invoked on every Definition request
251
- sig do
252
- overridable.params(
253
- response_builder: ResponseBuilders::CollectionResponseBuilder[T.any(
254
- Interface::Location,
255
- Interface::LocationLink,
256
- )],
257
- uri: URI::Generic,
258
- node_context: NodeContext,
259
- dispatcher: Prism::Dispatcher,
260
- ).void
261
- end
226
+ # @overridable
227
+ #: (ResponseBuilders::CollectionResponseBuilder[(Interface::Location | Interface::LocationLink)] response_builder, URI::Generic uri, NodeContext node_context, Prism::Dispatcher dispatcher) -> void
262
228
  def create_definition_listener(response_builder, uri, node_context, dispatcher); end
263
229
 
264
230
  # Creates a new Completion listener. This method is invoked on every Completion request
265
- sig do
266
- overridable.params(
267
- response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem],
268
- node_context: NodeContext,
269
- dispatcher: Prism::Dispatcher,
270
- uri: URI::Generic,
271
- ).void
272
- end
231
+ # @overridable
232
+ #: (ResponseBuilders::CollectionResponseBuilder[Interface::CompletionItem] response_builder, NodeContext node_context, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
273
233
  def create_completion_listener(response_builder, node_context, dispatcher, uri); end
234
+
235
+ # Creates a new Discover Tests listener. This method is invoked on every DiscoverTests request
236
+ # @overridable
237
+ #: (ResponseBuilders::TestCollection response_builder, Prism::Dispatcher dispatcher, URI::Generic uri) -> void
238
+ def create_discover_tests_listener(response_builder, dispatcher, uri); end
239
+
240
+ # Resolves the minimal set of commands required to execute the requested tests. Add-ons are responsible for only
241
+ # handling items related to the framework they add support for and have discovered themselves
242
+ # @overridable
243
+ #: (Array[Hash[Symbol, untyped]]) -> Array[String]
244
+ def resolve_test_commands(items)
245
+ []
246
+ end
274
247
  end
275
248
  end