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
@@ -3,93 +3,88 @@
3
3
 
4
4
  module RubyLsp
5
5
  class GlobalState
6
- extend T::Sig
7
-
8
- sig { returns(String) }
6
+ #: String
9
7
  attr_reader :test_library
10
8
 
11
- sig { returns(String) }
9
+ #: String
12
10
  attr_accessor :formatter
13
11
 
14
- sig { returns(T::Boolean) }
12
+ #: bool
15
13
  attr_reader :has_type_checker
16
14
 
17
- sig { returns(RubyIndexer::Index) }
15
+ #: RubyIndexer::Index
18
16
  attr_reader :index
19
17
 
20
- sig { returns(Encoding) }
18
+ #: Encoding
21
19
  attr_reader :encoding
22
20
 
23
- sig { returns(T::Boolean) }
21
+ #: bool
24
22
  attr_reader :top_level_bundle
25
23
 
26
- sig { returns(TypeInferrer) }
24
+ #: TypeInferrer
27
25
  attr_reader :type_inferrer
28
26
 
29
- sig { returns(ClientCapabilities) }
27
+ #: ClientCapabilities
30
28
  attr_reader :client_capabilities
31
29
 
32
- sig { returns(URI::Generic) }
30
+ #: URI::Generic
33
31
  attr_reader :workspace_uri
34
32
 
35
- sig { returns(T.nilable(String)) }
33
+ #: String?
36
34
  attr_reader :telemetry_machine_id
37
35
 
38
- sig { void }
36
+ #: -> void
39
37
  def initialize
40
- @workspace_uri = T.let(URI::Generic.from_path(path: Dir.pwd), URI::Generic)
41
- @encoding = T.let(Encoding::UTF_8, Encoding)
42
-
43
- @formatter = T.let("auto", String)
44
- @linters = T.let([], T::Array[String])
45
- @test_library = T.let("minitest", String)
46
- @has_type_checker = T.let(true, T::Boolean)
47
- @index = T.let(RubyIndexer::Index.new, RubyIndexer::Index)
48
- @supported_formatters = T.let({}, T::Hash[String, Requests::Support::Formatter])
49
- @type_inferrer = T.let(TypeInferrer.new(@index), TypeInferrer)
50
- @addon_settings = T.let({}, T::Hash[String, T.untyped])
51
- @top_level_bundle = T.let(
52
- begin
53
- Bundler.with_original_env { Bundler.default_gemfile }
54
- true
55
- rescue Bundler::GemfileNotFound, Bundler::GitError
56
- false
57
- end,
58
- T::Boolean,
59
- )
60
- @client_capabilities = T.let(ClientCapabilities.new, ClientCapabilities)
61
- @enabled_feature_flags = T.let({}, T::Hash[Symbol, T::Boolean])
62
- @mutex = T.let(Mutex.new, Mutex)
63
- @telemetry_machine_id = T.let(nil, T.nilable(String))
38
+ @workspace_uri = URI::Generic.from_path(path: Dir.pwd) #: URI::Generic
39
+ @encoding = Encoding::UTF_8 #: Encoding
40
+
41
+ @formatter = "auto" #: String
42
+ @linters = [] #: Array[String]
43
+ @test_library = "minitest" #: String
44
+ @has_type_checker = true #: bool
45
+ @index = RubyIndexer::Index.new #: RubyIndexer::Index
46
+ @supported_formatters = {} #: Hash[String, Requests::Support::Formatter]
47
+ @type_inferrer = TypeInferrer.new(@index) #: TypeInferrer
48
+ @addon_settings = {} #: Hash[String, untyped]
49
+ @top_level_bundle = begin
50
+ Bundler.with_original_env { Bundler.default_gemfile }
51
+ true
52
+ rescue Bundler::GemfileNotFound, Bundler::GitError
53
+ false
54
+ end #: bool
55
+ @client_capabilities = ClientCapabilities.new #: ClientCapabilities
56
+ @enabled_feature_flags = {} #: Hash[Symbol, bool]
57
+ @mutex = Mutex.new #: Mutex
58
+ @telemetry_machine_id = nil #: String?
64
59
  end
65
60
 
66
- sig { type_parameters(:T).params(block: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T)) }
61
+ #: [T] { -> T } -> T
67
62
  def synchronize(&block)
68
63
  @mutex.synchronize(&block)
69
64
  end
70
65
 
71
- sig { params(addon_name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
66
+ #: (String addon_name) -> Hash[Symbol, untyped]?
72
67
  def settings_for_addon(addon_name)
73
68
  @addon_settings[addon_name]
74
69
  end
75
70
 
76
- sig { params(identifier: String, instance: Requests::Support::Formatter).void }
71
+ #: (String identifier, Requests::Support::Formatter instance) -> void
77
72
  def register_formatter(identifier, instance)
78
73
  @supported_formatters[identifier] = instance
79
74
  end
80
75
 
81
- sig { returns(T.nilable(Requests::Support::Formatter)) }
76
+ #: -> Requests::Support::Formatter?
82
77
  def active_formatter
83
78
  @supported_formatters[@formatter]
84
79
  end
85
80
 
86
- sig { returns(T::Array[Requests::Support::Formatter]) }
81
+ #: -> Array[Requests::Support::Formatter]
87
82
  def active_linters
88
83
  @linters.filter_map { |name| @supported_formatters[name] }
89
84
  end
90
85
 
91
86
  # Applies the options provided by the editor and returns an array of notifications to send back to the client
92
- sig { params(options: T::Hash[Symbol, T.untyped]).returns(T::Array[Notification]) }
87
+ #: (Hash[Symbol, untyped] options) -> Array[Notification]
93
88
  def apply_options(options)
94
89
  notifications = []
95
90
  direct_dependencies = gather_direct_dependencies
@@ -183,17 +178,17 @@ module RubyLsp
183
178
  notifications
184
179
  end
185
180
 
186
- sig { params(flag: Symbol).returns(T.nilable(T::Boolean)) }
181
+ #: (Symbol flag) -> bool?
187
182
  def enabled_feature?(flag)
188
183
  @enabled_feature_flags[:all] || @enabled_feature_flags[flag]
189
184
  end
190
185
 
191
- sig { returns(String) }
186
+ #: -> String
192
187
  def workspace_path
193
- T.must(@workspace_uri.to_standardized_path)
188
+ @workspace_uri.to_standardized_path #: as !nil
194
189
  end
195
190
 
196
- sig { returns(String) }
191
+ #: -> String
197
192
  def encoding_name
198
193
  case @encoding
199
194
  when Encoding::UTF_8
@@ -205,14 +200,14 @@ module RubyLsp
205
200
  end
206
201
  end
207
202
 
208
- sig { returns(T::Boolean) }
203
+ #: -> bool
209
204
  def supports_watching_files
210
205
  @client_capabilities.supports_watching_files
211
206
  end
212
207
 
213
208
  private
214
209
 
215
- sig { params(direct_dependencies: T::Array[String], all_dependencies: T::Array[String]).returns(String) }
210
+ #: (Array[String] direct_dependencies, Array[String] all_dependencies) -> String
216
211
  def detect_formatter(direct_dependencies, all_dependencies)
217
212
  # NOTE: Intentionally no $ at end, since we want to match rubocop-shopify, etc.
218
213
  return "rubocop_internal" if direct_dependencies.any?(/^rubocop/)
@@ -228,7 +223,7 @@ module RubyLsp
228
223
 
229
224
  # Try to detect if there are linters in the project's dependencies. For auto-detection, we always only consider a
230
225
  # single linter. To have multiple linters running, the user must configure them manually
231
- sig { params(dependencies: T::Array[String], all_dependencies: T::Array[String]).returns(T::Array[String]) }
226
+ #: (Array[String] dependencies, Array[String] all_dependencies) -> Array[String]
232
227
  def detect_linters(dependencies, all_dependencies)
233
228
  linters = []
234
229
 
@@ -239,7 +234,7 @@ module RubyLsp
239
234
  linters
240
235
  end
241
236
 
242
- sig { params(dependencies: T::Array[String]).returns(String) }
237
+ #: (Array[String] dependencies) -> String
243
238
  def detect_test_library(dependencies)
244
239
  if dependencies.any?(/^rspec/)
245
240
  "rspec"
@@ -259,7 +254,7 @@ module RubyLsp
259
254
  end
260
255
  end
261
256
 
262
- sig { params(dependencies: T::Array[String]).returns(T::Boolean) }
257
+ #: (Array[String] dependencies) -> bool
263
258
  def detect_typechecker(dependencies)
264
259
  return false if ENV["RUBY_LSP_BYPASS_TYPECHECKER"]
265
260
 
@@ -268,17 +263,17 @@ module RubyLsp
268
263
  false
269
264
  end
270
265
 
271
- sig { returns(T::Boolean) }
266
+ #: -> bool
272
267
  def bin_rails_present
273
268
  File.exist?(File.join(workspace_path, "bin/rails"))
274
269
  end
275
270
 
276
- sig { returns(T::Boolean) }
271
+ #: -> bool
277
272
  def dot_rubocop_yml_present
278
273
  File.exist?(File.join(workspace_path, ".rubocop.yml"))
279
274
  end
280
275
 
281
- sig { returns(T::Array[String]) }
276
+ #: -> Array[String]
282
277
  def gather_direct_dependencies
283
278
  Bundler.with_original_env { Bundler.default_gemfile }
284
279
 
@@ -288,14 +283,14 @@ module RubyLsp
288
283
  []
289
284
  end
290
285
 
291
- sig { returns(T::Array[String]) }
286
+ #: -> Array[String]
292
287
  def gemspec_dependencies
293
288
  (Bundler.locked_gems&.sources || [])
294
289
  .grep(Bundler::Source::Gemspec)
295
290
  .flat_map { _1.gemspec&.dependencies&.map(&:name) }
296
291
  end
297
292
 
298
- sig { returns(T::Array[String]) }
293
+ #: -> Array[String]
299
294
  def gather_direct_and_indirect_dependencies
300
295
  Bundler.with_original_env { Bundler.default_gemfile }
301
296
  Bundler.locked_gems&.specs&.map(&:name) || []
@@ -25,6 +25,7 @@ require "fileutils"
25
25
  require "open3"
26
26
  require "securerandom"
27
27
  require "shellwords"
28
+ require "set"
28
29
 
29
30
  require "ruby-lsp"
30
31
  require "ruby_lsp/base_server"
@@ -79,6 +80,7 @@ require "ruby_lsp/requests/document_link"
79
80
  require "ruby_lsp/requests/document_symbol"
80
81
  require "ruby_lsp/requests/folding_ranges"
81
82
  require "ruby_lsp/requests/formatting"
83
+ require "ruby_lsp/requests/go_to_relevant_file"
82
84
  require "ruby_lsp/requests/hover"
83
85
  require "ruby_lsp/requests/inlay_hints"
84
86
  require "ruby_lsp/requests/on_type_formatting"
@@ -6,45 +6,32 @@ require "shellwords"
6
6
  module RubyLsp
7
7
  module Listeners
8
8
  class CodeLens
9
- extend T::Sig
10
9
  include Requests::Support::Common
11
10
 
12
- BASE_COMMAND = T.let(
13
- begin
14
- Bundler.with_original_env { Bundler.default_lockfile }
15
- "bundle exec ruby"
16
- rescue Bundler::GemfileNotFound
17
- "ruby"
18
- end,
19
- String,
20
- )
21
- ACCESS_MODIFIERS = T.let([:public, :private, :protected], T::Array[Symbol])
22
- SUPPORTED_TEST_LIBRARIES = T.let(["minitest", "test-unit"], T::Array[String])
23
- DESCRIBE_KEYWORD = T.let(:describe, Symbol)
24
- IT_KEYWORD = T.let(:it, Symbol)
25
- DYNAMIC_REFERENCE_MARKER = T.let("<dynamic_reference>", String)
26
-
27
- sig do
28
- params(
29
- response_builder: ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens],
30
- global_state: GlobalState,
31
- uri: URI::Generic,
32
- dispatcher: Prism::Dispatcher,
33
- ).void
34
- end
11
+ BASE_COMMAND = begin
12
+ Bundler.with_original_env { Bundler.default_lockfile }
13
+ "bundle exec ruby"
14
+ rescue Bundler::GemfileNotFound
15
+ "ruby"
16
+ end #: String
17
+ ACCESS_MODIFIERS = [:public, :private, :protected] #: Array[Symbol]
18
+ SUPPORTED_TEST_LIBRARIES = ["minitest", "test-unit"] #: Array[String]
19
+ DYNAMIC_REFERENCE_MARKER = "<dynamic_reference>" #: String
20
+
21
+ #: (ResponseBuilders::CollectionResponseBuilder[Interface::CodeLens] response_builder, GlobalState global_state, URI::Generic uri, Prism::Dispatcher dispatcher) -> void
35
22
  def initialize(response_builder, global_state, uri, dispatcher)
36
23
  @response_builder = response_builder
37
24
  @global_state = global_state
38
- @uri = T.let(uri, URI::Generic)
39
- @path = T.let(uri.to_standardized_path, T.nilable(String))
25
+ @uri = uri #: URI::Generic
26
+ @path = uri.to_standardized_path #: String?
40
27
  # visibility_stack is a stack of [current_visibility, previous_visibility]
41
- @visibility_stack = T.let([[:public, :public]], T::Array[T::Array[T.nilable(Symbol)]])
42
- @group_stack = T.let([], T::Array[String])
43
- @group_id = T.let(1, Integer)
44
- @group_id_stack = T.let([], T::Array[Integer])
28
+ @visibility_stack = [[:public, :public]] #: Array[Array[Symbol?]]
29
+ @group_stack = [] #: Array[String]
30
+ @group_id = 1 #: Integer
31
+ @group_id_stack = [] #: Array[Integer]
45
32
  # We want to avoid adding code lenses for nested definitions
46
- @def_depth = T.let(0, Integer)
47
- @spec_id = T.let(0, Integer)
33
+ @def_depth = 0 #: Integer
34
+ @spec_id = 0 #: Integer
48
35
 
49
36
  dispatcher.register(
50
37
  self,
@@ -59,7 +46,7 @@ module RubyLsp
59
46
  )
60
47
  end
61
48
 
62
- sig { params(node: Prism::ClassNode).void }
49
+ #: (Prism::ClassNode node) -> void
63
50
  def on_class_node_enter(node)
64
51
  @visibility_stack.push([:public, :public])
65
52
  class_name = node.constant_path.slice
@@ -79,7 +66,7 @@ module RubyLsp
79
66
  end
80
67
  end
81
68
 
82
- sig { params(node: Prism::ClassNode).void }
69
+ #: (Prism::ClassNode node) -> void
83
70
  def on_class_node_leave(node)
84
71
  @visibility_stack.pop
85
72
  @group_stack.pop
@@ -91,7 +78,7 @@ module RubyLsp
91
78
  end
92
79
  end
93
80
 
94
- sig { params(node: Prism::DefNode).void }
81
+ #: (Prism::DefNode node) -> void
95
82
  def on_def_node_enter(node)
96
83
  @def_depth += 1
97
84
  return if @def_depth > 1
@@ -114,12 +101,12 @@ module RubyLsp
114
101
  end
115
102
  end
116
103
 
117
- sig { params(node: Prism::DefNode).void }
104
+ #: (Prism::DefNode node) -> void
118
105
  def on_def_node_leave(node)
119
106
  @def_depth -= 1
120
107
  end
121
108
 
122
- sig { params(node: Prism::ModuleNode).void }
109
+ #: (Prism::ModuleNode node) -> void
123
110
  def on_module_node_enter(node)
124
111
  if (path = namespace_constant_name(node))
125
112
  @group_stack.push(path)
@@ -128,12 +115,12 @@ module RubyLsp
128
115
  end
129
116
  end
130
117
 
131
- sig { params(node: Prism::ModuleNode).void }
118
+ #: (Prism::ModuleNode node) -> void
132
119
  def on_module_node_leave(node)
133
120
  @group_stack.pop
134
121
  end
135
122
 
136
- sig { params(node: Prism::CallNode).void }
123
+ #: (Prism::CallNode node) -> void
137
124
  def on_call_node_enter(node)
138
125
  name = node.name
139
126
  arguments = node.arguments
@@ -151,23 +138,21 @@ module RubyLsp
151
138
  return
152
139
  end
153
140
 
154
- if [DESCRIBE_KEYWORD, IT_KEYWORD].include?(name)
155
- case name
156
- when DESCRIBE_KEYWORD
157
- add_spec_code_lens(node, kind: :group)
158
- @group_id_stack.push(@group_id)
159
- @group_id += 1
160
- when IT_KEYWORD
161
- add_spec_code_lens(node, kind: :example)
162
- end
141
+ case name
142
+ when :describe
143
+ add_spec_code_lens(node, kind: :group)
144
+ @group_id_stack.push(@group_id)
145
+ @group_id += 1
146
+ when :it, :specify # `specify` is an alias for `it`
147
+ add_spec_code_lens(node, kind: :example)
163
148
  end
164
149
  end
165
150
 
166
- sig { params(node: Prism::CallNode).void }
151
+ #: (Prism::CallNode node) -> void
167
152
  def on_call_node_leave(node)
168
153
  _, prev_visibility = @visibility_stack.pop
169
154
  @visibility_stack.push([prev_visibility, prev_visibility])
170
- if node.name == DESCRIBE_KEYWORD
155
+ if node.name == :describe
171
156
  @group_id_stack.pop
172
157
  @group_stack.pop
173
158
  end
@@ -175,7 +160,7 @@ module RubyLsp
175
160
 
176
161
  private
177
162
 
178
- sig { params(node: Prism::Node, name: String, command: String, kind: Symbol, id: String).void }
163
+ #: (Prism::Node node, name: String, command: String, kind: Symbol, ?id: String) -> void
179
164
  def add_test_code_lens(node, name:, command:, kind:, id: name)
180
165
  # don't add code lenses if the test library is not supported or unknown
181
166
  return unless SUPPORTED_TEST_LIBRARIES.include?(@global_state.test_library) && @path
@@ -221,15 +206,9 @@ module RubyLsp
221
206
  )
222
207
  end
223
208
 
224
- sig do
225
- params(
226
- group_stack: T::Array[String],
227
- spec_name: T.nilable(String),
228
- method_name: T.nilable(String),
229
- ).returns(String)
230
- end
209
+ #: (?group_stack: Array[String], ?spec_name: String?, ?method_name: String?) -> String
231
210
  def generate_test_command(group_stack: [], spec_name: nil, method_name: nil)
232
- path = T.must(@path)
211
+ path = @path #: as !nil
233
212
  command = BASE_COMMAND
234
213
  command += " -Itest" if File.fnmatch?("**/test/**/*", path, File::FNM_PATHNAME)
235
214
  command += " -Ispec" if File.fnmatch?("**/spec/**/*", path, File::FNM_PATHNAME)
@@ -237,44 +216,58 @@ module RubyLsp
237
216
 
238
217
  case @global_state.test_library
239
218
  when "minitest"
240
- last_dynamic_reference_index = group_stack.rindex(DYNAMIC_REFERENCE_MARKER)
241
- command += if last_dynamic_reference_index
242
- # In cases where the test path looks like `foo::Bar`
243
- # the best we can do is match everything to the right of it.
244
- # Tests are classes, dynamic references are only a thing for modules,
245
- # so there must be something to the left of the available path.
246
- group_stack = T.must(group_stack[last_dynamic_reference_index + 1..])
247
- if method_name
248
- " --name " + "/::#{Shellwords.escape(group_stack.join("::")) + "#" + Shellwords.escape(method_name)}$/"
249
- else
250
- # When clicking on a CodeLens for `Test`, `(#|::)` will match all tests
251
- # that are registered on the class itself (matches after `#`) and all tests
252
- # that are nested inside of that class in other modules/classes (matches after `::`)
253
- " --name " + "\"/::#{Shellwords.escape(group_stack.join("::"))}(#|::)/\""
254
- end
255
- elsif method_name
256
- # We know the entire path, do an exact match
257
- " --name " + Shellwords.escape(group_stack.join("::")) + "#" + Shellwords.escape(method_name)
258
- elsif spec_name
259
- " --name " + "\"/^#{Shellwords.escape(group_stack.join("::"))}##{Shellwords.escape(spec_name)}$/\""
260
- else
261
- # Execute all tests of the selected class and tests in
262
- # modules/classes nested inside of that class
263
- " --name " + "\"/^#{Shellwords.escape(group_stack.join("::"))}(#|::)/\""
264
- end
219
+ command += generate_minitest_command(group_stack, method_name, spec_name)
265
220
  when "test-unit"
266
- group_name = T.must(group_stack.last)
267
- command += " --testcase " + "/#{Shellwords.escape(group_name)}/"
221
+ command += generate_test_unit_command(group_stack, method_name)
222
+ end
223
+
224
+ command
225
+ end
226
+
227
+ #: (Array[String] group_stack, String? method_name, String? spec_name) -> String
228
+ def generate_minitest_command(group_stack, method_name, spec_name)
229
+ last_dynamic_reference_index = group_stack.rindex(DYNAMIC_REFERENCE_MARKER)
230
+
231
+ if last_dynamic_reference_index
232
+ # In cases where the test path looks like `foo::Bar`
233
+ # the best we can do is match everything to the right of it.
234
+ # Tests are classes, dynamic references are only a thing for modules,
235
+ # so there must be something to the left of the available path.
236
+ dynamic_stack = group_stack[last_dynamic_reference_index + 1..] #: as !nil
268
237
 
269
238
  if method_name
270
- command += " --name " + Shellwords.escape(method_name)
239
+ " --name " + "/::#{Shellwords.escape(dynamic_stack.join("::")) + "#" + Shellwords.escape(method_name)}$/"
240
+ else
241
+ # When clicking on a CodeLens for `Test`, `(#|::)` will match all tests
242
+ # that are registered on the class itself (matches after `#`) and all tests
243
+ # that are nested inside of that class in other modules/classes (matches after `::`)
244
+ " --name " + "\"/::#{Shellwords.escape(dynamic_stack.join("::"))}(#|::)/\""
271
245
  end
246
+ elsif method_name
247
+ # We know the entire path, do an exact match
248
+ " --name " + Shellwords.escape(group_stack.join("::")) + "#" + Shellwords.escape(method_name)
249
+ elsif spec_name
250
+ " --name " + "\"/^#{Shellwords.escape(group_stack.join("::"))}##{Shellwords.escape(spec_name)}$/\""
251
+ else
252
+ # Execute all tests of the selected class and tests in
253
+ # modules/classes nested inside of that class
254
+ " --name " + "\"/^#{Shellwords.escape(group_stack.join("::"))}(#|::)/\""
255
+ end
256
+ end
257
+
258
+ #: (Array[String] group_stack, String? method_name) -> String
259
+ def generate_test_unit_command(group_stack, method_name)
260
+ group_name = group_stack.last #: as !nil
261
+ command = " --testcase " + "/#{Shellwords.escape(group_name)}/"
262
+
263
+ if method_name
264
+ command += " --name " + Shellwords.escape(method_name)
272
265
  end
273
266
 
274
267
  command
275
268
  end
276
269
 
277
- sig { params(node: Prism::CallNode, kind: Symbol).void }
270
+ #: (Prism::CallNode node, kind: Symbol) -> void
278
271
  def add_spec_code_lens(node, kind:)
279
272
  arguments = node.arguments
280
273
  return unless arguments
@@ -312,7 +305,7 @@ module RubyLsp
312
305
  end
313
306
  end
314
307
 
315
- sig { params(group_stack: T::Array[String], method_name: T.nilable(String)).returns(String) }
308
+ #: (group_stack: Array[String], ?method_name: String?) -> String
316
309
  def generate_fully_qualified_id(group_stack:, method_name: nil)
317
310
  if method_name
318
311
  # For tests, this will be the test class and method name: `Foo::BarTest#test_baz`.